From a7c7db68a9edab299a30963a2e75a96e76cf7297 Mon Sep 17 00:00:00 2001 From: Evan Jarrett Date: Sun, 9 Aug 2026 20:49:24 -0500 Subject: [PATCH] dev: share the hold's network namespace with the appview The hold needs did:web:localhost%3A8080 so that the aud it presents in service tokens is accepted: atproto only allows a port-bearing did:web on localhost, and did:web:%3A is rejected by real PDSes. Joining the hold's netns (network_mode: service:atcr-hold) lets both reach each other on localhost. The appview's 5000 is published on the hold service, which owns the namespace. Dev compose only; nothing in deploy/ or CI references this file. Co-Authored-By: Claude Opus 5 (1M context) --- docker-compose.yml | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index aef7073..6665b7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,8 +5,15 @@ services: dockerfile: Dockerfile.dev image: atcr-appview-dev:latest container_name: atcr-appview - ports: - - "5000:5000" + # Option B: share the hold's network namespace so "localhost:8080" reaches the + # hold from inside appview. This lets the hold use did:web:localhost%3A8080, + # which is the only port-bearing did:web atproto accepts as a service-token + # `aud` (real PDSes reject did:web:%3A since atproto only allows + # ports on localhost). Ports for appview are published on the atcr-hold + # service below, since it owns the namespace. + network_mode: "service:atcr-hold" + depends_on: + - atcr-hold env_file: - ../atcr-secrets.env # Optional: Load from .env.appview file (create from .env.appview.example) @@ -19,7 +26,7 @@ services: # 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 + ATCR_SERVER_MANAGED_HOLDS: did:web:localhost%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 @@ -42,12 +49,9 @@ services: # 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 + # No `networks:`/`dns:`/`ports:` here — appview inherits atcr-hold's network + # namespace (see network_mode above). It is reachable on atcr-network at the + # hold's IP (172.28.0.3) and shares the hold's loopback. # The AppView is stateless - all storage is external: # - Manifests/Tags -> ATProto PDS (via middleware) # - Blobs/Layers -> Hold service (via ProxyBlobStore) @@ -59,9 +63,15 @@ services: # 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 + # appview shares this container's netns (network_mode: service:atcr-hold), + # so it is reachable at 127.0.0.1:5000 from here. Must match appview's + # auto-detected dev DID (addr :5000 -> http://127.0.0.1:5000). + HOLD_SERVER_APPVIEW_DID: did:web:127.0.0.1%3A5000 HOLD_SCANNER_SECRET: dev-secret - HOLD_SERVER_PUBLIC_URL: http://172.28.0.3:8080 + # localhost (not the 172.28 IP) so the hold's own DID is + # did:web:localhost%3A8080 — the aud appview presents, and the only + # port-bearing did:web a real PDS accepts. + HOLD_SERVER_PUBLIC_URL: http://localhost:8080 HOLD_REGISTRATION_OWNER_DID: did:plc:pddp4xt5lgnv2qsegbzzs4xg HOLD_REGISTRATION_ALLOW_ALL_CREW: true HOLD_SERVER_TEST_MODE: true @@ -92,6 +102,8 @@ services: container_name: atcr-hold ports: - "8080:8080" + # appview shares this namespace, so its port is published here. + - "5000:5000" volumes: # Mount source code for Air hot reload - .:/app:z