A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
73
fork

Configure Feed

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

1# Hold Crew Access Control 2 3## Overview 4 5ATCR uses crew-based access control for hold (storage) services. Crew records are stored in the **hold's embedded PDS** (not the owner's or user's PDS), making the hold a self-contained ATProto actor with its own access control. 6 7## Current Implementation 8 9### Records in Hold's PDS 10 11**Captain record** - Hold ownership (single record at `io.atcr.hold.captain/self`): 12```json 13{ 14 "$type": "io.atcr.hold.captain", 15 "owner": "did:plc:alice123", 16 "public": false, 17 "deployedAt": "2025-10-14T...", 18 "region": "iad", 19 "provider": "fly.io" 20} 21``` 22 23**Crew records** - Access control (one per member at `io.atcr.hold.crew/{rkey}`): 24```json 25{ 26 "$type": "io.atcr.hold.crew", 27 "member": "did:plc:bob456", 28 "role": "admin", 29 "permissions": ["blob:read", "blob:write"], 30 "addedAt": "2025-10-14T..." 31} 32``` 33 34### Authorization Logic 35 36Write authorization follows this priority: 37 38``` 39isAuthorizedWrite(userDID): 40 1. If userDID == captain.owner → ALLOW 41 2. If crew record exists for userDID → ALLOW 42 3. Default → DENY 43``` 44 45Read authorization depends on `HOLD_PUBLIC` setting: 46- **Public hold** (`HOLD_PUBLIC=true`): Anonymous + all authenticated users can read 47- **Private hold** (`HOLD_PUBLIC=false`): Requires crew membership for reads 48 49### Configuration 50 51```bash 52# Access control environment variables 53HOLD_PUBLIC=false # Require authentication for reads 54HOLD_ALLOW_ALL_CREW=false # Only explicit crew members can write 55``` 56 57### Crew Management 58 59Crew records are managed by the hold captain (owner) using standard ATProto operations on the hold's embedded PDS: 60 61**Add crew member:** 62```bash 63# Via hold's PDS (requires captain's OAuth) 64atproto put-record \ 65 --pds https://hold.example.com \ 66 --collection io.atcr.hold.crew \ 67 --rkey "{memberDID}" \ 68 --value '{ 69 "$type": "io.atcr.hold.crew", 70 "member": "did:plc:bob456", 71 "role": "admin", 72 "permissions": ["blob:read", "blob:write"], 73 "addedAt": "2025-10-14T12:00:00Z" 74 }' 75``` 76 77**Remove crew member:** 78```bash 79atproto delete-record \ 80 --pds https://hold.example.com \ 81 --collection io.atcr.hold.crew \ 82 --rkey "{memberDID}" 83``` 84 85**List crew members:** 86```bash 87# Via XRPC 88GET https://hold.example.com/xrpc/com.atproto.repo.listRecords?repo={holdDID}&collection=io.atcr.hold.crew 89``` 90 91## Authentication Flow 92 93``` 941. User pushes image to atcr.io/alice/myapp 95 962. AppView gets service token from alice's PDS: 97 GET /xrpc/com.atproto.server.getServiceAuth?aud={holdDID} 98 Response: { "token": "..." } 99 1003. AppView calls hold with service token: 101 POST /xrpc/io.atcr.hold.initiateUpload 102 Authorization: Bearer {serviceToken} 103 1044. Hold validates service token: 105 - Checks token is from alice's PDS 106 - Extracts alice's DID from token 107 1085. Hold checks crew membership: 109 - Queries its own PDS: com.atproto.repo.getRecord 110 - Collection: io.atcr.hold.crew 111 - Record key: alice's DID 112 1136. If crew record found → allow upload 114 Else → deny with 403 Forbidden 115``` 116 117**Trust model:** "Trust but verify" 118- User OAuth'd to AppView (proves identity) 119- Service token from user's PDS (proves AppView is acting on behalf of user) 120- Crew record in hold's PDS (proves user has access to this hold) 121 122## Use Cases 123 124### 1. Personal Hold (Private) 125 126```bash 127# Owner only 128HOLD_PUBLIC=false 129HOLD_ALLOW_ALL_CREW=false 130# No additional crew records needed - captain has implicit access 131``` 132 133### 2. Team Hold (Shared) 134 135```bash 136# Multiple team members 137HOLD_PUBLIC=false 138HOLD_ALLOW_ALL_CREW=false 139 140# Captain adds crew members: 141# - did:plc:alice (admin) 142# - did:plc:bob (member) 143# - did:plc:charlie (member) 144``` 145 146### 3. Public Hold (Community) 147 148```bash 149# Allow any authenticated user (TODO: Implement HOLD_ALLOW_ALL_CREW) 150HOLD_PUBLIC=true 151HOLD_ALLOW_ALL_CREW=true 152``` 153 154## Planned Features 155 156### Pattern-Based Access Control 157 158**Status:** Planned but not yet implemented. 159 160**Concept:** Allow crew records with pattern matching instead of explicit DIDs: 161 162```json 163{ 164 "$type": "io.atcr.hold.crew", 165 "memberPattern": "*.example.com", 166 "role": "write" 167} 168``` 169 170**Use cases:** 171- `"*"` - Allow all authenticated users 172- `"*.company.com"` - Allow all users from company domain 173- `"*.community.social"` - Allow all community members 174 175**Implementation needed:** 176- Add `memberPattern` field to crew record schema (make `member` optional) 177- Add handle resolution (DID → handle lookup) 178- Add pattern matching logic 179- Update authorization to check patterns 180 181### Barred List (Access Revocation) 182 183**Status:** Planned but not yet implemented. 184 185**Concept:** Explicit deny list that overrides crew membership: 186 187```json 188{ 189 "$type": "io.atcr.hold.crew.barred", 190 "member": "did:plc:former-employee", 191 "reason": "No longer with company", 192 "barredAt": "2025-10-13T12:00:00Z" 193} 194``` 195 196**Priority:** Barred list checked before crew list. 197 198### HOLD_ALLOW_ALL_CREW 199 200**Status:** Environment variable exists but full implementation pending. 201 202**Concept:** Automatically create/manage wildcard crew record via env var: 203 204```bash 205HOLD_ALLOW_ALL_CREW=true # Creates crew record with memberPattern: "*" 206``` 207 208**Implementation needed:** 209- Auto-create wildcard crew record on startup if env=true 210- Auto-delete wildcard crew record if env changes to false 211- Use well-known rkey "allow-all" for managed record 212 213## Architecture Notes 214 215### Why Hold's Embedded PDS? 216 217**Key insight:** Crew records are **shared data** about the hold, not user-specific data. 218 219**Benefits:** 220- **Self-contained**: Hold is independent ATProto actor 221- **Portable**: Hold can move without coordinating with user PDSs 222- **Discoverable**: Query hold's PDS to see who has access 223- **Standard**: Uses normal ATProto sync endpoints (subscribeRepos, getRecord, listRecords) 224 225**Comparison:** 226- **User's PDS**: Stores user-specific data (manifests, sailor profile) 227- **Hold's PDS**: Stores hold-specific data (captain, crew, configuration) 228- Clear separation of concerns 229 230### Security Considerations 231 2321. **Public Records**: Crew records are public (anyone can see who has access to a hold) 2332. **Service Tokens**: Hold trusts user's PDS to issue valid service tokens 2343. **DID-Based**: Crew membership is DID-based (permanent), not handle-based 2354. **Captain Control**: Only captain can modify crew records (via OAuth to hold's PDS) 236 237## Future Improvements 238 2391. **Crew management UI** - Web interface for adding/removing crew members 2402. **Pattern-based matching** - Implement `memberPattern` field 2413. **Barred list** - Implement access revocation 2424. **Role-based permissions** - Fine-grained permissions beyond read/write 2435. **Temporary access** - Time-limited crew membership (`expiresAt` field) 2446. **Audit logging** - Track access grants/denials 245 246## References 247 248- [EMBEDDED_PDS.md](./EMBEDDED_PDS.md) - Embedded PDS architecture details 249- [BYOS.md](./BYOS.md) - BYOS deployment and usage 250- [ATProto Lexicon Spec](https://atproto.com/specs/lexicon)