atcr-appview

evan.jarrett.net / atcr-appview

ATProto Container Registry - OCI-compliant registry using AT Protocol for manifest storage

Pull this image

docker pull atcr.io/evan.jarrett.net/atcr-appview:latest

Overview

ATCR AppView

The registry frontend component of ATCR (ATProto Container Registry)

Overview

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.

What AppView Does

AppView is the orchestration layer that:

  • Serves the OCI Distribution API V2 - Compatible with Docker, containerd, podman, and all OCI clients
  • Resolves ATProto identities - Converts handles (alice.bsky.social) and DIDs (did:plc:xyz123) to PDS endpoints
  • Routes manifests - Stores container image manifests as ATProto records in users’ Personal Data Servers
  • Routes blobs - Proxies blob (layer) operations to hold services for S3-compatible storage
  • Provides web UI - Browse repositories, search images, view tags, track pull counts, manage stars
  • Manages authentication - Validates OAuth tokens and issues registry JWTs to Docker clients

The ATCR Ecosystem

AppView is the frontend of a multi-component architecture:

  1. AppView (this component) - Registry API + web interface
  2. Hold Service - Storage backend with embedded PDS for blob storage
  3. Credential Helper - Client-side tool for ATProto OAuth authentication

Data flow:

Docker Client → AppView (resolves identity) → User's PDS (stores manifest)
                    ↓
              Hold Service (stores blobs in S3/Storj/etc.)

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.

When to Run Your Own AppView

Most users can simply use https://atcr.io - you don’t need to run your own AppView.

Run your own AppView if you want to:

  • Host a private/organizational container registry with ATProto authentication
  • Run a public registry for a specific community
  • Customize the registry UI or policies
  • Maintain full control over registry infrastructure

Prerequisites:

  • A running Hold service (required for blob storage)
  • (Optional) Domain name with SSL/TLS certificates for production
  • (Optional) Access to ATProto Jetstream for real-time indexing

Quick Start

Using Docker Compose

The fastest way to run AppView alongside a Hold service:

# Clone repository
git clone https://tangled.org/@evan.jarrett.net/at-container-registry
cd atcr

# Copy and configure environment
cp .env.appview.example .env.appview
# Edit .env.appview - set ATCR_DEFAULT_HOLD_DID (see Configuration below)

# Start services
docker-compose up -d

# Verify
curl http://localhost:5000/v2/

Minimal Configuration

At minimum, you must set:

# Required: Default hold service for blob storage
ATCR_DEFAULT_HOLD_DID=did:web:127.0.0.1:8080

# Recommended for production
ATCR_BASE_URL=https://registry.example.com
ATCR_HTTP_ADDR=:5000

See Configuration Reference below for all options.

Configuration Reference

AppView is configured entirely via environment variables. Load them with:

source .env.appview
./bin/atcr-appview serve

Or via Docker Compose (recommended).

Server Configuration

ATCR_HTTP_ADDR

  • Default: :5000
  • Description: HTTP listen address for the registry API and web UI
  • Example: :5000, :8080, 0.0.0.0:5000

