Skip to content

Commit b566a69

Browse files
neutralclaude
andcommitted
Review round 2: LLP 0251 never existed, and section 5 promised status fields hyp status drops
- The rollout is LLP 0242-0244 (04330ab, #794), on master; LLP 0251 is an unmerged number on a sibling branch, so three references dangled. - Section 5 listed proxy_mode, expiry, cert path and permitted/intercepted hosts as hyp status output; ProxyTrustReport carries three fields and hyp status --json drops the source details block those live in. - Section 3's / anchor rationale was inverted: the fail-closed guard is what stops record-everything, and the source.js merge is what stops the default install recording nothing. - 0231's forward-ref uses the corpus's Designed-by name. - Acceptance: purge deletes the CA while the running daemon keeps its own, so a purge-then-attach silently downgrades to base URL; the step-7 restore raced the CA mint the same way. Config backed up and restored, anchors folded into their links (#proxy-attach-preflight is LLP 0232's, not 0245's). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 8c74bda commit b566a69

4 files changed

Lines changed: 94 additions & 46 deletions

File tree

docs/ACCEPTANCE.md

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -541,8 +541,8 @@ about base-URL attach, which is the default and is already exercised by
541541
**Requires:**
542542

543543
- A real Mac (the keychain trust dialog and `launchctl` env delivery are
544-
Darwin-only; see [LLP 0237](../llp/0237-attach-trusts-ca-in-login-keychain.decision.md)
545-
#darwin-only). Do not attempt this on Linux; the design states Remote
544+
Darwin-only; see [LLP 0237 #darwin-only](../llp/0237-attach-trusts-ca-in-login-keychain.decision.md#darwin-only)).
545+
Do not attempt this on Linux; the design states Remote
546546
Control inbound is unsupported there under proxy mode and there is no
547547
dialog or launchd table to check.
548548
- Claude Code installed and signed in, with Remote Control reachable from a
@@ -551,9 +551,14 @@ about base-URL attach, which is the default and is already exercised by
551551
- HypAware installed from the package under test, with no prior HypAware CA
552552
trusted in the login keychain (a machine that has run this procedure
553553
before will not see the dialog again in step 2; that is expected, not a
554-
failure, per [LLP 0238](../llp/0238-long-lived-ca-full-provider-constraints.decision.md)
555-
#ca-survives-detach. Run `hyp detach claude --purge` first if you need a
556-
clean first-trust observation).
554+
failure, per
555+
[LLP 0238 #ca-survives-detach](../llp/0238-long-lived-ca-full-provider-constraints.decision.md#ca-survives-detach)).
556+
If you need a clean first-trust observation, run `hyp detach claude --purge`
557+
**and then `hyp daemon restart`**, and confirm
558+
`~/.hyp/hypaware/tls/ca-cert.pem` is back before you start: purge deletes
559+
the CA, the running daemon holds its own copy in memory and never re-mints,
560+
and step 2 reads that file to decide which mode to attach in. Attaching
561+
against the gap silently produces a base-URL attach.
557562

558563
**Related:** [LLP 0231](../llp/0231-proxy-mode-capture.rfc.md) (the request),
559564
[LLP 0245](../llp/0245-proxy-mode-capture.design.md) (the design this
@@ -571,25 +576,34 @@ procedure gates, sections 1, 4 and 6),
571576
run never touches):
572577

573578
```sh
579+
cp ~/.hyp/hypaware-config.json ~/.hyp/hypaware-config.json.pre-acceptance
574580
jq '.plugins |= map(if .name == "@hypaware/ai-gateway"
575581
then .config.proxy_mode = true else . end)' \
576582
~/.hyp/hypaware-config.json > /tmp/hypaware-config.json \
577583
&& mv /tmp/hypaware-config.json ~/.hyp/hypaware-config.json
578584
hyp daemon install
579585
hyp daemon start
586+
hyp daemon restart # reload the config edit above
580587
hyp status
581588
ls ~/.hyp/hypaware/tls/ca-cert.pem
582589
```
583590

591+
The backup copy is how you put your machine back afterwards; nothing below
592+
restores the original `proxy_mode` for you. The `restart` is not
593+
redundant: a daemon that was already installed and running does not re-read
594+
the config on `install`/`start`, so without it the CA check at the end of
595+
this step fails for a reason that has nothing to do with the feature.
596+
584597
Pass condition: `hyp status` shows the daemon running and, under
585598
`sources:`, the gateway source `[started]`, and the CA file exists. Check
586599
all three: the `jq` edit is a silent no-op if the config has no
587600
`@hypaware/ai-gateway` entry, and on a centrally managed host a local
588601
`proxy_mode` write loses the LLP 0031 layer merge outright. The CA is the
589602
only proof that proxy mode is actually being *served*, and step 2 reads
590603
exactly that file to decide which mode to attach in
591-
([LLP 0245#claude-attach](../llp/0245-proxy-mode-capture.design.md#claude-attach)
592-
#proxy-attach-preflight). With no CA, `hyp attach claude` does not refuse,
604+
([LLP 0245 #claude-attach](../llp/0245-proxy-mode-capture.design.md#claude-attach),
605+
realizing [LLP 0232 #proxy-attach-preflight](../llp/0232-claude-attaches-by-proxy.decision.md#proxy-attach-preflight)).
606+
With no CA, `hyp attach claude` does not refuse,
593607
it quietly attaches in base-URL mode instead, and every step below would
594608
then be measuring the wrong feature.
595609

@@ -605,8 +619,8 @@ procedure gates, sections 1, 4 and 6),
605619
text: it must name (or the surrounding attach output must state) all
606620
three hosts the CA is constrained to, `api.anthropic.com`,
607621
`api.openai.com`, and `chatgpt.com`, not only the one this attach is for
608-
([LLP 0238](../llp/0238-long-lived-ca-full-provider-constraints.decision.md)
609-
#full-provider-constraints: one certificate, one dialog, every provider,
622+
([LLP 0238 #full-provider-constraints](../llp/0238-long-lived-ca-full-provider-constraints.decision.md#full-provider-constraints):
623+
one certificate, one dialog, every provider,
610624
and the grant must be informed about all of them). Approve it.
611625

612626
Confirm the settings write:
@@ -621,17 +635,17 @@ procedure gates, sections 1, 4 and 6),
621635
`_CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL` must be **absent** (or, if this
622636
machine previously ran base-URL attach, must have been released by the
623637
mode migration, not merely left stale)
624-
([LLP 0232](../llp/0232-claude-attaches-by-proxy.decision.md)
625-
#mode-migration). `NODE_EXTRA_CA_CERTS` must point at
638+
([LLP 0232 #mode-migration](../llp/0232-claude-attaches-by-proxy.decision.md#mode-migration)).
639+
`NODE_EXTRA_CA_CERTS` must point at
626640
`~/.hyp/hypaware/tls/ca-cert.pem` (or
627641
`$HYP_HOME/hypaware/tls/ca-cert.pem`).
628642

629643
3. Confirm the launchd environment, respecting the terminal caveat: only
630644
processes launchd starts **after** the `setenv` see the variable, and a
631645
terminal app is single-process, so a window that predates step 2 will not
632646
see it even though it is genuinely set
633-
([LLP 0239](../llp/0239-node-use-system-ca-via-launchd.decision.md)
634-
#terminals-predating-attach). Read attach's own final output line first:
647+
([LLP 0239 #terminals-predating-attach](../llp/0239-node-use-system-ca-via-launchd.decision.md#terminals-predating-attach)).
648+
Read attach's own final output line first:
635649
it states this caveat explicitly. Then, in the **same terminal window**
636650
you ran step 2 in (do not open a new window yet):
637651

@@ -704,8 +718,7 @@ procedure gates, sections 1, 4 and 6),
704718
`ProxyTrustReport`).
705719

706720
6. Detach and confirm the CA and its trust survive, per
707-
[LLP 0238](../llp/0238-long-lived-ca-full-provider-constraints.decision.md)
708-
#ca-survives-detach:
721+
[LLP 0238 #ca-survives-detach](../llp/0238-long-lived-ca-full-provider-constraints.decision.md#ca-survives-detach):
709722

710723
```sh
711724
hyp detach claude
@@ -758,9 +771,18 @@ procedure gates, sections 1, 4 and 6),
758771
```sh
759772
hyp daemon install
760773
hyp daemon start
774+
ls ~/.hyp/hypaware/tls/ca-cert.pem # wait for this before attaching
761775
hyp attach claude
776+
cp ~/.hyp/hypaware-config.json.pre-acceptance ~/.hyp/hypaware-config.json
762777
```
763778

779+
Do not skip the `ls`. The gateway mints the CA after it boots and
780+
`hyp attach claude` does not wait for it, so an attach fired immediately
781+
after `start` can find no CA and silently write a base-URL attach,
782+
leaving your working machine with Remote Control broken. Restore the
783+
config backup last, and `hyp daemon restart` after it if you want the
784+
machine back on its original mode.
785+
764786
### If it fails
765787

766788
- `hyp attach claude` reports a base-URL attach, or writes
@@ -777,10 +799,16 @@ procedure gates, sections 1, 4 and 6),
777799
- No dialog in step 2 on a machine that has never run this procedure:
778800
confirm no earlier HypAware install already trusted a CA under this same
779801
login keychain (`security find-certificate -c "HypAware Local CA" ...`);
780-
if one exists, `hyp detach claude --purge` first for a clean observation.
781-
A refused or dismissed dialog is not a failure on its own: attach still
782-
completes and states plainly that Remote Control inbound will not work
783-
([LLP 0237#attach-anyway-on-refusal](../llp/0237-attach-trusts-ca-in-login-keychain.decision.md#attach-anyway-on-refusal));
802+
if one exists, `hyp detach claude --purge` first for a clean observation,
803+
then `hyp daemon restart` and confirm `~/.hyp/hypaware/tls/ca-cert.pem` is
804+
back before re-attaching. Purge deletes the CA and never touches config,
805+
and the running daemon keeps serving the copy it loaded at boot rather than
806+
re-minting, so a `hyp attach claude` in that window finds no CA and quietly
807+
writes a base-URL attach instead of raising the dialog again. That reads
808+
identically to "still no dialog", so check the CA file rather than the
809+
symptom. A refused or dismissed dialog is not a failure on its own:
810+
attach still completes and states plainly that Remote Control inbound will not work
811+
([LLP 0237 #attach-anyway-on-refusal](../llp/0237-attach-trusts-ca-in-login-keychain.decision.md#attach-anyway-on-refusal));
784812
re-running `hyp attach claude` retries the dialog.
785813
- Step 3's `echo $NODE_USE_SYSTEM_CA` is empty even after a full quit and
786814
reopen of the terminal application: confirm you quit the *application*,

llp/0231-proxy-mode-capture.rfc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
**Date:** 2026-08-14
88
**Related:** LLP 0016, LLP 0044, LLP 0045, LLP 0049, LLP 0066, LLP 0086, LLP 0114, LLP 0116, LLP 0176, LLP 0192, LLP 0206
99
**Spawns:** LLP 0232, LLP 0233, LLP 0234, LLP 0235
10-
**Design:** LLP 0245
10+
**Designed-by:** LLP 0245, proxy-mode capture technical design
1111

1212
> Claude Code disables **Remote Control** whenever `ANTHROPIC_BASE_URL` points
1313
> anywhere other than `api.anthropic.com`. Attach repoints exactly that key, so

llp/0245-proxy-mode-capture.design.md

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
**Systems:** Gateway, Sources, Config, Plugins, Privacy, Core, Daemon
66
**Generated-by:** neutral
77
**Related:** LLP 0231, LLP 0232, LLP 0233, LLP 0234, LLP 0235, LLP 0236,
8-
LLP 0237, LLP 0238, LLP 0239, LLP 0246, LLP 0247, LLP 0044, LLP 0045,
9-
LLP 0114, LLP 0192, LLP 0206
8+
LLP 0237, LLP 0238, LLP 0239, LLP 0242, LLP 0243, LLP 0244, LLP 0246,
9+
LLP 0247, LLP 0044, LLP 0045, LLP 0114, LLP 0192, LLP 0206
1010

1111
> Technical design for the proxy-mode capture stack the accepted RFC
1212
> LLP 0231 asked for: Claude Code routed through the gateway with
@@ -28,7 +28,8 @@ is the implementation design that binds those decisions to the tree.
2828

2929
The design is realized on `master` by three commits: `fa701a7e` (#782, the
3030
transport, aperture, CA and attach), `d0f7c4ad` (#792, the status and trust
31-
reporting surface), and the rollout work covered separately by LLP 0251.
31+
reporting surface), and `04330abb` (#794, the LLP 0242-0244 rollout that
32+
turns proxy mode on).
3233
File paths and function names below are verified against that tree; the
3334
tests named in section 7 exist and gate it. What this document adds to the
3435
corpus is the request-level design of record: the one place the whole
@@ -37,7 +38,9 @@ above.
3738

3839
This change set deliberately excludes who *turns proxy mode on*. Fresh
3940
install composition and the existing-install migration are LLP 0242's
40-
problem and are designed in LLP 0251, which depends on this change set.
41+
problem, settled by LLP 0243 (the picker row composes `proxy_mode`) and
42+
LLP 0244 (attach offers the migration), and already landed on `master`.
43+
Their own design of record belongs to that change set, not this one.
4144

4245
## 1. Data flow, end to end {#data-flow}
4346

@@ -152,10 +155,13 @@ The CA lives in core, not in the gateway plugin, because `hyp detach` and
152155
configures it separately (LLP 0234).
153156
- `shouldRecordProxyExchange(upstream, pathname)`: the recording anchor is
154157
`recordPrefix ?? prefix`, and an anchor of `/` or empty records nothing;
155-
failing closed is the default. Note the fallback: an upstream with no
156-
`record_prefix` records under its routing prefix, so section 3's
157-
`source.js` merge is what keeps a routing `path_prefix` of `/` from
158-
reading as record-everything. The routing matcher is deliberately not reused: the
158+
failing closed is the default, so a routing prefix of `/` can never read
159+
as record-everything. Note the fallback: an upstream with no
160+
`record_prefix` records under its routing prefix, which is why the
161+
`source.js` merge below matters. The `hyp init` preset writes
162+
`path_prefix = "/"`, so without the merge the anchor was `/`, the
163+
fail-closed guard suppressed every request, and the default install
164+
recorded nothing at all. The routing matcher is deliberately not reused: the
159165
Anthropic route matcher accepts an `sk-ant-` bearer alone, which under a
160166
proxy is true of every request to the host and measurably reopened the
161167
aperture (LLP 0234 #recording-is-opt-in-per-path).
@@ -165,13 +171,15 @@ The CA lives in core, not in the gateway plugin, because `hyp detach` and
165171

166172
`hypaware-core/plugins-workspace/ai-gateway/src/source.js` merges each
167173
adapter preset's declared `path_prefix` (and `provider`) onto the merged
168-
upstream entry as `record_prefix`, because operator routing config wins
169-
over presets and a routing `path_prefix` of `/` must never read as
170-
record-everything; without this the default install recorded nothing at
171-
all (LLP 0234). It also owns the status surface: `proxy_mode`,
172-
`ca_fingerprint`, `ca_not_after`, `ca_cert_path`, `ca_permitted_hosts`,
173-
intercepted hosts, and `proxy_mode_error` when CA preparation failed while
174-
the gateway kept reverse-proxying.
174+
upstream entry as `record_prefix`. Operator config still wins the *routing*
175+
question, but the record anchor belongs to the adapter that registered the
176+
preset: an operator writing `path_prefix = "/"` is saying "route everything
177+
on this host here", not "record everything on this host", and the
178+
fail-closed guard turns that into recording nothing at all on a default
179+
install (LLP 0234). It also owns the gateway's own status details:
180+
`proxy_mode`, `ca_fingerprint`, `ca_not_after`, `ca_cert_path`,
181+
`ca_permitted_hosts`, `intercept_hosts`, and `proxy_mode_error` when CA
182+
preparation failed while the gateway kept reverse-proxying.
175183

176184
`hypaware-core/plugins-workspace/ai-gateway/src/config.js` reads the
177185
switch: `proxy_mode` is on only when the config field is literally `true`
@@ -252,13 +260,24 @@ delete a different install's key material.
252260

253261
## 5. Status surface {#status}
254262

255-
`src/core/commands/status.js` and `src/core/daemon/types.d.ts`
256-
(`ProxyTrustReport`) report: gateway `proxy_mode`, CA fingerprint, expiry,
257-
cert path, permitted and intercepted hosts, `proxy_mode_error`, keychain
258-
trust state, and whether `NODE_USE_SYSTEM_CA` is live in the launchd
259-
environment (`launchctl getenv`). "The dialog was cancelled last month" is
260-
diagnosable without re-running attach (LLP 0237), and the aperture is
261-
readable without grepping a boot log (LLP 0233).
263+
Two surfaces, and they carry different things.
264+
265+
`hyp status` reports the trust half: `src/core/commands/status.js` renders
266+
the `proxy trust:` block from `ProxyTrustReport`
267+
(`src/core/daemon/types.d.ts`), which is exactly three facts: the CA
268+
fingerprint, keychain trust state, and whether `NODE_USE_SYSTEM_CA` is live
269+
in the launchd environment (`launchctl getenv`). Trust and launchd state are
270+
tri-state, because "the probe could not run" is not the claim "not trusted".
271+
"The dialog was cancelled last month" is diagnosable without re-running
272+
attach (LLP 0237).
273+
274+
The aperture half lives in the gateway source's own status details
275+
(section 3): `proxy_mode`, `ca_not_after`, `ca_cert_path`,
276+
`ca_permitted_hosts`, `intercept_hosts` and `proxy_mode_error`, readable
277+
without grepping a boot log (LLP 0233). Note that `hyp status --json` maps
278+
each source to name, plugin and state only and drops the details block, so
279+
those fields are read from the daemon status file
280+
(`hyp daemon status --json`), not from `hyp status`.
262281

263282
## 6. Failure modes {#failure-modes}
264283

llp/0248-proxy-mode-capture.plan.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,14 @@ Two, both documentation:
9090
## Out of scope {#out-of-scope}
9191

9292
Who turns proxy mode on (fresh-install composition, existing-install
93-
migration) is LLP 0242's problem, was designed in LLP 0251 per the design's
94-
section 0, and in fact already landed on `master` as #794; it belongs to
95-
its own change set either way. Codex stays base-URL (RFC 0231). The
93+
migration) is LLP 0242's problem, settled by LLP 0243 and LLP 0244 and
94+
already landed on `master` as #794 (`04330abb`, this branch's merge base);
95+
it belongs to its own change set either way. Codex stays base-URL
96+
(RFC 0231). The
9697
`upstream_proxy` field-testing question and claude-code#75050 are recorded
9798
open items, not tasks.
9899

99100
## Tasks
100101

101102
- id: T1 branch: task/proxy-mode-capture/T1 deps: [] complexity: 3 -- Write the missing manual acceptance procedure `claude_proxy_capture` in docs/ACCEPTANCE.md, in the same shape as the existing `codex_desktop_capture` and `openclaw_capture` entries (what it proves, what it does not prove, prerequisites, exact commands, pass condition, an "If it fails" section). Opt-in/manual, needs a real Mac. It must cover, from LLP 0245 sections 1, 4 and 6: real `hyp daemon install`/start, `hyp attach claude` in proxy mode writing only HTTPS_PROXY and NODE_EXTRA_CA_CERTS into ~/.claude/settings.json, the macOS keychain trust dialog naming all INTERCEPT_PROVIDER_HOSTS, NODE_USE_SYSTEM_CA visible via `launchctl getenv` with the fully-quit-and-reopen-terminal caveat (LLP 0239), a Claude Code session producing rows in ai_gateway_messages attributable via entrypoint while Remote Control inbound still works (the RFC's whole point), `hyp status` reporting the ProxyTrustReport fields, detach restoring env keys while the CA and keychain trust survive (LLP 0238), and `hyp detach claude --purge` plus `hyp daemon uninstall` removing CA, trust and launchd residue. Also add `claude_proxy_capture` to the written-procedures list in CLAUDE.md's Smoke Test Model section (currently lists only codex_desktop_capture). Do NOT touch any code: the mechanisms are all built and green on master (see #audit). Verification is `npm test` still green and the two docs reading consistently; no new automated tests. Prose rules apply: no em dashes anywhere.
102-
- id: T2 branch: task/proxy-mode-capture/T2 deps: [] complexity: 1 -- Append the design-of-record forward-ref to the request: in llp/0231-proxy-mode-capture.rfc.md's metadata header add a line `**Design:** LLP 0245` directly after the `**Spawns:**` line. This is a trivial editorial forward-ref, explicitly permitted on an Accepted doc; change nothing else in the file (no body edits, no status change). Check llp/0232 through llp/0239 headers and confirm they need no equivalent edit (0232, 0233 and 0235 already carry Extended-by/Superseded-by lines; the design cites all of them from its own side, and per-decision back-refs from code already exist). Verification: `npm test` untouched and green, and a grep shows exactly one new line added under llp/.
103+
- id: T2 branch: task/proxy-mode-capture/T2 deps: [] complexity: 1 -- Append the design-of-record forward-ref to the request: in llp/0231-proxy-mode-capture.rfc.md's metadata header add a line `**Designed-by:** LLP 0245` directly after the `**Spawns:**` line (`Designed-by` is the corpus's existing name for this forward-ref relation; see llp/0044). This is a trivial editorial forward-ref, explicitly permitted on an Accepted doc; change nothing else in the file (no body edits, no status change). Check llp/0232 through llp/0239 headers and confirm they need no equivalent edit (0232, 0233 and 0235 already carry Extended-by/Superseded-by lines; the design cites all of them from its own side, and per-decision back-refs from code already exist). Verification: `npm test` untouched and green, and a grep shows exactly one new line added under llp/.

0 commit comments

Comments
 (0)