A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
atcr.io
docker
container
atproto
go
1package atproto
2
3import (
4 "context"
5 "net"
6 "net/http"
7 "sync"
8 "time"
9
10 "github.com/bluesky-social/indigo/atproto/identity"
11 "github.com/earthboundkid/versioninfo/v2"
12)
13
14var (
15 // Shared identity directory instance (singleton)
16 sharedDirectory identity.Directory
17 directoryOnce sync.Once
18
19 // testMode allows HTTP did:web resolution (IPs, non-TLS) for local development.
20 // Set via SetTestMode() on startup.
21 testMode bool
22)
23
24// SetTestMode enables relaxed did:web resolution for local development,
25// allowing HTTP and IP-based did:web identifiers that the indigo directory rejects.
26func SetTestMode(enabled bool) {
27 testMode = enabled
28}
29
30// IsTestMode returns whether test mode is enabled.
31func IsTestMode() bool {
32 return testMode
33}
34
35// GetDirectory returns a shared identity.Directory instance with a 24-hour cache TTL.
36// This is based on indigo's DefaultDirectory() with event-driven cache invalidation.
37//
38// Cache entries are invalidated via Jetstream events (identity changes, account status)
39// which allows for a longer TTL while maintaining freshness. The Purge() method is called
40// when identity or account events are received, ensuring the cache reflects real-time changes.
41//
42// Using a shared instance ensures all identity lookups across the application
43// use the same cache, which is more memory-efficient and provides better cache hit rates.
44func GetDirectory() identity.Directory {
45 directoryOnce.Do(func() {
46 base := identity.BaseDirectory{
47 PLCURL: identity.DefaultPLCURL,
48 HTTPClient: http.Client{
49 Timeout: time.Second * 10,
50 Transport: &http.Transport{
51 // would want this around 100ms for services doing lots of handle resolution. Impacts PLC connections as well, but not too bad.
52 IdleConnTimeout: time.Millisecond * 1000,
53 MaxIdleConns: 100,
54 },
55 },
56 Resolver: net.Resolver{
57 Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
58 d := net.Dialer{Timeout: time.Second * 3}
59 return d.DialContext(ctx, network, address)
60 },
61 },
62 TryAuthoritativeDNS: true,
63 // primary Bluesky PDS instance only supports HTTP resolution method
64 SkipDNSDomainSuffixes: []string{".bsky.social"},
65 UserAgent: "indigo-identity/" + versioninfo.Short(),
66 }
67 // Cache configuration:
68 // - capacity: 250,000 entries
69 // - hitTTL: 24 hours (event-driven invalidation via Jetstream provides freshness)
70 // - errTTL: 2 minutes
71 // - invalidHandleTTL: 5 minutes
72 cached := identity.NewCacheDirectory(&base, 250_000, time.Hour*24, time.Minute*2, time.Minute*5)
73 sharedDirectory = cached
74 })
75 return sharedDirectory
76}