Repository navigation
refimpl: add first-stage Rust AGT Guardian - #187
Arham-Begani wants to merge 10 commits into
Conversation
Signed-off-by: Arham Begani <arhambegani2@gmail.com>
|
I completed an independent substantive review of the first-stage Rust Guardian against the ACS v0.1.0 schemas and existing TypeScript AGT behavior. I checked handshake/method declaration and dispatch, failure handling, AGT→ACS verdict mapping, session/hash-chain handling, Rust↔TS parity coverage, and the documented limitations around duplicate request_id, wire chain_hash, signatures, ASK/ask_details, and DEFER. |
Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
|
I read this stage against the TypeScript Guardian it ports, concentrating on the session store, since that is where both implementations hold state a policy decision actually reads. The chain behaviour is faithful and worth saying before the finding. The 1,024 retained-session cap matches
The window is real because The cause looks structural rather than local. This store keeps recency in Smaller, same file. The eviction line reports the cap but not what it causes. Upstream's message reads "A further step on this session starts a new chain at seq 1, which the Inspector renders as a chain break", and its doc gives the reason: the eviction and the chain break it causes are otherwise two unexplained facts in two different places. Upstream reports eviction twice over, and the second report is Neither |
Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
|
Thanks for the review. I addressed the session-store finding.
I also added the requested regression coverage using 1,025 sessions, verified the recreated session retains the propagated labels and restarts at I additionally moved the Rust↔TypeScript live parity comparison into CI. All current checks, including Rust tests, clippy, formatting, TypeScript tests, and the live parity test, are passing. Would appreciate a final review when you get a chance. |
|
That is the right shape. Routing The The regression test pins it at the right place: against the previous commit it fails on Since you asked for a final pass, one more parity observation, explicitly not a request for changes. The TypeScript store wraps its eviction reporter in a try/catch, and its own comment says why: a reporter that throws would surface as a Guardian failure on whichever unrelated step happened to overflow the cap, and a Guardian-side failure under the default posture runs the tool ungoverned, so a broken reporter must not cause the thing the cap exists to prevent. The bare I would not hold the PR for it. The direction is fail-closed rather than fail-open, since a poisoned lock sends Nothing else from me, and the finding is closed. One thing worth saying plainly so you are not waiting on the wrong signal: I am not a code owner here and this is a comment rather than a review, so it does not move the merge. That still needs one of the names in CODEOWNERS. |
Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
rocklambros
left a comment
There was a problem hiding this comment.
@Arham-Begani, the session-store fix holds up. replace_labels now goes through ensure_session, and the 1,025-session test passes in CI. I also read eb609e44, which landed just after Mayur's last comment, and the eviction warning can't panic anymore. Most of what I have left is small, but three things need to change before this merges.
-
The Guardian runs whatever
opait finds.policy.rs:18-33falls back toopaon PATH and only checks thatopa versionexits 0. The TS Guardian runs the OPA binary pinned inagt/bun.lockby sha512.manifest.yaml:11-14already records an OPA loader quirk that dropsdata.jsonand answers allow for everything, so a developer with a different Homebrewopacan end up with an allow-all Guardian and nothing would notice. Please parse theopa versionoutput and refuse to start unless it says0.70.0. In the same spirit, the CI job installs OPA withnpm install ... agent-control-specification-opa-linux-x64@0.3.1-beta.0, with no integrity check. You can movebun install --frozen-lockfileinreference-implementations/agtabove the Rust test step, setACS_OPA_PATHin the jobenv:to the binary underagt/node_modules/agent-control-specification-opa-linux-x64/bin/opa, and drop the npm step. -
The advertised deadline ties with the OPA deadline.
server.rs:129sends"default_ms": 5000, and the pinned SDK's OPA timeout is also 5s. The host starts its timer before OPA even spawns, so on a slow OPA the host gives up first, and with the default posture (main.rs:17,proceed) the tool runs. The README'sACS_OPA_TIMEOUT_MS=30000makes that gap 25 seconds. Please either advertise the OPA timeout plus about a second, or refuse to start when the OPA timeout isn't belowdefault_ms, and add a unit test. The TS Guardian has the same pair, so I'll file an issue for that side. -
The parity wording promises more than the nine cases check.
README.md:45says cross-language drift "fails the Reference implementation workflow", and the PR body says all five AGT verdict kinds are mapped. The nine cases use one session, valid ids, and one invalid payload. I compared a few inputs: a non-object argument gets different error text from each Guardian, and a handshake error hasdata.pointerin TS but nodatain Rust (server.rs:119). Aurn:uuid:session id or a space-separated timestamp gets allowed by TS and denied by Rust. Warn, escalate, andevaluation_failedaren't covered at all. Adding cases is the better fix, but it's also fine to narrow the wording to "nine cases" and list what isn't compared. Please also refresh the Testing section, since CI now runs 4 tests, not 3 plus 1.
These aren't blockers:
- Four
eprintln!calls are still left inserver.rs(lines 101, 247, 259, 386). Two of them run while a log mutex is held, so a failed stderr write can poison the lock and stop the audit logs with no message. Using the samelet _ = writeln!(io::stderr().lock(), ...)pattern you used insession.rsfixes all four. - The new
LICENSING.mdrow puts the Rust README under CC BY-SA, but the TS reference's READMEs fall under the Apache catch-all (LICENSING.md:55-57). Unless you meant that, it's simplest to drop the row. - Nothing will ever propose updates for the new
Cargo.lock(301 crates), because.github/dependabot.ymlonly coversgithub-actionsanduv. Please add acargoentry for/reference-implementations/agt-rust, copying the existing weekly schedule andcooldown: default-days: 7. It's a.github/change, so I'll approve that part. policy.rs:74uses the same regex text as the TS annotator, but Rust's\band\sare Unicode-aware, so a few inputs match differently. Please don't "fix" Rust to match TS on U+FEFF, because the TS side is the one that's wrong there. I'll handle that separately.
I'll re-run CI against current integration once you push, since the last run merged against 770f1e0.
Sync current integration, require the pinned OPA version, install its CI binary from the frozen AGT lockfile, and advertise the evaluator deadline plus delivery time. Add regressions, make audit logging non-panicking, correct licensing and parity claims, and enable Cargo dependency updates. AI-assisted-by: OpenAI Codex Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
|
Review follow-up is pushed in 208698c, synchronized with integration at 8a45952.
All current checks pass, including Linux Rust tests, live Rust/TypeScript comparison, TypeScript checks, Python guards, strict documentation build, and zizmor. Local Rust results are 6 unit tests plus 1 HTTP integration test. The PR description records the separate Windows Python-suite limitations and links the successful Linux runs. This update was prepared with Codex assistance. The contributor personally reviewed the focused patch and full PR, certified the DCO, and explicitly authorized sign-offs on the new commit and six older follow-up commits. The metadata rewrite preserves every code tree; every PR commit now carries a human sign-off. The Unicode-aware Rust regex remains unchanged as requested. |
54fa594 to
208698c
Compare
|
@rocklambros Could you re-review A formal re-review request was attempted but GitHub rejected it for insufficient account permissions, so I am recording the request here. No review approval or merge is being inferred from the green checks. This follow-up was prepared with OpenAI Codex assistance. |
|
@rocklambros, could you re-review The source is unchanged since the October 19 update. Please let me know if there is a remaining blocker. This follow-up was prepared with AI assistance. |
rocklambros
left a comment
There was a problem hiding this comment.
@Arham-Begani thanks for the follow-up. Everything from my October 12 review is in 208698c: the exact OPA 0.70.0 guard, the frozen-lockfile install with ACS_OPA_PATH, the 1000 ms margin with the overflow check and its tests, the narrowed nine-case claim, the stderr writes that no longer panic, the licensing row, and the Dependabot entry.
Your last CI run was from October 18, and PRs #272, #273 and #309 changed the TypeScript Guardian since then, so I closed and reopened the PR to rerun the workflow against current integration. Both jobs pass.
Two small things, neither a blocker. The Dependabot entry labels rust, and that label doesn't exist in the repo, so Dependabot will warn the way it already does for python. Once PR #229 lands, the TypeScript Guardian will check signatures, replay and timestamps and this port won't yet, so the README's limits sentence will need a follow-up then.
Approving.
|
Thanks for the re-review and for rerunning against current integration. I verified that both jobs in the new Reference implementation run executed and passed, including the live Rust/TypeScript comparison on the merge of I've recorded the two non-blocking follow-ups: the missing This response and verification were prepared with AI assistance. |
|
@Arham-Begani, thanks for your work here. Since this PR opened, the project merged shared expectations for Guardian reference implementations in
Could you check the PR against the rest of that README too? Thank you @rocklambros cc for visibility |
Merge integration bba1b84 without rewriting published history. Require both audit sinks, cap segment size and retention, preserve retained records during rotation, and deny valid steps when a required audit record cannot be kept. Check writes, flushes and synchronization; latch failures; reject incomplete history and duplicate configured sink ownership. Remove only the unsupported rust label from the Cargo Dependabot entry. Validated the unchanged Rust subtree with 18 unit tests, four HTTP tests, strict format/check/Clippy and nine documented live comparisons. Final Python guards (1,206), strict docs and source assertions pass separately. Signing and replay protection remain outstanding review work. AI assistance: OpenAI Codex assisted with this review follow-up and validation. Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
|
Thanks, @artmaro. Commit 754dce0 adds bounded, fail-closed audit logging and removes the invalid Dependabot label. Current-head upstream CI passes. Signing, key_id handling and replay protection remain outstanding; no full ACS-Core conformance is claimed. Could a lead assign issue #88 to me and confirm whether the remaining shared requirements must land in this PR or can be handled as agreed follow-ups? |
ACS-Core requires a §10 signature on every message and §10.3 replay protection, and the Rust Guardian had neither, so its wire was unauthenticated and a captured request could be resent. With HMAC secrets configured, every request except system/ping must carry an HMAC-SHA256 signature over the RFC 8785 canonical envelope, keyed per session by HKDF-SHA256 of the secret its key_id names. A missing, unsupported or mismatched signature, or an unknown key_id, is refused with SIGNATURE_INVALID, and an unknown key_id is never tried against another secret. Responses to verified requests are signed under that request's key and key_id, including the denial that replaces a decision when an audit record cannot be kept. Secrets come from a key file or the ACS_HMAC_KEY_ID and ACS_HMAC_SECRET pair the TypeScript host adapter reads, must be standard base64 of at least 32 bytes, and several can be configured at once so a secret is rotated without an outage. No secret or derived key reaches messages, logs or errors. Requests outside the skew window are refused with TIMESTAMP_OUT_OF_WINDOW, and a repeated request_id within a session with REPLAY_DETECTED. Replay state is bounded and refuses new ids instead of evicting live ones. The handshake now declares signature_algorithms_supported and skew_window_ms. Without a secret the Guardian warns and runs unsigned, which keeps the CI comparison with the unsigned TypeScript Guardian. The optional nonce is not checked and replay state does not survive a restart; the README says so. Validated with 24 unit tests (including the §10 example key and RFC 8785 ordering and number cases), five HTTP tests against the pinned AGT bundle, strict fmt and Clippy, the nine live comparisons, a Node crypto interoperability check of signed requests and responses, and the 1,206 Python guards. AI assistance: Claude Code wrote this change; I reviewed it and ran the checks. Signed-off-by: Arham Begani <123586002+Arham-Begani@users.noreply.github.com>
Summary
First stage of the Rust port for #88: a Rust AGT Guardian at
reference-implementations/agt-rust/using the existing AGT manifest and policy,mapping.yaml, and ACS v0.1.0 schemas. The existing TypeScript Claude Code and OpenCode shims can send requests to this server. No policy or schema copy is introduced.The modules separate HTTP/JSON-RPC dispatch, schema validation, SDK integration and egress annotation, verdict mapping, session state, and local audit logs.
Review updates
integration.ACS_OPA_TIMEOUT_MS=30000produces 31000 ms. Reject an unrepresentable deadline.Tested behavior and limits
The checked-in comparison covers nine cases against live Rust and TypeScript Guardians with the same policy: allow, deny, request and result redaction, egress denial, WebFetch allowance, invalid payload, an undispatched method, and handshake. Eight compare complete responses. The handshake compares everything except the intentionally different
timeout_config.default_ms, whose positive integer shape is still checked.This is evidence for those cases, not general parity. Non-object arguments, handshake validation error data, alternative UUID/timestamp formats, warn/escalate verdicts, and evaluation failures are not covered. The Rust SDK collapses absent
result_labelsand an empty array to the same vector; the Guardian conservatively retains labels for an empty vector. That IFC limitation remains documented.Validation of this update
cargo fmt --all -- --check: passed.cargo check --workspace --locked: passed.cargo clippy --workspace --all-targets --all-features --locked -- -D warnings: passed.ACS_OPA_PATH=<locked AGT OPA 0.70.0> ACS_OPA_TIMEOUT_MS=30000 cargo test --workspace --all-features --locked: 6 unit tests and 1 HTTP integration test passed. These include exact-version and deadline regression tests; the integration test checks the negotiated deadline.bun tests/compare-guardians.mjsagainst both live servers: all nine cases passed, with only the documented handshake deadline excluded from equality.bun run typecheckin the TypeScript reference: passed.uv run mkdocs build --strict: passed on Windows.The full Python guard suite does not pass on this Windows environment. A local harness outside the contribution shortens oversized test display IDs and selects Git Bash without changing test bodies or parameter values. It completed with 908 passed, 10 failed, 1 skipped. Failures involve Unix executable/shebang stubs, a missing
python3in test-provided shell environments, and Windows symlink privileges. An unadapted run also encounters Windows' environment-variable length limit. WSL cannot start because virtualization is disabled. These are local platform limitations. The subsequent upstream Linux CI passed on this update: Reference implementation (Rust formatting/check/clippy/tests/live comparison and TypeScript checks), Python guards and documentation build, and workflow security scan. Site deployment is skipped for the pull request.Remaining scope
The Rust host adapter, Inspector, and conformance runner are outside this stage. Only the two tool gates supported by the TypeScript reference are evaluated; no ACS-Core conformance claim is made.
AI assistance: Codex helped implement and test this update. The contributor personally reviewed the focused patch, certified the project's DCO, and authorized the human sign-off on the review-fix commit.
Part of #88.