Repository navigation
Description
Problem
WorldCore::restore (pkgs/id/src/world/core.rs:138) accepts a JournalEntry::Issued for participant id 0 — the reserved host/system identity that mint can never issue — and the restored capability then authorizes as participant 0.
Participant 0 is reserved throughout the world code:
WorldCore::newstartsnext_participant_id: 1(core.rs:89) andmintassigns ids only from that counter (core.rs:873), so no live path ever issues id 0.- Host-authored chat is committed as participant 0: "Commit a host-authored chat line as participant 0" (
system_chat, core.rs:574-576). - Journal
Systemevents are only accepted when they claimparticipant_id == 0(core.rs:303), andProgramInstalleddocuments "participant_idis zero for this host-authored event" (pkgs/id/src/world/mod.rs:341-342).
The restore Issued arm (core.rs:172-232) checks for a duplicate id, a known parent, uses >= 1, and a delegate not outliving its parent — but never that participant.id != 0. A journal carrying an Issued entry for id 0 therefore rebuilds a capability record for the host identity itself, with a digest the journal author chose.
Verified this run (laptop probe, main 1712287)
A probe test added to src/world/tests.rs on the laptop (worktree ~/git/fleet/probe-restore-id0, since removed after the run) built a journal of Created + one Issued entry with participant.id = 0, display name host, GUEST scopes, and the digest of a self-chosen token 0.<secret>:
WorldCore::restoresucceeded (the probe's first expectation).core.chat(&cap, "forged host line")with the forged token succeeded, and the committed event carriesparticipant_id == 0— indistinguishable in the event log and in viewer projections from a genuine host/system line.core.snapshot(&cap)lists participant 0 among the world's participants.
Probe result: world::tests::tests::probe_restore_issues_participant_zero ... ok (1 passed, 1581 filtered out, cargo test --lib, default features).
Why it matters
- Host impersonation. Anyone who can present a journal (world import, fork, backup restore, a compromised or malicious world export) gains a working capability for the host identity: chat and inputs committed as participant 0 read as host-authored to every client and to the world program.
- It composes with id(world): journal restore never validates Committed/Checkpoint event authors — inputs and chat from never-issued participants restore, replay, and project #2894. That issue covers restore never validating
Committed/Checkpointevent authors. The natural fix there — "the author must be an issued participant or 0" — is defeated if a journal can also issue id 0 to itself: the forged record makes host-authored events "valid" and gives the forger the matching live token. - It persists.
checkpoint_entries(core.rs:448-460) re-emits anIssuedentry for every capability record, so once restored, the id-0 record is carried into every compacted journal. - Restore's own contract says a journal with "unknown participants" is refused, never partially applied (core.rs:130-136). Participant 0 is the one id that is never a participant.
Related but distinct: #2890 (Issued scopes), #2894 (Committed/Checkpoint authors), #2958 (participant count), #2960 (Issued display names), umbrella #2898.
Acceptance criteria
-
WorldCore::restorerefuses a journal whoseIssuedentry names participant id 0, with an error that names the reserved id; the refusal happens before any later entry is applied (no partial restore). - A regression test in
pkgs/id/src/world/tests.rs(in the style ofrestore_refuses_a_delegate_outliving_its_parent) covers the id-0Issuedcase, including when the id-0 entry follows legitimate entries. - Legitimate journals are unaffected: existing restore/checkpoint round-trip tests pass unchanged, and id 0 remains usable exactly where it is today — host-authored
System/ProgramInstalledevents andsystem_chat(participant 0 as an author, never as an issued capability). - The fix does not mint or reserve any new participant id, and does not change the journal wire format.
Activity
dezren39 commentedon Oct 31, 2026
🤖 Agent note · filed
WorldCore::restore'sIssuedarm on main 1712287 plus a laptop probe test (worktree~/git/fleet/probe-restore-id0): restore accepted anIssuedentry for participant 0, the forged token0.<secret>authorized,chatcommitted an event withparticipant_id == 0, and the snapshot listed participant 0 (probe_restore_issues_participant_zero ... ok).5 remaining items
dezren39 commentedon Nov 1, 2026
🤖 Agent note · picked up
WorldCore::restorerefuses aJournalEntry::Issuednaming participant id 0 (reserved host identity), with a regression test including the id-0-after-legitimate-entries case.Merge pull request #3673 from developing-today/queue/5511-2972-restor…
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done — fixed and merged
fixed_merged). PR fix(id): journal restore refuses Issued participant 0 (#2972) #3673 merged as745edb0c45b314f78e09fa16fdf419c7c21cc15f; this issue auto-closed by the merge.Issuedarm inpkgs/id/src/world/core.rsnow refusesparticipant.id == 0before applying anything, with an error naming the reserved host identity; participant 0 remains usable as an author (System/ProgramInstalled events,system_chat). Regression testrestore_refuses_an_issued_participant_zerocovers the forged entry alone and after a legitimate entry.cargo test --lib restore_— 11 passed / 0 failed (new test + all pre-existing restore tests). Codex review of PR fix(id): journal restore refuses Issued participant 0 (#2972) #3673 completed with no findings.