Storage never binds a derived genesis to the identifier it stores under
- Dominant language
- Rust
- Stars
- 3
- Forks
- 0
- Avg merge
- 1d 42m
- Merged PRs (30d)
- 11
Description
When commits are submitted for a principal, the storage engine resolves that principal's genesis from the submitted material itself. It never compares the genesis it derived against the identifier the caller asked it to store under, and commit submission does not compare them either.
So the engine will store one principal's chain under another principal's identifier if asked to, and report success.
## Why this surfaced
A witness that follows an authority requests state for a named principal. An on-path party that rewrites only that name in the outgoing request receives a response which is byte-for-byte genuine and correctly signed — for a different principal. Every integrity property holds, because nothing was forged: the signature verifies, the report parses, and the entries genuinely belong to the principal the response is about.
The receiving witness then stores that chain under the identifier it asked about, serves it, and would sign it if it held its own identity. Demonstrated with distinct genesis key material, so this is not an artefact of shared test fixtures.
**That specific exposure is now closed**, at the point of consumption: the sync path compares the report's principal against the principal being synced and fails before writing anything. This issue is not about that path.
## What remains
The engine still accepts the mismatch, so the comparison in the sync path is the **only** thing preventing it. There is no defence in depth. Any future caller that reaches commit submission without performing an equivalent comparison reopens the same substitution, and nothing in the storage layer's own contract would stop it or record that it happened.
The property that should hold is local and checkable where the derivation already occurs: a chain's derived genesis must equal the identifier it is being stored under, or the submission is rejected. The engine has both values in hand at that moment.
## Related unaudited surface in the same file
This file also carries a change from earlier work — roughly sixty lines, altering the ingestion harness — that has never been reviewed. It is recorded among the unread surface in #142, and it is worth noting that the defect above lives in the same file: the one place nobody had read is the one place a critical property turned out to be missing.
Auditing that change and adding the binding above are naturally the same piece of work, since both require understanding how the engine resolves and stores a principal's identity.
## Scope note
Adding this check will reject submissions that are currently accepted. Before it lands, enumerate every legitimate producer of a mismatch and either establish that set is empty or handle each member — this project has produced two availability regressions by tightening a validation without doing that, both reachable by ordinary new principals. In particular, confirm how implicit-genesis and reindexed principals derive their identifiers, since those paths differ from the ordinary submission path.
Related: #141, #142.
Contributor guide
No contributing guide indexed for this repository
Research direction
Trace the storage engine's principal-resolution and commit-submission paths, then compare them with the sync-path check described in the issue. Review the roughly sixty-line ingestion-harness change recorded in #142, and investigate implicit-genesis and reindexed-principal producers before changing validation. Done means legitimate producers are accounted for and mismatched derived genesis identifiers are rejected without availability regressions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100