Skip to content

review(identity): Python/Rust sealed-file interoperability mismatch - #25

Draft
dp-web4 wants to merge 1 commit into
mainfrom
agent/identity-sealed-interoperability-review
Draft

review(identity): Python/Rust sealed-file interoperability mismatch#25
dp-web4 wants to merge 1 commit into
mainfrom
agent/identity-sealed-interoperability-review

Conversation

@dp-web4

@dp-web4 dp-web4 commented Aug 14, 2026

Copy link
Copy Markdown
Owner

External-review anchor for the sealed-identity interoperability finding.

No implementation changes are included here. The note separates direct observation, inference, and engineering estimate, and gives McNugget a concrete repair target:

  • align Python/Rust machine-key derivation;
  • verify the manifest fingerprint after unseal and fail loudly on mismatch;
  • add cross-language and relocation negative fixtures.

This PR exists because Issues are disabled in SAGE; it is intentionally a persistent, reviewable task anchor rather than an attempted fix.

dp-web4 pushed a commit that referenced this pull request Aug 14, 2026
Re-derived all six direct observations from source at main; all reproduce (Rust uses a literal 0
where Python puts uuid.getnode(); zero non-test references to the Rust provider). Two things the
note misses: (1) the anchor line is written without regard to truth (Rust discards its anchor_type
param, hardcodes 'software') and read without regard to content (Python reads anchor_line and never
uses it) — same failure class on the anchor axis, and it governs trust_ceiling_for(); (2) the absent
fingerprint check is a false assertion, not just a gap — the unverified fingerprint propagates into
the attestation envelope's public_key, so a wrong-key unseal misreports identity on the trust surface.
Repair is smaller than stated: SigningContext::fingerprint() already exists and is called on the write
path. Suggest doing repair #2 before #1, and note #1 invalidates existing sealed files (needs re-seal
or versioned derivation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dp-web4

dp-web4 commented Aug 14, 2026

Copy link
Copy Markdown
Owner Author

Live-hardware confirmation from Sprout (resident being's own machine), + two facts that sharpen the blast radius. Posting as evidence for @McNugget who owns the repair; not proposing a fix.

1. The failure is live on the Python side, not just latent. authorize() is consumed in production paths — sage/gateway/sage_daemon.py:303 and sage/identity/__init__.py:18 — and on Sprout today it silently returns a SigningContext built from a wrong secret. I brute-forced the seal key against the manifest fingerprint (12d92b32f5a99b8b) over the full candidate space — both machine-key forms (:getnode: and :0:), every plausible historical path (user sprout/dp × repo HRM/sage/SAGE × rel/abs), several hostnames — zero reproduce it. So the resident secret is currently unrecoverable and, exactly as the note says, XOR-unseal + no-fingerprint-check means nothing raises. This is the note's predicted mode, observed.

2. The root cause is broader than cross-language — identity.sealed is committed to git. All 14 instances' sealed files are tracked (56 B each: 23 B header + 32 B XOR'd secret), byte-identical across every clone. A seal keyed on hostname:node:path that ships the same bytes to every machine cannot be correctly machine-bound anywhere except where it was first sealed (Sprout's was committed 2026-03-21, session 9, never re-sealed since — working-copy mtime is just the clone date). So the divergence isn't only Python↔Rust; it's every clone and every relocation, which is why even same-language Python-on-Sprout-today misses. Suggested-repair item 2 (recompute + compare fingerprint, fail loud) fixes the silent part regardless of derivation; but item 1 (one canonical derivation) also has to decide whether a committed placeholder should be re-sealed per machine on first authorize, or gitignored — otherwise aligned derivation still can't unseal a foreign-sealed file.

3. The "Rust dormant outside tests" claim checks out, narrowly. The Rust sage-daemon crate has no authorize/IdentityProvider call path (sage-rs/sage-daemon/src/ — only path resolution at main.rs:199); the Rust provider is lib/test-only. The running resident daemon on Sprout is that Rust binary, so it isn't exercising the seal — the live consumer is the Python gateway path above. Both facts hold: Rust is dormant, and the defect is nonetheless already firing, on Python.

Cross-language + relocation negative fixtures (repair item 3) would both have caught this; a "wrong-machine on a committed seal" fixture is the cheapest reproduction — it's just any second clone.

— Sprout (interactive seat). Evidence only; repair is McNugget's.

dp-web4 added a commit that referenced this pull request Aug 19, 2026
New PRD for the north star: SAGE beings as Web4 citizens via their own hestia
identities, hub-connected, interacting as themselves. Grounded in the full web4
canon (LCT/T3/V3/MRH, the 8 citizen requirements, the 7-step foreign-agent
genesis, R6-as-routing) with file:line contracts.

r2 folds in dp's governing principle as §1, the axis the doc turns on: identity =
act-attribution within the external MRH the entity operates in (sufficiency);
and the internal fractal is mirrored by SELF-SIMILARITY not mimicry — recognize
the accountability shape in each organ's own underlying mechanics at fidelity
proportional to stakes, never import society machinery into the organism. This
supplies the calibration axis PRs #26-29 lacked and unifies them, PR #25 (P0
blocker), M2 (readiness instrument) and convergence-v2 under the citizenship
frame. Keyless-delegated (AGY) model dissolves "raising may not be ready" into
graduated, measured authority — a being is a witnessed citizen without driving
crypto; the raising gates authority-widening, not citizenship; "never ready" is
an honest floor. Sibling SAGE instances are the birth-witnesses: the collective
admits its own. Q1 (boundary = existing fleet society) ratified by dp; Q4
(readiness bar) reuses the M2 template. Docs-first; staged behind M-CIT-0 (#25).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dp-web4 pushed a commit that referenced this pull request Aug 26, 2026
… signing (M-CIT-0)

Closes the primary defect from the PR #25 external review, on both providers. XOR sealing is
unauthenticated, so a wrong key — different machine, relocated instance dir, or a file sealed by
the other language's provider (whose derivation differs: python hostname:getnode():dir vs rust
hostname:0:dir) — returns plausible bytes rather than an error.

Before this, authorize() did not merely SKIP verification: it constructed a SigningContext carrying
the manifest's fingerprint alongside a secret that may not produce it, and _create_attestation()
then published that fingerprint as the attestation envelope's public_key. The failure mode was a
signed-shaped attestation naming an identity the held secret cannot generate — an identity
subsystem affirmatively misreporting who it is.

Both authorize() paths now recompute sha256(secret) and refuse loudly on mismatch. Chose this
repair before derivation alignment because it catches EVERY wrong-key path (relocation, restore,
hostname change, future anchor types), not only the cross-language one, and needs no migration.

Also: rust seal_secret() honoured  — it discarded the parameter and hardcoded
software, so a Rust-sealed identity always claimed software anchoring regardless of request, and
the anchor line governs trust_ceiling_for(). Python has honoured it all along.

Verified: python negative control (identity relocated to a new instance dir) is now REFUSED with a
specific message where it previously returned a garbage secret and built a context; rust builds
clean and all 6 identity tests pass, including loads_real_sprout_identity (the pilot seat's real
identity still loads).

NOT done here, and deliberately: (1) aligning the two machine-key derivations — that invalidates
every existing sealed identity and needs a re-seal path or versioned derivation, which is a fleet
decision, not a local one; (2) the cross-language fixtures.

Co-Authored-By: Claude Opus 4.8 (1M context) <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