mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
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.
165 lines
7.5 KiB
YAML
165 lines
7.5 KiB
YAML
# ATCR AppView Configuration
|
|
# Generated with defaults — edit as needed.
|
|
|
|
# Configuration format version.
|
|
version: "0.1"
|
|
# Log level: debug, info, warn, error.
|
|
log_level: info
|
|
# Remote log shipping settings.
|
|
log_shipper:
|
|
# Log shipping backend: "victoria", "opensearch", or "loki". Empty disables shipping.
|
|
backend: ""
|
|
# Remote log service endpoint, e.g. "http://victorialogs:9428".
|
|
url: ""
|
|
# Number of log entries to buffer before flushing to the remote service.
|
|
batch_size: 100
|
|
# Maximum time between flushes, even if batch is not full.
|
|
flush_interval: 5s
|
|
# Basic auth username for the log service (optional).
|
|
username: ""
|
|
# Basic auth password for the log service (optional).
|
|
password: ""
|
|
# HTTP server and identity settings.
|
|
server:
|
|
# Listen address, e.g. ":5000" or "127.0.0.1:5000".
|
|
addr: :5000
|
|
# Public-facing URL for OAuth callbacks and JWT realm. Auto-detected if empty.
|
|
base_url: ""
|
|
# Allows HTTP (not HTTPS) for DID resolution and uses transition:generic OAuth scope.
|
|
test_mode: false
|
|
# Display name shown on OAuth authorization screens.
|
|
client_name: AT Container Registry
|
|
# Short name used in page titles and browser tabs.
|
|
client_short_name: ATCR
|
|
# Separate domains for OCI registry API (e.g. ["buoy.cr"]). First is primary. Browser visits redirect to BaseURL.
|
|
registry_domains: [127.0.0.1:5000, atcr.io]
|
|
# DIDs of holds this appview manages billing for (REQUIRED). The first entry is the default blob-storage hold. Tier updates are pushed to these holds.
|
|
managed_holds:
|
|
- did:web:172.28.0.3%3A8080
|
|
# Web UI settings.
|
|
ui:
|
|
# SQLite/libSQL database for OAuth sessions, stars, pull counts, and device approvals.
|
|
database_path: /var/lib/atcr/ui.db
|
|
# Visual theme name (e.g. "seamark"). Empty uses default atcr.io branding.
|
|
theme: "seamark"
|
|
# libSQL sync URL (libsql://...). Works with Turso cloud or self-hosted libsql-server. Leave empty for local-only SQLite.
|
|
libsql_sync_url: ""
|
|
# Auth token for libSQL sync. Required if libsql_sync_url is set.
|
|
libsql_auth_token: ""
|
|
# How often to sync with remote libSQL server. Default: 60s.
|
|
libsql_sync_interval: 1m0s
|
|
# Source code URL displayed in the footer "Source" link. Defaults to the upstream ATCR project.
|
|
source_url: https://tangled.org/evan.jarrett.net/at-container-registry
|
|
# Health check and cache settings.
|
|
health:
|
|
# How long to cache hold health check results.
|
|
cache_ttl: 15m0s
|
|
# How often to refresh hold health checks.
|
|
check_interval: 15m0s
|
|
# ATProto Jetstream event stream settings.
|
|
jetstream:
|
|
# Jetstream WebSocket endpoints, tried in order on failure.
|
|
urls:
|
|
- wss://jetstream2.us-west.bsky.network/subscribe
|
|
- wss://jetstream1.us-west.bsky.network/subscribe
|
|
- wss://jetstream2.us-east.bsky.network/subscribe
|
|
- wss://jetstream1.us-east.bsky.network/subscribe
|
|
# Sync existing records from PDS on startup.
|
|
backfill_enabled: true
|
|
# How often to re-run backfill to catch missed events. Set to 0 to only backfill on startup.
|
|
backfill_interval: 24h0m0s
|
|
# Endpoints used for backfill. MUST support com.atproto.sync.listReposByCollection. Tried in order on failure.
|
|
relay_endpoints:
|
|
- https://relay1.us-east.bsky.network
|
|
- https://relay1.us-west.bsky.network
|
|
# JWT authentication settings.
|
|
auth:
|
|
# X.509 certificate matching the JWT signing key (auto-generated on each boot from the JWT key in the database).
|
|
cert_path: /var/lib/atcr/auth/private-key.crt
|
|
# Credential helper download settings.
|
|
credential_helper:
|
|
# Tangled repository URL for credential helper downloads.
|
|
tangled_repo: ""
|
|
# Legal page customization for self-hosted instances.
|
|
legal:
|
|
# Organization name for Terms of Service and Privacy Policy. Defaults to server.client_name.
|
|
company_name: ""
|
|
# Governing law jurisdiction for legal terms.
|
|
jurisdiction: ""
|
|
# AI-powered image advisor settings.
|
|
ai:
|
|
# Anthropic API key for AI Image Advisor. Also reads CLAUDE_API_KEY env var as fallback.
|
|
api_key: ""
|
|
# ATProto labeler for content moderation (DMCA takedowns).
|
|
labeler:
|
|
# DID of the ATProto labeler (did:plc:... or did:web:...). Empty disables label filtering.
|
|
did: ""
|
|
# Stripe billing integration (requires -tags billing build).
|
|
billing:
|
|
# Stripe secret key. Can also be set via STRIPE_SECRET_KEY env var (takes precedence). Billing is enabled automatically when set.
|
|
stripe_secret_key: ""
|
|
# Stripe webhook signing secret. Can also be set via STRIPE_WEBHOOK_SECRET env var (takes precedence).
|
|
webhook_secret: ""
|
|
# ISO 4217 currency code (e.g. "usd").
|
|
currency: usd
|
|
# Redirect URL after successful checkout. Use {base_url} placeholder.
|
|
success_url: '{base_url}/settings/billing'
|
|
# Redirect URL after cancelled checkout. Use {base_url} placeholder.
|
|
cancel_url: '{base_url}/settings/billing'
|
|
# Subscription tiers ordered by rank (lowest to highest).
|
|
tiers:
|
|
- # Tier name. Position in list determines rank (0-based).
|
|
name: Free
|
|
# Short description shown on the plan card.
|
|
description: Get started with basic storage
|
|
# List of features included in this tier.
|
|
features: []
|
|
# Stripe price ID for monthly billing. Empty = free tier.
|
|
stripe_price_monthly: ""
|
|
# Stripe price ID for yearly billing.
|
|
stripe_price_yearly: ""
|
|
# Maximum webhooks for this tier (-1 = unlimited).
|
|
max_webhooks: 1
|
|
# Allow all webhook trigger types (not just first-scan).
|
|
webhook_all_triggers: false
|
|
# Enable AI Image Advisor for this tier.
|
|
ai_advisor: false
|
|
# Show supporter badge on user profiles for subscribers at this tier.
|
|
supporter_badge: false
|
|
- # Tier name. Position in list determines rank (0-based).
|
|
name: Supporter
|
|
# Short description shown on the plan card.
|
|
description: Get started with basic storage
|
|
# List of features included in this tier.
|
|
features: []
|
|
# Stripe price ID for monthly billing. Empty = free tier.
|
|
stripe_price_monthly: ""
|
|
# Stripe price ID for yearly billing.
|
|
stripe_price_yearly: "price_1SmK1mRROAC4bYmSwhTQ7RY9"
|
|
# Maximum webhooks for this tier (-1 = unlimited).
|
|
max_webhooks: 1
|
|
# Allow all webhook trigger types (not just first-scan).
|
|
webhook_all_triggers: true
|
|
# Enable AI Image Advisor for this tier.
|
|
ai_advisor: true
|
|
# Show supporter badge on user profiles for subscribers at this tier.
|
|
supporter_badge: true
|
|
- # Tier name. Position in list determines rank (0-based).
|
|
name: Pro
|
|
# Short description shown on the plan card.
|
|
description: More storage with scan-on-push
|
|
# List of features included in this tier.
|
|
features: []
|
|
# Stripe price ID for monthly billing. Empty = free tier.
|
|
stripe_price_monthly: "price_1SmK4QRROAC4bYmSxpr35HUl"
|
|
# Stripe price ID for yearly billing.
|
|
stripe_price_yearly: "price_1SmJuLRROAC4bYmSUgVCwZWo"
|
|
# Maximum webhooks for this tier (-1 = unlimited).
|
|
max_webhooks: 10
|
|
# Allow all webhook trigger types (not just first-scan).
|
|
webhook_all_triggers: true
|
|
# Enable AI Image Advisor for this tier.
|
|
ai_advisor: true
|
|
# Show supporter badge on user profiles for subscribers at this tier.
|
|
supporter_badge: true
|