Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ edition = "2021"
# the ROOT manifest (`[workspace.package].version`), so it MUST be set here for a
# release to fire (§3.6). The library crates (dig-node-core/dig-runtime/dig-wallet)
# keep their own independent versions — only the released binary tracks the workspace version.
version = "0.74.1"
version = "0.74.2"

# Release hardening, matching digstore: keep integer-overflow checks ON in release.
# The node parses untrusted serialized input and does offset/length arithmetic over
Expand Down
11 changes: 11 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -936,3 +936,14 @@ debt existing.
makes records expire before the holder re-announces. Pinning only the named relation would have left
the real one unguarded. No single value satisfies both bounds with margin, so closing the zero margin
is a wire-behaviour decision, not a test fix.

**Landing a capsule IS announcing it — verify at the land seam, not only at serve (#1623).** The
whole-store sync (`sync_module_from` → `cache_fetch_and_cache`) writes a downloaded module straight to
`<cache>/modules/<store>/<root>.module`, and the mere existence of that file makes the node a
discoverable DHT holder (§14.1) — the reshare/flywheel then multiplies the copy across peers. So the
old rationale "the synced module isn't trusted here; a tampered module fails the SERVE gate, not this
sync" was wrong: the serve gate never runs for a peer that only learns this node HOLDS the capsule
from its DHT announce. The fix reuses the #1576 reshare leg's `ChainAnchoredModuleVerifier` at the sync
seam (resolve the chain-anchored root, re-hash, compare) and refuses BEFORE the write, so an
unverified capsule never lands and is never announced. General lesson: a defense that only runs on one
of several exit paths is not a defense — verify at every seam that admits an artifact.
8 changes: 8 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -2310,6 +2310,14 @@ a root OTHER than the confirmed root MUST be rejected (never cached or served).
- **Fail-closed.** Gap-fill never pulls against an unconfirmable root (the §14.2 decision gates it).
- **Verification invariant.** Every served module is verified against the chain-anchored root at SERVE,
no matter how it arrived — a client read, a §21 whole-store sync, or a proactive/backfill gap-fill.
- **Verify-before-announce invariant.** A synced capsule MUST ALSO pass the chain-anchored verification
BEFORE it lands in the cache, because landing a capsule makes this node a DISCOVERABLE holder of it
(§14.1) — an announcement, not merely a local copy. The node resolves the store's chain-anchored root,
re-hashes the downloaded module against it, and refuses anything that is not the store's confirmed
generation, so the module never reaches disk and is never announced. The serve-time gate is not
sufficient on its own: a node that advertises a capsule poisons holder reputation and multiplies it
through the reshare flywheel whether or not any later read verifies it. This is the SAME chain-anchored
check the reshare leg applies at its own seam — one verification shape at every seam that admits a capsule.

### 14.3a. Whole-capsule download — two paths, chunked first

Expand Down
Loading
Loading