Skip to content

security: reserve daemon control namespaces (S3a) - #683

Merged
CueCrux-Myles merged 1 commit into
mainfrom
redsteel/s3-tenant-isolation
Aug 10, 2026
Merged

CueCrux-Myles merged 1 commit into
mainfrom
redsteel/s3-tenant-isolation

Conversation

@CueCrux-Myles

Copy link
Copy Markdown
Contributor

Slice S3a of the red-steel remediation replay (#630). Cherry-pick of f731d3c0, resolved against current main.

What it does

Reserves the daemon's own control namespaces so a generic fact-create or a .cruxpack import cannot write into them, and requires agent-private __agent::<owner>::<logical> entities to carry an explicit owner mapping rather than being remapped by guesswork.

Why this is S3a and not S4

The plan filed f731d3c0 under S4 (auth/authority binding). That was wrong, and it cost a build to find out.

ba642868 — the tenant-scoped fact lifecycle that S3 is actually about — does not compile without this commit. It inherits the __agent:: remapping block that binds entity, and references PackVerifyError::{InvalidAgentPrivateEntity, ReservedEntity, AgentPrivateOwnerMappingRequired} plus ConsolidationErrorV1::TargetDaemonOwned. Every one of those is introduced here.

I found it by attributing each missing symbol to its introducing commit rather than reasoning about the slice names:

missing symbol introduced by
InvalidAgentPrivateEntity f731d3c0
ReservedEntity f731d3c0
AgentPrivateOwnerMappingRequired f731d3c0
generic_create_reserved_entity_prefix f731d3c0
TargetDaemonOwned f731d3c0

It is also markedly easier to land alone: 2 conflicted files / 3 hunks, against ba642868's 7 files / 13 hunks.

The resolution

All three conflicts are reserved-prefix lists where both sides added entries since the fork:

  • main contributed __escrow__:: (key escrow M3b) and __candidate_fact__:: (auto-capture M1)
  • red-steel contributed the daemon control set

Resolved as a union, not by taking a side — which is what the WIP branch's own analysis said these needed. The two entries present on both sides were deduplicated. assert_unique_nonempty and the cruxpack/fact_privacy drift test both pass, so the union is checkable rather than a judgement call.

Order is not load-bearing in these lists (.contains, no binary search), which is why a union is safe here.

Verification

  • cargo check --workspace --all-targets --locked — clean
  • corecrux-memory 335 unit + 7 integration; corecruxd + crux-mcp 2052 — 0 failures
  • clippy at parity with main: both 0 under CI's actual gate (--workspace -- -D warnings). Worth noting for anyone checking: --all-targets reports 350 on main as well, and is not what CI runs — comparing against the wrong invocation makes this look like a regression when it is not.

Next in the slice

ba642868 + 33ec4ed1 (tenant-scoped fact lifecycle) stack on top of this, then 426ea8a0 + 41ee2515, then 16554b80.

Plan and decision log: PlanCrux/.agent/execplans/redsteel-remediation-replay-2026-08-07.md.

🤖 Generated with Claude Code

Cherry-picked from red-steel `f731d3c0`, resolved against current `main`.

Reserves the daemon's own control namespaces so a generic fact-create or
`.cruxpack` import cannot write into them, and so agent-private
`__agent::<owner>::<logical>` entities must carry an explicit owner mapping
rather than being remapped by guesswork.

Resolution note: the three conflicts are all reserved-prefix lists where
BOTH sides had added entries since the fork — `main` contributed
`__escrow__::` and `__candidate_fact__::` (key escrow M3b, auto-capture
M1), red-steel contributed the daemon control set. Resolved as a union, not
by taking a side; the two entries that appeared on both sides were
deduplicated. `assert_unique_nonempty` and the cruxpack/fact_privacy drift
test both pass, which is what makes the union checkable rather than a
judgement call.

**This commit is a prerequisite for the rest of the tenant-isolation slice,
which is why it moved.** The plan filed it under S4, but `ba642868`
(tenant-scoped fact lifecycle) does not compile without it: `ba642868`
inherits the `__agent::` remapping block that binds `entity`, and
references `PackVerifyError::{InvalidAgentPrivateEntity, ReservedEntity,
AgentPrivateOwnerMappingRequired}` and
`ConsolidationErrorV1::TargetDaemonOwned` — every one of which is
introduced here. Traced by attributing each missing symbol to its
introducing commit rather than by guessing at the ordering.

Verified: `cargo check --workspace --all-targets --locked` clean;
corecrux-memory 335 + 7 tests, corecruxd/crux-mcp 2052 tests, 0 failures;
clippy at parity with `main` (both 0 under CI's `--workspace -- -D
warnings`, which is the actual gate — `--all-targets` reports 350 on `main`
too and is not what CI runs).

Refs #630. Slice S3a of redsteel-remediation-replay-2026-08-07.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CueCrux-Myles

Copy link
Copy Markdown
Contributor Author

Independently checked the load-bearing claim here — that the three conflicted reserved-prefix lists were resolved as a union rather than by taking a side. Compared each list at origin/main against this branch:

file dropped from main added by this PR
crates/corecrux-memory/src/cruxpack.rs none 15
crates/corecrux-memory/src/fact_privacy.rs none 16

So main's __escrow__:: (key escrow M3b) and __candidate_fact__:: (auto-capture M1) both survive, and nothing else on main was lost in the resolution. That matches what the description says, and it is the failure mode that would actually have mattered — a dropped prefix silently unreserves a namespace, which no test would necessarily catch.

Auto-merge enabled; it will go in once Test and Coverage land.

@CueCrux-Myles
CueCrux-Myles enabled auto-merge August 9, 2026 21:14
@CueCrux-Myles
CueCrux-Myles added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 9701a9c Aug 10, 2026
21 checks passed
@CueCrux-Myles
CueCrux-Myles deleted the redsteel/s3-tenant-isolation branch August 10, 2026 21:50
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