Skip to content

ctutils v0.4.4 - #1569

Merged
tarcieri merged 1 commit into
masterfrom
ctutils/v0.4.4
Oct 23, 2026
Merged

tarcieri merged 1 commit into
masterfrom
ctutils/v0.4.4

Conversation

@tarcieri

Copy link
Copy Markdown
Member

Security

  • Fix and verify Choice::to_*_mask constant-time codegen (#1567)
  • Fix constant-time u128 comparisons on 32-bit targets (#1568)

@tarcieri
tarcieri merged commit a993c0b into master Oct 23, 2026
148 checks passed
@tarcieri
tarcieri deleted the ctutils/v0.4.4 branch October 23, 2026 01:03
tarcieri added a commit to RustCrypto/crypto-bigint that referenced this pull request Oct 23, 2026
This release includes important security fixes: RustCrypto/utils#1569
djc pushed a commit to rustsec/advisory-db that referenced this pull request Oct 23, 2026
This advisory covers issues fixed in v0.4.4:

RustCrypto/utils#1569
thomaseizinger added a commit to firezone/firezone that referenced this pull request Oct 23, 2026
Versions of `ctutils` before 0.4.4 can generate branches in the mask
generation of `Choice` on common platforms, and in its `u128`
comparisons on 32-bit targets, so they are not constant-time. This is a
new advisory that fails `cargo deny` on every PR.

Related: RustCrypto/utils#1569
CueCrux-Myles pushed a commit to CueCrux/Crux that referenced this pull request Oct 23, 2026
ctutils < 0.4.4 has non-constant-time mask generation and u128
comparisons (RustCrypto/utils#1569). It reaches the tree through
p256/ecdsa/hmac and turns Cargo deny red on every branch. Lockfile-only;
ctutils 0.4.5 needs Rust 1.87, inside the current MSRV.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011G8hmqqwypRjci676mqZ6Z
CueCrux-Myles pushed a commit to CueCrux/Crux that referenced this pull request Oct 23, 2026
ctutils < 0.4.4 has non-constant-time mask generation and u128
comparisons (RustCrypto/utils#1569). It reaches the tree through
p256/ecdsa/hmac and turns Cargo deny red on every branch. Lockfile-only;
ctutils 0.4.5 needs Rust 1.87, inside the current MSRV. The same bump
rides #834, so whichever lands first carries it to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011G8hmqqwypRjci676mqZ6Z
kunwarshivam pushed a commit to kunwarshivam/mandate that referenced this pull request Oct 23, 2026
`cargo deny --locked check` fails on main. In ctutils 0.4.2, the constant-time
`Choice` mask lacked an optimization barrier (RUSTSEC-2026-0385,
RustCrypto/utils#1569); 0.4.4 and later fix it. ctutils is a transitive
RustCrypto crate, reached through aes-gcm, crypto-bigint, digest, sec1 and
universal-hash (webpush, p256, sqlx-postgres). `cargo update -p ctutils` makes
a patch-level bump. Nothing else in Cargo.lock moves, and no manifest or
dependency row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU
kunwarshivam added a commit to kunwarshivam/mandate that referenced this pull request Oct 23, 2026
…s for RUSTSEC-2026-0385 (DEC-2334, DEC-2338) (#2322)

* CI (lane GO): run api-release in a full-checks-3 job of its own (DEC-2334, DEC-2338)

#2289 added `api-release` to `cargo xtask ci full` (DEC-2338 item 5) after
#2195 had split `full`'s parts into `full-checks` and `full-checks-2`
(DEC-2334). No CI job ran `api-release`, and
`the_full_jobs_run_exactly_the_full_group` failed on main.

Its release build of `mandate-api-server` takes about five minutes on its own.
Neither existing job has room for that under ten minutes (DEC-464):
`full-checks-2`'s `reference` alone is about 4.5 minutes, before its two
release builds. So it runs in `full-checks-3`, on the same selection, and
`full` needs all three. The verdict table now shows a failed, cancelled or
skipped `full-checks-3` failing `full`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

* Supply chain: bump ctutils 0.4.2 -> 0.4.5 for RUSTSEC-2026-0385

`cargo deny --locked check` fails on main. In ctutils 0.4.2, the constant-time
`Choice` mask lacked an optimization barrier (RUSTSEC-2026-0385,
RustCrypto/utils#1569); 0.4.4 and later fix it. ctutils is a transitive
RustCrypto crate, reached through aes-gcm, crypto-bigint, digest, sec1 and
universal-hash (webpush, p256, sqlx-postgres). `cargo update -p ctutils` makes
a patch-level bump. Nothing else in Cargo.lock moves, and no manifest or
dependency row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

---------

Co-authored-by: Claude <noreply@anthropic.com>
mmqz pushed a commit to mmqz/smart-downloader that referenced this pull request Oct 23, 2026
cargo-deny CI 拦截:ctutils 0.4.2 非恒时掩码生成与 u128 比较漏洞
(RUSTSEC-2026-0385,RustCrypto/utils#1569)。修复版 >=0.4.4。

与 tomjiu#162 代码变更无关——新披露 advisory,main 同样受影响;
纯 lockfile 升级,94 个依赖不变。
rodalladea added a commit to rodalladea/bloc that referenced this pull request Oct 23, 2026
## Change

CI repeatedly compiles the dependency auditor and builds separate images
for distribution and capacity testing. Cache the pinned auditor, advance
Rust caches per revision, and persist architecture-specific BuildKit
layers with a separate Rust dependency layer. Distribution smoke-tests
and measures capacity on the same retained image digest, building each
native platform once.

Documentation-only changes skip compilation and database fixtures.
Distribution-only changes retain source/policy checks without starting
database fixtures; runtime, templates, assets, charts, shared fixtures
and unknown paths retain full validation. Failed path selection retains
validation.

Update the existing transitive `ctutils` dependency from 0.4.3 to 0.4.4
to address
[RUSTSEC-2026-0385](RustCrypto/utils#1569). The
patch fixes constant-time mask generation and 128-bit comparisons. Other
dependency versions remain unchanged.

## Behavior and decisions

[CI behavior record](docs/behaviors/ci-efficiency.md):
`ci-efficiency-01` through `ci-efficiency-03`. Maintainer-approved scope
retains Debian slim, PostgreSQL 17/18 and native amd64/arm64 validation.

Workflow policy tests inspect parsed YAML structure rather than exact
indentation or mapping order. Documentation describes reproducible
checks rather than individual execution logs.

## Verification

- Passed Rust formatting, Clippy with warnings denied, ordinary tests
and documentation with warnings denied, including after the ctutils
update.
- Passed `cargo audit` against the updated advisory database with the
corrected lockfile.
- Passed all 44 Python CI/runner/distribution policy tests, including
failure propagation.
- Passed actionlint 1.7.12 and `git diff --check`.
- Built the real native ARM64 image and passed container checks C1–C6
for the Dockerfile change.
- Confirmed a source-only change reuses the dependency build layer and
recompiles only bloc.
- Confirmed reordered/reformatted workflow YAML is accepted, while
weakened selection gates or missing ARM64 coverage are rejected.

Hosted checks passed on commit `4440481`: [CI, including PostgreSQL
17/18](https://github.com/rodalladea/bloc/actions/runs/39952917865),
[dependency
audit](https://github.com/rodalladea/bloc/actions/runs/39952917971), and
PR title validation. Full hosted distribution, paired native capacity
measurements, cluster validation and cache hit rates remain to be
verified. No hosted elapsed-time improvement is claimed yet.

## Impact

No database migrations, capacity-limit changes or publication-permission
changes. PostgreSQL 17/18 and both native architectures remain covered.
Includes a transitive Rust security patch and a changelog entry. Adds
pinned `PyYAML==6.0.3` only for workflow policy tests, with safe loading
and documented installation; no new application or runtime-image
dependency is added.

## Checklist

- [x] PR title follows Conventional Commits.
- [x] Changes are scoped and contain no secrets or real identity data.
- [x] Relevant tests, documentation and security changelog are updated.
- [x] Changed CI behavior has agreed examples and test mappings; the
dependency patch preserves application behavior.
- [x] Implemented capabilities are distinguished from pending hosted
verification.
- [x] Existing publication permission boundaries and failure gates are
preserved; the dependency security advisory is linked.
devdaviddr added a commit to syntropyai-hq/syntropy that referenced this pull request Oct 23, 2026
Hosted `lint` fails at the `advisories` step on every open PR, in both
lanes, because of **RUSTSEC-2026-0385**
(https://rustsec.org/advisories/RUSTSEC-2026-0385). It affects `ctutils`
0.4.3, which comes in through `iroh` 1.4.1, a dev-dependency of
`syntropy-net`; the full tree is in the output below. The advisory's fix
is `>=0.4.4`. This PR runs `cargo update -p ctutils --precise 0.4.5`,
the latest 0.4.x, which unblocks `lint` on all open PRs.

Dependency policy: ADR-0018 §3, RFC-0062 §11.2. No new crate and no new
direct dependency, so the new-dependency approval rule does not apply.
`.github/CODEOWNERS` has no pattern that covers `Cargo.lock`, so this PR
needs no code-owner approval.

### Lock diff (only `ctutils` changes)

```diff
diff --git a/Cargo.lock b/Cargo.lock
index 255b97b..c452bfe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -691,9 +691,9 @@ dependencies = [
 
 [[package]]
 name = "ctutils"
-version = "0.4.3"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574"
+checksum = "38d28fb6aa7dd680b3ffd1a3e64e115c177bdba63c0ae7f9cd48129ff05c4fa0"
 dependencies = [
  "cmov",
 ]
```

### `cargo xtask deny --advisories` before (origin/main)

```
deny --advisories: cargo-deny 0.20.2, advisories, with /Users/admin/syntropy/.claude/worktrees/fix-ctutils/deny.toml on /Users/admin/syntropy/.claude/worktrees/fix-ctutils/Cargo.toml
error[vulnerability]: Non-constant-time mask generation and `u128` comparisons
   ┌─ /Users/admin/syntropy/.claude/worktrees/fix-ctutils/Cargo.lock:70:1
   │
70 │ ctutils 0.4.3 registry+https://github.com/rust-lang/crates.io-index
   │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
   │
   ├ ID: RUSTSEC-2026-0385
   ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0385
   ├ ## Summary
     
     automatically in CI using `cargo checkct`.
   ├ Announcement: RustCrypto/utils#1569
   ├ Solution: Upgrade to >=0.4.4 (try `cargo update -p ctutils`)
   ├ ctutils v0.4.3
     └── iroh v1.4.1
         └── (dev) syntropy-net v0.0.0
             ├── syntropy-api v0.0.0
             ├── syntropy-cluster v0.0.0
             └── syntropy-testkit v0.0.0
                 ├── (dev) syntropy-exec v0.0.0
                 ├── (dev) syntropy-io v0.0.0
                 │   ├── syntropy-exec v0.0.0 (*)
                 │   ├── syntropy-kernels-cpu v0.0.0
                 │   │   └── (dev) syntropy-exec v0.0.0 (*)
                 │   ├── syntropy-probe v0.0.0
                 │   └── syntropy-tier v0.0.0
                 ├── (dev) syntropy-kernels-cpu v0.0.0 (*)
                 ├── (dev) syntropy-manifest v0.0.0
                 │   └── syntropy-graph v0.0.0
                 │       └── syntropy-testkit v0.0.0 (*)
                 ├── (dev) syntropy-net v0.0.0 (*)
                 └── (dev) syntropy-spsc v0.0.0
                     ├── syntropy-exec v0.0.0 (*)
                     ├── syntropy-io v0.0.0 (*)
                     ├── syntropy-ipc v0.0.0
                     ├── syntropy-kernels-cpu v0.0.0 (*)
                     └── syntropy-net v0.0.0 (*)

advisories FAILED
deny --advisories: FAIL: advisories: cargo-deny exit status: 1
```

### `cargo xtask deny --advisories` after

```
deny --advisories: cargo-deny 0.20.2, advisories, with /Users/admin/syntropy/.claude/worktrees/fix-ctutils/deny.toml on /Users/admin/syntropy/.claude/worktrees/fix-ctutils/Cargo.toml
advisories ok
deny --advisories: PASS: advisories (ADR-0018 §3, RFC-0062 §11.2)
```

### Checks (macOS host, aarch64-apple-darwin, Rust 1.99.1, cargo-deny
0.20.2)

**Passed**
- `cargo update -p ctutils --precise 0.4.5`: `Updating ctutils v0.4.3 ->
v0.4.5`. The lock diff above changes only `ctutils`.
- `cargo xtask deny --advisories`: before FAIL (RUSTSEC-2026-0385),
after `PASS: advisories`.
- `cargo xtask deny`: `bans ok, licenses ok, sources ok`, `PASS`.
- `cargo xtask ci dep-diff --base <origin/main worktree> --head <this
worktree>`: `no new direct dependency or vendored component`, `registry:
no new crate to look up`, `PASS (dep-diff, registry)`.
- `cargo build --workspace --locked`: finished.
- `cargo test -p syntropy-net --locked`: 15 suites ok, 319 passed, 0
failed, 1 ignored.

**Failed**
- None.

**Not run**
- `cargo xtask ci lint` (full), which hosted CI runs.
- D5 naming check: this machine has no banned-terms list configured.
Only `Cargo.lock` changes.

Signed-off-by: devdaviddr <devdaviddr@gmail.com>
kunwarshivam added a commit to kunwarshivam/mandate that referenced this pull request Oct 23, 2026
…EC-2350 S4c follow-up) (#2311)

* Test the env-name pin, the vault CA mount and the exchange's launch on the deploy-manager's pods

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Pin each managed container's environment, mount the vault's CA, and launch the exchange with its vault flags

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Cite DEC-2332 for the env pin, the vault CA and the exchange's launch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Bind the API to the vault with its AppRole and the vault's CA, copied owner-only, where its binding is on (DEC-2338)

api.vaultBinding projects the vault-approle-api Secret's two keys and the
vault-ca CA into one read-only source directory, which an init container copies
into a memory-backed directory at 0600 as api.role-id, api.secret-id and
ca.crt, since mandate_vault's reader refuses any group or other bit; the API
mounts it read-only and names it in MANDATE_API_VAULT_DIR, beside
MANDATE_API_VAULT_ADDRESS, and its egress names the vault on 8200. Off by
default; staging's values turn it on, production, which runs no vault, may
not. Infrastructure design 3.1's API row says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Check that the API's AppRole is named by the api alone, its egress exactly, and its vault address exactly (DEC-2338)

Every workload's volumes, environment, arguments and init containers are scanned for the AppRole's
Secret, which only the api's projection may name. The api's whole egress is its unbound rules with
the one vault rule added, and the vault rule is exactly its workspace's vault pod on 8200. Seeded:
the AppRole mounted into each other process, an ipBlock or namespaceSelector on the vault rule, a
wide peer on 443, a portless rule, and a foreign vault address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Check that only the copy and the api mount the api pod's vault volumes (DEC-2338)

With the binding on, vault-sources is mounted by the vault-credentials init container alone, read-only,
and vault by it read-write and by the api read-only; with it off, neither anywhere. Seeded with a
copy of the api container, hardened as it is, mounting either volume as a container or an init
container.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Add the role provisioner's connection-role records and start an exchange only once its roles exist (journal v0.77, DEC-2350)

Journal spec §9.33 adds ConnectionRolesMade, ConnectionRolesRefused and ConnectionRolesRemoved on the
control stream at schema version 1, with rule 261 (a system actor and a cause) and stream rules 262
and 263 (each record tied to its OAuth request; a removal after the roles were made, each role once,
on its occasion, and the executor's role on a revocation only once no generation of its executor is
left running). §9.21 starts a token exchange only once its request's ConnectionRolesMade is read.
Connections spec §5.2 step 4 orders the roles first. The reference module connection_roles builds
the vectors: 36 invalid and 4 valid drafts, 19 rule-262 and rule-263 streams, 4 validator, 8 stream
and 7 vector mutants caught. Reserves and fills DEC-2350; adds the provisioner's backlog row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Name only the added delete as DEC-2350's: the provisioner's + on its role paths is DEC-2630 item 7's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Never remove the executor's role: the provisioner removes the token exchange's alone (DEC-2350 item 3)

Rule 263 narrows to one removal per made roles, of the token exchange's role, on a closed request or
a revocation. A kill switch's start after a revocation can always log in; the cost, one executor role
per closed or revoked connection granting a read of its own destroyed token, is recorded in DEC-2350
with the sweep left in the backlog. The executor's role is an invalid draft; its stream cases and the
process fold go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Tie each role removal to its own request's close, refresh the fixture, and grant delete on the exchange's mount alone (DEC-2350, review)

Rule 263 now holds a removal's causation_id to a closer of its connection journaled after the
latest ConnectionRolesMade, so it never cites an earlier request's refusal under a new request of
the same id (DEC-699). The fold and the oracle each read it their own way; four new streams and a
vector mutant pin it. DEC-2350 item 5 adds delete on the token exchange's mount's block only, so
the policy itself keeps the executor's role. fixtures/refcases/journal.json is regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Drop read from both provisioner role blocks, and send pinned list parameters as strings (DEC-2350 item 5, V2's live check)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* E21-33: the role provisioner's identity and AppRole, delete on the exchange's roles alone, tests pending (DEC-2350)

S2 of DEC-2350, the vault side, option (B): the executor's role is never
removed.

- Identity::Provisioner, provisioned with an AppRole and a secret id
  only when provisioning is given the workspace's namespace
  (KubernetesAuth::namespace, from --secrets-namespace); its policy is
  provisioner_policy for that namespace and nothing else.
- provisioner_policy grants delete on the token exchange's mount's
  block alone (stubbed seam: provisioner_capabilities).
- role_request sends the pinned list parameters, the namespace and the
  policy, as plain strings: OpenBao's allowed_parameters pin refuses a
  JSON array however it is filled, so every role request was refused
  (verified on OpenBao 2.7.1; stubbed seam: pinned).
- A pin test that the provisioner's + sits on exactly the two
  Kubernetes mounts' role paths, and a live test that the provisioner's
  own token makes both roles, is refused a widened role, an executor
  role's removal, a policy and a secret, and removes the exchange's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's identity and AppRole, delete on the exchange's roles alone (DEC-2350)

S2's implementation: provisioner_capabilities grants delete on the
token exchange's roles alone, and pinned sends a pinned list parameter
as a plain string. Deletes the pending markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the provisioner's role blocks grant no read (DEC-2350 item 5)

DEC-2350 item 5 now drops read from both blocks, since OpenBao refuses
a role read under required_parameters however it is granted: the
executor's block is create and update, the token exchange's create,
update and delete. The pin test asserts that, and the live test that
the provisioner reads neither role it makes. Merges #2278's head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's journal records and rules 261 to 263, tests pending (DEC-2350)

S3 of DEC-2350: journal spec v0.77 §9.33's ConnectionRolesMade,
ConnectionRolesRefused and ConnectionRolesRemoved on the control
stream, against the vectors' connection_roles section. Pending: the
catalogue entry, each base and valid draft parsing, every invalid
draft refused at its path, append judging rule 261 alone, and the
verifier's fold verify_connection_roles answering every stream case of
rules 262 and 263. The fold is stubbed; the parse tests fail on the
unknown event type, with behaviour-only rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's journal records and rules 261 to 263 (DEC-2350)

S3's implementation: the journal registers ConnectionRolesMade,
ConnectionRolesRefused and ConnectionRolesRemoved on the control
stream at schema version 1, catalogued with no configuration, and
append checks rule 261 on each (a system actor, then a cause). The
verifier's fold verify_connection_roles judges rules 262 and 263 as
the reference does: an answer names its own version-2 OAuth request,
once; a removal follows its connection's latest roles made, once, and
names a closer of those roles of its own occasion, a connect refusal
or a revocation journaled after them. Deletes the pending markers and
the four behaviour-only rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the policy provisioning writes for the provisioner's role is pinned without a server, test pending (DEC-2350)

Review item on S2: only a live server told provisioner_policy from
openbao_policy for the provisioner's role, so the gate missed a
mutant there. provisioned_policy, stubbed here, is the one choice
provision makes, and a unit test pins it: provisioner_policy for the
provisioner given the namespace, openbao_policy for every other
identity and for the provisioner without one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the policy provisioning writes for the provisioner's role is pinned without a server (DEC-2350)

provision writes each role's policy through provisioned_policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner makes a connection's roles and removes the exchange's, tests pending (DEC-2350)

S4a of DEC-2350: RoleProvisioner, the role provisioner's session with
the vault, as OpenBaoSource is when it logs in as the provisioner:
make_roles makes the executor's and then the token exchange's role as
role_request forms them, and remove_exchange_role removes the
exchange's. No operation removes an executor's role (DEC-2350 item 3).
RoleRefusal names a failure in ConnectionRolesRefused's two reasons.
Both operations and the error mapping are stubbed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner makes a connection's roles and removes the exchange's (DEC-2350)

S4a's implementation: make_roles POSTs the executor's and then the token
exchange's role_request, and remove_exchange_role DELETEs the
exchange's, a 404 being a role already gone. A refusal maps to
vault_unavailable for an outage or a sealed vault and to role_refused
for every other. Removes the temporary Unimplemented variant and the
pending markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a removal names a connect refusal of its own occasion, test pending (DEC-2350)

Mutants found two readings of rule 263 the vectors leave open: a
ConnectionRefused of another occasion taken for a request's close, and
a removal whose occasion does not name its closer's kind. A pending
test edits the removed_on_close case into each and expects rule 263
at the removal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a removal names a connect refusal of its own occasion (DEC-2350)

The fold compares a removal's named occasion against its closer's kind
in one step, an occasion that names neither standing for no closer, and
deletes the pending marker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's plan and pass, tests pending (DEC-2350)

S4b of DEC-2350: mandate-provisioner, the role provisioner's library.
plan folds the control stream into the steps owed: roles to make for
each unanswered version-2 OAuth request, and the exchange's role to
remove once a request whose roles were made closes, or its connection
is revoked. Provisioner::tick performs each on the vault within the
30 s deadline and journals it, with the opaque alert
connection_roles_refused (RB-12) on a refusal. The records' drafts are
real, with event ids derived from the step; the plan and the pass are
stubbed. Pending: a request's roles once, a refusal and a slow vault,
a close and a revocation removing only the exchange's role, a step
the journal missed done again and recorded once, two racing writers,
an unopened stream, and the plan against every valid vector stream.

The crate is at layer 9, above mandate-vault, with its layers row and
CODEOWNERS line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's plan and pass (DEC-2350)

S4b's implementation. plan folds the control stream as the journal's
verify_connection_roles reads rules 262 and 263: a version-2 OAuth
request with no answer is owed its roles, unless it closed first, and
a connection whose roles were made is owed the exchange's removal once
its request closes or it is revoked, with that first closer as cause.
tick performs the first step owed on the vault within the deadline,
journals it, and reads the stream again, so each record is drafted
against the stream as it is; another writer's record under it is read
again at most APPEND_TRIES times. A refusal raises
connection_roles_refused (RB-12) with no ids; a removal the vault does
not do is retried by the next pass. No step removes an executor's
role. Deletes the temporary Unimplemented variants and the pending
markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the live suite's vault set-up names the provisioning error it fails on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the plan's own connection, version and occasion readings, tests pending; the feature map's entry (DEC-2350)

Mutants found readings of the plan the vectors leave open: an API-key
or version-1 request, a close of one connection's request before its
answer, two connections' made roles, and a refusal of another occasion.
Three pending tests pin each over synthetic streams. The role
provisioner's feature-map entry names its code, tests and runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a provisioner's pass ends after the steps it owed (DEC-2350)

A pass takes at most the steps it first owed and APPEND_TRIES more, so
a stream that never shows a record committed ends the pass as a
journal halt rather than looping; the plan tests' pending markers go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: only the provisioner's session makes or removes roles, test pending

A session that never logged in, built for unit tests, lets the identity check be
tested with no vault running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: only the provisioner's session makes or removes roles

A role operation from any other session is refused role_refused before the vault
is reached. The DELETE's doc now says why a missing auth mount counts as removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's process, mandate-provisionerd (DEC-2350)

The provisioner over the Postgres control stream, written as mandate-deployd
writes it, and the vault, logged in by AppRole as the provisioner and again every
hour. A session near its token's end that no login replaced stops the process,
failed, so it never calls the vault with a lapsed token. --fixture runs it over
memory and a vault that keeps nothing, and a release build refuses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the fixture run is a debug build's block, not a release stub

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the workspace services' pinned list names the role provisioner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the workspace services' pinned list names the provisioner's process

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the API's owner-only vault copy becomes the chart's shared helper, by role

mandate.vaultCopy and mandate.vaultVolumes take the process's vault role; the
API's render is byte-for-byte the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the workspace chart's role provisioner, its AppRole alone and no Kubernetes credential (DEC-2350)

The role-provisioner process: one Deployment per workspace once its image is
named, reading ctl:<api.workspace> as mandate_role_provisioner, binding roles in
its own namespace, with its AppRole and the vault's CA copied owner-only by the
shared vault helper, no service-account token, and egress to the journal and its
vault on 8200 alone. Infrastructure design §3.1 gains its row; roles.sql, the
Compose file and initdb.sh are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's image, and its process marked built (DEC-2350)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's pod holds exactly its own volumes, and no token or ConfigMap besides (DEC-2350, review G1)

Its volume set is pinned by name and kind, and its projected volume to its Secret
and the vault's CA, with seeds for a projected service-account token, a token
in its vault sources, an extra ConfigMap, a host path, a downward API volume,
and a second kind on one volume. Infrastructure design §3.1 says its writing only
the three role records is its convention, not a database grant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* CI (lane GO): run api-release in a full-checks-3 job of its own (DEC-2334, DEC-2338)

#2289 added `api-release` to `cargo xtask ci full` (DEC-2338 item 5) after
#2195 had split `full`'s parts into `full-checks` and `full-checks-2`
(DEC-2334). No CI job ran `api-release`, and
`the_full_jobs_run_exactly_the_full_group` failed on main.

Its release build of `mandate-api-server` takes about five minutes on its own.
Neither existing job has room for that under ten minutes (DEC-464):
`full-checks-2`'s `reference` alone is about 4.5 minutes, before its two
release builds. So it runs in `full-checks-3`, on the same selection, and
`full` needs all three. The verdict table now shows a failed, cancelled or
skipped `full-checks-3` failing `full`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

* Supply chain: bump ctutils 0.4.2 -> 0.4.5 for RUSTSEC-2026-0385

`cargo deny --locked check` fails on main. In ctutils 0.4.2, the constant-time
`Choice` mask lacked an optimization barrier (RUSTSEC-2026-0385,
RustCrypto/utils#1569); 0.4.4 and later fix it. ctutils is a transitive
RustCrypto crate, reached through aes-gcm, crypto-bigint, digest, sec1 and
universal-hash (webpush, p256, sqlx-postgres). `cargo update -p ctutils` makes
a patch-level bump. Nothing else in Cargo.lock moves, and no manifest or
dependency row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

---------

Co-authored-by: Claude <noreply@anthropic.com>
kunwarshivam added a commit to kunwarshivam/mandate that referenced this pull request Oct 23, 2026
#2313)

* Test the env-name pin, the vault CA mount and the exchange's launch on the deploy-manager's pods

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Pin each managed container's environment, mount the vault's CA, and launch the exchange with its vault flags

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Cite DEC-2332 for the env pin, the vault CA and the exchange's launch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Start an OAuth connect's executor on its exchange's success (journal v0.74, DEC-2335)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Name J2 PR #2200 as an ES-22 atomic exception (DEC-2335 item 6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test the connect's executor: started on its exchange's success, stopped when its connection closes (E21-33 K2a, DEC-2336)

The tests half of DEC-77's pair. Phase::Succeeded joins the orchestrator port
and the fake can succeed an instance; the manager's fold and decision have no
stub form, so the tests that need a success read are pending as behaviour-only
rows until K2a's implementation lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Start the connect's executor on its exchange's success and stop it when its connection closes (E21-33 K2a, DEC-2336)

The implementation half of DEC-77's pair: the manager reads the request's
account, journals the executor's exchanged start while the request is open and
the account's executor never ran, wants it until a refusal or revocation, and
names each executor stop by the last reason taken away. The pending tests'
ignore lines and behaviour-only rows go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Name K2b's executor self-check dependency and the Deployment executor's account in DEC-2336's risks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test that each executor runs as its connection's own account (E21-33 K2b, DEC-2336 items 6 to 10)

The tests half of DEC-77's pair. The orchestrator port's start takes a Launch
of the exchange's lifetime and the executor's connection, and its stop the
connection; the driver passes none yet and the adapter ignores it, so the
tests that need it are pending as behaviour-only rows. DEC-2336 gains K2b's
items and the coordinator's F2 wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Run each manager-started executor as its connection's own account, and pin it at admission (E21-33 K2b, DEC-2336 items 6 to 10)

The implementation half of DEC-77's pair. The manager names an executor's
connection on its start and stop and asks once per account per pass; the
adapter makes and deletes the executor's hex account, names it and the
namespace in its environment, and reads a succeeded pod as Succeeded. The
chart pins a manager-started executor to an even-length hex account its
environment names, admits that account with the executor's label, and mounts
the vault token only on pods the manager starts. The pending tests' ignore
lines and behaviour-only rows go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test that an executor's start and stop name the same connection, the deployment's or the connect's (E21-33 K2b, review F1)

Four pending tests: a revoked connection the deployment does not name is never
chosen; a restart after the connect ended names the deployment's connection,
across the manager's restart; a stop names its generation's connection once
nothing else does; and no executor takes another account's connection.
DEC-2336 item 6 says which connection a start names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Name each executor generation's connection when its start is folded, from its deployment or its connect (E21-33 K2b, review F1)

An executor's start names the connection a running agent's deployment of its
account names, else its open connect's; the manager records it per generation
as it folds the start, so the stop names the same account and a restarted
manager names the same one. A revoked connection no deployment names is no
longer chosen. The pending tests' ignore lines and rows go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test that a kill switch reaches an executor no agent wants, once per switch (DEC-2344 items 1 and 3)

Pending E21-33. A covering kill switch gives an account's executor that every agent's retirement
left down exactly one start, cause kill_switch, journaled first and caused by the switch; each
later covering switch one more; a generation that ends on its own is never restarted; nothing
else starts it; and a switch the executor has not answered keeps the account's last retirement
from stopping it. A property over random deploys, retirements, kill switches of every scope,
ends, clock steps and manager restarts checks both against an oracle computed from the operations
alone. Each pending test has its behaviour-only row: the manager answers, no stub.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test that a kill-switch start crash-looping before it ends raises kill_switch_unapplied (DEC-2344 item 1)

Pending E21-33. A crash of the start is restarted, so one crash loses no switch; at the crash-loop
bound the manager journals ProcessCrashLooped and raises the opaque operator alert
kill_switch_unapplied, once, in place of crash_loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Extend the kill-switch property to crashes and the unapplied-switch alert (DEC-2344 item 1)

The executor no agent wants may now crash before it ends. The oracle allows a crash's restart only
before its loop and with no switch pending, requires the switch's one start when one is, never lets
the manager stop a generation a switch keeps, admits a crash loop only after a kill switch's
crashing start, and requires each such loop to raise kill_switch_unapplied once and every other
alert never.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Read a successful end as answering only the switches committed before its start (DEC-2344, journal v0.76 §9.21)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Add the role provisioner's connection-role records and start an exchange only once its roles exist (journal v0.77, DEC-2350)

Journal spec §9.33 adds ConnectionRolesMade, ConnectionRolesRefused and ConnectionRolesRemoved on the
control stream at schema version 1, with rule 261 (a system actor and a cause) and stream rules 262
and 263 (each record tied to its OAuth request; a removal after the roles were made, each role once,
on its occasion, and the executor's role on a revocation only once no generation of its executor is
left running). §9.21 starts a token exchange only once its request's ConnectionRolesMade is read.
Connections spec §5.2 step 4 orders the roles first. The reference module connection_roles builds
the vectors: 36 invalid and 4 valid drafts, 19 rule-262 and rule-263 streams, 4 validator, 8 stream
and 7 vector mutants caught. Reserves and fills DEC-2350; adds the provisioner's backlog row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Name only the added delete as DEC-2350's: the provisioner's + on its role paths is DEC-2630 item 7's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Never remove the executor's role: the provisioner removes the token exchange's alone (DEC-2350 item 3)

Rule 263 narrows to one removal per made roles, of the token exchange's role, on a closed request or
a revocation. A kill switch's start after a revocation can always log in; the cost, one executor role
per closed or revoked connection granting a read of its own destroyed token, is recorded in DEC-2350
with the sweep left in the backlog. The executor's role is an invalid draft; its stream cases and the
process fold go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Tie each role removal to its own request's close, refresh the fixture, and grant delete on the exchange's mount alone (DEC-2350, review)

Rule 263 now holds a removal's causation_id to a closer of its connection journaled after the
latest ConnectionRolesMade, so it never cites an earlier request's refusal under a new request of
the same id (DEC-699). The fold and the oracle each read it their own way; four new streams and a
vector mutant pin it. DEC-2350 item 5 adds delete on the token exchange's mount's block only, so
the policy itself keeps the executor's role. fixtures/refcases/journal.json is regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Start an executor no agent wants once per covering kill switch, and hold it until its switches are answered (DEC-2344 items 1 and 3)

The manager starts such an executor, cause kill_switch, journaled first, once for the covering
switches it has not answered, as the account of the connection its last generation ran as. It never
stops a generation a switch started or one a switch read while it ran is waiting on; a success ends
it, a crash restarts with back-off, and a crash loop raises kill_switch_unapplied in place of
crash_loop, again once from a restarted manager. The six pending retired.rs tests run, their
behaviour-only rows go, and new tests cover a revoked connection, the restarted alert, a wanted
executor's successful exit, and the oracle's seeded bugs. Two older tests follow DEC-2344's change.
The alert has its RB-14 section and its infrastructure design §8.2 row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Drop read from both provisioner role blocks, and send pinned list parameters as strings (DEC-2350 item 5, V2's live check)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Move the implementation's new tests here as pending: a revoked connection's start, the restarted alert, the oracle's seeded bugs (DEC-2344 item 1, review)

The four tests the implementation PR wrote fail on today's manager and are pending E21-33 with
behaviour-only rows: the N3 revoked-connection start, the restarted manager's one re-raise, the
seeded-bug harness, and manager.rs's stopped-agent test with DEC-2344's extra start. The property
expects the re-raise, and properties.rs exempts an executor's unwanted start, which retired.rs's
property checks more strictly. A wanted executor's restart after a success already passes, so it
is a plain test. Record::CrashLooped carries the alert it raises, crash_loop for every loop today,
so the harness can seed a loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Reflow RB-14's kill_switch_unapplied escalation step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test that a later switch after an unapplied loop starts a new run, that a held crash waits its back-off, and that a redeploy ends a kill-switch run (DEC-2344 item 1, review)

Three more tests, pending E21-33 with behaviour-only rows, each failing on today's manager: a
covering switch after an unapplied crash loop starts a new run that is restarted and can loop and
alert again; a crashed kill-switch generation waits backoff(0) before exactly one restart; and a
deployment during a kill-switch run makes its later loop an ordinary crash_loop, after a manager
restart too. The property's oracle lets such a start begin a new run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Begin a new run at a kill switch's start after an unapplied loop, and merge #2257's new tests (DEC-2344 item 1, review)

A kill_switch start of an executor no agent wants clears the last run's restarts, crash loop and
alert, so a later covering switch after an unapplied loop is restarted after a crash, loops again,
and is reported again: a second ProcessCrashLooped and a second kill_switch_unapplied. The merge
takes #2257's three new tests and deletes their ignore lines and rows; a held crash's back-off and
a redeploy's ordinary loop already held and are now pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Test that a deploy and retire during a kill switch's generation does not stop it (DEC-2344 item 3, review)

Pending E21-33 with its behaviour-only row: the generation a covering switch started stays
running through an agent's deployment and retirement on the account, and its own end is not
restarted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Let the property's oracle keep a kill-switch run held through a deployment (DEC-2344 item 3, review)

A deployment on the account no longer ends a held run in the oracle's view: only its own
successful end does, so a crash of it is still restarted after the agent retires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Keep a kill switch's run held through a deployment and retirement, and classify its loop at the loop (DEC-2344 item 3, review)

resume no longer clears held, and a kill_switch start made while the executor is wanted no longer
clears it either, so an agent deployed and retired while a switch's generation runs never gets
that generation stopped before it ends itself (rule 13). A stop the manager journals is only ever
of a generation it does not hold, so folding it clears held, and a replay agrees. A crash loop
leaves a switch unapplied only when no agent wants the executor at the loop. #2257's new test runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* E21-33: the role provisioner's journal records and rules 261 to 263, tests pending (DEC-2350)

S3 of DEC-2350: journal spec v0.77 §9.33's ConnectionRolesMade,
ConnectionRolesRefused and ConnectionRolesRemoved on the control
stream, against the vectors' connection_roles section. Pending: the
catalogue entry, each base and valid draft parsing, every invalid
draft refused at its path, append judging rule 261 alone, and the
verifier's fold verify_connection_roles answering every stream case of
rules 262 and 263. The fold is stubbed; the parse tests fail on the
unknown event type, with behaviour-only rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's journal records and rules 261 to 263 (DEC-2350)

S3's implementation: the journal registers ConnectionRolesMade,
ConnectionRolesRefused and ConnectionRolesRemoved on the control
stream at schema version 1, catalogued with no configuration, and
append checks rule 261 on each (a system actor, then a cause). The
verifier's fold verify_connection_roles judges rules 262 and 263 as
the reference does: an answer names its own version-2 OAuth request,
once; a removal follows its connection's latest roles made, once, and
names a closer of those roles of its own occasion, a connect refusal
or a revocation journaled after them. Deletes the pending markers and
the four behaviour-only rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* Test a kill switch's start during an open connect, a connect during a held run, a connect's ordinary loop, and a stop a restart must not undo (DEC-2351 item 2, review)

Three pending E21-33 with rows, each failing on today's manager, and one plain test that passes
today: an executor only a connect wants loops as crash_loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Hold a kill switch's run by the agents alone, and say so in the loop's alert, and merge #2257's tests (DEC-2351 item 2, review)

A kill_switch start of an executor holds its run when no agent of its account is deployed and not
stopped, whatever connect request wants it, and the crash loop's alert says kill_switch_unapplied
when the run is held and no agent wants the executor at the loop, as journal spec rule 264 reads it;
a runtime's start never holds. A deployment on a connection revoked since counts until its
AgentStopped, as the rule reads it. The merge takes #2257's four new tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* E21-33: a removal names a connect refusal of its own occasion, test pending (DEC-2350)

Mutants found two readings of rule 263 the vectors leave open: a
ConnectionRefused of another occasion taken for a request's close, and
a removal whose occasion does not name its closer's kind. A pending
test edits the removed_on_close case into each and expects rule 263
at the removal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a removal names a connect refusal of its own occasion (DEC-2350)

The fold compares a removal's named occasion against its closer's kind
in one step, an occasion that names neither standing for no closer, and
deletes the pending marker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the token exchange waits for its request's roles, tests pending (DEC-2350 S6)

A connect's token exchange starts only once its request's ConnectionRolesMade is
read (journal spec §9.21, §9.33). The new roles suite and the exchange property's
oracle say so; the existing connect tests gain the Made row their scenarios now
need. The pending tests fail on the manager's behaviour, which has no stub to stop
at, and are named in xtask/behaviour-only until the implementation lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the seeded double launch waits for a first launch to copy (DEC-2350 S6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the token exchange waits for its request's roles (DEC-2350 S6)

The manager reads the role provisioner's ConnectionRolesMade and keeps each
request's event id; a connect's token exchange is wanted only once the Made that
answers its own request is read, so a refusal, a record for another request, or
none starts nothing, and the connect ends by its deadline (journal spec §9.21).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* CI (lane GO): run api-release in a full-checks-3 job of its own (DEC-2334, DEC-2338)

#2289 added `api-release` to `cargo xtask ci full` (DEC-2338 item 5) after
#2195 had split `full`'s parts into `full-checks` and `full-checks-2`
(DEC-2334). No CI job ran `api-release`, and
`the_full_jobs_run_exactly_the_full_group` failed on main.

Its release build of `mandate-api-server` takes about five minutes on its own.
Neither existing job has room for that under ten minutes (DEC-464):
`full-checks-2`'s `reference` alone is about 4.5 minutes, before its two
release builds. So it runs in `full-checks-3`, on the same selection, and
`full` needs all three. The verdict table now shows a failed, cancelled or
skipped `full-checks-3` failing `full`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

* Supply chain: bump ctutils 0.4.2 -> 0.4.5 for RUSTSEC-2026-0385

`cargo deny --locked check` fails on main. In ctutils 0.4.2, the constant-time
`Choice` mask lacked an optimization barrier (RUSTSEC-2026-0385,
RustCrypto/utils#1569); 0.4.4 and later fix it. ctutils is a transitive
RustCrypto crate, reached through aes-gcm, crypto-bigint, digest, sec1 and
universal-hash (webpush, p256, sqlx-postgres). `cargo update -p ctutils` makes
a patch-level bump. Nothing else in Cargo.lock moves, and no manifest or
dependency row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

---------

Co-authored-by: Claude <noreply@anthropic.com>
kunwarshivam added a commit to kunwarshivam/mandate that referenced this pull request Oct 23, 2026
…EC-2350 S4b-2 follow-up) (#2308)

* Add the role provisioner's connection-role records and start an exchange only once its roles exist (journal v0.77, DEC-2350)

Journal spec §9.33 adds ConnectionRolesMade, ConnectionRolesRefused and ConnectionRolesRemoved on the
control stream at schema version 1, with rule 261 (a system actor and a cause) and stream rules 262
and 263 (each record tied to its OAuth request; a removal after the roles were made, each role once,
on its occasion, and the executor's role on a revocation only once no generation of its executor is
left running). §9.21 starts a token exchange only once its request's ConnectionRolesMade is read.
Connections spec §5.2 step 4 orders the roles first. The reference module connection_roles builds
the vectors: 36 invalid and 4 valid drafts, 19 rule-262 and rule-263 streams, 4 validator, 8 stream
and 7 vector mutants caught. Reserves and fills DEC-2350; adds the provisioner's backlog row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Name only the added delete as DEC-2350's: the provisioner's + on its role paths is DEC-2630 item 7's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Never remove the executor's role: the provisioner removes the token exchange's alone (DEC-2350 item 3)

Rule 263 narrows to one removal per made roles, of the token exchange's role, on a closed request or
a revocation. A kill switch's start after a revocation can always log in; the cost, one executor role
per closed or revoked connection granting a read of its own destroyed token, is recorded in DEC-2350
with the sweep left in the backlog. The executor's role is an invalid draft; its stream cases and the
process fold go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Tie each role removal to its own request's close, refresh the fixture, and grant delete on the exchange's mount alone (DEC-2350, review)

Rule 263 now holds a removal's causation_id to a closer of its connection journaled after the
latest ConnectionRolesMade, so it never cites an earlier request's refusal under a new request of
the same id (DEC-699). The fold and the oracle each read it their own way; four new streams and a
vector mutant pin it. DEC-2350 item 5 adds delete on the token exchange's mount's block only, so
the policy itself keeps the executor's role. fixtures/refcases/journal.json is regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* Drop read from both provisioner role blocks, and send pinned list parameters as strings (DEC-2350 item 5, V2's live check)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmJtE8JXVQjyqTteJ5b9Cm

* E21-33: the role provisioner's identity and AppRole, delete on the exchange's roles alone, tests pending (DEC-2350)

S2 of DEC-2350, the vault side, option (B): the executor's role is never
removed.

- Identity::Provisioner, provisioned with an AppRole and a secret id
  only when provisioning is given the workspace's namespace
  (KubernetesAuth::namespace, from --secrets-namespace); its policy is
  provisioner_policy for that namespace and nothing else.
- provisioner_policy grants delete on the token exchange's mount's
  block alone (stubbed seam: provisioner_capabilities).
- role_request sends the pinned list parameters, the namespace and the
  policy, as plain strings: OpenBao's allowed_parameters pin refuses a
  JSON array however it is filled, so every role request was refused
  (verified on OpenBao 2.7.1; stubbed seam: pinned).
- A pin test that the provisioner's + sits on exactly the two
  Kubernetes mounts' role paths, and a live test that the provisioner's
  own token makes both roles, is refused a widened role, an executor
  role's removal, a policy and a secret, and removes the exchange's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's identity and AppRole, delete on the exchange's roles alone (DEC-2350)

S2's implementation: provisioner_capabilities grants delete on the
token exchange's roles alone, and pinned sends a pinned list parameter
as a plain string. Deletes the pending markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the provisioner's role blocks grant no read (DEC-2350 item 5)

DEC-2350 item 5 now drops read from both blocks, since OpenBao refuses
a role read under required_parameters however it is granted: the
executor's block is create and update, the token exchange's create,
update and delete. The pin test asserts that, and the live test that
the provisioner reads neither role it makes. Merges #2278's head.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's journal records and rules 261 to 263, tests pending (DEC-2350)

S3 of DEC-2350: journal spec v0.77 §9.33's ConnectionRolesMade,
ConnectionRolesRefused and ConnectionRolesRemoved on the control
stream, against the vectors' connection_roles section. Pending: the
catalogue entry, each base and valid draft parsing, every invalid
draft refused at its path, append judging rule 261 alone, and the
verifier's fold verify_connection_roles answering every stream case of
rules 262 and 263. The fold is stubbed; the parse tests fail on the
unknown event type, with behaviour-only rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's journal records and rules 261 to 263 (DEC-2350)

S3's implementation: the journal registers ConnectionRolesMade,
ConnectionRolesRefused and ConnectionRolesRemoved on the control
stream at schema version 1, catalogued with no configuration, and
append checks rule 261 on each (a system actor, then a cause). The
verifier's fold verify_connection_roles judges rules 262 and 263 as
the reference does: an answer names its own version-2 OAuth request,
once; a removal follows its connection's latest roles made, once, and
names a closer of those roles of its own occasion, a connect refusal
or a revocation journaled after them. Deletes the pending markers and
the four behaviour-only rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the policy provisioning writes for the provisioner's role is pinned without a server, test pending (DEC-2350)

Review item on S2: only a live server told provisioner_policy from
openbao_policy for the provisioner's role, so the gate missed a
mutant there. provisioned_policy, stubbed here, is the one choice
provision makes, and a unit test pins it: provisioner_policy for the
provisioner given the namespace, openbao_policy for every other
identity and for the provisioner without one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the policy provisioning writes for the provisioner's role is pinned without a server (DEC-2350)

provision writes each role's policy through provisioned_policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner makes a connection's roles and removes the exchange's, tests pending (DEC-2350)

S4a of DEC-2350: RoleProvisioner, the role provisioner's session with
the vault, as OpenBaoSource is when it logs in as the provisioner:
make_roles makes the executor's and then the token exchange's role as
role_request forms them, and remove_exchange_role removes the
exchange's. No operation removes an executor's role (DEC-2350 item 3).
RoleRefusal names a failure in ConnectionRolesRefused's two reasons.
Both operations and the error mapping are stubbed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner makes a connection's roles and removes the exchange's (DEC-2350)

S4a's implementation: make_roles POSTs the executor's and then the token
exchange's role_request, and remove_exchange_role DELETEs the
exchange's, a 404 being a role already gone. A refusal maps to
vault_unavailable for an outage or a sealed vault and to role_refused
for every other. Removes the temporary Unimplemented variant and the
pending markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a removal names a connect refusal of its own occasion, test pending (DEC-2350)

Mutants found two readings of rule 263 the vectors leave open: a
ConnectionRefused of another occasion taken for a request's close, and
a removal whose occasion does not name its closer's kind. A pending
test edits the removed_on_close case into each and expects rule 263
at the removal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a removal names a connect refusal of its own occasion (DEC-2350)

The fold compares a removal's named occasion against its closer's kind
in one step, an occasion that names neither standing for no closer, and
deletes the pending marker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's plan and pass, tests pending (DEC-2350)

S4b of DEC-2350: mandate-provisioner, the role provisioner's library.
plan folds the control stream into the steps owed: roles to make for
each unanswered version-2 OAuth request, and the exchange's role to
remove once a request whose roles were made closes, or its connection
is revoked. Provisioner::tick performs each on the vault within the
30 s deadline and journals it, with the opaque alert
connection_roles_refused (RB-12) on a refusal. The records' drafts are
real, with event ids derived from the step; the plan and the pass are
stubbed. Pending: a request's roles once, a refusal and a slow vault,
a close and a revocation removing only the exchange's role, a step
the journal missed done again and recorded once, two racing writers,
an unopened stream, and the plan against every valid vector stream.

The crate is at layer 9, above mandate-vault, with its layers row and
CODEOWNERS line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's plan and pass (DEC-2350)

S4b's implementation. plan folds the control stream as the journal's
verify_connection_roles reads rules 262 and 263: a version-2 OAuth
request with no answer is owed its roles, unless it closed first, and
a connection whose roles were made is owed the exchange's removal once
its request closes or it is revoked, with that first closer as cause.
tick performs the first step owed on the vault within the deadline,
journals it, and reads the stream again, so each record is drafted
against the stream as it is; another writer's record under it is read
again at most APPEND_TRIES times. A refusal raises
connection_roles_refused (RB-12) with no ids; a removal the vault does
not do is retried by the next pass. No step removes an executor's
role. Deletes the temporary Unimplemented variants and the pending
markers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the live suite's vault set-up names the provisioning error it fails on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the plan's own connection, version and occasion readings, tests pending; the feature map's entry (DEC-2350)

Mutants found readings of the plan the vectors leave open: an API-key
or version-1 request, a close of one connection's request before its
answer, two connections' made roles, and a refusal of another occasion.
Three pending tests pin each over synthetic streams. The role
provisioner's feature-map entry names its code, tests and runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: a provisioner's pass ends after the steps it owed (DEC-2350)

A pass takes at most the steps it first owed and APPEND_TRIES more, so
a stream that never shows a record committed ends the pass as a
journal halt rather than looping; the plan tests' pending markers go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: only the provisioner's session makes or removes roles, test pending

A session that never logged in, built for unit tests, lets the identity check be
tested with no vault running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: only the provisioner's session makes or removes roles

A role operation from any other session is refused role_refused before the vault
is reached. The DELETE's doc now says why a missing auth mount counts as removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the role provisioner's process, mandate-provisionerd (DEC-2350)

The provisioner over the Postgres control stream, written as mandate-deployd
writes it, and the vault, logged in by AppRole as the provisioner and again every
hour. A session near its token's end that no login replaced stops the process,
failed, so it never calls the vault with a lapsed token. --fixture runs it over
memory and a vault that keeps nothing, and a release build refuses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the fixture run is a debug build's block, not a release stub

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the workspace services' pinned list names the role provisioner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: the workspace services' pinned list names the provisioner's process

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* E21-33: CI builds the role provisioner in release and checks it refuses its fixture

cargo xtask ci provisionerd-release, in full and in check, like stub-release:
it builds mandate-provisionerd with --release when the diff selects it, runs it
with --fixture, and requires exit 2 with fixture_not_built alone on stderr.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

* CI (lane GO): run api-release in a full-checks-3 job of its own (DEC-2334, DEC-2338)

#2289 added `api-release` to `cargo xtask ci full` (DEC-2338 item 5) after
#2195 had split `full`'s parts into `full-checks` and `full-checks-2`
(DEC-2334). No CI job ran `api-release`, and
`the_full_jobs_run_exactly_the_full_group` failed on main.

Its release build of `mandate-api-server` takes about five minutes on its own.
Neither existing job has room for that under ten minutes (DEC-464):
`full-checks-2`'s `reference` alone is about 4.5 minutes, before its two
release builds. So it runs in `full-checks-3`, on the same selection, and
`full` needs all three. The verdict table now shows a failed, cancelled or
skipped `full-checks-3` failing `full`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

* Supply chain: bump ctutils 0.4.2 -> 0.4.5 for RUSTSEC-2026-0385

`cargo deny --locked check` fails on main. In ctutils 0.4.2, the constant-time
`Choice` mask lacked an optimization barrier (RUSTSEC-2026-0385,
RustCrypto/utils#1569); 0.4.4 and later fix it. ctutils is a transitive
RustCrypto crate, reached through aes-gcm, crypto-bigint, digest, sec1 and
universal-hash (webpush, p256, sqlx-postgres). `cargo update -p ctutils` makes
a patch-level bump. Nothing else in Cargo.lock moves, and no manifest or
dependency row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoxcMViCbnGUTPSaduuGYU

* Run the role provisioner's release check in full-checks-3

Main's full-job split has no room for it in full-checks-2, so it runs beside the API's release
build, about two minutes more, under ten (DEC-464).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5V2t72QcDywyLPViJtmFF

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant