···11-{
22- "lexicon": 1,
33- "id": "io.atcr.hold",
44- "defs": {
55- "main": {
66- "type": "record",
77- "description": "Storage hold definition for Bring Your Own Storage (BYOS). Defines where blobs are stored.",
88- "key": "any",
99- "record": {
1010- "type": "object",
1111- "required": ["endpoint", "owner", "createdAt"],
1212- "properties": {
1313- "endpoint": {
1414- "type": "string",
1515- "format": "uri",
1616- "description": "URL of the hold service (e.g., 'https://hold1.example.com')"
1717- },
1818- "owner": {
1919- "type": "string",
2020- "format": "did",
2121- "description": "DID of the hold owner"
2222- },
2323- "public": {
2424- "type": "boolean",
2525- "description": "Whether this hold allows public blob reads (pulls) without authentication. Writes always require crew membership.",
2626- "default": false
2727- },
2828- "createdAt": {
2929- "type": "string",
3030- "format": "datetime",
3131- "description": "Hold creation timestamp"
3232- }
3333- }
3434- }
3535- }
3636- }
3737-}
+47
lexicons/io/atcr/hold/captain.json
···11+{
22+ "lexicon": 1,
33+ "id": "io.atcr.hold.captain",
44+ "defs": {
55+ "main": {
66+ "type": "record",
77+ "description": "Represents the hold's ownership and metadata. Stored as a singleton record at rkey 'self' in the hold's embedded PDS.",
88+ "key": "literal:self",
99+ "record": {
1010+ "type": "object",
1111+ "required": ["owner", "public", "allowAllCrew", "enableBlueskyPosts", "deployedAt"],
1212+ "properties": {
1313+ "owner": {
1414+ "type": "string",
1515+ "format": "did",
1616+ "description": "DID of the hold owner"
1717+ },
1818+ "public": {
1919+ "type": "boolean",
2020+ "description": "Whether this hold allows public blob reads (pulls) without authentication"
2121+ },
2222+ "allowAllCrew": {
2323+ "type": "boolean",
2424+ "description": "Allow any authenticated user to register as crew"
2525+ },
2626+ "enableBlueskyPosts": {
2727+ "type": "boolean",
2828+ "description": "Enable Bluesky posts when manifests are pushed"
2929+ },
3030+ "deployedAt": {
3131+ "type": "string",
3232+ "format": "datetime",
3333+ "description": "RFC3339 timestamp of when the hold was deployed"
3434+ },
3535+ "region": {
3636+ "type": "string",
3737+ "description": "S3 region where blobs are stored"
3838+ },
3939+ "provider": {
4040+ "type": "string",
4141+ "description": "Deployment provider (e.g., fly.io, aws, etc.)"
4242+ }
4343+ }
4444+ }
4545+ }
4646+ }
4747+}
+13-20
lexicons/io/atcr/hold/crew.json
···44 "defs": {
55 "main": {
66 "type": "record",
77- "description": "Crew membership for a storage hold. Stored in the hold owner's PDS to maintain control over write access. Supports explicit DIDs (with backlinks), wildcard access, and handle patterns. Crew members can push blobs to the hold. Read access is controlled by the hold's public flag, not crew membership.",
77+ "description": "Crew member in a hold's embedded PDS. Grants access permissions to push blobs to the hold. Stored in the hold's embedded PDS (one record per member).",
88 "key": "any",
99 "record": {
1010 "type": "object",
1111- "required": ["hold", "role", "createdAt"],
1111+ "required": ["member", "role", "permissions", "addedAt"],
1212 "properties": {
1313- "hold": {
1414- "type": "string",
1515- "format": "at-uri",
1616- "description": "AT-URI of the hold record (e.g., 'at://did:plc:owner/io.atcr.hold/hold1')"
1717- },
1813 "member": {
1914 "type": "string",
2015 "format": "did",
2121- "description": "DID of crew member (for individual access with backlinks). Exactly one of 'member' or 'memberPattern' must be set."
2222- },
2323- "memberPattern": {
2424- "type": "string",
2525- "description": "Pattern for matching multiple users. Supports wildcards: '*' (all users), '*.domain.com' (handle glob). Exactly one of 'member' or 'memberPattern' must be set."
1616+ "description": "DID of the crew member"
2617 },
2718 "role": {
2819 "type": "string",
2929- "description": "Member's role/permissions for write access. 'owner' = hold owner, 'write' = can push blobs. Read access is controlled by hold's public flag.",
3030- "knownValues": ["owner", "write"]
2020+ "description": "Member's role in the hold",
2121+ "knownValues": ["owner", "admin", "write", "read"]
3122 },
3232- "expiresAt": {
3333- "type": "string",
3434- "format": "datetime",
3535- "description": "Optional expiration for this membership"
2323+ "permissions": {
2424+ "type": "array",
2525+ "description": "Specific permissions granted to this member",
2626+ "items": {
2727+ "type": "string"
2828+ }
3629 },
3737- "createdAt": {
3030+ "addedAt": {
3831 "type": "string",
3932 "format": "datetime",
4040- "description": "Membership creation timestamp"
3333+ "description": "RFC3339 timestamp of when the member was added"
4134 }
4235 }
4336 }
+48
lexicons/io/atcr/hold/layer.json
···11+{
22+ "lexicon": 1,
33+ "id": "io.atcr.hold.layer",
44+ "defs": {
55+ "main": {
66+ "type": "record",
77+ "key": "tid",
88+ "description": "Represents metadata about a container layer stored in the hold. Stored in the hold's embedded PDS for tracking and analytics.",
99+ "record": {
1010+ "type": "object",
1111+ "required": ["digest", "size", "mediaType", "repository", "userDid", "userHandle", "createdAt"],
1212+ "properties": {
1313+ "digest": {
1414+ "type": "string",
1515+ "description": "Layer digest (e.g., sha256:abc123...)"
1616+ },
1717+ "size": {
1818+ "type": "integer",
1919+ "description": "Size in bytes"
2020+ },
2121+ "mediaType": {
2222+ "type": "string",
2323+ "description": "Media type (e.g., application/vnd.oci.image.layer.v1.tar+gzip)"
2424+ },
2525+ "repository": {
2626+ "type": "string",
2727+ "description": "Repository this layer belongs to"
2828+ },
2929+ "userDid": {
3030+ "type": "string",
3131+ "format": "did",
3232+ "description": "DID of user who uploaded this layer"
3333+ },
3434+ "userHandle": {
3535+ "type": "string",
3636+ "format": "handle",
3737+ "description": "Handle of user (for display purposes)"
3838+ },
3939+ "createdAt": {
4040+ "type": "string",
4141+ "format": "datetime",
4242+ "description": "RFC3339 timestamp of when the layer was uploaded"
4343+ }
4444+ }
4545+ }
4646+ }
4747+ }
4848+}
+7-2
lexicons/io/atcr/manifest.json
···88 "key": "tid",
99 "record": {
1010 "type": "object",
1111- "required": ["repository", "digest", "mediaType", "schemaVersion", "holdEndpoint", "createdAt"],
1111+ "required": ["repository", "digest", "mediaType", "schemaVersion", "createdAt"],
1212 "properties": {
1313 "repository": {
1414 "type": "string",
···1919 "type": "string",
2020 "description": "Content digest (e.g., 'sha256:abc123...')"
2121 },
2222+ "holdDid": {
2323+ "type": "string",
2424+ "format": "did",
2525+ "description": "DID of the hold service where blobs are stored (e.g., 'did:web:hold01.atcr.io'). Primary reference for hold resolution."
2626+ },
2227 "holdEndpoint": {
2328 "type": "string",
2429 "format": "uri",
2525- "description": "Hold service endpoint where blobs are stored (e.g., 'https://hold1.bob.com'). Historical reference."
3030+ "description": "Hold service endpoint URL where blobs are stored. DEPRECATED: Use holdDid instead. Kept for backward compatibility."
2631 },
2732 "mediaType": {
2833 "type": "string",
+3-3
pkg/appview/middleware/registry.go
···3333type validationCacheEntry struct {
3434 serviceToken string
3535 validUntil time.Time
3636- err error // Cached error for fast-fail
3737- mu sync.Mutex // Per-entry lock to serialize cache population
3838- inFlight bool // True if another goroutine is fetching the token
3636+ err error // Cached error for fast-fail
3737+ mu sync.Mutex // Per-entry lock to serialize cache population
3838+ inFlight bool // True if another goroutine is fetching the token
3939 done chan struct{} // Closed when fetch completes
4040}
4141
···926926 childDigest := digest.FromBytes(childManifest)
927927928928 tests := []struct {
929929- name string
930930- manifestList []byte
931931- childExists bool // Whether the child manifest exists
932932- wantErr bool
933933- wantErrType string // "ErrManifestBlobUnknown" or empty
934934- checkErrDigest string // Expected digest in error
929929+ name string
930930+ manifestList []byte
931931+ childExists bool // Whether the child manifest exists
932932+ wantErr bool
933933+ wantErrType string // "ErrManifestBlobUnknown" or empty
934934+ checkErrDigest string // Expected digest in error
935935 }{
936936 {
937937 name: "valid manifest list - child exists",
+1-1
pkg/atproto/lexicon.go
···4141 // TangledProfileCollection is the collection name for tangled profiles
4242 // Stored in hold's embedded PDS (singleton record at rkey "self")
4343 TangledProfileCollection = "sh.tangled.actor.profile"
4444-4444+4545 // BskyPostCollection is the collection name for Bluesky posts
4646 BskyPostCollection = "app.bsky.feed.post"
4747