# The full Tessera network, containerized. See docs/DEPLOY.md. # # you --▶ CLIENT-PROXY --▶ RELAY --(internal)--▶ EXIT --(its egress IP)--▶ dest # │ ▲ # └──▶ ISSUER (authority) ──────────┘ one ARC key domain with EXIT # (the client connects to the ISSUER to obtain a credential — so the issuer sees # the client's IP at issuance; ARC still can't link it to browsing. In a real # deployment the client reaches the issuer over Tor too. All-local demo => moot.) # # - ISSUER mints PoW-gated credentials and shares ONE ARC key with this one EXIT # (ARC is keyed-verification — the exit needs that key to verify presentations). # Do not scale `exit` with this same key. A multi-exit fleet needs one # issuer/key file/key pin per independent exit domain. # - RELAY is the credential-blind first hop (learns {client, exit}, never the dest). # - EXIT is credential-gated (learns {dest, a valid token}, never the client) and # egresses from its own IP — which the destination sees and which MUST be clean # (the external piece Tessera cannot manufacture). # - CLIENT-PROXY is what you point a browser/curl at: it obtains a credential from # the issuer and routes each request through the loop on a fresh unlinkable token. # # Run locally: docker compose -f deploy/docker-compose.yaml up --build # then: curl -x http://127.0.0.1:8120 https://example.com # Deploy in a TEE: see deploy/dstack/docker-compose.yaml + docs/DEPLOY.md. # Shared container hardening, merged into every service below. These are # user-space network apps that bind high ports and write only to mounted volumes, # so they need no Linux capabilities, no privilege escalation, and no writable # root filesystem (a tmpfs covers /tmp; the key volume stays writable). Logs are # size-capped so a long-running node can't fill the host disk. x-hardening: &hardening read_only: true tmpfs: - /tmp cap_drop: - ALL security_opt: - "no-new-privileges:true" logging: driver: json-file options: max-size: "10m" max-file: "3" restart: unless-stopped services: issuer: <<: *hardening build: context: .. dockerfile: Dockerfile image: tessera-node:0.1.0 command: ["tessera-issuer"] environment: TESSERA_ISSUER_LISTEN: "0.0.0.0:8121" # The authority creates the shared ARC key on this volume; the exit loads it. TESSERA_KEY_FILE: "/keys/server.key" TESSERA_POW_DIFFICULTY: "14" volumes: - tessera-key:/keys exit: <<: *hardening image: tessera-node:0.1.0 command: ["tessera-proxy"] environment: # Bind on all interfaces inside the container; the exit's own IP is the # egress the destination sees (set TESSERA_UPSTREAM=tor:HOST:PORT to egress # via a Tor SOCKS proxy instead — but then the destination sees a Tor exit). TESSERA_LISTEN: "0.0.0.0:8118" TESSERA_UPSTREAM: "direct" # Same shared key as the issuer, so credentials it mints verify here. TESSERA_KEY_FILE: "/keys/server.key" # Durable replay protection for this single exit across restarts. This is # not a distributed multi-exit store. TESSERA_SPENT_TAG_FILE: "/keys/spent-tags.log" # Counts-only health endpoint (loopback) for the healthcheck below. TESSERA_HEALTH_LISTEN: "127.0.0.1:9118" volumes: - tessera-key:/keys depends_on: - issuer healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9118/readyz"] interval: 30s timeout: 5s retries: 3 # The exit binds its port only AFTER establishing the shared ARC key, which # polls up to ~60s for the issuer to mint it on a cold start — give the # readiness probe that long before it can mark the node unhealthy. start_period: 60s relay: <<: *hardening image: tessera-node:0.1.0 command: ["tessera-relay"] environment: TESSERA_RELAY_LISTEN: "0.0.0.0:8119" TESSERA_EXIT_ADDR: "exit:8118" depends_on: - exit client: <<: *hardening image: tessera-node:0.1.0 command: ["tessera-client"] environment: TESSERA_ISSUER: "issuer:8121" TESSERA_RELAY: "relay:8119" TESSERA_EXIT: "exit:8118" TESSERA_CLIENT_LISTEN: "0.0.0.0:8120" depends_on: - relay - issuer ports: # The client entry point you point curl/your browser at. Bound to localhost # only — this is your machine's local proxy, not a public service. - "127.0.0.1:8120:8120" volumes: # Holds the shared ARC server key. In a real multi-node/TEE deployment this is # exactly what the dstack KMS should seal to the enclaves (docs/DEPLOY.md); # a plain volume is the local-demo stand-in. This volume is one key domain: # add more exits with their own issuer/key volume/pin, not by sharing this one. tessera-key: