Skip to content

a store whose on-chain root advances silently un-publishes itself — no body is persisted at the new root #294

Description

@MichaelTaylor3d

Task

Nothing keeps a persisted profile body in step with its store's on-chain root. A root advances, no body is
persisted for the new root, and the DPB store silently becomes unservable — the publisher ends up
holding a body it would itself reject.
Close that gap.

Measured on real hardware, 2026-08-21

Machine A (Windows, dig-app 12.34.0 on disk, dig-node 0.134.0), store 111eb8bc…:

  • The DPB store holds one body, from Aug 16, 18 bytes, no image, at root 716513ed….
  • The store's on-chain root has advanced to 371a39b0…, and no body was ever persisted for it.
  • A's own node refuses its only body"the chain is the authority" — and returns body_b64: null
    at the real root.

So there is nothing at the tip for any peer to announce or serve. A publisher that has moved its store
forward is, from the network's point of view, publishing nothing at all — and it gets no signal that this
has happened.

The guard is working correctly — that is what makes this the interesting failure

Machine B (different host, region, install, node version, provably cold — no profiles dir at all,
evidenced 01:21:12Z) was handed the same stale body and independently derived the identical tip root
371a39b0… and refused
, storing nothing.

Two independent chain reads agreeing on the tip is corroboration rather than a self-consistent stub: the
AnchoredRoot guard is genuinely wired to a live chain read and fails closed. NC-9 is satisfied here.

B is peered with A and its journal shows profile-body sync (opcodes 223/224/225) up, so this is not
connectivity and not a missing opcode layer. The transport was never reached.

Why it blocks the round trip

This is the merged-but-never-composed class, found one layer earlier than expected — in publish, not
transport. put-body/get-body each anchor correctly in isolation; nothing owns the invariant between
them, which is that a store whose root advanced needs a body at the new root or it has silently
un-published itself.

Shape questions

  • Who notices? A publisher whose root advanced has no error to see — the failure is the absence of a
    later write. Detection has to be active, not a returned error.
  • Whose job is the re-persist? Re-anchoring an existing body under a new root is not always right —
    the root may have advanced because the content changed. Distinguish "same body, new root" from "root
    moved on, body genuinely stale".
  • What should get-body say? Today it returns null, which is indistinguishable from "no such
    profile". A publisher debugging this cannot tell an un-published store from an empty one.
  • Is refusing correct at all here? Yes — do not weaken the guard to make this pass. The guard is the
    only reason the two machines agreed. The fix belongs on the publish side.

Also worth a look while in here

There is no CLI surface to fetch a profile by DIDdig-node profile offers only
put-body/get-body, both keyed on <STORE_ID> <ROOT>. That leg of the round trip is untested rather
than failing, because there is no way to attempt it.

Parent

https://github.com/DIG-Network/dig_ecosystem/issues/3131 (which stays open), under
https://github.com/DIG-Network/dig_ecosystem/issues/3008.

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:businessa person can DO something new, or money moves, or a shipped surface stops lying to them

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions