diff --git a/apps/main-app/src/routes/xrpc.ts b/apps/main-app/src/routes/xrpc.ts index 638a3f9..a643c69 100644 --- a/apps/main-app/src/routes/xrpc.ts +++ b/apps/main-app/src/routes/xrpc.ts @@ -58,7 +58,12 @@ const serviceJwtVerifier = new ServiceJwtVerifier({ serviceDid: SERVICE_DID as any, resolver: new CompositeDidDocumentResolver({ methods: { - plc: new PlcDidDocumentResolver(), + // Defaults to the public PLC directory. A local development network runs its own, + // and service-auth verification fails against the public one because the DIDs + // only exist locally. + plc: new PlcDidDocumentResolver( + Bun.env.WISP_PLC_DIRECTORY_URL ? { apiUrl: Bun.env.WISP_PLC_DIRECTORY_URL } : undefined, + ), web: new WebDidDocumentResolver(), }, }), diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 4455369..0307ca9 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -131,6 +131,29 @@ services: timeout: 3s retries: 30 + # The PDS proxies XRPC to the Wisp service by resolving its `did:web` document. + # @atproto/identity only allows plain HTTP when the hostname is exactly `localhost`, + # which inside the PDS container means the container itself. This sidecar shares the + # PDS's network namespace and forwards that port to the host, so `did:web:localhost%3A8000` + # reaches the Wisp service running on the developer's machine. Production uses a real + # HTTPS `did:web`, so this exists only to make the same code path testable locally. + pds-host-bridge: + profiles: [dev] + image: alpine/socat:1.8.0.0 + depends_on: + pds: + condition: service_healthy + network_mode: "service:pds" + # Both loopback families are bridged: Node may resolve `localhost` to either, and a + # listener on only one of them fails intermittently depending on resolution order. + # The image's entrypoint is socat itself, so it is overridden to run both listeners. + entrypoint: ["/bin/sh", "-c"] + command: + - >- + socat TCP4-LISTEN:8000,fork,reuseaddr,bind=127.0.0.1 TCP:host.docker.internal:8000 & + socat TCP6-LISTEN:8000,fork,reuseaddr,bind=[::1] TCP:host.docker.internal:8000 + restart: unless-stopped + pds-init: profiles: [dev] image: alpine:3.20