Files
at-container-registry/docker-compose.yml
T
Evan Jarrett ab4a4ebf9d admin panel long running imrovements, billing fixes, ui cleanup
1. Multiple registry domains + per-user domain preference

The biggest feature. The appview can serve several registry domains (e.g. buoy.cr, atcr.io),
and users can now pick which one shows up in their pull/push commands.

- Lexicon/record: adds registryDomain (and documents ociClient)
to the sailor profile (lexicons/.../profile.json, pkg/atproto/lexicon.go).
- DB: new registry_domain column on users (schema.sql + migration 0027),
with GetUserByDID/Handle reads, UpdateUserRegistryDomain writer,
and Jetstream caching it on profile updates (writes unconditionally so clearing propagates).
- UI/handlers: new UpdateRegistryDomainHandler + /api/profile/registry-domain route,
a <select> in the user settings panel (only shown when >1 domain configured), and resolveRegistryURL()
which falls back to the primary domain if the user's pref is stale/removed. Tests added for all of it.

2. default_hold_did removed → first managed_holds entry is the default

Consolidates two overlapping config fields into one. ServerConfig.DefaultHoldDID is gone;
PrimaryHoldDID() now returns managed_holds[0]. managed_holds is now REQUIRED.
Updated in config, validation, server wiring, test harness, example YAML, and the deploy template.

3. Admin long-running operations → generic background-job framework

New pkg/hold/admin/jobs.go introduces a reusable startJob/jobRegistry pattern
 (a detached context.Background() job + a /admin/api/jobs/{key}/status polling endpoint).
This replaces the bespoke scan-backfill goroutine state machine, and now also wraps crew tier remap and crew import
all three previously looped synchronously on the request context and got 504'd/cancelled mid-run by the reverse proxy.
 Forms switched from POST-redirect to htmx fragments (job_progress.html, job_result.html, crew_import_results.html)
 the old crew_import_results.html page and scan_backfill_progress.html partial were deleted.
This is also captured as a new rule in CLAUDE.md.

4. Cascade-delete manifest on last-tag deletion

DeleteTagHandler now, after removing the last tag pointing to a digest, cascade-deletes the manifest itself
 (PDS + DB + hold blob purge) — but only if it's not a child of a manifest list (multi-arch parent).
 New GetTagDigest and ShouldCascadeDeleteManifest queries back it, plus cascade_delete_test.go.
 Also switches tag rkey computation to the atproto.RepositoryTagToRKey helper.

5. Billing simplification

Drops the OwnerBadge config option (hold-owner supporter badge).
The user-profile template no longer special-cases an "owner" badge value (only "Captain").
Example tiers renamed to the nautical scheme (deckhand/bosun/quartermaster).

6. Build/deploy: go generate always runs via Make

make generate is now a phony target that always runs go generate ./... (regenerating cbor_gen, icon sprites, etc.),
 and build-trixie depends on it. The deploy tooling (provision.go/update.go)
drops its own runGenerate calls since the Makefile handles it.

7. New cmd/firehose-tap tool (untracked)

A standalone CLI that subscribes to a com.atproto.sync.subscribeRepos endpoint and pretty-prints events,
with emphasis on Sync 1.1 compliance fields (per-op prev CIDs, commit prevData) and a --validate CI mode.
Fits with the recent "more sync1.1 compliant" commit.
2026-06-05 20:57:25 -05:00

188 lines
6.2 KiB
YAML

