Repository navigation
ci(teeth): eight shards, the count read from the workflow, and stale PR runs cancelled - #481
Merged
Merged
Conversation
…e upstream Four cases for the MCP broker, added before the fix so CI shows them red on the current code: - a JSON-RPC batch of two tools/call over HTTP is refused with 400 and -32600, nothing reaches the upstream and the PDP is never asked, with a policy that denies every tool; - the stdio transport refuses the same batch; - a bare string, a number, null, and an object whose method is not a string are refused the same way; - one request and a client's JSON-RPC response (id and result, no method) still reach the upstream, so the fix cannot refuse too much. Scenarios in features/a-batch-cannot-carry-a-call-past-the-gate.feature, each bound to its test. README badge and PROGRESS.md count 2418. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every check the broker makes keys on `method` read from one JSON object. A batch is an array, so its method read as empty and every tools/call check was skipped: the policy decision, the identity requirement, drift, the taint gate, the argument scans and the tool_call record, and a batched tools/list answer was never scanned. The array was forwarded as it came, over HTTP and stdio alike. process_call, which both transports reach, now refuses first, before an upstream is resolved or a secret read: a message that is not one JSON object, or whose method is present and not a string, gets JSON-RPC -32600 with a null id and HTTP 400. MCP removed batching in its 2025-06-18 revision. A JSON-RPC response (no method) is still carried. Invariant 142 in CLAUDE.md. The previous commit's tests failed on the unfixed broker in CI (run 40258757907: three refused-case tests red at the "nothing reached the upstream" assertion, the must-not-fire case green). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…visories rustls-webpki 0.103.15 -> 0.103.16 (RUSTSEC-2026-0382) and webpki-roots 1.0.8 -> 1.0.11 (RUSTSEC-2026-0381) in both lockfiles, the workspace and crates/cluster. wasmtime 49.0.3 -> 49.0.4 (RUSTSEC-2026-0378, RUSTSEC-2026-0379) in the workspace lockfile only, staying on 49.x; the cranelift and pulley crates move with it from 0.136.3 / 49.0.3 to 0.136.4 / 49.0.4. Lockfile-only: no manifest changes, no new crate. crypto-bigint 0.5.5 (RUSTSEC-2026-0387) is the fifth advisory and needs the p256 0.14 migration in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKcmLyFYX5vcY81ze4tHQ1
…026-0387) p256 0.13 is pulled only by this workspace's own crates (cloud, delegation, dpop, gateway). Moving all six manifest lines to 0.14 brings elliptic-curve 0.14.3, ecdsa 0.17.0, pkcs8 0.11, sec1 0.8, signature 3.0 and rfc6979 0.6, and replaces crypto-bigint 0.5.5 with 0.7.9, so `cargo tree -i crypto-bigint --all-features --target all` no longer shows a 0.5.x. The code changes are two renames, nothing else: - `VerifyingKey::to_encoded_point(compress)` is `to_sec1_point(compress)` in ecdsa 0.17; `x()`, `y()` and `as_bytes()` on the result are unchanged. - `SigningKey::random(&mut OsRng)` is deprecated, and rand_core is 0.10 with no `OsRng`; the test fixtures that made a random key now call `<SigningKey as p256::elliptic_curve::Generate>::generate()`. Production key generation (gateway checkpoints) already drew bytes with getrandom 0.2 and `SigningKey::from_slice`, which is unchanged. `Signature::from_slice`, `to_bytes`, `VerifyingKey::from_sec1_bytes`, `SecretKey::from_pkcs8_pem` / `from_sec1_pem` / `from_pkcs8_der` / `from_sec1_der` keep their names and signatures. P-256 keeps `NORMALIZE_S = false`, so a high-S signature still verifies and signing still does not normalise s, as in 0.13. One rustfmt collapse in an audit_sign test follows the shorter name; the assertion is the same. The gateway manifest comment on hmac is corrected: hmac 0.12 reached the lockfile through p256 0.13's rfc6979, and since 0.14 (which uses hmac 0.13) the gateway's own line is its only edge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKcmLyFYX5vcY81ze4tHQ1
The external P-256 values the suite already checked were three keyryx thumbprints (measured from keyryx's Go code) and agent-passport's checkpoint vectors, which it verifies but does not produce. Nothing held a signature this code produces, a SEC1 public key, or a PKCS#8 / SEC1 private key parse to an external value, so a curve library that changed one of those could pass the suite as long as it agreed with itself. Three tests now hold the bytes to RFC 6979 appendix A.2.5 (a P-256 key, its point, its deterministic SHA-256 signatures), with the base64 forms, the RFC 7638 thumbprint and the PKCS#8 / SEC1 encodings written out by Python `cryptography` 50.0.1, never by p256: - gateway::checkpoints: the checkpoint key's JWK x and y, its kid, and the raw r||s over "sample" and "test". - cloud::devices: the RFC signature verifies under the uncompressed and compressed SEC1 key; with its halves swapped, or in DER, it is refused. - cloud::audit_sign: the RFC key loads from PKCS#8 PEM, SEC1 PEM, PKCS#8 DER, SEC1 DER and a raw scalar, and each signs the same empty-chain manifest to the same public key and signature bytes. Each went red against a deliberately broken encoding first (padded standard base64 for x and y; s before r; a DER-only signature parser; a compressed public key and a DER signature in the manifest) and green on the real code. All three also pass unchanged on main with p256 0.13.2, so 0.13 and 0.14 put the same bytes on the wire. Encoding the manifest's public key compressed passed every older test; only the new one caught it. Scenarios: features/the-p256-wire-format-is-unchanged.feature, five, each bound. The suite goes from 2414 to 2417; README.md and PROGRESS.md say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKcmLyFYX5vcY81ze4tHQ1
`secretscan / tree` flagged the PKCS#8 header of the RFC 6979 appendix A.2.5 P-256 key that the audit-signing-key encoding test loads (crates/cloud/src/audit_sign.rs, fingerprint 3021d90eb9437b2d, the same header fingerprint already allowed for the test keys in crates/cloud/tests/it/oidc.rs and crates/dpop/src/lib.rs). The key is a published test vector and signs nothing. The SEC1 header in the same file was already covered by its existing entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKcmLyFYX5vcY81ze4tHQ1
…cp-broker-refuses-batches # Conflicts: # PROGRESS.md # README.md
…PR runs cancelled The teeth harness ran as four shards of 11.6 to 19.3 minutes each on main (run 39356312313), the long pole of every CI run; its cases add up to 54.7 minutes (run 40259164936). Eight shards halve the cases each runner carries. The count was written twice, as the matrix and as `shards=4` in the partition gate, and nothing checked that they agreed (invariant 113 said so). The gate now reads it from ci.yml, where the matrix, TEETH_SHARD and the job name each state it, and refuses when they disagree or the matrix is not 1..N. Three teeth cases plant each fault: a matrix that skips a shard, a divisor left at four, the matrix gone. `concurrency` cancels a pull request's older run when a newer push arrives; a push to main is never cancelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TAIPANBOX
enabled auto-merge
October 26, 2026 04:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The teeth harness is the long pole of every CI run here: four shards of 11.6 to 19.3 minutes each on main (run 39356312313). Its cases add up to 54.7 minutes across the shards (per-case timestamps of run 40259164936), 9.0 of them in the five
stated-numberscases, each of which runs the full suite.shards=4inscripts/teeth-shards-cover-every-case.sh, and nothing checked that they agreed (invariant 113 named that gap). The gate now reads the count fromci.yml, where the teeth job's matrix, itsTEETH_SHARDdivisor and its name each state it, and refuses when the three disagree or the matrix is not1..N.concurrencycancels a pull request's older run when a newer push arrives. A push to main is never cancelled: its group is its own commit.Proof
ci.yml: a matrix that skips a shard, a divisor left at four under a matrix of eight, and the matrix gone (which must say it measured nothing). Each was also run by hand against the gate: all three fail with the expected message, and the unmutated workflow passes.Order
This branch sits on top of #478, which itself carries #480, so its diff shows their commits until they merge. Its own change is the last commit,
ci(teeth): eight shards, ....🤖 Generated with Claude Code