Skip to content

feat(digstore-remote): clone_store accepts an optional generation root - #70

Draft
MichaelTaylor3d wants to merge 5 commits into
mainfrom
feat/1903-clone-store-root
Draft

feat(digstore-remote): clone_store accepts an optional generation root#70
MichaelTaylor3d wants to merge 5 commits into
mainfrom
feat/1903-clone-store-root

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

DO NOT MERGE — gate round in progress

Task

Give DigClient::clone_store (and pull's full-module GET) a way to request a SPECIFIC generation root, so a §21 clone against the public gateway is a single rooted GET /stores/{id}/module?root=<hex> instead of a rootless GET that the gateway answers with a 307 redirect.

Why

  • DigClient::new deliberately sets reqwest::redirect::Policy::none() (crates/digstore-remote/src/client.rs:185-198, SSRF guard). hub.dig.net §16 (#1886) resolves a rootless module read with 307 -> ?root=<head>. Combined, a rootless clone_store/pull from the digstore CLI against rpc.dig.net returns ClientError::Status(307) and never downloads. The CLI already holds the root it wants (fetch() -> descriptor.current_root) before it calls clone_store; it just has no parameter to pass it through.
  • dig-node's §21 fallback (clone_whole_store) wants to pin want_root for the same reason.

Scope

  • digstore-remote: clone_store gains a root: Option<&Bytes32> argument; Some(r) -> ?root=<hex> and the served ETag root MUST equal r (else Verification); None -> unchanged rootless behaviour. pull's full-module GET carries ?root=<remote_root> (it already knows it).
  • digstore-cli clone_from passes the descriptor root.
  • SPEC.md updated in the same PR (§4.2).
  • Workspace version bump: minor (new capability, additive to the protocol; signature change on an unpublished 0.x crate).

Blast radius checked

In-tree callers of clone_store: crates/digstore-cli/src/ops/remote_ops.rs, crates/digstore-remote/tests/client_roundtrip.rs. Out-of-tree: dig-node crates/dig-node-core/src/lib.rs (git-pinned rev 161c2a31; adopts on its own bump — noted on the ticket).

How verified

(filled in by the lane: unit tests in client_roundtrip.rs; guest wasm rebuilt; CLI reinstalled --force --locked; integration tests against the INSTALLED binary.)

Refs https://github.com/DIG-Network/dig_ecosystem/issues/1903
Parent epic: https://github.com/DIG-Network/dig_ecosystem/issues/3212

Closes DIG-Network/dig_ecosystem#1903

Co-Authored-By: Claude noreply@anthropic.com

MichaelTaylor3d and others added 5 commits September 6, 2026 08:35
Refs DIG-Network/dig_ecosystem#1903

Co-Authored-By: Claude <noreply@anthropic.com>
…903)

Co-Authored-By: Claude <noreply@anthropic.com>
Add DigClient::clone_store_at(store_id, root, verify, on_progress): a
rooted GET /stores/{id}/module?root=<hex> that refuses (before the body
is downloaded, before verify/on_progress run) any response whose ETag
names a different generation than the one requested. clone_store now
delegates to it with root=None, byte-identical to the prior rootless
request.

pull's full-module download is now pinned to the remote head it already
resolved (?root=<remote_root>) and checks the served ETag agrees before
returning PullResult::Module, instead of trusting an unpinned rootless
GET.

Server side: get_module/head_module accept an optional ?root= query.
Precedence: 422 on a malformed root (evaluated before any backend call)
-> 404 unknown store -> 404 UnknownRoot on a well-formed root that is
not the served head -> 304 on a matching If-None-Match (GET only) -> 200
with ETag = the served root. A rooted request is never silently
downgraded to serving the head.

digstore-cli's clone_from now calls clone_store_at with the remote_root
fetch already resolved, so a head that advances between fetch and the
module download surfaces as a clean error instead of installing an
unrequested generation.

Refs DIG-Network/dig_ecosystem#1903

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant