···11# ATCR AppView
2233-The **AppView** is the OCI-compliant registry server for ATCR (ATProto Container Registry). It provides the Docker Registry HTTP API V2 and a web interface for browsing container images.
33+> The registry frontend component of ATCR (ATProto Container Registry)
4455-## What is AppView?
55+## Overview
6677-AppView serves as the central registry server that:
77+**AppView** is the frontend server component of ATCR. It serves as the OCI-compliant registry API endpoint and web interface that Docker clients interact with when pushing and pulling container images.
8899-- **Serves OCI Distribution API** - Compatible with Docker, containerd, podman, and other OCI clients
1010-- **Resolves ATProto identities** - Converts handles and DIDs to PDS endpoints
1111-- **Routes manifests** - Stores container manifests as ATProto records in users' Personal Data Servers
1212-- **Routes blobs** - Proxies blob operations to hold services (S3-compatible storage)
1313-- **Provides web UI** - Browse, search, and star repositories
99+### What AppView Does
1010+1111+AppView is the orchestration layer that:
1212+1313+- **Serves the OCI Distribution API V2** - Compatible with Docker, containerd, podman, and all OCI clients
1414+- **Resolves ATProto identities** - Converts handles (`alice.bsky.social`) and DIDs (`did:plc:xyz123`) to PDS endpoints
1515+- **Routes manifests** - Stores container image manifests as ATProto records in users' Personal Data Servers
1616+- **Routes blobs** - Proxies blob (layer) operations to hold services for S3-compatible storage
1717+- **Provides web UI** - Browse repositories, search images, view tags, track pull counts, manage stars
1818+- **Manages authentication** - Validates OAuth tokens and issues registry JWTs to Docker clients
1919+2020+### The ATCR Ecosystem
14211515-## Image Format
2222+AppView is the **frontend** of a multi-component architecture:
16231717-Container images use ATProto identities:
2424+1. **AppView** (this component) - Registry API + web interface
2525+2. **[Hold Service](https://atcr.io/r/evan.jarrett.net/atcr-hold)** - Storage backend with embedded PDS for blob storage
2626+3. **Credential Helper** - Client-side tool for ATProto OAuth authentication
18272828+**Data flow:**
1929```
2020-atcr.io/alice.bsky.social/myapp:latest
2121-atcr.io/did:plc:xyz123/myapp:latest
3030+Docker Client → AppView (resolves identity) → User's PDS (stores manifest)
3131+ ↓
3232+ Hold Service (stores blobs in S3/Storj/etc.)
2233```
23342424-## Using ATCR
3535+Manifests (small JSON metadata) live in users' ATProto PDS, while blobs (large binary layers) live in hold services. AppView orchestrates the routing between these components.
25362626-### Push Images
3737+## When to Run Your Own AppView
3838+3939+Most users can simply use **https://atcr.io** - you don't need to run your own AppView.
4040+4141+**Run your own AppView if you want to:**
4242+- Host a private/organizational container registry with ATProto authentication
4343+- Run a public registry for a specific community
4444+- Customize the registry UI or policies
4545+- Maintain full control over registry infrastructure
4646+4747+**Prerequisites:**
4848+- A running [Hold service](https://atcr.io/r/evan.jarrett.net/atcr-hold) (required for blob storage)
4949+- (Optional) Domain name with SSL/TLS certificates for production
5050+- (Optional) Access to ATProto Jetstream for real-time indexing
5151+5252+## Quick Start
5353+5454+### Using Docker Compose
5555+5656+The fastest way to run AppView alongside a Hold service:
27572858```bash
2929-# Install credential helper
3030-curl -fsSL https://atcr.io/install.sh | bash
5959+# Clone repository
6060+git clone https://tangled.org/@evan.jarrett.net/at-container-registry
6161+cd atcr
6262+6363+# Copy and configure environment
6464+cp .env.appview.example .env.appview
6565+# Edit .env.appview - set ATCR_DEFAULT_HOLD_DID (see Configuration below)
31663232-# Configure Docker (add to ~/.docker/config.json)
3333-{
3434- "credHelpers": {
3535- "atcr.io": "atcr"
3636- }
3737-}
6767+# Start services
6868+docker-compose up -d
38693939-# Push images (authenticates automatically)
4040-docker tag myapp:latest atcr.io/yourhandle/myapp:latest
4141-docker push atcr.io/yourhandle/myapp:latest
7070+# Verify
7171+curl http://localhost:5000/v2/
4272```
43734444-### Pull Images
7474+### Minimal Configuration
7575+7676+At minimum, you must set:
45774678```bash
4747-# Public images (no auth required)
4848-docker pull atcr.io/alice.bsky.social/myapp:latest
7979+# Required: Default hold service for blob storage
8080+ATCR_DEFAULT_HOLD_DID=did:web:127.0.0.1:8080
49815050-# Private images (automatic OAuth authentication)
5151-docker pull atcr.io/yourhandle/private-app:latest
8282+# Recommended for production
8383+ATCR_BASE_URL=https://registry.example.com
8484+ATCR_HTTP_ADDR=:5000
5285```
53865454-## Running Your Own AppView
8787+See **Configuration Reference** below for all options.
8888+8989+## Configuration Reference
9090+9191+AppView is configured entirely via environment variables. Load them with:
9292+```bash
9393+source .env.appview
9494+./bin/atcr-appview serve
9595+```
9696+9797+Or via Docker Compose (recommended).
9898+9999+### Server Configuration
100100+101101+#### `ATCR_HTTP_ADDR`
102102+- **Default:** `:5000`
103103+- **Description:** HTTP listen address for the registry API and web UI
104104+- **Example:** `:5000`, `:8080`, `0.0.0.0:5000`
105105+106106+#### `ATCR_BASE_URL`
107107+- **Default:** Auto-detected from `ATCR_HTTP_ADDR` (e.g., `http://127.0.0.1:5000`)
108108+- **Description:** Public URL for the AppView service. Used to generate OAuth redirect URIs and JWT realm claims.
109109+- **Development:** Auto-detection works fine (`http://127.0.0.1:5000`)
110110+- **Production:** Set to your public URL (e.g., `https://atcr.example.com`)
111111+- **Example:** `https://atcr.io`, `http://127.0.0.1:5000`
112112+113113+#### `ATCR_SERVICE_NAME`
114114+- **Default:** Derived from `ATCR_BASE_URL` hostname, or `atcr.io`
115115+- **Description:** Service name used for JWT `service` and `issuer` fields. Controls token scope.
116116+- **Example:** `atcr.io`, `registry.example.com`
117117+118118+#### `ATCR_DEBUG_ADDR`
119119+- **Default:** `:5001`
120120+- **Description:** Debug listen address for pprof debugging endpoints
121121+- **Example:** `:5001`, `:6060`
122122+123123+### Storage Configuration
124124+125125+#### `ATCR_DEFAULT_HOLD_DID` ⚠️ REQUIRED
126126+- **Default:** None (required)
127127+- **Description:** DID of the default hold service for blob storage. Used when users don't have their own hold configured in their sailor profile. AppView routes all blob operations to this hold.
128128+- **Format:** `did:web:hostname[:port]`
129129+- **Docker Compose:** `did:web:atcr-hold:8080` (internal Docker network)
130130+- **Local dev:** `did:web:127.0.0.1:8080`
131131+- **Production:** `did:web:hold01.atcr.io`
132132+- **Note:** This hold must be reachable from AppView. To find a hold's DID, visit `https://hold-url/.well-known/did.json`
133133+134134+### Authentication Configuration
135135+136136+#### `ATCR_AUTH_KEY_PATH`
137137+- **Default:** `/var/lib/atcr/auth/private-key.pem`
138138+- **Description:** Path to JWT signing private key (RSA). Auto-generated if missing.
139139+- **Note:** Keep this secure - it signs all registry JWTs issued to Docker clients
140140+141141+#### `ATCR_AUTH_CERT_PATH`
142142+- **Default:** `/var/lib/atcr/auth/private-key.crt`
143143+- **Description:** Path to JWT signing certificate. Auto-generated if missing.
144144+- **Note:** Paired with `ATCR_AUTH_KEY_PATH`
145145+146146+#### `ATCR_TOKEN_EXPIRATION`
147147+- **Default:** `300` (5 minutes)
148148+- **Description:** JWT token expiration in seconds. Registry JWTs are short-lived for security.
149149+- **Recommendation:** Keep between 300-900 seconds (5-15 minutes)
150150+151151+### Web UI Configuration
152152+153153+#### `ATCR_UI_ENABLED`
154154+- **Default:** `true`
155155+- **Description:** Enable the web interface. Set to `false` to run registry API only (no web UI, no database).
156156+- **Use case:** API-only deployments where you don't need the browsing interface
157157+158158+#### `ATCR_UI_DATABASE_PATH`
159159+- **Default:** `/var/lib/atcr/ui.db`
160160+- **Description:** SQLite database path for UI data (OAuth sessions, stars, pull counts, repository metadata)
161161+- **Note:** For multi-instance deployments, use PostgreSQL (see production docs)
162162+163163+### Logging Configuration
164164+165165+#### `ATCR_LOG_LEVEL`
166166+- **Default:** `info`
167167+- **Options:** `debug`, `info`, `warn`, `error`
168168+- **Description:** Log verbosity level
169169+- **Development:** Use `debug` for detailed troubleshooting
170170+- **Production:** Use `info` or `warn`
171171+172172+#### `ATCR_LOG_FORMATTER`
173173+- **Default:** `text`
174174+- **Options:** `text`, `json`
175175+- **Description:** Log output format
176176+- **Production:** Use `json` for structured logging (easier to parse with log aggregators)
177177+178178+### Hold Health Check Configuration
179179+180180+AppView periodically checks if hold services are reachable and caches results to display health indicators in the UI.
181181+182182+#### `ATCR_HEALTH_CHECK_INTERVAL`
183183+- **Default:** `15m`
184184+- **Description:** How often to check health of hold endpoints in the background
185185+- **Format:** Duration string (e.g., `5m`, `15m`, `30m`, `1h`)
186186+- **Recommendation:** 15-30 minutes for production
187187+188188+#### `ATCR_HEALTH_CACHE_TTL`
189189+- **Default:** `15m`
190190+- **Description:** How long to cache health check results before re-checking
191191+- **Format:** Duration string (e.g., `15m`, `30m`, `1h`)
192192+- **Note:** Should be >= `ATCR_HEALTH_CHECK_INTERVAL` for efficiency
193193+194194+### Jetstream Configuration (ATProto Event Streaming)
195195+196196+Jetstream provides real-time indexing of ATProto records (manifests, tags) into the AppView database for the web UI.
197197+198198+#### `JETSTREAM_URL`
199199+- **Default:** `wss://jetstream2.us-west.bsky.network/subscribe`
200200+- **Description:** Jetstream WebSocket URL for real-time ATProto events
201201+- **Note:** Connects to Bluesky's public Jetstream by default
552025656-Deploy your own registry instance with Docker Compose:
203203+#### `ATCR_BACKFILL_ENABLED`
204204+- **Default:** `false`
205205+- **Description:** Enable periodic sync of historical ATProto records. Set to `true` for production to ensure database completeness.
206206+- **Recommendation:** Enable for production AppView instances
207207+208208+#### `ATCR_RELAY_ENDPOINT`
209209+- **Default:** `https://relay1.us-east.bsky.network`
210210+- **Description:** ATProto relay endpoint for backfill sync API
211211+- **Note:** Used when `ATCR_BACKFILL_ENABLED=true`
212212+213213+#### `ATCR_BACKFILL_INTERVAL`
214214+- **Default:** `1h`
215215+- **Description:** How often to run backfill sync
216216+- **Format:** Duration string (e.g., `30m`, `1h`, `2h`, `24h`)
217217+218218+### Legacy Configuration
219219+220220+#### `TEST_MODE`
221221+- **Default:** `false`
222222+- **Description:** Enable test mode (skips some validations). Do not use in production.
223223+224224+## Web Interface Features
225225+226226+The AppView web UI provides:
227227+228228+- **Home page** - Featured repositories and recent pushes feed
229229+- **Repository pages** - View tags, manifests, pull instructions, health status
230230+- **Search** - Find repositories by owner handle or repository name
231231+- **User profiles** - View a user's repositories and activity
232232+- **Stars** - Favorite repositories (requires OAuth login)
233233+- **Pull counts** - Track image pull statistics
234234+- **Multi-arch support** - Display platform-specific manifests (linux/amd64, linux/arm64)
235235+- **Health indicators** - Real-time hold service reachability status
236236+- **Install scripts** - Host credential helper installation scripts at `/install.sh`
237237+238238+## Deployment Scenarios
239239+240240+### Public Registry (like atcr.io)
241241+242242+Open to all ATProto users:
5724358244```bash
5959-# Create configuration
6060-cp .env.appview.example .env.appview
6161-# Edit .env.appview with your settings
245245+# AppView config
246246+ATCR_BASE_URL=https://registry.example.com
247247+ATCR_DEFAULT_HOLD_DID=did:web:hold01.example.com
248248+ATCR_UI_ENABLED=true
249249+ATCR_BACKFILL_ENABLED=true
622506363-# Start services
6464-docker-compose up -d
251251+# Hold config (linked hold service)
252252+HOLD_PUBLIC=true # Allow public pulls
253253+HOLD_ALLOW_ALL_CREW=true # Allow all authenticated users to push
65254```
662556767-### Configuration
256256+### Private Organizational Registry
257257+258258+Restricted to crew members only:
682596969-Key environment variables:
260260+```bash
261261+# AppView config
262262+ATCR_BASE_URL=https://registry.internal.example.com
263263+ATCR_DEFAULT_HOLD_DID=did:web:hold.internal.example.com
264264+ATCR_UI_ENABLED=true
265265+266266+# Hold config (linked hold service)
267267+HOLD_PUBLIC=false # Require auth for pulls
268268+HOLD_ALLOW_ALL_CREW=false # Only owner + explicit crew can push
269269+HOLD_OWNER=did:plc:your-org-did # Organization DID
270270+```
271271+272272+### Development/Testing
702737171-- `ATCR_HTTP_ADDR` - HTTP listen address (default: `:5000`)
7272-- `ATCR_BASE_URL` - Public URL for OAuth/JWT realm
7373-- `ATCR_DEFAULT_HOLD_DID` - Default hold service DID for blob storage (required)
7474-- `ATCR_UI_ENABLED` - Enable web interface (default: `true`)
7575-- `JETSTREAM_URL` - ATProto event stream URL for real-time updates
274274+Local Docker Compose setup:
762757777-See [deployment documentation](https://tangled.org/@evan.jarrett.net/at-container-registry/blob/main/deploy/README.md) for production setup.
276276+```bash
277277+# AppView config
278278+ATCR_HTTP_ADDR=:5000
279279+ATCR_DEFAULT_HOLD_DID=did:web:atcr-hold:8080
280280+ATCR_LOG_LEVEL=debug
782817979-## Features
282282+# Hold config (linked hold service)
283283+STORAGE_DRIVER=filesystem
284284+STORAGE_ROOT_DIR=/tmp/atcr-hold
285285+HOLD_PUBLIC=true
286286+HOLD_ALLOW_ALL_CREW=true
287287+```
802888181-- ✅ **OCI-compliant** - Full Docker Registry API V2 support
8282-- ✅ **ATProto OAuth** - Secure authentication with DPoP
8383-- ✅ **Decentralized storage** - Manifests stored in users' PDS
8484-- ✅ **Web UI** - Browse repositories, view tags, search images
8585-- ✅ **Real-time updates** - Jetstream integration for live indexing
8686-- ✅ **Multi-arch support** - ARM64, AMD64, and other platforms
8787-- ✅ **BYOS** - Bring Your Own Storage via hold services
289289+## Production Deployment
882908989-## Storage Architecture
291291+For production deployments with:
292292+- Multiple AppView instances (load balancing)
293293+- PostgreSQL database (instead of SQLite)
294294+- SSL/TLS certificates
295295+- Systemd service files
296296+- Log rotation
297297+- Monitoring
902989191-**Hybrid model:**
9292-- **Manifests** → ATProto records in user's PDS (small JSON metadata)
9393-- **Blobs** → Hold services with S3-compatible backends (large binary layers)
299299+See **[deploy/README.md](https://tangled.org/@evan.jarrett.net/at-container-registry/blob/main/deploy/README.md)** for comprehensive production deployment guide.
943009595-This design keeps metadata portable and federated while leveraging cheap blob storage for layers.
301301+### Quick Production Checklist
963029797-## License
303303+Before going to production:
983049999-MIT
305305+- [ ] Set `ATCR_BASE_URL` to your public HTTPS URL
306306+- [ ] Set `ATCR_DEFAULT_HOLD_DID` to a production hold service
307307+- [ ] Enable Jetstream backfill (`ATCR_BACKFILL_ENABLED=true`)
308308+- [ ] Use `ATCR_LOG_FORMATTER=json` for structured logging
309309+- [ ] Secure JWT keys (`ATCR_AUTH_KEY_PATH`, `ATCR_AUTH_CERT_PATH`)
310310+- [ ] Configure SSL/TLS termination (nginx/Caddy/Cloudflare)
311311+- [ ] Set up database backups (if using SQLite, consider PostgreSQL)
312312+- [ ] Monitor hold health checks
313313+- [ ] Test OAuth flow end-to-end
314314+- [ ] Verify Docker push/pull works
100315101101----
316316+## Configuration Files Reference
102317103103-**Documentation:** https://tangled.org/@evan.jarrett.net/at-container-registry
104104-**Source Code:** https://tangled.org/@evan.jarrett.net/at-container-registry
318318+- **[.env.appview.example](https://tangled.org/@evan.jarrett.net/at-container-registry/blob/main/.env.appview.example)** - All available environment variables with documentation
319319+- **[deploy/.env.prod.template](https://tangled.org/@evan.jarrett.net/at-container-registry/blob/main/deploy/.env.prod.template)** - Production configuration template
320320+- **[deploy/README.md](https://tangled.org/@evan.jarrett.net/at-container-registry/blob/main/deploy/README.md)** - Production deployment guide
321321+- **[Hold Service Documentation](https://atcr.io/r/evan.jarrett.net/atcr-hold)** - Storage backend setup