BYOK Personal Data Server (PDS) written in Go
ipfs vow atproto pds go
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: save atproto service in CreateDidCredentialsFromPublicKey

+54 -51
+2
go.mod
··· 2 2 3 3 go 1.26.0 4 4 5 + replace github.com/bluesky-social/indigo => github.com/julienrbrt/indigo v0.0.0-20260311083708-4ee8c7379cc4 6 + 5 7 require ( 6 8 github.com/bluesky-social/indigo v0.0.0-20260203235305-a86f3ae1f8ec 7 9 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792
+2 -2
go.sum
··· 10 10 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 11 github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= 12 12 github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= 13 - github.com/bluesky-social/indigo v0.0.0-20260203235305-a86f3ae1f8ec h1:fubriMftMNEmb35sF07gDCsdUSEd0+EIDebt/+5oQRU= 14 - github.com/bluesky-social/indigo v0.0.0-20260203235305-a86f3ae1f8ec/go.mod h1:VG/LeqLGNI3Ew7lsYixajnZGFfWPv144qbUddh+Oyag= 15 13 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 16 14 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 17 15 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= ··· 202 200 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 203 201 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 204 202 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 203 + github.com/julienrbrt/indigo v0.0.0-20260311083708-4ee8c7379cc4 h1:XxTkwj9q5/RkOx0GEmJ39f0BUPMuvQjITxhYfRijxsg= 204 + github.com/julienrbrt/indigo v0.0.0-20260311083708-4ee8c7379cc4/go.mod h1:VG/LeqLGNI3Ew7lsYixajnZGFfWPv144qbUddh+Oyag= 205 205 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 206 206 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 207 207 github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+23 -13
plc/client.go
··· 19 19 ) 20 20 21 21 type Client struct { 22 - h *http.Client 23 - service string 24 - pdsHostname string 25 - rotationKey *atcrypto.PrivateKeyK256 22 + h *http.Client 23 + service string 24 + pdsHostname string 25 + rotationKey *atcrypto.PrivateKeyK256 26 + serviceAuthKey *atcrypto.PrivateKeyP256 26 27 } 27 28 28 29 type ClientArgs struct { 29 - H *http.Client 30 - Service string 31 - RotationKey []byte 32 - PdsHostname string 30 + H *http.Client 31 + Service string 32 + RotationKey []byte 33 + PdsHostname string 34 + ServiceAuthKey *atcrypto.PrivateKeyP256 33 35 } 34 36 35 37 func NewClient(args *ClientArgs) (*Client, error) { ··· 47 49 } 48 50 49 51 return &Client{ 50 - h: args.H, 51 - service: args.Service, 52 - rotationKey: rk, 53 - pdsHostname: args.PdsHostname, 52 + h: args.H, 53 + service: args.Service, 54 + rotationKey: rk, 55 + pdsHostname: args.PdsHostname, 56 + serviceAuthKey: args.ServiceAuthKey, 54 57 }, nil 55 58 } 56 59 ··· 110 113 }(recovery) 111 114 } 112 115 116 + // Derive the service-auth public key for the atproto_service verification method. 117 + serviceAuthPub, err := c.serviceAuthKey.PublicKey() 118 + if err != nil { 119 + return nil, err 120 + } 121 + 113 122 creds := DidCredentials{ 114 123 VerificationMethods: map[string]string{ 115 - "atproto": pubsigkey.DIDKey(), 124 + "atproto": pubsigkey.DIDKey(), 125 + "atproto_service": serviceAuthPub.DIDKey(), 116 126 }, 117 127 RotationKeys: rotationKeys, 118 128 AlsoKnownAs: []string{
+24 -21
server/server.go
··· 336 336 return nil, err 337 337 } 338 338 339 - h := util.RobustHTTPClient() 340 - 341 - plcClient, err := plc.NewClient(&plc.ClientArgs{ 342 - H: h, 343 - Service: "https://plc.directory", 344 - PdsHostname: args.Hostname, 345 - RotationKey: rkbytes, 346 - }) 347 - if err != nil { 348 - return nil, err 349 - } 350 - 351 339 jwkbytes, err := os.ReadFile(args.JwkPath) 352 340 if err != nil { 353 341 return nil, err ··· 363 351 return nil, err 364 352 } 365 353 354 + // Wrap the JWK private key as an atcrypto.PrivateKeyP256 for ATProto-compatible 355 + // signing. Convert via ecdh to get the raw private key bytes. 356 + ecdhKey, err := pkey.ECDH() 357 + if err != nil { 358 + return nil, fmt.Errorf("converting private key to ecdh: %w", err) 359 + } 360 + atpKey, err := atcrypto.ParsePrivateBytesP256(ecdhKey.Bytes()) 361 + if err != nil { 362 + return nil, fmt.Errorf("wrapping JWK private key for atcrypto: %w", err) 363 + } 364 + 365 + h := util.RobustHTTPClient() 366 + 367 + plcClient, err := plc.NewClient(&plc.ClientArgs{ 368 + H: h, 369 + Service: "https://plc.directory", 370 + PdsHostname: args.Hostname, 371 + RotationKey: rkbytes, 372 + ServiceAuthKey: atpKey, 373 + }) 374 + if err != nil { 375 + return nil, err 376 + } 377 + 366 378 oauthCli := &http.Client{ 367 379 Timeout: 10 * time.Second, 368 380 } ··· 381 393 } 382 394 383 395 cookieStore := sessions.NewCookieStore([]byte(args.SessionSecret)) 384 - 385 - // Wrap the JWK private key as an atcrypto.PrivateKeyP256 for ATProto-compatible 386 - // signing. atcrypto.ParsePrivateBytesP256 expects the raw 32-byte D scalar. 387 - dBytes := make([]byte, 32) 388 - pkey.D.FillBytes(dBytes) 389 - atpKey, err := atcrypto.ParsePrivateBytesP256(dBytes) 390 - if err != nil { 391 - return nil, fmt.Errorf("wrapping JWK private key for atcrypto: %w", err) 392 - } 393 396 394 397 s := &Server{ 395 398 http: h,