services:
atcr-appview:
build:
context: .
dockerfile: Dockerfile.dev
image: atcr-appview-dev:latest
container_name: atcr-appview
ports:
- "5000:5000"
env_file:
- ../atcr-secrets.env
# Optional: Load from .env.appview file (create from .env.appview.example)
# env_file:
# - .env.appview
# Base config: config-appview.example.yaml (passed via Air entrypoint)
# Env vars below override config file values for local dev
environment:
# ATCR_SERVER_CLIENT_NAME: "Seamark"
# ATCR_SERVER_CLIENT_SHORT_NAME: "Seamark"
# First entry is the default blob-storage hold. Comma-separate for multiple:
# ATCR_SERVER_MANAGED_HOLDS: "did:web:a,did:web:b" (Viper splits on commas).
ATCR_SERVER_MANAGED_HOLDS: did:web:172.28.0.3%3A8080
# Labeler URL (HTTP for dev — ParseLabelerURL accepts it directly so we don't
# have to round-trip through did:web → https:// resolution).
ATCR_LABELER_DID: did:web:172.28.0.4%3A5002
ATCR_SERVER_TEST_MODE: true
ATCR_LOG_LEVEL: debug
LOG_SHIPPER_BACKEND: victoria
LOG_SHIPPER_URL: http://172.28.0.10:9428
# Limit local Docker logs - real logs go to Victoria Logs
# Local logs just for live tailing (docker logs -f)
logging:
driver: json-file
options:
max-size: "10m"
max-file: "1"
volumes:
# Mount source code for Air hot reload
- .:/app:z
# Cache go modules between rebuilds
- go-mod-cache:/go/pkg/mod
# UI database (includes OAuth sessions, devices, and Jetstream cache)
- atcr-ui:/var/lib/atcr
restart: unless-stopped
dns:
- 8.8.8.8
- 1.1.1.1
networks:
atcr-network:
ipv4_address: 172.28.0.2
# The AppView is stateless - all storage is external:
# - Manifests/Tags -> ATProto PDS (via middleware)
# - Blobs/Layers -> Hold service (via ProxyBlobStore)
# - OAuth tokens -> SQLite database (atcr-ui volume)
atcr-hold:
env_file:
- ../atcr-secrets.env # Load S3/Storj credentials from external file
# Base config: config-hold.example.yaml (passed via Air entrypoint)
# Env vars below override config file values for local dev
environment:
HOLD_SERVER_APPVIEW_DID: did:web:172.28.0.2%3A5000
HOLD_SCANNER_SECRET: dev-secret
HOLD_SERVER_PUBLIC_URL: http://172.28.0.3:8080
HOLD_REGISTRATION_OWNER_DID: did:plc:pddp4xt5lgnv2qsegbzzs4xg
HOLD_REGISTRATION_ALLOW_ALL_CREW: true
HOLD_SERVER_TEST_MODE: true
HOLD_LOG_LEVEL: debug
# Subscribe to the dev labeler so takedowns purge records on this hold and
# GC honors the reversibility window. Same value the appview uses for
# ATCR_LABELER_DID — accepts a did:web identifier or a raw URL.
HOLD_LABELER_DID: did:web:172.28.0.4%3A5002
# Short grace window for dev so the takedown→GC path is exercisable without
# waiting weeks. Production default is 720h (30 days).
HOLD_LABELER_GRACE_WINDOW: 1h
LOG_SHIPPER_BACKEND: victoria
LOG_SHIPPER_URL: http://172.28.0.10:9428
# S3 storage config comes from env_file (AWS_*, S3_*)
# Limit local Docker logs - real logs go to Victoria Logs
# Local logs just for live tailing (docker logs -f)
logging:
driver: json-file
options:
max-size: "10m"
max-file: "1"
build:
context: .
dockerfile: Dockerfile.dev
args:
AIR_CONFIG: .air.hold.toml
BILLING_ENABLED: "true"
image: atcr-hold-dev:latest
container_name: atcr-hold
ports:
- "8080:8080"
volumes:
# Mount source code for Air hot reload
- .:/app:z
# Cache go modules between rebuilds
- go-mod-cache:/go/pkg/mod
# PDS data (carstore SQLite + signing keys)
- atcr-hold:/var/lib/atcr-hold
restart: unless-stopped
dns:
- 8.8.8.8
- 1.1.1.1
networks:
atcr-network:
ipv4_address: 172.28.0.3
atcr-labeler:
# Base config: config-labeler.example.yaml (passed via Air entrypoint).
# Env vars below override config file values for local dev.
#
# Why did:web for dev: did:plc would submit a real PLC operation to plc.directory
# for every fresh dev environment, polluting production with throwaway DIDs that
# point at 172.28.0.x. did:web is purely self-served via /.well-known/did.json so
# nothing leaks. Switch to plc + a real public_url for production.
environment:
LABELER_LABELER_DID_METHOD: web
LABELER_LABELER_PUBLIC_URL: http://172.28.0.4:5002
LABELER_LABELER_OWNER_DID: did:plc:pddp4xt5lgnv2qsegbzzs4xg
LABELER_LABELER_DATA_DIR: /var/lib/atcr-labeler
LABELER_SERVER_TEST_MODE: true
LABELER_LOG_LEVEL: debug
LOG_SHIPPER_BACKEND: victoria
LOG_SHIPPER_URL: http://172.28.0.10:9428
logging:
driver: json-file
options:
max-size: "10m"
max-file: "1"
build:
context: .
dockerfile: Dockerfile.dev
args:
AIR_CONFIG: .air.labeler.toml
image: atcr-labeler-dev:latest
container_name: atcr-labeler
ports:
- "5002:5002"
volumes:
# Mount source code for Air hot reload
- .:/app:z
- go-mod-cache:/go/pkg/mod
# Persist signing key + did.txt + label database across container restarts so
# dev signatures stay verifiable. Wipe with `docker compose down -v` to reset.
- atcr-labeler:/var/lib/atcr-labeler
restart: unless-stopped
dns:
- 8.8.8.8
- 1.1.1.1
networks:
atcr-network:
ipv4_address: 172.28.0.4
# Victoria Logs for centralized log storage
# Uncomment to enable, then set LOG_SHIPPER_* env vars above
victorialogs:
image: victoriametrics/victoria-logs:latest
container_name: victorialogs
ports:
- "9428:9428"
volumes:
- victorialogs-data:/victoria-logs-data
command:
- "-storageDataPath=/victoria-logs-data"
- "-retentionPeriod=7d"
restart: unless-stopped
networks:
atcr-network:
ipv4_address: 172.28.0.10
networks:
atcr-network:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/24
volumes:
atcr-hold:
atcr-labeler:
atcr-auth:
atcr-ui:
go-mod-cache:
victorialogs-data: