Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
26621bc
feat(gateway): build standalone MCP gateway (Track 1.2, SG-FR-1..5, S…
pjpoulose Aug 29, 2026
d563119
Merge branch 'release/v0.5.0-candidate' into feature/track-1.2-standa…
pjpoulose Sep 3, 2026
90bba22
refactor(gateway): swap mode-gate.js's private mode-selection logic f…
pjpoulose Sep 3, 2026
c0cefed
fix(gateway): preserve invocation order and disconnect/anomaly eviden…
pjpoulose Sep 3, 2026
0f8f881
fix(gsa-mcp-shim): persist disconnect/anomaly evidence into the seale…
pjpoulose Sep 3, 2026
2cf80d2
fix(gateway): seed granted tools, scope disconnects, freeze on claim …
pjpoulose Sep 3, 2026
09dc03d
fix(gateway): downstream hardening -- collision rejection, full init …
pjpoulose Sep 3, 2026
51515d7
fix(gateway): reject weak bearer tokens and bound HTTP agent request …
pjpoulose Sep 3, 2026
3b41d0f
fix(gateway): reject non-ed25519 signing keys, clean up on listener-s…
pjpoulose Sep 3, 2026
99c97f4
fix(checks): validate HEAD.json shape before comparing it to the chai…
pjpoulose Sep 3, 2026
c406344
test(gateway): replace flaky fixed-sleep SIGTERM-drain barrier with a…
pjpoulose Sep 3, 2026
d3bd696
feat(gateway): board-lock decisions for PR review — sampling forward,…
pjpoulose Sep 4, 2026
e95d5f5
feat(gateway): board-lock decisions for PR review — sampling forward,…
pjpoulose Sep 4, 2026
1fd6fa4
feat(gateway): board-lock decisions for PR review — sampling forward,…
pjpoulose Sep 4, 2026
b49d2bf
feat(gateway): board-lock decisions for PR review — sampling forward,…
pjpoulose Sep 4, 2026
da41cd9
feat(gateway): board-lock decisions for PR review — sampling forward,…
pjpoulose Sep 4, 2026
7c92d52
feat(gateway): board-lock decisions for PR review — sampling forward,…
pjpoulose Sep 4, 2026
6786ec9
test(gateway): cover board-lock decisions — sampling forward, token_r…
pjpoulose Sep 4, 2026
7d6f27b
test(gateway): cover board-lock decisions — sampling forward, token_r…
pjpoulose Sep 4, 2026
e9f3f4a
test(gateway): cover board-lock decisions — sampling forward, token_r…
pjpoulose Sep 4, 2026
eef5091
fix(gateway): send stateless _meta to HTTP downstreams, fix downstrea…
pjpoulose Sep 4, 2026
e4491a4
fix(gateway): reject non-POST agent HTTP requests, reject listener bi…
pjpoulose Sep 4, 2026
cc7c6cc
fix(gateway): honor tool-level isError, preserve downstream JSON-RPC …
pjpoulose Sep 4, 2026
3664930
fix(gateway): wire the real log function into GatewayProxy's new per-…
pjpoulose Sep 4, 2026
72172f0
fix(gateway): require the verified chain's first entry to have seq===1
pjpoulose Sep 4, 2026
6634443
docs(gateway): fix KNOWN-LIMITATIONS.md section 9 contradicting the n…
pjpoulose Sep 4, 2026
7102d0b
test(gateway): cover isError/rpcError/init-lifecycle/structured-log f…
pjpoulose Sep 4, 2026
67c026d
test(gateway): send initialize before tools/call in drain tests
pjpoulose Sep 4, 2026
a232a32
test(gateway): e2e coverage for HTTP-downstream _meta, non-POST rejec…
pjpoulose Sep 4, 2026
f124ca3
test(gateway): cover the chain-must-start-at-seq-1 fix
pjpoulose Sep 4, 2026
75ccb67
fix(graphlint): extend R5 constitutional-scripts exemption to subdire…
pjpoulose Sep 8, 2026
45a1b88
fix(tests): pin the HTTP sampling e2e test to one keep-alive socket
pjpoulose Sep 8, 2026
668b42f
fix: correct previous commit that accidentally pushed placeholder con…
pjpoulose Sep 8, 2026
53d9b2b
feat(gateway): explicit Mcp-Session-Id for HTTP agent transport, repl…
pjpoulose Sep 8, 2026
50565b0
docs+tests: reflect explicit Mcp-Session-Id HTTP agent session identity
pjpoulose Sep 8, 2026
162564c
fix: address CodeRabbit PR review comments (round 1)
pjpoulose Sep 9, 2026
56f289b
fix: address CodeRabbit PR review comments (round 1, part 2)
pjpoulose Sep 9, 2026
d819704
fix: address CodeRabbit PR review comments (round 1, part 3)
pjpoulose Sep 9, 2026
577eb40
fix: address CodeRabbit PR review comments (round 1, part 4 - downstr…
pjpoulose Sep 9, 2026
0dfc661
fix: address CodeRabbit PR review comments (round 1, part 5 - gateway…
pjpoulose Sep 9, 2026
5914ad4
fix: address round-2 Codex PR review findings (8 fixes)
pjpoulose Sep 9, 2026
cd6d4e8
fix: restore downstream.js content (corrected accidental placeholder …
pjpoulose Sep 9, 2026
10021b2
test: add regression tests for round-2 Codex PR review findings
pjpoulose Sep 9, 2026
7de681c
fix: address PR #29 review round-3 Codex/CodeRabbit findings (10 fixes)
pjpoulose Sep 9, 2026
a9f6a1b
fix: PR #29 round-3 findings, part 2 (agent-transport.js, gateway.js,…
pjpoulose Sep 9, 2026
fcbdabb
fix: PR #29 round-3 findings, part 3 (gateway.js)
pjpoulose Sep 9, 2026
f1853fe
fix: PR #29 round-3 findings, part 4 of 4 (proxy.js)
pjpoulose Sep 9, 2026
2d3d722
test: add regression tests for round-3 PR #29 review findings
pjpoulose Sep 9, 2026
e5afd4a
test: round-3 findings, part 2 of 3 (tests/gateway/proxy/run-tests.js)
pjpoulose Sep 9, 2026
15d0d27
test: round-3 findings, part 3 of 3 (tests/gateway/e2e/run-tests.js)
pjpoulose Sep 9, 2026
d1b4234
fix: PR #29 round 4, part 1 of 10 (config.js)
pjpoulose Sep 12, 2026
8a100eb
fix: PR #29 round 4, part 2 of 10 (agent-transport.js)
pjpoulose Sep 12, 2026
3ee917a
fix: PR #29 round 4, part 3 of 10 (downstream.js)
pjpoulose Sep 12, 2026
35ed647
fix: PR #29 round 4, part 4 of 10 (proxy.js)
pjpoulose Sep 12, 2026
92ba362
fix: PR #29 round 4, part 5 of 10 (session.js)
pjpoulose Sep 12, 2026
70ff788
fix: PR #29 round 4, part 6 of 10 (gateway.js)
pjpoulose Sep 12, 2026
bdf32ba
test: PR #29 round 4 findings, part 7 of 10 (config/run-tests.js)
pjpoulose Sep 12, 2026
aec1195
test: PR #29 round 4 findings, part 8 of 10 (downstream/run-tests.js)
pjpoulose Sep 12, 2026
f4178bd
test: PR #29 round 4 findings, part 9 of 10 (proxy/run-tests.js)
pjpoulose Sep 12, 2026
92940e0
test: PR #29 round 4 findings, part 10 of 10 (e2e/run-tests.js)
pjpoulose Sep 12, 2026
22a7008
fix: PR #29 round 2 findings, part 1 of 5 (gateway.js)
pjpoulose Sep 12, 2026
5f411db
fix: PR #29 round 2 findings, part 2 of 5 (session.js)
pjpoulose Sep 12, 2026
aa5d063
fix: PR #29 round 2 findings, part 3 of 5 (downstream.js)
pjpoulose Sep 12, 2026
f4ed0b7
test: PR #29 round 2 findings, part 4 of 5 (session/run-tests.js)
pjpoulose Sep 12, 2026
112ec27
test: PR #29 round 2 findings, part 5 of 5 (downstream/run-tests.js)
pjpoulose Sep 12, 2026
c1146ca
fix: PR #29 round 5 findings, part 1 of 4 (downstream.js)
pjpoulose Sep 12, 2026
24180d6
fix: PR #29 round 5 findings, part 2 of 4 (proxy.js)
pjpoulose Sep 12, 2026
de3876b
fix: PR #29 round 5 findings, part 3 of 4 (agent-transport.js)
pjpoulose Sep 12, 2026
372bd9d
docs: PR #29 round 5 findings, part 4 of 4 (KNOWN-LIMITATIONS.md)
pjpoulose Sep 12, 2026
ea96bad
fix: PR #29 round 6 finding (downstream.js HTTP error-presence branch)
pjpoulose Sep 12, 2026
63f50df
fix: address PR review comments (continuation round 1)
pjpoulose Sep 12, 2026
eb8d1c5
fix: address PR review comments (continuation round 1, part 2/2)
pjpoulose Sep 12, 2026
7b2b7e1
fix: address PR review comments (continuation round 2)
pjpoulose Sep 12, 2026
a712668
fix(gateway/agent-transport): seal HTTP session on failed initialize …
pjpoulose Sep 12, 2026
3646187
fix(gateway/downstream): validate initialize result, strip secrets fr…
pjpoulose Sep 12, 2026
eead5fb
fix(gateway): wire secretEnvNames and onUnmatchedResponse into startG…
pjpoulose Sep 12, 2026
3718c5d
fix(gateway/proxy): bound completed call history per session (PR #29 …
pjpoulose Sep 12, 2026
ee2ee8c
test(gateway/downstream): add regression coverage for round-8 findings
pjpoulose Sep 12, 2026
edd45cd
test(gateway/proxy): add regression coverage for completed-call histo…
pjpoulose Sep 12, 2026
c75e55c
test(gateway/e2e): add regression coverage for HTTP session validate-…
pjpoulose Sep 12, 2026
09da01e
fix: correct byte-exact content of e2e test file (previous push had s…
pjpoulose Sep 12, 2026
04fc156
fix(gateway/proxy): re-validate writer-claim immediately before chain…
pjpoulose Sep 12, 2026
c2dcb02
fix(gateway): Cluster C wiring + Cluster D status surface + Cluster E…
pjpoulose Sep 12, 2026
d4df498
test(gateway): add Cluster C writer-claim re-validation tests
pjpoulose Sep 12, 2026
a13f0da
test(gateway): add e2e coverage for Cluster D status command + Cluste…
pjpoulose Sep 12, 2026
90280cf
fix(gateway): add recordUnmatchedResponseAnomaly, cap anomalies per s…
pjpoulose Sep 13, 2026
e9a67a2
test(gateway): cover recordUnmatchedResponseAnomaly and the anomaly cap
pjpoulose Sep 13, 2026
c6f4851
fix(gateway): fix completed-call cap accounting, gate tools on notifi…
pjpoulose Sep 13, 2026
b92f9a5
test(gateway): cover init-lifecycle notification gating and completed…
pjpoulose Sep 13, 2026
d61d4bc
fix(gateway): bound stdio child termination, add protocol-version all…
pjpoulose Sep 13, 2026
9b0c227
test(gateway): cover bounded stdio termination and the protocol-versi…
pjpoulose Sep 13, 2026
9a30ef1
fix(gateway): route unmatched responses through the dedicated anomaly…
pjpoulose Sep 13, 2026
16f0a33
test(gateway): cover downstream-sampling completed-call cap and start…
pjpoulose Sep 13, 2026
0a64919
test(gateway): verify bundle persistence on failed-init and add initi…
pjpoulose Sep 13, 2026
6692938
fix(tests): skip Windows-unreproducible SIGTERM-escalation timing ass…
pjpoulose Sep 14, 2026
9a42749
fix: restore dynamic template-literal interpolations corrupted in pre…
pjpoulose Sep 14, 2026
becee40
fix: 8-item re-triage round on PR #29 (writer-claim renewal, session …
pjpoulose Sep 19, 2026
2c00732
fix: derive model_call granted from negotiated sampling capability, n…
pjpoulose Sep 19, 2026
8f2fdc2
fix: flag structurally malformed tools/call results distinctly from t…
pjpoulose Sep 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions KNOWN-LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,29 @@ There is no `fs.flock`, no `fcntl`, no `O_EXLOCK`, and no `LOCK_*` constant on a

**Update (2026-08-26) — the zombie branch is now verified, not just reachable.** `pidAlive`'s `/proc`-based zombie check (a dead-but-unreaped owner reads as state `Z`) had been exercised by CI on Linux but never actually asserted against a real zombie — every prior test only ran it against live, non-zombie processes. `tests/state-store/pidalive-zombie/` closes that specific gap with a fixture that forges a genuine, self-verified Linux zombie (a `python3 os.fork()` child held unreaped) and asserts `pidAlive` returns `false` against it; see `.plans/v0.5.0/PIDALIVE-ZOMBIE-TEST-TRD-2026-08-24.md` for the design. This closes evidence for that one branch of `pidAlive` — it does not change the pid-reuse-across-namespaces or mtime-vs-wall-clock approximations described above, which remain.

## 9. The MCP shim seals a session it is handed — it does not capture live traffic
## 9. Three of the shim's four capture modes remain unbuilt — the local-proxy mode now exists

`scripts/gsa-mcp-shim.js` (`sealBoundaryBundle`) takes an already-assembled `session` object — `initialize`, the granted `tools[]`, and the `calls[]` already made — and maps it into a signed boundary-tier (profile A) attestation bundle. That is the whole of what exists: hashing, tracing, and sealing data that is passed in. **Nothing produces that `session` object from a live MCP connection.**
`scripts/gsa-mcp-shim.js` (`sealBoundaryBundle`) takes an already-assembled `session` object — `initialize`, the granted `tools[]`, and the `calls[]` already made — and maps it into a signed boundary-tier (profile A) attestation bundle. That contract is unchanged and still narrow: the shim itself never observes an MCP connection directly, only the `session` shape it is handed.

The design doc ([`.plans/v0.3.0/graphsmith-v0.3.0-mcp-attestation-shim-design.md`](.plans/v0.3.0/graphsmith-v0.3.0-mcp-attestation-shim-design.md), §4) describes four deployment modes for capturing real traffic: a local proxy sitting between an MCP client and its servers, a client-side plugin hooking an agent's MCP middleware, server-side middleware wrapping a tool server, and a gateway integration for enterprise MCP gateways. **None of the four exist in code.** There is no JSON-RPC interceptor, no stdio wrapper, no HTTP reverse proxy, and no gateway-attachment point anywhere in the tree — confirmed by grepping the current source for `gateway`/`proxy`/`interceptor`: the only hits are an unrelated word-count comment and a JS `Proxy`-trap comment, neither part of MCP capture.
**Update (2026-09-04) — deployment mode 1 (local proxy) is now built.** The design doc (`.plans/v0.3.0/graphsmith-v0.3.0-mcp-attestation-shim-design.md`, §4 -- **not present in this reviewed tree**; referenced here for historical continuity with mode 1's own design lineage, not as a resolvable link) describes four ways to produce that `session` object from a live MCP connection: a local proxy sitting between an MCP client and its servers, a client-side plugin hooking an agent's own MCP middleware, server-side middleware wrapping a tool server, and an integration point inside an enterprise MCP gateway. `scripts/gateway/` (`gateway.js`, `proxy.js`, `downstream.js`, `agent-transport.js`, `session.js`) is deployment mode 1: it sits between an agent and one or more real downstream MCP servers, speaks both legs of the protocol itself, builds the `session` object from the live traffic it proxies, and calls `chain.appendSession` (which calls `sealBoundaryBundle`) when each agent connection closes. See the Standalone Gateway TRD (`.plans/v0.5.0/STANDALONE-GATEWAY-TRD-2026-08-22.md` -- **not present in this reviewed tree**, same caveat as above) for its design and section 10 below for that gateway's own residual scope limit.

**What this costs, precisely.** A caller who wants a real boundary attestation for a live MCP session must build the capture themselves — record `initialize`, `tools/list`, and every `tools/call` into the `session` shape the shim expects — before `sealBoundaryBundle` can do anything. The shim's own selftest (`--selftest` in the same file) demonstrates this: it hand-constructs a `session` literal in the test rather than deriving one from a running MCP exchange, because there is no code path that would derive one.
**What still doesn't exist.** Modes 2–4 — a client-side plugin hooking an agent's own MCP middleware, server-side middleware wrapping a tool server, and an attachment point inside a THIRD-PARTY enterprise MCP gateway (distinct from this repo's own standalone one) — remain unbuilt. A caller who cannot or does not want to run this repo's own gateway process in front of their MCP traffic still has no capture path and must assemble the `session` object themselves, exactly as before this update.

*Control:* the shim's contract is narrow and honestly scoped — it never claims to observe anything beyond the `session` object it is given (the file's own header comment states the boundary-only, profile-A-never-full-profiles limit), and the produced bundle's `decision_record.md` states plainly that the agent's plan was not observed. There is no false "captured live" claim anywhere in the emitted bundle.
*Control:* the shim's contract is narrow and honestly scoped — it never claims to observe anything beyond the `session` object it is given (the file's own header comment states the boundary-only, profile-A-never-full-profiles limit), and the produced bundle's `decision_record.md` states plainly that the agent's plan was not observed. There is no false "captured live" claim anywhere in the emitted bundle, and this document no longer claims zero capture paths exist now that one does.

**What would be needed to do it properly:** implement one of the four deployment modes in §4 of the design doc — most naturally the local proxy, since it needs no changes to the agent or to any MCP server. That is unbuilt work, not a documentation gap.
**What would be needed to do it properly:** implement one of the remaining three deployment modes in §4 of the design doc, for callers who need capture without running this repo's own gateway process. That is unbuilt work, not a documentation gap.

## 10. The standalone gateway's HTTP agent sessions are keyed by an explicit session ID, with a bounded idle-timeout lifecycle (updated 2026-09-08)

**Superseded.** Until 2026-09-08 this section documented that `scripts/gateway/agent-transport.js`'s agent-facing HTTP listener identified a session by the underlying TCP socket (`req.socket`), and recorded a 2026-09-04 board decision to deliberately NOT build anything past that. Paul asked, informally, whether the HTTP transport should carry an explicit session identifier instead -- citing the MCP streamable-HTTP spec's own `Mcp-Session-Id` convention -- and said yes. Before building, this project sought a second opinion from an external panel of five non-Anthropic frontier models (openai/gpt-6-astra, x-ai/grok-4.5, google/gemini-3.1-pro-preview, deepseek/deepseek-v3.2, qwen/qwen3-max) on what scope was actually correct, since "yes" was compatible with a small labeling change or a much larger reconnect/resume rewrite. 4 of 5 recommended a socket-scoped ID that only fixes the naming/observability problem; x-ai/grok-4.5 dissented, arguing that leaves the actual pooling-proxy defect (below) unfixed and that a *coherent* minimal HTTP session model is not much more code. Paul chose grok-4.5's design. Full brief and all five raw responses: this project's decision log, entry "2026-09-08 -- external panel: HTTP agent session identity."

**What's implemented now.** Sessions are keyed by an explicit, server-minted `Mcp-Session-Id` (128 random bits), minted on a header-less `initialize` and returned on the response header. Every later request on that session must echo the header; an unrecognized/expired ID gets 404. A session ends on an explicit `DELETE` (204), after 30 minutes of inactivity (fixed for now -- not yet a config field, see `scripts/gateway/agent-transport.js`'s own header comment on why), or when the gateway process shuts down (`gateway.js#stop` already force-closes every session still open in `proxy.sessions`, unconditionally). The TCP socket that happened to carry a given request is no longer part of session identity at all -- this is still `session_boundary: "connection"` (the schema's only implemented value; `"time_window"` is still rejected at startup), just with "connection" now meaning "this MCP session," not "this TCP socket."

**What this fixes.** A connection-pooling reverse proxy multiplexing several distinct agents over one shared backend socket can no longer corrupt session identity (identity no longer comes from the socket at all), and one well-behaved agent whose HTTP client rotates connections mid-session (a recycled keep-alive socket, a client-side reconnect) no longer loses its already-`initialize`d session for a reason outside its control.

**What this deliberately still does NOT do -- disclosed, not hidden.** This is not full HTTP session resumption. There is no cross-process session store (a session dies with the gateway process, same as before), no session replay/recovery after a crash, and no configurable idle-timeout window yet. An agent that vanishes uncleanly (crash, network partition, no `DELETE`) now leaves its session open -- and counted in `active_sessions`, and un-sealed in the audit chain -- for up to the 30-minute idle window, instead of the near-instant cleanup the old socket-close handler gave for free; that is the deliberate trade for no longer conflating "this TCP connection ended" with "this agent is done." Building the fuller reconnect/replay/multi-node session store that a real `time_window` boundary would eventually need remains out of scope until a concrete deployment defines that lifecycle for real -- the same discipline the original 2026-09-04 decision applied, now to a narrower remaining gap.

*Control:* documented here, in `scripts/gateway/agent-transport.js`'s own header comment, and in the project's decision log (external panel brief + all five responses).

---
*This document is publication-hygiene clean. See also the worked offline-verifier transcript in [`docs/examples/offline-verify.md`](docs/examples/offline-verify.md).*
Loading
Loading