hyp detach claude --purge followed by hyp attach claude silently lands in base-URL mode, and nothing on any surface says so.
Found on a coworker's real macOS install while diagnosing a separate two-password-dialog report. Reproduces by reading on master at a23a3ea (the tagged, unpublished v1.23.0).
Repro
On a machine with proxy_mode: true in the effective config and Claude attached in proxy mode:
hyp detach claude --purge
hyp attach claude
Expected: attach re-establishes proxy mode, minting and trusting a CA (one password dialog).
Actual: attach reports success and writes a base-URL marker. No CA is minted, no keychain dialog appears, NODE_EXTRA_CA_CERTS/HTTPS_PROXY are never written, and Remote Control inbound stays broken. No warning is printed and no diagnostic appears in hyp status.
Why
Three correct-in-isolation behaviours compose into a dead end.
1. Attach decides its mode from the CA file, not from config.
hypaware-core/plugins-workspace/claude/src/index.js:200
const ca = await readLocalCaInfo({ stateRoot: defaultStateRoot(ctx.env) })
const result = await attach({
...,
...(ca ? { mode: MODE_PROXY, caCertPath: ca.certPath } : {}),
})
This is deliberate (LLP 0232 #proxy-attach-preflight): "the mode it writes is the mode the gateway is serving, not the one someone asked for." Attach never mints a CA; only the daemon does, in prepareInterception (hypaware-core/plugins-workspace/ai-gateway/src/source.js:479), which runs at gateway source launch or reload.
2. --purge deletes the CA but leaves proxy_mode on and does not restart the daemon.
purgeProxyTrustResidue (src/core/commands/clients.js:1586) removes the CA files, the keychain trust, and the launchd env. Nothing in the detach path writes proxy_mode, so the config still claims proxy mode while no CA exists. The running daemon holds its already-loaded CA in memory and has no reason to relaunch the source, so nothing re-mints.
3. The migration prompt that would repair this returns early.
src/core/commands/clients.js:842
if (effectiveGateway?.config?.proxy_mode === true) return
The offer to switch an install to proxy mode is skipped precisely because the config says proxy mode is already on. The config is right about intent and wrong about reality, and this is the one place that could notice.
Why it is silent
- Attach's platform-warning branch is
if (ca && darwin) {...} else if (ca) {...}. There is no else for the missing-CA case, so a downgrade produces no warning (.../claude/src/index.js:225).
collectProxyTrust bails on if (!ca) return null (src/core/daemon/status.js:1210), so the entire proxy trust: block disappears from hyp status rather than reporting a problem.
The result is an install that reports healthy while capture has quietly changed shape and Remote Control inbound is dead.
Recovery (works today, for the runbook)
hyp daemon restart
ls ~/.hyp/hypaware/tls/ca-cert.pem # must exist before attaching
hyp attach claude
The restart re-mints the CA because prepareInterception sees proxy_mode: true. Attach then takes the proxy branch. The base-URL to proxy switch itself is sound: releaseUnmanagedKeys (.../claude/src/settings.js:479, LLP 0232 #mode-migration) restores or removes the keys the old mode owned, so no manual settings editing is needed and no detach is required first.
Re-running --purge before re-attaching deletes the freshly minted CA and returns to the same state.
Options
Not proposing a fix, since which surface owns the truth is a design call:
- Make
--purge leave a consistent world. Either restart the gateway source as part of the purge so the CA is re-minted, or clear proxy_mode so config and disk agree.
- Let attach report the mismatch. Attach cannot see config by design, but its CLI caller can: at
clients.js:842, distinguish "proxy_mode true and a CA exists" from "proxy_mode true and no CA", and on the latter say so and name the repair instead of returning.
- Make
hyp status name the state. proxy_mode: true with no CA on disk is currently invisible; today collectProxyTrust returns null for it. The inverse case (proxy mode off, CA present) is already warned about as aigw.proxy_mode_stale_ca in source.js:441, so this is the missing half of an existing pair.
(3) is the smallest honest change and is independently useful; (1) is what stops users reaching the state at all.
Related
hyp detach claude --purgefollowed byhyp attach claudesilently lands in base-URL mode, and nothing on any surface says so.Found on a coworker's real macOS install while diagnosing a separate two-password-dialog report. Reproduces by reading on master at
a23a3ea(the tagged, unpublished v1.23.0).Repro
On a machine with
proxy_mode: truein the effective config and Claude attached in proxy mode:Expected: attach re-establishes proxy mode, minting and trusting a CA (one password dialog).
Actual: attach reports success and writes a base-URL marker. No CA is minted, no keychain dialog appears,
NODE_EXTRA_CA_CERTS/HTTPS_PROXYare never written, and Remote Control inbound stays broken. No warning is printed and no diagnostic appears inhyp status.Why
Three correct-in-isolation behaviours compose into a dead end.
1. Attach decides its mode from the CA file, not from config.
hypaware-core/plugins-workspace/claude/src/index.js:200This is deliberate (LLP 0232
#proxy-attach-preflight): "the mode it writes is the mode the gateway is serving, not the one someone asked for." Attach never mints a CA; only the daemon does, inprepareInterception(hypaware-core/plugins-workspace/ai-gateway/src/source.js:479), which runs at gateway source launch or reload.2.
--purgedeletes the CA but leavesproxy_modeon and does not restart the daemon.purgeProxyTrustResidue(src/core/commands/clients.js:1586) removes the CA files, the keychain trust, and the launchd env. Nothing in the detach path writesproxy_mode, so the config still claims proxy mode while no CA exists. The running daemon holds its already-loaded CA in memory and has no reason to relaunch the source, so nothing re-mints.3. The migration prompt that would repair this returns early.
src/core/commands/clients.js:842The offer to switch an install to proxy mode is skipped precisely because the config says proxy mode is already on. The config is right about intent and wrong about reality, and this is the one place that could notice.
Why it is silent
if (ca && darwin) {...} else if (ca) {...}. There is noelsefor the missing-CA case, so a downgrade produces no warning (.../claude/src/index.js:225).collectProxyTrustbails onif (!ca) return null(src/core/daemon/status.js:1210), so the entireproxy trust:block disappears fromhyp statusrather than reporting a problem.The result is an install that reports healthy while capture has quietly changed shape and Remote Control inbound is dead.
Recovery (works today, for the runbook)
The restart re-mints the CA because
prepareInterceptionseesproxy_mode: true. Attach then takes the proxy branch. The base-URL to proxy switch itself is sound:releaseUnmanagedKeys(.../claude/src/settings.js:479, LLP 0232#mode-migration) restores or removes the keys the old mode owned, so no manual settings editing is needed and no detach is required first.Re-running
--purgebefore re-attaching deletes the freshly minted CA and returns to the same state.Options
Not proposing a fix, since which surface owns the truth is a design call:
--purgeleave a consistent world. Either restart the gateway source as part of the purge so the CA is re-minted, or clearproxy_modeso config and disk agree.clients.js:842, distinguish "proxy_mode true and a CA exists" from "proxy_mode true and no CA", and on the latter say so and name the repair instead of returning.hyp statusname the state.proxy_mode: truewith no CA on disk is currently invisible; todaycollectProxyTrustreturns null for it. The inverse case (proxy mode off, CA present) is already warned about asaigw.proxy_mode_stale_cainsource.js:441, so this is the missing half of an existing pair.(3) is the smallest honest change and is independently useful; (1) is what stops users reaching the state at all.
Related
#proxy-attach-preflight,#mode-migration), LLP 0238 (#ca-survives-detach).