An atproto PDS written in Go
at v0.7.2 2.5 kB view raw
1package server 2 3import ( 4 "context" 5 "slices" 6 "strings" 7 "time" 8 9 "github.com/bluesky-social/indigo/api/atproto" 10 "github.com/bluesky-social/indigo/atproto/atcrypto" 11 "github.com/bluesky-social/indigo/events" 12 "github.com/bluesky-social/indigo/util" 13 "github.com/haileyok/cocoon/internal/helpers" 14 "github.com/haileyok/cocoon/models" 15 "github.com/haileyok/cocoon/plc" 16 "github.com/labstack/echo/v4" 17) 18 19type ComAtprotoSubmitPlcOperationRequest struct { 20 Operation plc.Operation `json:"operation"` 21} 22 23func (s *Server) handleSubmitPlcOperation(e echo.Context) error { 24 logger := s.logger.With("name", "handleIdentitySubmitPlcOperation") 25 26 repo := e.Get("repo").(*models.RepoActor) 27 28 var req ComAtprotoSubmitPlcOperationRequest 29 if err := e.Bind(&req); err != nil { 30 logger.Error("error binding", "error", err) 31 return helpers.ServerError(e, nil) 32 } 33 34 if err := e.Validate(req); err != nil { 35 return helpers.InputError(e, nil) 36 } 37 if !strings.HasPrefix(repo.Repo.Did, "did:plc:") { 38 return helpers.InputError(e, nil) 39 } 40 41 op := req.Operation 42 43 k, err := atcrypto.ParsePrivateBytesK256(repo.SigningKey) 44 if err != nil { 45 logger.Error("error parsing key", "error", err) 46 return helpers.ServerError(e, nil) 47 } 48 required, err := s.plcClient.CreateDidCredentials(k, "", repo.Actor.Handle) 49 if err != nil { 50 logger.Error("error crating did credentials", "error", err) 51 return helpers.ServerError(e, nil) 52 } 53 54 for _, expectedKey := range required.RotationKeys { 55 if !slices.Contains(op.RotationKeys, expectedKey) { 56 return helpers.InputError(e, nil) 57 } 58 } 59 if op.Services["atproto_pds"].Type != "AtprotoPersonalDataServer" { 60 return helpers.InputError(e, nil) 61 } 62 if op.Services["atproto_pds"].Endpoint != required.Services["atproto_pds"].Endpoint { 63 return helpers.InputError(e, nil) 64 } 65 if op.VerificationMethods["atproto"] != required.VerificationMethods["atproto"] { 66 return helpers.InputError(e, nil) 67 } 68 if op.AlsoKnownAs[0] != required.AlsoKnownAs[0] { 69 return helpers.InputError(e, nil) 70 } 71 72 if err := s.plcClient.SendOperation(e.Request().Context(), repo.Repo.Did, &op); err != nil { 73 return err 74 } 75 76 if err := s.passport.BustDoc(context.TODO(), repo.Repo.Did); err != nil { 77 logger.Warn("error busting did doc", "error", err) 78 } 79 80 s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{ 81 RepoIdentity: &atproto.SyncSubscribeRepos_Identity{ 82 Did: repo.Repo.Did, 83 Seq: time.Now().UnixMicro(), // TODO: no 84 Time: time.Now().Format(util.ISO8601), 85 }, 86 }) 87 88 return nil 89}