Files
at-container-registry/docs/CREW_ACCESS_CONTROL.md
T

251 lines
6.7 KiB
Markdown

# Hold Crew Access Control
## Overview
ATCR 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.
## Current Implementation
### Records in Hold's PDS
**Captain record** - Hold ownership (single record at `io.atcr.hold.captain/self`):
```json
{
"$type": "io.atcr.hold.captain",
"owner": "did:plc:alice123",
"public": false,
"deployedAt": "2025-10-14T...",
"region": "iad",
"provider": "fly.io"
}
```
**Crew records** - Access control (one per member at `io.atcr.hold.crew/{rkey}`):
```json
{
"$type": "io.atcr.hold.crew",
"member": "did:plc:bob456",
"role": "admin",
"permissions": ["blob:read", "blob:write"],
"addedAt": "2025-10-14T..."
}
```
### Authorization Logic
Write authorization follows this priority:
```
isAuthorizedWrite(userDID):
1. If userDID == captain.owner → ALLOW
2. If crew record exists for userDID → ALLOW
3. Default → DENY
```
Read authorization depends on `HOLD_PUBLIC` setting:
- **Public hold** (`HOLD_PUBLIC=true`): Anonymous + all authenticated users can read
- **Private hold** (`HOLD_PUBLIC=false`): Requires crew membership for reads
### Configuration
```bash
# Access control environment variables
HOLD_PUBLIC=false # Require authentication for reads
HOLD_ALLOW_ALL_CREW=false # Only explicit crew members can write
```
### Crew Management
Crew records are managed by the hold captain (owner) using standard ATProto operations on the hold's embedded PDS:
**Add crew member:**
```bash
# Via hold's PDS (requires captain's OAuth)
atproto put-record \
--pds https://hold.example.com \
--collection io.atcr.hold.crew \
--rkey "{memberDID}" \
--value '{
"$type": "io.atcr.hold.crew",
"member": "did:plc:bob456",
"role": "admin",
"permissions": ["blob:read", "blob:write"],
"addedAt": "2025-10-14T12:00:00Z"
}'
```
**Remove crew member:**
```bash
atproto delete-record \
--pds https://hold.example.com \
--collection io.atcr.hold.crew \
--rkey "{memberDID}"
```
**List crew members:**
```bash
# Via XRPC
GET https://hold.example.com/xrpc/com.atproto.repo.listRecords?repo={holdDID}&collection=io.atcr.hold.crew
```
## Authentication Flow
```
1. User pushes image to atcr.io/alice/myapp
2. AppView gets service token from alice's PDS:
GET /xrpc/com.atproto.server.getServiceAuth?aud={holdDID}
Response: { "token": "..." }
3. AppView calls hold with service token:
POST /xrpc/io.atcr.hold.initiateUpload
Authorization: Bearer {serviceToken}
4. Hold validates service token:
- Checks token is from alice's PDS
- Extracts alice's DID from token
5. Hold checks crew membership:
- Queries its own PDS: com.atproto.repo.getRecord
- Collection: io.atcr.hold.crew
- Record key: alice's DID
6. If crew record found → allow upload
Else → deny with 403 Forbidden
```
**Trust model:** "Trust but verify"
- User OAuth'd to AppView (proves identity)
- Service token from user's PDS (proves AppView is acting on behalf of user)
- Crew record in hold's PDS (proves user has access to this hold)
## Use Cases
### 1. Personal Hold (Private)
```bash
# Owner only
HOLD_PUBLIC=false
HOLD_ALLOW_ALL_CREW=false
# No additional crew records needed - captain has implicit access
```
### 2. Team Hold (Shared)
```bash
# Multiple team members
HOLD_PUBLIC=false
HOLD_ALLOW_ALL_CREW=false
# Captain adds crew members:
# - did:plc:alice (admin)
# - did:plc:bob (member)
# - did:plc:charlie (member)
```
### 3. Public Hold (Community)
```bash
# Allow any authenticated user (TODO: Implement HOLD_ALLOW_ALL_CREW)
HOLD_PUBLIC=true
HOLD_ALLOW_ALL_CREW=true
```
## Planned Features
### Pattern-Based Access Control
**Status:** Planned but not yet implemented.
**Concept:** Allow crew records with pattern matching instead of explicit DIDs:
```json
{
"$type": "io.atcr.hold.crew",
"memberPattern": "*.example.com",
"role": "write"
}
```
**Use cases:**
- `"*"` - Allow all authenticated users
- `"*.company.com"` - Allow all users from company domain
- `"*.community.social"` - Allow all community members
**Implementation needed:**
- Add `memberPattern` field to crew record schema (make `member` optional)
- Add handle resolution (DID → handle lookup)
- Add pattern matching logic
- Update authorization to check patterns
### Barred List (Access Revocation)
**Status:** Planned but not yet implemented.
**Concept:** Explicit deny list that overrides crew membership:
```json
{
"$type": "io.atcr.hold.crew.barred",
"member": "did:plc:former-employee",
"reason": "No longer with company",
"barredAt": "2025-10-13T12:00:00Z"
}
```
**Priority:** Barred list checked before crew list.
### HOLD_ALLOW_ALL_CREW
**Status:** Environment variable exists but full implementation pending.
**Concept:** Automatically create/manage wildcard crew record via env var:
```bash
HOLD_ALLOW_ALL_CREW=true # Creates crew record with memberPattern: "*"
```
**Implementation needed:**
- Auto-create wildcard crew record on startup if env=true
- Auto-delete wildcard crew record if env changes to false
- Use well-known rkey "allow-all" for managed record
## Architecture Notes
### Why Hold's Embedded PDS?
**Key insight:** Crew records are **shared data** about the hold, not user-specific data.
**Benefits:**
- **Self-contained**: Hold is independent ATProto actor
- **Portable**: Hold can move without coordinating with user PDSs
- **Discoverable**: Query hold's PDS to see who has access
- **Standard**: Uses normal ATProto sync endpoints (subscribeRepos, getRecord, listRecords)
**Comparison:**
- **User's PDS**: Stores user-specific data (manifests, sailor profile)
- **Hold's PDS**: Stores hold-specific data (captain, crew, configuration)
- Clear separation of concerns
### Security Considerations
1. **Public Records**: Crew records are public (anyone can see who has access to a hold)
2. **Service Tokens**: Hold trusts user's PDS to issue valid service tokens
3. **DID-Based**: Crew membership is DID-based (permanent), not handle-based
4. **Captain Control**: Only captain can modify crew records (via OAuth to hold's PDS)
## Future Improvements
1. **Crew management UI** - Web interface for adding/removing crew members
2. **Pattern-based matching** - Implement `memberPattern` field
3. **Barred list** - Implement access revocation
4. **Role-based permissions** - Fine-grained permissions beyond read/write
5. **Temporary access** - Time-limited crew membership (`expiresAt` field)
6. **Audit logging** - Track access grants/denials
## References
- [EMBEDDED_PDS.md](./EMBEDDED_PDS.md) - Embedded PDS architecture details
- [BYOS.md](./BYOS.md) - BYOS deployment and usage
- [ATProto Lexicon Spec](https://atproto.com/specs/lexicon)