Absent commit root is signalled by an empty string rather than by type
- Dominant language
- Rust
- Stars
- 3
- Forks
- 0
- Avg merge
- 1d 42m
- Merged PRs (30d)
- 11
Description
A principal at genesis has no commit root — the specification's implicit promotion means the principal root equals the state root and no commit root exists yet. The storage layer signals this by returning an **empty string** from root re-derivation, while the three sibling roots each error explicitly if they are empty.
So one field in that struct carries a sentinel meaning "absent" and the other three do not, and nothing in the type says so. Every consumer has to know.
## Why this is worth changing
An empty string standing for "no commit root" is indistinguishable, on the wire and in the type, from a commit root that happens to be the empty digest. Callers cannot tell the two apart without knowing the convention, and a caller that does not know the convention will treat the sentinel as a value.
That has already happened once. A validation was added requiring every root to parse as a tagged digest — correct for the three that are always present, wrong for the one that is legitimately empty at genesis. The result was a 500 on the most ordinary request a newly registered principal makes: querying its own tip. The consuming layer has since made absence explicit in *its* model, so the immediate defect is closed, but the sentinel still originates here and the next consumer inherits the same trap.
## What resolving this involves
Absence belongs in the signature rather than in a convention: an optional field, so a caller that ignores the distinction fails to compile instead of failing at runtime. The empty string can remain the wire encoding — that part is a serialization detail and the format is stable — but it should stop being how the value is represented in code.
Worth checking whether the same sentinel shape exists on other derived values while making the change.
Found during remediation work, where a consumer's own model was changed to compensate. Related: #141.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by tracing the storage-layer root re-derivation and the consuming model changed during remediation, using related issue #141 for context. Check whether sibling derived values use the same sentinel shape; done means absence is represented explicitly in the code while the stable empty-string wire encoding remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- authentication, security
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100