A container registry that uses the AT Protocol for manifest storage and S3 for blob storage.
0
fork

Configure Feed

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

at main 190 lines 8.0 kB view raw view rendered
1# ATCR Hold Service 2 3Hold Service is the BYOS (Bring Your Own Storage) blob storage backend for ATCR. It stores container image layers in your own S3-compatible storage (AWS S3, Storj, Minio, UpCloud, etc.) and generates presigned URLs so clients transfer data directly to/from S3. Each hold runs an embedded ATProto PDS with its own DID, repository, and crew-based access control. 4 5Hold Service is one component of the ATCR ecosystem: 6 71. **[AppView](https://atcr.io/r/evan.jarrett.net/atcr-appview)** — Registry API + web interface 82. **Hold Service** (this component) — Storage backend with embedded PDS 93. **Credential Helper** — Client-side tool for ATProto OAuth authentication 10 11``` 12Docker Client --> AppView (resolves identity) --> User's PDS (stores manifest) 13 | 14 Hold Service (generates presigned URL) 15 | 16 S3/Storj/etc. (client uploads/downloads directly) 17``` 18 19Manifests (small JSON metadata) live in users' ATProto PDS. Blobs (large binary layers) live in hold services. AppView orchestrates the routing. 20 21## When to Run Your Own Hold 22 23Most users can push to the default hold at **https://hold01.atcr.io** — you don't need to run your own. 24 25Run your own hold if you want to: 26- Control where your container layer data is stored (own S3 bucket, geographic region) 27- Manage access for a team or organization via crew membership 28- Run a shared hold for a community or project 29- Use a CDN pull zone for faster downloads 30 31**Prerequisites:** S3-compatible storage with a bucket already created, and a domain with TLS for production. 32 33## Quick Start 34 35### 1. Generate Configuration 36 37```bash 38# Build the hold binary 39go build -o bin/atcr-hold ./cmd/hold 40 41# Generate a fully-commented config file with all defaults 42./bin/atcr-hold config init config-hold.yaml 43``` 44 45Or generate config from Docker without building locally: 46 47```bash 48docker run --rm -i $(docker build -q -f Dockerfile.hold .) config init > config-hold.yaml 49``` 50 51The generated file documents every option with inline comments. Edit only what you need. 52 53### 2. Minimal Configuration 54 55Only three things need to be set — everything else has sensible defaults: 56 57```yaml 58storage: 59 access_key: "YOUR_S3_ACCESS_KEY" 60 secret_key: "YOUR_S3_SECRET_KEY" 61 bucket: "your-bucket-name" 62 endpoint: "https://gateway.storjshare.io" # omit for AWS S3 63 64server: 65 public_url: "https://hold.example.com" 66 67registration: 68 owner_did: "did:plc:your-did-here" 69``` 70 71- **`server.public_url`** — Your hold's public HTTPS URL. This becomes the hold's `did:web` identity. 72- **`storage.bucket`** — S3 bucket name (must already exist). 73- **`registration.owner_did`** — Your ATProto DID. Creates you as captain (admin) on first boot. Get yours from: `https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=yourhandle.bsky.social` 74 75### 3. Build and Run with Docker 76 77```bash 78# Build the image 79docker build -f Dockerfile.hold -t atcr-hold:latest . 80 81# Run it 82docker run -d \ 83 --name atcr-hold \ 84 -p 8080:8080 \ 85 -v $(pwd)/config-hold.yaml:/config.yaml:ro \ 86 -v atcr-hold-data:/var/lib/atcr-hold \ 87 atcr-hold:latest serve --config /config.yaml 88``` 89 90- **`/var/lib/atcr-hold`** — Persistent volume for the embedded PDS (carstore database + signing keys). Back this up. 91- **Port 8080** — Default listen address. Put a reverse proxy (Caddy, nginx) in front for TLS. 92- The image is built `FROM scratch` — the binary includes SQLite statically linked. 93- Optional: `docker build --build-arg BILLING_ENABLED=true` to include Stripe billing support. 94 95## Configuration 96 97Config loads in layers: **defaults → YAML file → environment variables**. Later layers override earlier ones. 98 99All YAML fields can be overridden with environment variables using the `HOLD_` prefix and `_` path separators. For example, `server.public_url` becomes `HOLD_SERVER_PUBLIC_URL`. 100 101S3 credentials also accept standard AWS environment variable names: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `S3_BUCKET`, `S3_ENDPOINT`. 102 103For the complete configuration reference with all options and defaults, see [`config-hold.example.yaml`](../config-hold.example.yaml) or run `atcr-hold config init`. 104 105## Access Control 106 107| Setting | Who can pull | Who can push | 108|---|---|---| 109| `server.public: true` | Anyone | Captain + crew with `blob:write` | 110| `server.public: false` (default) | Crew with `blob:read` | Captain + crew with `blob:write` | 111| + `registration.allow_all_crew: true` | (per above) | Any authenticated user | 112 113The captain (set via `registration.owner_did`) has all permissions implicitly. `blob:write` implies `blob:read`. 114 115Authentication uses ATProto service tokens: AppView requests a token from the user's PDS scoped to the hold's DID, then includes it in XRPC requests. The hold validates the token and checks crew membership. 116 117See [BYOS.md](BYOS.md) for the full authorization model. 118 119## Optional Subsystems 120 121| Subsystem | Default | Config key | Notes | 122|---|---|---|---| 123| Admin panel | Enabled | `admin.enabled` | Web UI for crew, settings, and storage management | 124| Quotas | Disabled | `quota.tiers` | Tier-based storage limits (e.g., deckhand=5GB, bosun=50GB) | 125| Garbage collection | Disabled | `gc.enabled` | Nightly cleanup of orphaned blobs and records | 126| Vulnerability scanner | Disabled | `scanner.secret` | Requires separate scanner service; see [SBOM_SCANNING.md](SBOM_SCANNING.md) | 127| Billing (Stripe) | Disabled | Build flag + env | Build with `--build-arg BILLING_ENABLED=true`; see [BILLING.md](BILLING.md) | 128| Bluesky posts | Disabled | `registration.enable_bluesky_posts` | Posts push notifications from hold's identity | 129 130## Hold Identity 131 132**did:web (default)** — Derived from `server.public_url` with zero setup. `https://hold.example.com` becomes `did:web:hold.example.com`. The DID document is served at `/.well-known/did.json`. Tied to domain ownership — if you lose the domain, you lose the identity. 133 134**did:plc (portable)** — Set `database.did_method: plc` in config. Registered with plc.directory. Survives domain changes. Requires a rotation key (auto-generated at `{database.path}/rotation.key`). Use `database.did` to adopt an existing DID for recovery or migration. 135 136## Verification 137 138After starting your hold, verify it's working: 139 140```bash 141# Health check — should return {"version":"..."} 142curl https://hold.example.com/xrpc/_health 143 144# DID document — should return valid JSON with service endpoints 145curl https://hold.example.com/.well-known/did.json 146 147# Captain record — should show your owner DID 148curl "https://hold.example.com/xrpc/com.atproto.repo.listRecords?repo=HOLD_DID&collection=io.atcr.hold.captain" 149 150# Crew records 151curl "https://hold.example.com/xrpc/com.atproto.repo.listRecords?repo=HOLD_DID&collection=io.atcr.hold.crew" 152``` 153 154Replace `HOLD_DID` with your hold's DID (from the `/.well-known/did.json` response). 155 156## Docker Compose 157 158```yaml 159services: 160 atcr-hold: 161 build: 162 context: . 163 dockerfile: Dockerfile.hold 164 command: ["serve", "--config", "/config.yaml"] 165 volumes: 166 - ./config-hold.yaml:/config.yaml:ro 167 - atcr-hold-data:/var/lib/atcr-hold 168 ports: 169 - "8080:8080" 170 healthcheck: 171 test: ["CMD", "/healthcheck", "http://localhost:8080/xrpc/_health"] 172 interval: 30s 173 timeout: 10s 174 retries: 3 175 start_period: 30s 176 177volumes: 178 atcr-hold-data: 179``` 180 181For production with TLS termination, see [`deploy/docker-compose.prod.yml`](../deploy/docker-compose.prod.yml) which includes a Caddy reverse proxy. 182 183## Further Reading 184 185- [`config-hold.example.yaml`](../config-hold.example.yaml) — Complete configuration reference with inline comments 186- [BYOS.md](BYOS.md) — Bring Your Own Storage architecture and authorization model 187- [HOLD_XRPC_ENDPOINTS.md](HOLD_XRPC_ENDPOINTS.md) — XRPC endpoint reference 188- [BILLING.md](BILLING.md) — Stripe billing integration 189- [QUOTAS.md](QUOTAS.md) — Quota management 190- [SBOM_SCANNING.md](SBOM_SCANNING.md) — Vulnerability scanning