package atproto import ( "context" "net" "net/http" "sync" "time" "github.com/bluesky-social/indigo/atproto/identity" "github.com/earthboundkid/versioninfo/v2" ) var ( // Shared identity directory instance (singleton) sharedDirectory identity.Directory directoryOnce sync.Once // testMode allows HTTP did:web resolution (IPs, non-TLS) for local development. // Set via SetTestMode() on startup. testMode bool ) // SetTestMode enables relaxed did:web resolution for local development, // allowing HTTP and IP-based did:web identifiers that the indigo directory rejects. func SetTestMode(enabled bool) { testMode = enabled } // IsTestMode returns whether test mode is enabled. func IsTestMode() bool { return testMode } // GetDirectory returns a shared identity.Directory instance with a 24-hour cache TTL. // This is based on indigo's DefaultDirectory() with event-driven cache invalidation. // // Cache entries are invalidated via Jetstream events (identity changes, account status) // which allows for a longer TTL while maintaining freshness. The Purge() method is called // when identity or account events are received, ensuring the cache reflects real-time changes. // // Using a shared instance ensures all identity lookups across the application // use the same cache, which is more memory-efficient and provides better cache hit rates. func GetDirectory() identity.Directory { directoryOnce.Do(func() { base := identity.BaseDirectory{ PLCURL: identity.DefaultPLCURL, 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(), } // Cache configuration: // - capacity: 250,000 entries // - hitTTL: 24 hours (event-driven invalidation via Jetstream provides freshness) // - errTTL: 2 minutes // - invalidHandleTTL: 5 minutes cached := identity.NewCacheDirectory(&base, 250_000, time.Hour*24, time.Minute*2, time.Minute*5) sharedDirectory = cached }) return sharedDirectory }