fix(13-02): retire the Python model-proxy sidecar and the OpenRouter open relay

Closes the live production exposure this plan targets: /aiui/api/claude/
and /aiui/api/ollama/ proxied straight through with "no session gate
needed", and /aiui/api/openrouter/ was a plain unauthenticated relay to a
paid third-party API the node holds no key for (T-13-08/T-13-09/T-13-10).

image-recipe/configs/nginx-archipelago.conf (BOTH server blocks, ~line 49
and ~line 961 — a fix applied to only one leaves the exposure live on
whichever block serves the request, T-13-15):
- /aiui/api/claude/ and /aiui/api/ollama/ proxy_pass re-pointed from
  127.0.0.1:3142 / 127.0.0.1:11434 to the Rust daemon at 127.0.0.1:5678
  (no trailing path component, so the daemon's own prefix match sees the
  full request URI)
- Forward the session Cookie header to the daemon so it can re-derive auth
- location /aiui/api/openrouter/ deleted outright in both blocks
- Old comment "API key managed by proxy, no session gate needed" (the
  reasoning error that produced the exposure) replaced with rationale

scripts/deploy-to-target.sh: deleted the embedded claude-api-proxy.py
heredoc, its systemd unit creation/enable/restart, the ANTHROPIC_API_KEY
extraction, and the 3141->3142 sed fixups. Added an unconditional step that
stops/disables/removes any pre-existing claude-api-proxy unit and deletes
/opt/archipelago/claude-api-proxy.py and
<data_dir>/secrets/claude-api-proxy.env on every deploy — so
already-provisioned nodes actually lose the old unauthenticated listener,
not just newly-deployed ones.

scripts/setup-aiui-server.sh: dropped the hard ANTHROPIC_API_KEY
requirement and the patch-nginx-claude.py step; the script's remaining job
is the AIUI dist rsync. (Also drops the FileBrowser-fix step that lived
here — that logic already exists, and is kept, in deploy-to-target.sh; this
script narrows to exactly what its rewritten header now says it does.)

core/archipelago/src/api/rpc/system/handlers.rs: `claude_api_key` setting
branch no longer writes a second key copy to secrets/claude-api-proxy.env
or restarts claude-api-proxy. secrets/claude-api-key (0600) remains the
single ledger, with a comment naming it as such.

`cargo build --package archipelago` succeeds. Verified via grep against
every acceptance criterion in 13-02-PLAN.md's Task 2 (openrouter count 0,
3142 gone from nginx, both location blocks present, PORT=3142 gone,
claude-api-proxy gone from handlers.rs, secrets/claude-api-key present).
Task 3 (real-node curl/systemd verification, S-15) is NOT done in this
commit — see 13-02-SUMMARY.md.

Continues WIP checkpoint 13b576da (reset --soft, recommitted atomically
per task per plan protocol).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-03 14:44:25 -04:00
co-authored by Claude Opus 5
parent 97921d9932
commit b28cc3eeaa
4 changed files with 76 additions and 240 deletions
+28 -30
View File
@@ -46,12 +46,21 @@ server {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# AIUI Claude API proxy (API key managed by proxy, no session gate needed)
# AIUI Claude API proxy — re-pointed to the Rust daemon (127.0.0.1:5678),
# which enforces the session cookie itself and reads the node's single
# key ledger (data_dir/secrets/claude-api-key). The old comment here said
# "API key managed by proxy, no session gate needed" — that confuses key
# *secrecy* with spend *authorization* and is the reasoning error that
# made this an unauthenticated door into a paid API (T-13-08/T-13-09).
# Do not point this at a standalone process again. No trailing path on
# proxy_pass: nginx forwards the request URI unmodified so the daemon's
# own prefix match sees the full /aiui/api/claude/... path.
location /aiui/api/claude/ {
proxy_pass http://127.0.0.1:3142/;
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_buffering off;
proxy_cache off;
proxy_connect_timeout 120s;
@@ -59,25 +68,18 @@ server {
proxy_send_timeout 120s;
}
# AIUI OpenRouter API proxy (API key managed by proxy, no session gate needed)
location /aiui/api/openrouter/ {
set $upstream_1 "https://openrouter.ai/api/";
proxy_pass $upstream_1;
proxy_http_version 1.1;
proxy_set_header Host openrouter.ai;
proxy_ssl_server_name on;
proxy_connect_timeout 120s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
# AIUI Ollama (local AI) proxy — localhost:11434
# AIUI Ollama (local AI) proxy — same daemon, same session gate as above.
# The standalone AIUI OpenRouter relay that used to live here is deleted
# outright: the node holds no key for that backend, it is not in the
# model backend chain, and an unauthenticated proxy_pass to a paid
# third-party API from the node's IP was a plain open relay (T-13-10).
# AIUI's own standalone/dev mode keeps its own proxy and is unaffected.
location /aiui/api/ollama/ {
proxy_pass http://127.0.0.1:11434/;
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_buffering off;
proxy_cache off;
proxy_connect_timeout 120s;
@@ -958,11 +960,17 @@ server {
try_files $uri $uri/ /aiui/index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# See the HTTP server block above for the full rationale: re-pointed to
# the session-gated Rust daemon (T-13-08/T-13-09), OpenRouter relay
# deleted outright (T-13-10). Both server blocks must carry this fix —
# a change applied to only one leaves the exposure live on whichever
# block actually serves the request (T-13-15).
location /aiui/api/claude/ {
proxy_pass http://127.0.0.1:3142/;
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_buffering off;
proxy_cache off;
proxy_connect_timeout 120s;
@@ -970,27 +978,17 @@ server {
proxy_send_timeout 120s;
}
location /aiui/api/ollama/ {
proxy_pass http://127.0.0.1:11434/;
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_buffering off;
proxy_cache off;
proxy_connect_timeout 120s;
proxy_read_timeout 300s;
proxy_send_timeout 120s;
}
location /aiui/api/openrouter/ {
set $upstream_6 "https://openrouter.ai/api/";
proxy_pass $upstream_6;
proxy_http_version 1.1;
proxy_set_header Host openrouter.ai;
proxy_ssl_server_name on;
proxy_connect_timeout 120s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
# Icons, favicon, manifest — always revalidate (no heuristic caching)
location ~* ^/(favicon\.ico|manifest\.webmanifest|assets/icon/) {