From e974b9ddf43d5c53c0f47b03cb42aab0cae09450 Mon Sep 17 00:00:00 2001 From: Shail Patel Date: Sat, 18 Oct 2025 01:08:01 -0400 Subject: [PATCH] knotserver/config: add PLC URL config option The knotserver can now use alternative PLCs for DID resolution by setting the env var KNOT_SERVER_PLC_URL. The default identity directory was copied out of the at proto lib and updated to take in a target url for the PLC being used to do this. Signed-off-by: Shail Patel --- guard/guard.go | 2 +- idresolver/resolver.go | 30 ++++++++++++++++++++++++++++-- knotserver/config/config.go | 1 + knotserver/ingester.go | 4 ++-- knotserver/internal.go | 2 +- knotserver/router.go | 2 +- 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/guard/guard.go b/guard/guard.go index cb8c86fe..732c0686 100644 --- a/guard/guard.go +++ b/guard/guard.go @@ -196,7 +196,7 @@ func Run(ctx context.Context, cmd *cli.Command) error { } func resolveIdentity(ctx context.Context, l *slog.Logger, didOrHandle string) *identity.Identity { - resolver := idresolver.DefaultResolver() + resolver := idresolver.DefaultResolver(identity.DefaultPLCURL) ident, err := resolver.ResolveIdent(ctx, didOrHandle) if err != nil { l.Error("Error resolving handle", "error", err, "handle", didOrHandle) diff --git a/idresolver/resolver.go b/idresolver/resolver.go index cf67df55..39402e2e 100644 --- a/idresolver/resolver.go +++ b/idresolver/resolver.go @@ -42,6 +42,32 @@ func BaseDirectory() identity.Directory { return &base } +func DefaultDirectory(plcUrl string) identity.Directory { + base := identity.BaseDirectory{ + PLCURL: plcUrl, + HTTPClient: http.Client{ + Timeout: time.Second * 10, + Transport: &http.Transport{ + // would want this around 100ms for services doing lots of handle resolution. Impacts PLC connections as well, but not too bad. + IdleConnTimeout: time.Millisecond * 1000, + MaxIdleConns: 100, + }, + }, + Resolver: net.Resolver{ + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + d := net.Dialer{Timeout: time.Second * 3} + return d.DialContext(ctx, network, address) + }, + }, + TryAuthoritativeDNS: true, + // primary Bluesky PDS instance only supports HTTP resolution method + SkipDNSDomainSuffixes: []string{".bsky.social"}, + UserAgent: "indigo-identity/" + versioninfo.Short(), + } + cached := identity.NewCacheDirectory(&base, 250_000, time.Hour*24, time.Minute*2, time.Minute*5) + return &cached +} + func RedisDirectory(url string) (identity.Directory, error) { hitTTL := time.Hour * 24 errTTL := time.Second * 30 @@ -49,9 +75,9 @@ func RedisDirectory(url string) (identity.Directory, error) { return redisdir.NewRedisDirectory(BaseDirectory(), url, hitTTL, errTTL, invalidHandleTTL, 10000) } -func DefaultResolver() *Resolver { +func DefaultResolver(plcUrl string) *Resolver { return &Resolver{ - directory: identity.DefaultDirectory(), + directory: DefaultDirectory(plcUrl), } } diff --git a/knotserver/config/config.go b/knotserver/config/config.go index edaa95cb..a844c2c1 100644 --- a/knotserver/config/config.go +++ b/knotserver/config/config.go @@ -22,6 +22,7 @@ type Server struct { JetstreamEndpoint string `env:"JETSTREAM_ENDPOINT, default=wss://jetstream1.us-west.bsky.network/subscribe"` Owner string `env:"OWNER, required"` LogDids bool `env:"LOG_DIDS, default=true"` + PlcUrl string `env:"PLC_URL, default=https://plc.directory"` // This disables signature verification so use with caution. Dev bool `env:"DEV, default=false"` diff --git a/knotserver/ingester.go b/knotserver/ingester.go index 1a3413ec..3af51812 100644 --- a/knotserver/ingester.go +++ b/knotserver/ingester.go @@ -120,7 +120,7 @@ func (h *Knot) processPull(ctx context.Context, event *models.Event) error { } // resolve this aturi to extract the repo record - resolver := idresolver.DefaultResolver() + resolver := idresolver.DefaultResolver(h.c.Server.PlcUrl) ident, err := resolver.ResolveIdent(ctx, repoAt.Authority().String()) if err != nil || ident.Handle.IsInvalidHandle() { return fmt.Errorf("failed to resolve handle: %w", err) @@ -233,7 +233,7 @@ func (h *Knot) processCollaborator(ctx context.Context, event *models.Event) err return err } - resolver := idresolver.DefaultResolver() + resolver := idresolver.DefaultResolver(h.c.Server.PlcUrl) subjectId, err := resolver.ResolveIdent(ctx, record.Subject) if err != nil || subjectId.Handle.IsInvalidHandle() { diff --git a/knotserver/internal.go b/knotserver/internal.go index 3f5cc877..0408514e 100644 --- a/knotserver/internal.go +++ b/knotserver/internal.go @@ -145,7 +145,7 @@ func (h *InternalHandle) PostReceiveHook(w http.ResponseWriter, r *http.Request) func (h *InternalHandle) replyCompare(line git.PostReceiveLine, repoOwner string, gitRelativeDir string, repoName string, ctx context.Context) ([]string, error) { l := h.l.With("handler", "replyCompare") - userIdent, err := idresolver.DefaultResolver().ResolveIdent(ctx, repoOwner) + userIdent, err := idresolver.DefaultResolver(h.c.Server.PlcUrl).ResolveIdent(ctx, repoOwner) user := repoOwner if err != nil { l.Error("Failed to fetch user identity", "err", err) diff --git a/knotserver/router.go b/knotserver/router.go index 56fd9eac..6644a81f 100644 --- a/knotserver/router.go +++ b/knotserver/router.go @@ -36,7 +36,7 @@ func Setup(ctx context.Context, c *config.Config, db *db.DB, e *rbac.Enforcer, j l: log.FromContext(ctx), jc: jc, n: n, - resolver: idresolver.DefaultResolver(), + resolver: idresolver.DefaultResolver(c.Server.PlcUrl), } err := e.AddKnot(rbac.ThisServer) -- 2.43.0