# DC AMD Proxy

Полный воспроизводимый install/recovery/migration-контракт находится в
[`OPERATIONS.md`](./OPERATIONS.md). Этот README фиксирует краткую runtime-модель;
операционные изменения должны одновременно обновлять runbook.

`dc-amd-proxy` is the dedicated, controllable boundary between NODE.DC Map
Gateway and the adjacent AMD VPN machine. It is deliberately independent from
the historic `proxy-contur` service.

After pairing it has no direct Cesium egress: every approved Cesium/Bing
request is tunnelled through the authenticated AMD connector
(`172.22.0.183:8791`). The NAS always addresses this stable LAN endpoint;
the AMD host alone owns its changing VPN exit.

This is a single restricted transport path, not a per-tile relay chain. The
NAS keeps a small, bounded pool of HTTP/1.1 TLS tunnels per approved upstream
host, so adjacent tile requests reuse the existing `NAS → AMD → VPN` channel.
The pool is limited to eight active and four idle sockets per host by default;
it cannot become a general-purpose proxy or consume an unbounded amount of the
workstation's network resources.

`/status` exposes only safe operational counters: active/idle/queued sockets
per approved origin, the retained peak queue, logical/in-flight/terminal
requests, tunnels opened, reused sockets, bounded retries, complete and partial
response bytes, separate queue/CONNECT+TLS/TTFB/retry/total timing, body-stream
failures, client aborts, and the last short error code. Every accepted egress
request is accounted exactly once even when the browser disconnects while it
is queued, connecting, following a redirect, waiting for headers, or retrying.
The status never contains an Ion token, connector token, full URL, or query
parameters.

An upstream `Authorization` header is retained only across same-origin
redirects. A redirect from the Cesium API origin to an assets or Bing origin
is followed only after that header is removed, so an API/master bearer cannot
cross provider credential boundaries.

Only an idempotent `GET`/`HEAD` that loses an already reused keep-alive socket
is retried, once. The failed reused-socket attempt and the terminal retry
attempt are logged separately with numeric transport timings and no URL or
credential fields. A response body that makes no progress for 30 seconds is
terminated and counted as a stream failure, so a stalled VPN connection cannot
occupy one of the eight per-origin sockets forever. These limits are local to
Cesium egress and do not modify routing or connectivity of the AMD workstation.

## Pairing and operational state

The active service has an explicitly narrow first-pair contract:

- `POST /api/pair` accepts exactly one connector access token and only from
  the configured AMD LAN source address;
- the AMD operator invokes a supplied local script; it reads the token inside
  the AMD installation and sends it without printing it or putting it in chat;
- the NAS writes the paired value only to its private runtime directory, never
  to `.env`, SMB inbox, a deploy artifact, browser code, or logs;
- the Map Gateway fetch contract (`/proxy/cesium/fetch`) remains private to
  the NAS LAN address and still requires the runner-synchronised map egress
  token;
- the service follows only the fixed Cesium Ion/Bing host allowlist and cannot
  fall back to direct NAS Internet egress.

## Deployment order

1. Deploy this service and confirm `/healthz` reports `awaiting_pair`.
   Map Gateway remains on its existing egress during this step.
2. On AMD, run the supplied one-time pairing script. It sends the locally-held
   connector secret directly to the NAS and reports no secret value.
3. Verify the private Map Gateway request through this service.
4. Apply the separate, minimal Map Gateway switch patch. It changes only its
   private egress hostname from the historical service to `dc-amd-proxy`.

The initial 001 deployment was intentionally inert. It is superseded by this
active configuration; it did not alter NAS routing, the NAS VPN/Tailscale
configuration, or the Windows workstation's VPN configuration.

## Why host networking is narrow here

Synology's Docker published-port proxy replaces the caller address before it
reaches a bridged container. That makes a strict one-time AMD source-address
check impossible. This service therefore uses the host network namespace but
binds its own HTTP listener only to `172.22.0.222:8790`; it is not bound to all
NAS interfaces. No host route, VPN, DNS, Tailscale setting or firewall rule is
changed. The forwarding code still has one hard-coded outbound path only:
the authenticated AMD connector.
