# Bring Your Own Storage (BYOS) ## Overview ATCR supports "Bring Your Own Storage" (BYOS) for blob storage. Users can: - Deploy their own hold service with embedded PDS - Control access via crew membership in the hold's PDS - Keep blob data in their own S3/Storj/Minio while manifests stay in their user PDS ## Architecture ``` ┌──────────────────────────────────────────┐ │ ATCR AppView (API) │ │ - Manifests → User's PDS │ │ - Auth & service token management │ │ - Blob routing via XRPC │ │ - Profile management │ └────────────┬─────────────────────────────┘ │ │ Hold discovery priority: │ 1. io.atcr.sailor.profile.defaultHold (DID) │ 2. io.atcr.hold records (legacy) │ 3. AppView default_hold_did ▼ ┌──────────────────────────────────────────┐ │ User's PDS │ │ - io.atcr.sailor.profile (hold DID) │ │ - io.atcr.manifest (with holdDid) │ └────────────┬─────────────────────────────┘ │ │ Service token from user's PDS ▼ ┌──────────────────────────────────────────┐ │ Hold Service (did:web:hold.example.com) │ │ ├── Embedded PDS │ │ │ ├── Captain record (ownership) │ │ │ └── Crew records (access control) │ │ ├── XRPC multipart upload endpoints │ │ └── Storage driver (S3/Storj/etc.) │ └──────────────────────────────────────────┘ ``` ## Hold Service Components Each hold is a full ATProto actor with: - **DID**: `did:web:hold.example.com` (hold's identity) - **Embedded PDS**: Stores captain + crew records (shared data) - **Storage backend**: S3, Storj, Minio, filesystem, etc. - **XRPC endpoints**: Standard ATProto + custom OCI multipart upload ### Records in Hold's PDS **Captain record** (`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** (`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..." } ``` ### Sailor Profile (User's PDS) Users set their preferred hold in their sailor profile: ```json { "$type": "io.atcr.sailor.profile", "defaultHold": "did:web:hold.example.com", "createdAt": "2025-10-02T...", "updatedAt": "2025-10-02T..." } ``` ## Deployment ### Configuration Hold service is configured entirely via environment variables: ```bash # Hold identity (REQUIRED) HOLD_PUBLIC_URL=https://hold.example.com HOLD_OWNER=did:plc:your-did-here # Storage backend STORAGE_DRIVER=s3 AWS_ACCESS_KEY_ID=your_access_key AWS_SECRET_ACCESS_KEY=your_secret_key AWS_REGION=us-east-1 S3_BUCKET=my-blobs # Access control HOLD_PUBLIC=false # Require authentication for reads HOLD_ALLOW_ALL_CREW=false # Only explicit crew members can write # Embedded PDS HOLD_DATABASE_PATH=/var/lib/atcr-hold/hold.db HOLD_DATABASE_KEY_PATH=/var/lib/atcr-hold/keys ``` ### Running Locally ```bash # Build go build -o bin/atcr-hold ./cmd/hold # Run (with env vars or .env file) export HOLD_PUBLIC_URL=http://localhost:8080 export HOLD_OWNER=did:plc:your-did-here export STORAGE_DRIVER=filesystem export STORAGE_ROOT_DIR=/tmp/atcr-hold export HOLD_DATABASE_PATH=/tmp/atcr-hold/hold.db ./bin/atcr-hold ``` On first run, the hold service creates: - Captain record in embedded PDS (making you the owner) - Crew record for owner with all permissions - DID document at `/.well-known/did.json` ### Deploy to Fly.io ```bash # Create fly.toml cat > fly.toml <