ATCR_BASE_URL

  • Default: Auto-detected from ATCR_HTTP_ADDR (e.g., http://127.0.0.1:5000)
  • Description: Public URL for the AppView service. Used to generate OAuth redirect URIs and JWT realm claims.
  • Development: Auto-detection works fine (http://127.0.0.1:5000)
  • Production: Set to your public URL (e.g., https://atcr.example.com)
  • Example: https://atcr.io, http://127.0.0.1:5000

ATCR_SERVICE_NAME

  • Default: Derived from ATCR_BASE_URL hostname, or atcr.io
  • Description: Service name used for JWT service and issuer fields. Controls token scope.
  • Example: atcr.io, registry.example.com

ATCR_DEBUG_ADDR

  • Default: :5001
  • Description: Debug listen address for pprof debugging endpoints
  • Example: :5001, :6060

Storage Configuration

ATCR_DEFAULT_HOLD_DID ⚠️ REQUIRED

  • Default: None (required)
  • 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.
  • Format: did:web:hostname[:port]
  • Docker Compose: did:web:atcr-hold:8080 (internal Docker network)
  • Local dev: did:web:127.0.0.1:8080
  • Production: did:web:hold01.atcr.io
  • Note: This hold must be reachable from AppView. To find a hold’s DID, visit https://hold-url/.well-known/did.json

Authentication Configuration

ATCR_AUTH_KEY_PATH

  • Default: /var/lib/atcr/auth/private-key.pem
  • Description: Path to JWT signing private key (RSA). Auto-generated if missing.
  • Note: Keep this secure - it signs all registry JWTs issued to Docker clients

ATCR_AUTH_CERT_PATH

  • Default: /var/lib/atcr/auth/private-key.crt
  • Description: Path to JWT signing certificate. Auto-generated if missing.
  • Note: Paired with ATCR_AUTH_KEY_PATH

ATCR_TOKEN_EXPIRATION

  • Default: 300 (5 minutes)
  • Description: JWT token expiration in seconds. Registry JWTs are short-lived for security.
  • Recommendation: Keep between 300-900 seconds (5-15 minutes)

Web UI Configuration

ATCR_UI_ENABLED

  • Default: true
  • Description: Enable the web interface. Set to false to run registry API only (no web UI, no database).
  • Use case: API-only deployments where you don’t need the browsing interface

ATCR_UI_DATABASE_PATH

  • Default: /var/lib/atcr/ui.db
  • Description: SQLite database path for UI data (OAuth sessions, stars, pull counts, repository metadata)
  • Note: For multi-instance deployments, use PostgreSQL (see production docs)

Logging Configuration

ATCR_LOG_LEVEL

  • Default: info
  • Options: debug, info, warn, error
  • Description: Log verbosity level
  • Development: Use debug for detailed troubleshooting
  • Production: Use info or warn

ATCR_LOG_FORMATTER

  • Default: text
  • Options: text, json
  • Description: Log output format
  • Production: Use json for structured logging (easier to parse with log aggregators)

Hold Health Check Configuration

AppView periodically checks if hold services are reachable and caches results to display health indicators in the UI.

ATCR_HEALTH_CHECK_INTERVAL

  • Default: 15m
  • Description: How often to check health of hold endpoints in the background
  • Format: Duration string (e.g., 5m, 15m, 30m, 1h)
  • Recommendation: 15-30 minutes for production

ATCR_HEALTH_CACHE_TTL

  • Default: 15m
  • Description: How long to cache health check results before re-checking
  • Format: Duration string (e.g., 15m, 30m, 1h)
  • Note: Should be >= ATCR_HEALTH_CHECK_INTERVAL for efficiency

Jetstream Configuration (ATProto Event Streaming)

Jetstream provides real-time indexing of ATProto records (manifests, tags) into the AppView database for the web UI.

JETSTREAM_URL

  • Default: wss://jetstream2.us-west.bsky.network/subscribe
  • Description: Jetstream WebSocket URL for real-time ATProto events
  • Note: Connects to Bluesky’s public Jetstream by default

ATCR_BACKFILL_ENABLED

  • Default: false
  • Description: Enable periodic sync of historical ATProto records. Set to true for production to ensure database completeness.
  • Recommendation: Enable for production AppView instances

ATCR_RELAY_ENDPOINT

  • Default: https://relay1.us-east.bsky.network
  • Description: ATProto relay endpoint for backfill sync API
  • Note: Used when ATCR_BACKFILL_ENABLED=true

ATCR_BACKFILL_INTERVAL

  • Default: 1h
  • Description: How often to run backfill sync
  • Format: Duration string (e.g., 30m, 1h, 2h, 24h)

Legacy Configuration

TEST_MODE

  • Default: false
  • Description: Enable test mode (skips some validations). Do not use in production.

Web Interface Features

The AppView web UI provides:

  • Home page - Featured repositories and recent pushes feed
  • Repository pages - View tags, manifests, pull instructions, health status
  • Search - Find repositories by owner handle or repository name
  • User profiles - View a user’s repositories and activity
  • Stars - Favorite repositories (requires OAuth login)
  • Pull counts - Track image pull statistics
  • Multi-arch support - Display platform-specific manifests (linux/amd64, linux/arm64)
  • Health indicators - Real-time hold service reachability status
  • Install scripts - Host credential helper installation scripts at /install.sh

Deployment Scenarios

Public Registry (like atcr.io)

Open to all ATProto users:

# AppView config
ATCR_BASE_URL=https://registry.example.com
ATCR_DEFAULT_HOLD_DID=did:web:hold01.example.com
ATCR_UI_ENABLED=true
ATCR_BACKFILL_ENABLED=true

# Hold config (linked hold service)
HOLD_PUBLIC=true                    # Allow public pulls
HOLD_ALLOW_ALL_CREW=true           # Allow all authenticated users to push

Private Organizational Registry

Restricted to crew members only:

# AppView config
ATCR_BASE_URL=https://registry.internal.example.com
ATCR_DEFAULT_HOLD_DID=did:web:hold.internal.example.com
ATCR_UI_ENABLED=true

# Hold config (linked hold service)
HOLD_PUBLIC=false                   # Require auth for pulls
HOLD_ALLOW_ALL_CREW=false          # Only owner + explicit crew can push
HOLD_OWNER=did:plc:your-org-did    # Organization DID

Development/Testing

Local Docker Compose setup:

# AppView config
ATCR_HTTP_ADDR=:5000
ATCR_DEFAULT_HOLD_DID=did:web:atcr-hold:8080
ATCR_LOG_LEVEL=debug

# Hold config (linked hold service)
STORAGE_DRIVER=filesystem
STORAGE_ROOT_DIR=/tmp/atcr-hold
HOLD_PUBLIC=true
HOLD_ALLOW_ALL_CREW=true

Production Deployment

For production deployments with:

  • Multiple AppView instances (load balancing)
  • PostgreSQL database (instead of SQLite)
  • SSL/TLS certificates
  • Systemd service files
  • Log rotation
  • Monitoring

See deploy/README.md for comprehensive production deployment guide.

Quick Production Checklist

Before going to production:

  • Set ATCR_BASE_URL to your public HTTPS URL
  • Set ATCR_DEFAULT_HOLD_DID to a production hold service
  • Enable Jetstream backfill (ATCR_BACKFILL_ENABLED=true)
  • Use ATCR_LOG_FORMATTER=json for structured logging
  • Secure JWT keys (ATCR_AUTH_KEY_PATH, ATCR_AUTH_CERT_PATH)
  • Configure SSL/TLS termination (nginx/Caddy/Cloudflare)
  • Set up database backups (if using SQLite, consider PostgreSQL)
  • Monitor hold health checks
  • Test OAuth flow end-to-end
  • Verify Docker push/pull works

Configuration Files Reference

Tags

latest
sha256:741c6d92ad43532b92ddc184edca78578ea6597f9092b93e7e003214c0f24ec3
docker pull atcr.io/evan.jarrett.net/atcr-appview:latest

Manifests

📄 Image
sha256:741c6d92ad43532b92ddc184edca78578ea6597f9092b93e7e003214c0f24ec3
Tags: latest
📄 Image
sha256:756a257908cffbbacf9216749ab89bb3f3c30028cb7f5281bc3b62ce061cddd3
(untagged)
📄 Image
sha256:5fd1872fe76aed4e055c150b9abdc909c21b17fac257eafd9804dfc040166416
(untagged)
📄 Image
sha256:79024fa8051299524f1f575e0190ed99392ed66868443dd743d19a31f7ae54ac
(untagged)
📄 Image ⚠️ Offline
sha256:746e95a7272ef67c8be8f9b3b895a067d96201bb677ee0605d6658fbb20b2963
(untagged)
📄 Image ⚠️ Offline
sha256:ba14c66dd570a66dd4f584dd4d347ea250b96c9bb149d873351541e64475ecb2
(untagged)
📄 Image ⚠️ Offline
sha256:051400a6451dbf62673b61389496ae300f4c0f5a1276e85bb664d68846f78af9
(untagged)
📄 Image
sha256:c1dbd884bc08176a0bf4698d9d408302f0b663f7a445c7583f57273ddaa924d5
(untagged)