Skip to content

Commit 1215492

Browse files
philcunliffeneutralclaudetest
authored
The stale-CA warning names a remedy that cannot work, and AGENTS.md omits openclaw_capture (#831) (#833)
Two independently reachable findings from the #815 triage, both pre-existing on master and outside that PR's diff. `aigw.proxy_mode_stale_ca` fires when `proxy_mode` is off but an interception CA is still on disk, and told the operator to "run `hyp attach claude` to move it back to base-URL mode". Attach derives its mode from that same CA (LLP 0232 #proxy-attach-preflight), so in exactly this state a re-attach picks proxy mode again: the named remedy is a no-op. The warning now names the two that land, removing the CA with `hyp detach claude --purge` and re-attaching, or turning `proxy_mode` back on. The existing blind-tunnel test pins the reason string so the wrong remedy cannot come back. AGENTS.md's "Written acceptance procedures" list has named only `codex_desktop_capture` since #570 added `openclaw_capture` to docs/ACCEPTANCE.md, so the second written procedure was invisible to anyone reading the repo guidance rather than the doc. Listed it. Co-authored-by: neutral <neutral@hyparam.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: test <test@test.com>
1 parent 8e8ac2c commit 1215492

2 files changed

Lines changed: 26 additions & 7 deletions

File tree

hypaware-core/plugins-workspace/ai-gateway/src/source.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -432,17 +432,23 @@ async function prepareInterception(ctx, config, upstreams, liveState) {
432432
if (!config.proxyMode) {
433433
// Proxy mode is off, but a CA on disk means a client was attached in proxy
434434
// mode at some point and may still have `HTTPS_PROXY` pointing here. Serve
435-
// blind tunnels so its egress keeps working, and say so loudly: the repair
436-
// is a re-attach (or a detach), and nothing else on the machine will
437-
// volunteer that.
435+
// blind tunnels so its egress keeps working, and say so loudly: nothing
436+
// else on the machine will volunteer that this install is in the degraded
437+
// state. A plain re-attach is NOT the remedy: attach leaves the CA where
438+
// it is on purpose (the trust is offered back, never taken), so the file
439+
// that puts the install in this state survives the re-attach. The
440+
// remedies that land are removing the CA (`hyp detach claude --purge`,
441+
// then re-attach) or turning `proxy_mode` back on.
442+
// @ref LLP 0262#migration [constrained-by]: attach offers the CA back rather than removing it, so it cannot clear this state on its own
438443
const stale = await readLocalCaInfo({ stateRoot: defaultStateRoot(ctx.env) })
439444
if (stale) {
440445
liveState.interceptionError = 'proxy_mode is off but a local CA is installed'
441446
ctx.log.warn('aigw.proxy_mode_stale_ca', {
442447
[Attr.PLUGIN]: PLUGIN_NAME,
443448
ca_cert_path: stale.certPath,
444449
reason: 'serving blind tunnels so an already-attached client keeps working; ' +
445-
'run `hyp attach claude` to move it back to base-URL mode, or `hyp detach claude`',
450+
're-attaching leaves this CA on disk, so run `hyp detach claude --purge` ' +
451+
'and re-attach to clear the proxy residue, or turn proxy_mode back on',
446452
})
447453
return { tunnelOnly: true }
448454
}

test/plugins/ai-gateway-proxy-mode.test.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,10 +555,13 @@ test('proxy mode turned off with a CA still installed serves blind tunnels', asy
555555
const echoPort = echoAddress && typeof echoAddress === 'object' ? echoAddress.port : 0
556556
t.after(() => new Promise((resolve) => echo.close(() => resolve(undefined))))
557557

558-
/** @type {{ level: string, event: string }[]} */
558+
/** @type {{ level: string, event: string, attrs: Record<string, unknown> }[]} */
559559
const logged = []
560560
/** @param {string} level */
561-
const record = (level) => (/** @type {string} */ event) => logged.push({ level, event })
561+
const record = (level) => (
562+
/** @type {string} */ event,
563+
/** @type {Record<string, unknown> | undefined} */ attrs
564+
) => logged.push({ level, event, attrs: attrs ?? {} })
562565
const ctx = /** @type {any} */ ({
563566
// proxy_mode deliberately absent, as if the operator turned it back off.
564567
config: {
@@ -576,7 +579,17 @@ test('proxy mode turned off with a CA still installed serves blind tunnels', asy
576579
const details = /** @type {any} */ ((await source.status()).details)
577580
assert.equal(details.proxy_mode, false)
578581
assert.match(details.proxy_mode_error, /a local CA is installed/)
579-
assert.equal(logged.some((l) => l.event === 'aigw.proxy_mode_stale_ca'), true)
582+
const staleWarn = logged.find((l) => l.event === 'aigw.proxy_mode_stale_ca')
583+
assert.ok(staleWarn, 'the stale-CA warning is emitted')
584+
585+
// The warning has to name a remedy that actually works. Attach deliberately
586+
// leaves the CA on disk (it offers the trust back rather than taking it), so
587+
// telling the operator to re-attach leaves the install exactly as degraded
588+
// as it was, every time, until the CA is gone.
589+
// @ref LLP 0262#migration [tests]: the stale-CA remedy cannot be a plain re-attach
590+
const reason = String(staleWarn.attrs.reason ?? '')
591+
assert.match(reason, /hyp detach claude --purge/)
592+
assert.doesNotMatch(reason, /run `hyp attach claude` to move it back/)
580593

581594
// The tunnel is still served, so an already-attached client keeps its egress
582595
// instead of losing all HTTPS. (The byte-level round trip is covered by

0 commit comments

Comments
 (0)