+58
.github/workflows/docker-image.yml
+58
.github/workflows/docker-image.yml
···+# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.+# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.+# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.+# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.+# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.+# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
+25
Dockerfile
+25
Dockerfile
···+RUN apt-get update && apt-get install -y dumb-init runit ca-certificates && rm -rf /var/lib/apt/lists/*
+4
Makefile
+4
Makefile
+63
-59
README.md
+63
-59
README.md
···Cocoon is a PDS implementation in Go. It is highly experimental, and is not ready for any production use.-Just because something is implemented doesn't mean it is finisehd. Tons of these are returning bad errors, don't do validation properly, etc. I'll make a "second pass" checklist at some point to do all of that.+Just because something is implemented doesn't mean it is finished. Tons of these are returning bad errors, don't do validation properly, etc. I'll make a "second pass" checklist at some point to do all of that.+- [x] `com.atproto.repo.importRepo` (Works "okay". You still have to handle PLC operations on your own when migrating. Use with extreme caution.)+- [x] `com.atproto.moderation.createReport` (Note: this should be handled by proxying, not actually implemented in the PDS)
-163
blockstore/blockstore.go
-163
blockstore/blockstore.go
···-if err := bs.db.Raw("SELECT * FROM blocks WHERE did = ? AND cid = ?", nil, bs.did, cid.Bytes()).Scan(&block).Error; err != nil {-if err := bs.db.Exec("UPDATE repos SET root = ?, rev = ? WHERE did = ?", nil, root.Bytes(), rev, bs.did).Error; err != nil {
-186
cmd/admin/main.go
-186
cmd/admin/main.go
···-if err := db.Exec("INSERT INTO invite_codes (did, code, remaining_use_count) VALUES (?, ?, ?)", forDid, code, uses).Error; err != nil {-if err := db.Exec("UPDATE repos SET password = ? WHERE did = ?", hashed, did.String()).Error; err != nil {
+193
-9
cmd/cocoon/main.go
+193
-9
cmd/cocoon/main.go
··················+if err := db.Exec("INSERT INTO invite_codes (did, code, remaining_use_count) VALUES (?, ?, ?)", forDid, code, uses).Error; err != nil {+if err := db.Exec("UPDATE repos SET password = ? WHERE did = ?", hashed, did.String()).Error; err != nil {
+45
cspell.json
+45
cspell.json
···
+3
-2
go.mod
+3
-2
go.mod
·········
+4
-4
go.sum
+4
-4
go.sum
···github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=-github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b h1:elwfbe+W7GkUmPKFX1h7HaeHvC/kC0XJWfiEHC62xPg=-github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b/go.mod h1:yjdhLA1LkK8VDS/WPUoYPo25/Hq/8rX38Ftr67EsqKY=+github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe h1:VBhaqE5ewQgXbY5SfSWFZC/AwHFo7cHxZKFYi2ce9Yo=+github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe/go.mod h1:RuQVrCGm42QNsgumKaR6se+XkFKfCPNwdCiTvqKRUck=github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=···github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=+github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=+github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=···github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=github.com/ipfs/go-blockservice v0.5.2/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk=-github.com/ipfs/go-bs-sqlite3 v0.0.0-20221122195556-bfcee1be620d h1:9V+GGXCuOfDiFpdAHz58q9mKLg447xp0cQKvqQrAwYE=-github.com/ipfs/go-bs-sqlite3 v0.0.0-20221122195556-bfcee1be620d/go.mod h1:pMbnFyNAGjryYCLCe59YDLRv/ujdN+zGJBT1umlvYRM=
+74
-55
identity/identity.go
+74
-55
identity/identity.go
···+func ResolveHandleFromWellKnown(ctx context.Context, cli *http.Client, handle string) (string, error) {+return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode)············
+15
-5
identity/passport.go
+15
-5
identity/passport.go
···············
+13
internal/helpers/helpers.go
+13
internal/helpers/helpers.go
······
+17
-2
models/models.go
+17
-2
models/models.go
············
+8
oauth/client/client.go
+8
oauth/client/client.go
+397
oauth/client/manager.go
+397
oauth/client/manager.go
···+jwksCache := cache.NewCache[string, jwk.Key]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)+metadataCache := cache.NewCache[string, Metadata]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)+// TODO: this is kinda bad but whatever for now. there could obviously be more than one jwk, and we need to+func (cm *Manager) getClientMetadata(ctx context.Context, clientId string) (*Metadata, error) {+func (cm *Manager) getClientJwks(ctx context.Context, clientId, jwksUri string) (jwk.Key, error) {+return nil, errors.New("token_endpoint_auth_method `none` must not have token_endpoint_auth_signing_alg")+return nil, fmt.Errorf("unsupported client authentication method `%s`", metadata.TokenEndpointAuthMethod)+return nil, errors.New("the `code` response type requires that `grant_types` contains `authorization_code`")+return nil, errors.New("private-use uri scheme redirect uris are only allowed for native apps")+if strings.HasPrefix(u.String(), fmt.Sprintf("%s://", u.Scheme)) || u.Hostname() != "" || u.Port() != "" {+return tld == "test" || tld == "local" || tld == "localhost" || tld == "invalid" || tld == "example"
+24
oauth/client/metadata.go
+24
oauth/client/metadata.go
···
-8
oauth/client.go
-8
oauth/client.go
-390
oauth/client_manager/client_manager.go
-390
oauth/client_manager/client_manager.go
···-jwksCache := cache.NewCache[string, jwk.Key]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)-metadataCache := cache.NewCache[string, oauth.ClientMetadata]().WithLRU().WithMaxKeys(500).WithTTL(5 * time.Minute)-func (cm *ClientManager) GetClient(ctx context.Context, clientId string) (*oauth.Client, error) {-// TODO: this is kinda bad but whatever for now. there could obviously be more than one jwk, and we need to-func (cm *ClientManager) getClientMetadata(ctx context.Context, clientId string) (*oauth.ClientMetadata, error) {-func (cm *ClientManager) getClientJwks(ctx context.Context, clientId, jwksUri string) (jwk.Key, error) {-return nil, errors.New("token_endpoint_auth_method `none` must not have token_endpoint_auth_signing_alg")-return nil, fmt.Errorf("unsupported client authentication method `%s`", metadata.TokenEndpointAuthMethod)-return nil, errors.New("the `code` response type requires that `grant_types` contains `authorization_code`")-return nil, errors.New("private-use uri scheme redirect uris are only allowed for native apps")-if strings.HasPrefix(u.String(), fmt.Sprintf("%s://", u.Scheme)) || u.Hostname() != "" || u.Port() != "" {-return tld == "test" || tld == "local" || tld == "localhost" || tld == "invalid" || tld == "example"
-20
oauth/client_metadata.go
-20
oauth/client_metadata.go
···
-251
oauth/dpop/dpop_manager/dpop_manager.go
-251
oauth/dpop/dpop_manager/dpop_manager.go
···-args.Logger.Warn("nonce secret passed to dpop manager was nil. existing sessions may break. consider saving and restoring your nonce.")-func (dm *DpopManager) CheckProof(reqMethod, reqUrl string, headers http.Header, accessToken *string) (*dpop.Proof, error) {-}, jwt.WithValidMethods([]string{"ES256", "ES384", "ES512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "EdDSA"}))-return nil, fmt.Errorf(`invalid dpop proof jwt: "htu" mismatch. reqUrl: %s, parsed: %s, normalized: %s`, reqUrl, parsedHtu, helpers.OauthNormalizeHtu(u))
-28
oauth/dpop/dpop_manager/jti_cache.go
-28
oauth/dpop/dpop_manager/jti_cache.go
···-cache := cache.NewCache[string, bool]().WithTTL(24 * time.Hour).WithLRU().WithTTL(constants.JTITtl)
+28
oauth/dpop/jti_cache.go
+28
oauth/dpop/jti_cache.go
···+cache := cache.NewCache[string, bool]().WithTTL(24 * time.Hour).WithLRU().WithTTL(constants.JTITtl)
+253
oauth/dpop/manager.go
+253
oauth/dpop/manager.go
···+args.Logger.Warn("nonce secret passed to dpop manager was nil. existing sessions may break. consider saving and restoring your nonce.")+func (dm *Manager) CheckProof(reqMethod, reqUrl string, headers http.Header, accessToken *string) (*Proof, error) {+}, jwt.WithValidMethods([]string{"ES256", "ES384", "ES512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "EdDSA"}))+return nil, fmt.Errorf(`invalid dpop proof jwt: "htu" mismatch. reqUrl: %s, parsed: %s, normalized: %s`, reqUrl, parsedHtu, helpers.OauthNormalizeHtu(u))
-108
oauth/dpop/nonce/nonce.go
-108
oauth/dpop/nonce/nonce.go
···
+108
oauth/dpop/nonce.go
+108
oauth/dpop/nonce.go
···
+32
oauth/helpers.go
+32
oauth/helpers.go
······
+3
-26
oauth/provider/client_auth.go
+3
-26
oauth/provider/client_auth.go
······-func (p *Provider) AuthenticateClient(ctx context.Context, req AuthenticateClientRequestBase, proof *dpop.Proof, opts *AuthenticateClientOptions) (*oauth.Client, *ClientAuth, error) {+func (p *Provider) AuthenticateClient(ctx context.Context, req AuthenticateClientRequestBase, proof *dpop.Proof, opts *AuthenticateClientOptions) (*client.Client, *ClientAuth, error) {···-func (p *Provider) Authenticate(_ context.Context, req AuthenticateClientRequestBase, client *oauth.Client) (*ClientAuth, error) {+func (p *Provider) Authenticate(_ context.Context, req AuthenticateClientRequestBase, client *client.Client) (*ClientAuth, error) {
+83
oauth/provider/models.go
+83
oauth/provider/models.go
···+CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method" validate:"required"`
+8
-64
oauth/provider/provider.go
+8
-64
oauth/provider/provider.go
······-CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method" validate:"required"`
+5
-5
plc/client.go
+5
-5
plc/client.go
············-func (c *Client) CreateDID(sigkey *crypto.PrivateKeyK256, recovery string, handle string) (string, *Operation, error) {+func (c *Client) CreateDID(sigkey *atcrypto.PrivateKeyK256, recovery string, handle string) (string, *Operation, error) {···
+2
-2
plc/types.go
+2
-2
plc/types.go
······
+85
recording_blockstore/recording_blockstore.go
+85
recording_blockstore/recording_blockstore.go
···+func (bs *RecordingBlockstore) Get(ctx context.Context, c cid.Cid) (blockformat.Block, error) {+func (bs *RecordingBlockstore) PutMany(ctx context.Context, blocks []blockformat.Block) error {
+30
server/blockstore_variant.go
+30
server/blockstore_variant.go
···
+37
-7
server/handle_account.go
+37
-7
server/handle_account.go
···-if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE sub = ? AND expires_at >= ? ORDER BY created_at ASC", nil, repo.Repo.Did, now).Scan(&tokens).Error; err != nil {+if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE sub = ? AND created_at < ? ORDER BY created_at ASC", nil, repo.Repo.Did, oldestPossibleSession).Scan(&tokens).Error; err != nil {s.logger.Error("couldnt fetch oauth sessions for account", "did", repo.Repo.Did, "error", err)···
+1
-1
server/handle_actor_get_preferences.go
+1
-1
server/handle_actor_get_preferences.go
+2
-11
server/handle_identity_update_handle.go
+2
-11
server/handle_identity_update_handle.go
·········
+2
-3
server/handle_import_repo.go
+2
-3
server/handle_import_repo.go
·········
+9
-2
server/handle_oauth_par.go
+9
-2
server/handle_oauth_par.go
······// TODO: this seems wrong. should be a way to get the entire request url i believe, but this will work for nowdpopProof, err := s.oauthProvider.DpopManager.CheckProof(e.Request().Method, "https://"+s.config.Hostname+e.Request().URL.String(), e.Request().Header, nil)client, clientAuth, err := s.oauthProvider.AuthenticateClient(e.Request().Context(), parRequest.AuthenticateClientRequestBase, dpopProof, &provider.AuthenticateClientOptions{···
+13
-12
server/handle_oauth_token.go
+13
-12
server/handle_oauth_token.go
·········proof, err := s.oauthProvider.DpopManager.CheckProof(e.Request().Method, e.Request().URL.String(), e.Request().Header, nil)client, clientAuth, err := s.oauthProvider.AuthenticateClient(e.Request().Context(), req.AuthenticateClientRequestBase, proof, &provider.AuthenticateClientOptions{······
+28
-16
server/handle_proxy.go
+28
-16
server/handle_proxy.go
··················
+2
-2
server/handle_repo_get_record.go
+2
-2
server/handle_repo_get_record.go
······return s.handleProxy(e) // TODO: this should be getting handled like...if we don't find it in the db. why doesn't it throw error up there?
+2
-2
server/handle_repo_list_records.go
+2
-2
server/handle_repo_list_records.go
······
+2
-2
server/handle_repo_list_repos.go
+2
-2
server/handle_repo_list_repos.go
+50
-8
server/handle_repo_upload_blob.go
+50
-8
server/handle_repo_upload_blob.go
············+Credentials: credentials.NewStaticCredentials(s.s3Config.AccessKey, s.s3Config.SecretKey, ""),
+45
server/handle_server_activate_account.go
+45
server/handle_server_activate_account.go
···+if err := s.db.Exec("UPDATE repos SET deactivated = ? WHERE did = ?", nil, false, urepo.Repo.Did).Error; err != nil {
+65
server/handle_server_check_account_status.go
+65
server/handle_server_check_account_status.go
···+if err := s.db.Raw("SELECT COUNT(*) AS ct FROM blocks WHERE did = ?", nil, urepo.Repo.Did).Scan(&blockCtResp).Error; err != nil {+if err := s.db.Raw("SELECT COUNT(*) AS ct FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&recCtResp).Error; err != nil {+if err := s.db.Raw("SELECT COUNT(*) AS ct FROM blobs WHERE did = ?", nil, urepo.Repo.Did).Scan(&blobCtResp).Error; err != nil {
+2
-2
server/handle_server_confirm_email.go
+2
-2
server/handle_server_confirm_email.go
······
+4
-14
server/handle_server_create_account.go
+4
-14
server/handle_server_create_account.go
······s.logger.Error("error creating signing key", "endpoint", "com.atproto.server.createAccount", "error", err)······
+2
-2
server/handle_server_create_session.go
+2
-2
server/handle_server_create_session.go
+46
server/handle_server_deactivate_account.go
+46
server/handle_server_deactivate_account.go
···+if err := s.db.Exec("UPDATE repos SET deactivated = ? WHERE did = ?", nil, true, urepo.Repo.Did).Error; err != nil {
+8
-6
server/handle_server_get_service_auth.go
+8
-6
server/handle_server_get_service_auth.go
············
+2
-2
server/handle_server_get_session.go
+2
-2
server/handle_server_get_session.go
+2
-2
server/handle_server_refresh_session.go
+2
-2
server/handle_server_refresh_session.go
+2
-2
server/handle_server_reset_password.go
+2
-2
server/handle_server_reset_password.go
···
+3
-4
server/handle_server_update_email.go
+3
-4
server/handle_server_update_email.go
······
+79
-8
server/handle_sync_get_blob.go
+79
-8
server/handle_sync_get_blob.go
······if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? AND cid = ?", nil, did, c.Bytes()).Scan(&blob).Error; err != nil {···-if err := s.db.Raw("SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", nil, blob.ID).Scan(&parts).Error; err != nil {+if err := s.db.Raw("SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", nil, blob.ID).Scan(&parts).Error; err != nil {
+14
-12
server/handle_sync_get_blocks.go
+14
-12
server/handle_sync_get_blocks.go
············
+2
-2
server/handle_sync_get_repo_status.go
+2
-2
server/handle_sync_get_repo_status.go
+14
server/handle_sync_list_blobs.go
+14
server/handle_sync_list_blobs.go
······
-18
server/handle_sync_subscribe_repos.go
-18
server/handle_sync_subscribe_repos.go
···conn, err := websocket.Upgrade(e.Response().Writer, e.Request(), e.Response().Header(), 1<<10, 1<<10)······
+275
server/middleware.go
+275
server/middleware.go
···+s.logger.Error("service auth lxm incorrect", "lxm", lxm, "expected", pts[len(pts)-1], "error", err)+if err := s.db.Raw("SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", nil, tokenstr).Scan(&result).Error; err != nil {+proof, err := s.oauthProvider.DpopManager.CheckProof(e.Request().Method, "https://"+s.config.Hostname+e.Request().URL.String(), e.Request().Header, to.StringPtr(accessToken))+if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE token = ?", nil, accessToken).Scan(&oauthToken).Error; err != nil {
+27
-21
server/repo.go
+27
-21
server/repo.go
············+// HACK: if a record doesn't contain a $type, we can manually set it here based on the op's collection························
+55
-291
server/server.go
+55
-291
server/server.go
·····················-s.logger.Error("service auth lxm incorrect", "lxm", lxm, "expected", pts[len(pts)-1], "error", err)-return e.JSON(400, map[string]string{"error": "ExpiredToken", "message": "token has expired"})-if err := s.db.Raw("SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", nil, tokenstr).Scan(&result).Error; err != nil {-proof, err := s.oauthProvider.DpopManager.CheckProof(e.Request().Method, "https://"+s.config.Hostname+e.Request().URL.String(), e.Request().Header, to.StringPtr(accessToken))-if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE token = ?", nil, accessToken).Scan(&oauthToken).Error; err != nil {-return e.JSON(400, map[string]string{"error": "ExpiredToken", "message": "token has expired"})············if args.SmtpUser == "" || args.SmtpPass == "" || args.SmtpHost == "" || args.SmtpPort == "" || args.SmtpEmail == "" || args.SmtpName == "" {-args.Logger.Warn("not enough smpt args were provided. mailing will not work for your server.")+args.Logger.Warn("not enough smtp args were provided. mailing will not work for your server.")mail := mailyak.New(args.SmtpHost+":"+args.SmtpPort, smtp.PlainAuth("", args.SmtpUser, args.SmtpPass, args.SmtpHost))···s.echo.POST("/xrpc/com.atproto.server.resetPassword", s.handleServerResetPassword, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)s.echo.POST("/xrpc/com.atproto.server.updateEmail", s.handleServerUpdateEmail, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)s.echo.GET("/xrpc/com.atproto.server.getServiceAuth", s.handleServerGetServiceAuth, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)+s.echo.GET("/xrpc/com.atproto.server.checkAccountStatus", s.handleServerCheckAccountStatus, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)+s.echo.POST("/xrpc/com.atproto.server.deactivateAccount", s.handleServerDeactivateAccount, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)+s.echo.POST("/xrpc/com.atproto.server.activateAccount", s.handleServerActivateAccount, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)s.echo.POST("/xrpc/com.atproto.repo.createRecord", s.handleCreateRecord, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)···s.echo.GET("/xrpc/app.bsky.actor.getPreferences", s.handleActorGetPreferences, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)s.echo.POST("/xrpc/app.bsky.actor.putPreferences", s.handleActorPutPreferences, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)-s.echo.GET("/xrpc/*", s.handleProxy, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)-s.echo.POST("/xrpc/*", s.handleProxy, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)s.echo.POST("/xrpc/com.atproto.server.createInviteCode", s.handleCreateInviteCode, s.handleAdminMiddleware)s.echo.POST("/xrpc/com.atproto.server.createInviteCodes", s.handleCreateInviteCodes, s.handleAdminMiddleware)+s.echo.GET("/xrpc/*", s.handleProxy, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)+s.echo.POST("/xrpc/*", s.handleProxy, s.handleLegacySessionMiddleware, s.handleOauthSessionMiddleware)···+func (s *Server) UpdateRepo(ctx context.Context, did string, root cid.Cid, rev string) error {+if err := s.db.Exec("UPDATE repos SET root = ?, rev = ? WHERE did = ?", nil, root.Bytes(), rev, did).Error; err != nil {
+5
-4
server/templates/account.html
+5
-4
server/templates/account.html
···
+137
sqlite_blockstore/sqlite_blockstore.go
+137
sqlite_blockstore/sqlite_blockstore.go
···+if err := bs.db.Raw("SELECT * FROM blocks WHERE did = ? AND cid = ?", nil, bs.did, cid.Bytes()).Scan(&block).Error; err != nil {