Young's Mind
05 Operations

πŸ”΄ The tunnel is an unauthenticated path into the LAB default profile

Status: OPEN β€” 1 active diagnostic equivalent, not a card, because the fix spans three hosts and the credential step is Min's lane.

πŸ”΄ The tunnel is an unauthenticated path into the LAB default profile

Status: OPEN β€” 1 active diagnostic equivalent, not a card, because the fix spans three hosts and the credential step is Min's lane.

The finding, in one paragraph

Tam's lab-a2a-tunnel.service forwards 127.0.0.1:19100 β†’ LAB 127.0.0.1:9900. :9900 is the LAB default profile A2A endpoint, running in localhost-only mode because no A2A_BEARER_TOKEN is configured. In that mode the adapter authenticates by socket address, so the tunnel's terminating SSH connection on LAB is indistinguishable from a local process. I dispatched a real JSON-RPC method through the tunnel from the laptop with no credentials and it executed. The default profile advertises terminal, file, browser, code_execution and cronjob, and it is the profile whose .env holds OPENCODE_GO_API_KEY and OPENCODE_ZEN_API_KEY.

Evidence (measured, both hosts, 2026-10-20 ~22:35 AEDT)

#CheckCommandResult
1:9900 serves the default profilecurl http://127.0.0.1:9900/healthserved_agents:[{slug:default, profile:default}]
2It is in localhost-only modegrep 'serving Agent Card' ~/.hermes/logs/gateway.log | tail -1http://127.0.0.1:9900 (localhost-only) as 'hermes-LAB'
3No token anywhere on LABgrep -c '^A2A_' /home/lab/.hermes/.env + all 5 profile .env0 in all 6 files
4The mode means socket-identityplugins/platforms/a2a/security.py:74,88-90localhost_only() β†’ authenticate() returns ip:<client_ip> for any request
5The tunnel is live and enabledsystemctl --user is-enabled lab-a2a-tunnel.service (laptop)enabled; ss shows 127.0.0.1:19100 LISTEN (ssh pid 354087)
6An unauthenticated method executed through the tunnelcurl -X POST http://127.0.0.1:19100/ … method=GetTask (from laptop)HTTP 200, real JSON-RPC error -32001 task not found β€” identical to the LAB loopback control
7The surface is broadcurl http://127.0.0.1:9900/.well-known/agent-card.json35 skills incl. terminal, file, browser, code_execution, cronjob
8Credentials live in that profilegrep -oE '^[A-Za-z0-9_]*(API_KEY|TOKEN)[A-Za-z0-9_]*' /home/lab/.hermes/.envOPENCODE_GO_API_KEY, OPENCODE_ZEN_API_KEY (names only; no values read)
9The laptop's own A2A has no authgrep -ciE 'authorization|bearer|401' ~/tam-a2a/server.py0 hits; binds 0.0.0.0:8443; runs hermes --profile tam -z … -t a2a,web,file,terminal
10Latent second exposuresocat TCP-LISTEN:9901,fork,bind=0.0.0.0 (a2a-relay.service)bound all-interfaces; no ufw rule for 9901, default deny (incoming) β†’ not reachable from LAB (HTTP 000). Latent, not live.

⚠️ The control that would have caught this returned a FALSE GREEN

A narrow, safe probe through the tunnel asked the LAB default-profile agent for the NAMES only of credential-shaped env vars. It answered:

"there is no default-profile .env at /home/lab/.hermes/profiles/default/.env, so nothing to enumerate"

False β€” and false in the dangerous direction. The default profile has no profiles/default/ directory, because the default profile IS the HERMES_HOME root: /home/lab/.hermes/.env (mode 600, holds both provider keys). The probe looked in the wrong place, found nothing, and reported "no exposure".

A security control that measures the wrong path returns "clean" and is indistinguishable from a secure system. The fix is one line in the probe β€” but until it is fixed, no credential-enumeration probe against this fleet can be trusted to say "none found".

Honest limits

  • No secret value was obtained and none was attempted. The probe asked for names only and returned an empty list (wrong path); I did not re-probe for values.
  • The Β§6 proof is a read-only JSON-RPC method (GetTask on a non-existent id). It shows the method dispatched and executed; it does not demonstrate a write, a tool call, or a credential read through the tunnel.
  • I could not get the laptop's unauthenticated :8443 endpoint to execute anything β€” the agent behind it refused a harmless read-only request on prompt-injection grounds. That refusal was correct and is recorded as the peer's success, not the probe's failure. It means the server has no auth and the agent currently applies judgement β€” a control that is not a control.
  • No configuration, unit, or credential was modified. Read-only lane.
  • ⚠️ One temporary artefact was created on the Pulse host and removed: a throwaway TCP listener (127.0.0.1:19555 β†’ later 0.0.0.0:19555) under a temporary user unit wire-probe.service, used to capture the raw bytes of one inbound request and prove the transport carries full messages (Β§7a of the study file). Torn down and verified afterwards: unit stopped, disabled and its file deleted; daemon-reload run; the temporary ufw rule deleted; port 19555 free; no ufw rule; unit inactive. The tam-a2a server, its unit, and its configuration were never touched.
  • ⚠️ One probe of mine returned an empty body and it was NOT a dropped request. The request arrived (the Pulse state DB holds it at 2848 chars); the reply is what failed, because tam-a2a/server.py returns only when hermes --profile tam finishes, under a 480 s cap, and that endpoint was carrying 27 concurrent probe messages with 7 hitting the same timeout. An empty response is not evidence of a dropped request β€” measure the request path (the stored inbound record, or the raw bytes).

The fix, in order, each step independently verifiable

#ActionHostVerify
1Set A2A_BEARER_TOKEN on the default profile + per-peer auth: {type: bearer, token: …} rowsLABunauthenticated request through the tunnel β†’ 401; with token β†’ 200. Requires a gateway restart (it cannot restart itself).
2Add a bearer check to tam-a2a/server.py (and eva-a2a, eve-a2a), or retire them for the Hermes adapter on the tam profile (which already has a 20-char token)laptopunauthenticated message/send β†’ 401 and no hermes --profile tam spawned
3Rebind a2a-relay to 127.0.0.1 or add an explicit ufw rule for 9901laptopss -tlnp shows the bind address; off-host connect refused
4Rotate OPENCODE_GO_API_KEY / OPENCODE_ZEN_API_KEY (shared across all 6 profiles)LAB + laptopOwner: Min β€” credential lane, not mine
5Fix the credential-enumeration probe path (default profile = HERMES_HOME root)wherever the probe livesprobe against a known-key profile returns the names

Rotation (step 4) is precautionary, not remediation of a confirmed breach. The exposure is latent-to-reachable; I have no evidence it was exercised. The keys are cheap to rotate and shared across every profile, so rotate rather than reason about it.

Not wrong, and worth saying

  • The tunnel is the right shape for the stranded-card problem β€” it gives Pulse a real route into LAB instead of a card no dispatcher can spawn. The fix is to authenticate the path, not to remove it.
  • :9900 still binds 127.0.0.1 only. Sol's card t_0ce0920b (bind LAN-only + firewall rule for .34) is still open and still correct; the tunnel does not satisfy it and does not close it.
  • LAB's dispatch_profiles is now echo, min, mira, nova, sol β€” correctly excluding tam. That part of the stranded-card fix landed and holds.
  • The tam profile's A2A_BEARER_TOKEN already exists (A2A_HOST=0.0.0.0, length 20) β€” but it protects the Hermes adapter, not the legacy tam-a2a/server.py that owns :8443, and not :9900 on LAB.
Copyright Β© 2026