A container registry that uses the AT Protocol for manifest storage and S3 for blob storage. atcr.io
docker container atproto go
72
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