objective categorical abstract machine language personal data server
at main 14 kB view raw
1open Pegasus 2open Dream 3 4let () = 5 Rate_limiter.Shared.register ~name:"repo-write-hour" ~duration_ms:Util.hour 6 ~points:5000 ; 7 Rate_limiter.Shared.register ~name:"repo-write-day" ~duration_ms:Util.day 8 ~points:35000 9 10let handlers = 11 [ (* meta *) 12 (get, "/", Api.Root.handler) 13 ; (get, "/robots.txt", Api.Robots.handler) 14 ; (get, "/xrpc/_health", Api.Health.handler) 15 ; (get, "/.well-known/did.json", Api.Well_known.did_json) 16 ; ( get 17 , "/.well-known/oauth-protected-resource" 18 , Api.Well_known.oauth_protected_resource ) 19 ; ( get 20 , "/.well-known/oauth-authorization-server" 21 , Api.Well_known.oauth_authorization_server ) 22 ; (get, "/.well-known/atproto-did", Api.Well_known.atproto_did) 23 ; (options, "/xrpc/**", Xrpc.handler (fun _ -> Dream.empty `No_Content)) 24 ; (* oauth *) 25 (options, "/oauth/par", Xrpc.handler (fun _ -> Dream.empty `No_Content)) 26 ; (post, "/oauth/par", Api.Oauth_.Par.post_handler) 27 ; (get, "/oauth/authorize", Api.Oauth_.Authorize.get_handler) 28 ; (post, "/oauth/authorize", Api.Oauth_.Authorize.post_handler) 29 ; (options, "/oauth/token", Xrpc.handler (fun _ -> Dream.empty `No_Content)) 30 ; (post, "/oauth/token", Api.Oauth_.Token.post_handler) 31 ; (* account ui *) 32 (get, "/account", Api.Account_.Index.get_handler) 33 ; (post, "/account", Api.Account_.Index.post_handler) 34 ; (get, "/account/security", Api.Account_.Security.Index.get_handler) 35 ; (post, "/account/security", Api.Account_.Security.Index.post_handler) 36 ; ( get 37 , "/account/security/backup-codes" 38 , Api.Account_.Security.Backup_codes.count_handler ) 39 ; ( post 40 , "/account/security/backup-codes/regenerate" 41 , Api.Account_.Security.Backup_codes.regenerate_handler ) 42 ; ( get 43 , "/account/security/totp/setup" 44 , Api.Account_.Security.Totp.setup_handler ) 45 ; ( post 46 , "/account/security/totp/verify" 47 , Api.Account_.Security.Totp.verify_handler ) 48 ; ( post 49 , "/account/security/totp/disable" 50 , Api.Account_.Security.Totp.disable_handler ) 51 ; ( get 52 , "/account/security/keys" 53 , Api.Account_.Security.Security_key.list_handler ) 54 ; ( post 55 , "/account/security/keys/setup" 56 , Api.Account_.Security.Security_key.setup_handler ) 57 ; ( post 58 , "/account/security/keys/:id/verify" 59 , Api.Account_.Security.Security_key.verify_handler ) 60 ; ( post 61 , "/account/security/keys/:id/resync" 62 , Api.Account_.Security.Security_key.resync_handler ) 63 ; ( delete 64 , "/account/security/keys/:id" 65 , Api.Account_.Security.Security_key.delete_handler ) 66 ; (get, "/account/permissions", Api.Account_.Permissions.get_handler) 67 ; (post, "/account/permissions", Api.Account_.Permissions.post_handler) 68 ; (get, "/account/identity", Api.Account_.Identity.get_handler) 69 ; (post, "/account/identity", Api.Account_.Identity.post_handler) 70 ; (get, "/account/login", Api.Account_.Login.get_handler) 71 ; (post, "/account/login", Api.Account_.Login.post_handler) 72 ; (get, "/account/signup", Api.Account_.Signup.get_handler) 73 ; (post, "/account/signup", Api.Account_.Signup.post_handler) 74 ; ( get 75 , "/account/signup/check-handle" 76 , Api.Account_.Signup.check_handle_handler ) 77 ; (get, "/account/migrate", Api.Account_.Migrate.get_handler) 78 ; (post, "/account/migrate", Api.Account_.Migrate.post_handler) 79 ; (post, "/account/switch", Api.Account_.Login.switch_account_handler) 80 ; (get, "/account/logout", Api.Account_.Logout.handler) 81 ; (get, "/account/reset", Api.Account_.Password_reset.get_handler) 82 ; (post, "/account/reset", Api.Account_.Password_reset.post_handler) 83 ; (* passkey management (authed) *) 84 (get, "/account/passkeys", Api.Account_.Passkeys.list_handler) 85 ; ( get 86 , "/account/passkeys/register/options" 87 , Api.Account_.Passkeys.register_options_handler ) 88 ; ( post 89 , "/account/passkeys/register/verify" 90 , Api.Account_.Passkeys.register_verify_handler ) 91 ; (delete, "/account/passkeys/:id", Api.Account_.Passkeys.delete_handler) 92 ; (post, "/account/passkeys/:id/rename", Api.Account_.Passkeys.rename_handler) 93 ; (* passkey login (unauthed) *) 94 ( get 95 , "/account/passkeys/login/options" 96 , Api.Account_.Passkeys.login_options_handler ) 97 ; ( post 98 , "/account/passkeys/login/verify" 99 , Api.Account_.Passkeys.login_verify_handler ) 100 ; (* admin ui *) 101 (get, "/admin", Api.Admin_.Index.handler) 102 ; (get, "/admin/login", Api.Admin_.Login.get_handler) 103 ; (post, "/admin/login", Api.Admin_.Login.post_handler) 104 ; (get, "/admin/users", Api.Admin_.Users.get_handler) 105 ; (post, "/admin/users", Api.Admin_.Users.post_handler) 106 ; (get, "/admin/invites", Api.Admin_.Invites.get_handler) 107 ; (post, "/admin/invites", Api.Admin_.Invites.post_handler) 108 ; (get, "/admin/blobs", Api.Admin_.Blobs.get_handler) 109 ; (post, "/admin/blobs", Api.Admin_.Blobs.post_handler) 110 ; (get, "/admin/blobs/view", Api.Admin_.Blobs.view_handler) 111 ; (* unauthed *) 112 ( get 113 , "/xrpc/com.atproto.server.describeServer" 114 , Api.Server.DescribeServer.handler ) 115 ; (get, "/xrpc/com.atproto.repo.describeRepo", Api.Repo.DescribeRepo.handler) 116 ; ( get 117 , "/xrpc/com.atproto.identity.resolveHandle" 118 , Api.Identity.ResolveHandle.handler ) 119 ; (* admin *) 120 ( post 121 , "/xrpc/com.atproto.admin.deleteAccount" 122 , Api.Admin.DeleteAccount.handler ) 123 ; ( get 124 , "/xrpc/com.atproto.admin.getAccountInfo" 125 , Api.Admin.GetAccountInfo.handler ) 126 ; ( get 127 , "/xrpc/com.atproto.admin.getAccountInfos" 128 , Api.Admin.GetAccountInfos.handler ) 129 ; ( get 130 , "/xrpc/com.atproto.admin.getInviteCodes" 131 , Api.Admin.GetInviteCodes.handler ) 132 ; (post, "/xrpc/com.atproto.admin.sendEmail", Api.Admin.SendEmail.handler) 133 ; ( post 134 , "/xrpc/com.atproto.admin.updateAccountEmail" 135 , Api.Admin.UpdateAccountEmail.handler ) 136 ; ( post 137 , "/xrpc/com.atproto.admin.updateAccountHandle" 138 , Api.Admin.UpdateAccountHandle.handler ) 139 ; (* account management *) 140 ( post 141 , "/xrpc/com.atproto.server.createInviteCode" 142 , Api.Server.CreateInviteCode.handler ) 143 ; ( post 144 , "/xrpc/com.atproto.server.createInviteCodes" 145 , Api.Server.CreateInviteCodes.handler ) 146 ; ( post 147 , "/xrpc/com.atproto.server.createAccount" 148 , Api.Server.CreateAccount.handler ) 149 ; ( post 150 , "/xrpc/com.atproto.server.createSession" 151 , Api.Server.CreateSession.handler ) 152 ; (get, "/xrpc/com.atproto.server.getSession", Api.Server.GetSession.handler) 153 ; ( post 154 , "/xrpc/com.atproto.server.refreshSession" 155 , Api.Server.RefreshSession.handler ) 156 ; ( post 157 , "/xrpc/com.atproto.server.deleteSession" 158 , Api.Server.DeleteSession.handler ) 159 ; ( get 160 , "/xrpc/com.atproto.server.getServiceAuth" 161 , Api.Server.GetServiceAuth.handler ) 162 ; ( get 163 , "/xrpc/com.atproto.server.checkAccountStatus" 164 , Api.Server.CheckAccountStatus.handler ) 165 ; ( post 166 , "/xrpc/com.atproto.server.activateAccount" 167 , Api.Server.ActivateAccount.handler ) 168 ; ( post 169 , "/xrpc/com.atproto.server.requestEmailConfirmation" 170 , Api.Server.RequestEmailConfirmation.handler ) 171 ; ( post 172 , "/xrpc/com.atproto.server.requestEmailUpdate" 173 , Api.Server.RequestEmailUpdate.handler ) 174 ; ( post 175 , "/xrpc/com.atproto.server.confirmEmail" 176 , Api.Server.ConfirmEmail.handler ) 177 ; ( post 178 , "/xrpc/com.atproto.server.requestPasswordReset" 179 , Api.Server.RequestPasswordReset.handler ) 180 ; ( post 181 , "/xrpc/com.atproto.server.resetPassword" 182 , Api.Server.ResetPassword.handler ) 183 ; ( post 184 , "/xrpc/com.atproto.server.reserveSigningKey" 185 , Api.Server.ReserveSigningKey.handler ) 186 ; ( post 187 , "/xrpc/com.atproto.server.requestAccountDelete" 188 , Api.Server.RequestAccountDelete.handler ) 189 ; ( post 190 , "/xrpc/com.atproto.server.deleteAccount" 191 , Api.Server.DeleteAccount.handler ) 192 ; ( post 193 , "/xrpc/com.atproto.server.deactivateAccount" 194 , Api.Server.DeactivateAccount.handler ) 195 ; ( get 196 , "/xrpc/com.atproto.repo.listMissingBlobs" 197 , Api.Repo.ListMissingBlobs.handler ) 198 ; ( post 199 , "/xrpc/com.atproto.identity.updateHandle" 200 , Api.Identity.UpdateHandle.handler ) 201 ; ( post 202 , "/xrpc/com.atproto.server.updateEmail" 203 , Api.Server.UpdateEmail.handler ) 204 ; (* plc *) 205 ( get 206 , "/xrpc/com.atproto.identity.getRecommendedDidCredentials" 207 , Api.Identity.GetRecommendedDidCredentials.handler ) 208 ; ( post 209 , "/xrpc/com.atproto.identity.requestPlcOperationSignature" 210 , Api.Identity.RequestPlcOperationSignature.handler ) 211 ; ( post 212 , "/xrpc/com.atproto.identity.signPlcOperation" 213 , Api.Identity.SignPlcOperation.handler ) 214 ; ( post 215 , "/xrpc/com.atproto.identity.submitPlcOperation" 216 , Api.Identity.SubmitPlcOperation.handler ) 217 ; (* repo *) 218 (post, "/xrpc/com.atproto.repo.applyWrites", Api.Repo.ApplyWrites.handler) 219 ; (post, "/xrpc/com.atproto.repo.createRecord", Api.Repo.CreateRecord.handler) 220 ; (post, "/xrpc/com.atproto.repo.putRecord", Api.Repo.PutRecord.handler) 221 ; (get, "/xrpc/com.atproto.repo.getRecord", Api.Repo.GetRecord.handler) 222 ; (get, "/xrpc/com.atproto.repo.listRecords", Api.Repo.ListRecords.handler) 223 ; (post, "/xrpc/com.atproto.repo.deleteRecord", Api.Repo.DeleteRecord.handler) 224 ; (post, "/xrpc/com.atproto.repo.uploadBlob", Api.Repo.UploadBlob.handler) 225 ; (post, "/xrpc/com.atproto.repo.importRepo", Api.Repo.ImportRepo.handler) 226 ; (* sync *) 227 (get, "/xrpc/com.atproto.sync.getRepo", Api.Sync.GetRepo.handler) 228 ; (get, "/xrpc/com.atproto.sync.getRepoStatus", Api.Sync.GetRepoStatus.handler) 229 ; ( get 230 , "/xrpc/com.atproto.sync.getLatestCommit" 231 , Api.Sync.GetLatestCommit.handler ) 232 ; (get, "/xrpc/com.atproto.sync.listRepos", Api.Sync.ListRepos.handler) 233 ; (get, "/xrpc/com.atproto.sync.getRecord", Api.Sync.GetRecord.handler) 234 ; (get, "/xrpc/com.atproto.sync.getBlocks", Api.Sync.GetBlocks.handler) 235 ; (get, "/xrpc/com.atproto.sync.getBlob", Api.Sync.GetBlob.handler) 236 ; (get, "/xrpc/com.atproto.sync.listBlobs", Api.Sync.ListBlobs.handler) 237 ; ( get 238 , "/xrpc/com.atproto.sync.subscribeRepos" 239 , Api.Sync.SubscribeRepos.handler ) 240 ; (* misc *) 241 ( get 242 , "/xrpc/app.bsky.actor.getPreferences" 243 , Api.Proxy.AppBskyActorGetPreferences.handler ) 244 ; ( post 245 , "/xrpc/app.bsky.actor.putPreferences" 246 , Api.Proxy.AppBskyActorPutPreferences.handler ) 247 ; (get, "/xrpc/app.bsky.feed.getFeed", Api.Proxy.AppBskyFeedGetFeed.handler) 248 ] 249 250let public_loader _root path _request = 251 match Public.read path with 252 | None -> 253 Dream.empty `Not_Found 254 | Some asset -> 255 Dream.respond 256 ~headers:[("Cache-Control", "public, max-age=31536000")] 257 asset 258 259let static_routes = 260 [Dream.get "/public/**" (Dream.static ~loader:public_loader "")] 261 262let serve () = 263 Printexc.record_backtrace true ; 264 Printexc.register_printer Errors.printer ; 265 Dream.initialize_log ~level:Env.log_level () ; 266 List.iter (fun src -> 267 match Logs.Src.name src with 268 (* useless noise on debug level *) 269 | "cohttp.lwt.io" | "cohttp.lwt.server" | "tls.tracing" | "tls.config" -> 270 Logs.Src.set_level src None 271 | _ -> 272 () ) 273 @@ Logs.Src.list () ; 274 let%lwt db = Data_store.connect ~create:true () in 275 S3.Backup.start () ; 276 Dream.serve ~interface:"0.0.0.0" ~port:8008 277 @@ Dream.pipeline 278 [ Dream.logger 279 ; Dream.set_secret (Env.jwt_key |> Kleidos.privkey_to_multikey) 280 ; Dream.cookie_sessions 281 ; Xrpc.dpop_middleware 282 ; Xrpc.cors_middleware ] 283 @@ Dream.router 284 @@ List.map 285 (fun (fn, path, handler) -> 286 fn path (fun req -> handler ({req; db} : Xrpc.init)) ) 287 handlers 288 @ static_routes 289 @ [ Dream.get "/xrpc/**" (Xrpc.service_proxy_handler db) 290 ; Dream.post "/xrpc/**" (Xrpc.service_proxy_handler db) ] 291 292let create_invite ?(uses = 1) () = 293 let%lwt db = Data_store.connect ~create:true () in 294 let%lwt code = 295 Api.Server.CreateInviteCode.create_invite_code ~db ~did:"admin" 296 ~use_count:uses 297 in 298 print_endline 299 ("invite code created with " ^ string_of_int uses ^ " use(s): " ^ code) 300 |> Lwt.return 301 302let migrate_blobs ?did () = 303 match did with 304 | Some did -> 305 print_endline ("migrating blobs for user " ^ did) ; 306 let%lwt _ = S3.Blob_migration.migrate_user ~did in 307 Lwt.return_unit 308 | None -> 309 print_endline "migrating all blobs to S3" ; 310 S3.Blob_migration.migrate_all () 311 312let rebuild_mst ~did () = 313 print_endline ("rebuilding MST for " ^ did) ; 314 let%lwt repo = Repository.load did in 315 match%lwt Repository.rebuild_mst repo with 316 | Ok (commit_cid, commit) -> 317 print_endline 318 (Printf.sprintf "MST rebuilt successfully, new commit: %s (rev: %s)" 319 (Cid.to_string commit_cid) commit.rev ) ; 320 Lwt.return_unit 321 | Error exn -> 322 print_endline ("error rebuilding MST: " ^ Printexc.to_string exn) ; 323 exit 1 324 325let print_usage () = 326 print_endline 327 @@ String.trim 328 {| 329usage: pegasus [command] 330 331commands: 332 serve start the PDS 333 create-invite [uses] create an invite code with an optional number of uses (default: 1) 334 migrate-blobs migrate all local blobs to S3 335 migrate-blobs <did> migrate blobs for a specific user to S3 336 rebuild-mst <did> rebuild MST from records table (recovery tool) 337 338see also: gen-keys 339|} 340 341let () = 342 let args = Array.to_list Sys.argv |> List.tl in 343 match args with 344 | [] | ["serve"] -> 345 Lwt_main.run (serve ()) 346 | ["create-invite"] -> 347 Lwt_main.run (create_invite ()) 348 | ["create-invite"; uses] -> 349 let uses = int_of_string uses in 350 Lwt_main.run (create_invite ~uses ()) 351 | ["migrate-blobs"] -> 352 Lwt_main.run (migrate_blobs ()) 353 | ["migrate-blobs"; did] -> 354 Lwt_main.run (migrate_blobs ~did ()) 355 | ["rebuild-mst"; did] -> 356 Lwt_main.run (rebuild_mst ~did ()) 357 | ["help"] | ["--help"] | ["-h"] -> 358 print_usage () 359 | cmd :: _ -> 360 print_endline ("unknown command: " ^ cmd) ; 361 print_usage () ; 362 exit 1