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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project are documented here.
This project adheres to [Semantic Versioning](https://semver.org) and
[Conventional Commits](https://www.conventionalcommits.org).

## [0.37.0] - 2026-07-16

### Features
- **cli:** Control-parity subcommands (`info`/`config`/`cache`/`stores`/`sync`/`updater`/`subscriptions`) — a CLI verb for every `control.*` method the extension drives, thin-dispatched over the loopback control plane with `--json` (#426)
- **cli:** `peers` — view + manage peer connections, IPv6-first address display, parity with the extension's peer surface (#559)

## [0.35.0] - 2026-07-16

### Features
Expand Down
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 @@ -31,7 +31,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.36.0"
version = "0.37.0"

# 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
50 changes: 49 additions & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -1284,12 +1284,60 @@ write) is cancelled and reported as a failed kick, so it can never block the pas

`run` (default when no subcommand; serves in the foreground and is the unix-service entrypoint) ·
`run-service` (hidden; the Windows SCM entrypoint, §9.4; behaves as `run` off Windows) ·
`install` · `uninstall` · `start` · `stop` · `status` · `pair` (§7.11) · `open` (§8.5).
`install` · `uninstall` · `start` · `stop` · `status` · `pair` (§7.11) · `open` (§8.5) ·
the **control-parity** subcommands `info` · `config` · `cache` · `stores` · `sync` · `updater` ·
`subscriptions` (§8.6) · `peers` (§8.7).

The `dign` alias binary (§2.1a) exposes this SAME subcommand set with the SAME semantics — `dign
<subcommand>` is equivalent to `dig-node <subcommand>` in every respect except the reported program
name.

### 8.6. Control-parity subcommands (#426)

For EVERY gated `control.*` method the DIG Chrome extension drives (§7), the CLI exposes an
equivalent subcommand, so an operator/agent can drive the node from a terminal exactly as the
extension drives it from a browser. Each subcommand is a THIN dispatch — it calls the SAME
`control.*` method over the node's loopback endpoint, presenting the MASTER control token
(`X-Dig-Control-Token`, read WITHOUT minting — §7.11/#501); no CLI logic is forked from the control
plane. A mutating CLI control is therefore gated by the identical capability as the WS surface (the
on-disk master token = local-machine control), never an unauthenticated backdoor.

- `info` → `control.status` — the rich node status (version, uptime, cache, hosted-store +
cached-capsule counts, §21 sync availability). DISTINCT from `status` (§8.3), which is an
unauthenticated `/health` liveness probe; `info` is the token-gated detailed view.
- `config [get]` → `control.config.get`; `config set-upstream <url>` → `control.config.setUpstream`.
- `cache [get]` → `control.cache.get`; `cache set-cap <bytes>` → `control.cache.setCap`;
`cache clear` → `control.cache.clear`.
- `stores [list]` → `control.hostedStores.list`; `stores pin|unpin|status <store>` →
`control.hostedStores.pin|unpin|status`.
- `sync [status]` → `control.sync.status`; `sync trigger <store>` → `control.sync.trigger`.
- `updater [status]` → `control.updater.status`; `updater set-channel <ch>` / `pause [--until <s>]`
/ `resume` / `check-now` → the matching `control.updater.*`.
- `subscriptions [list]` → `control.listSubscriptions`; `subscriptions add|remove <store_id>` →
`control.subscribe`/`control.unsubscribe`.

**Parity is enforced mechanically.** `control::CONTROL_METHODS` is the canonical set of every
`control.*` method the node resolves; a compile-time-adjacent test asserts every method in it is
reachable from a CLI verb, so a new node control method cannot ship without a CLI subcommand.

### 8.7. `peers` — view + manage peer connections (#559)

`peers` reaches parity with the extension's peer surface (`src/features/peers/peersApi.ts`):

- `peers [list]` → `control.peerStatus` — the live peer status: running flag, connected count,
relay reservation, and (when a newer node fills the optional field) the per-peer list. Peer
addresses are displayed **IPv6-first, IPv4 second** per the ecosystem §5.2 address-family policy.
- `peers connect <peer>` → `control.peers.connect`; `peers disconnect <peer>` →
`control.peers.disconnect`; `peers ban <peer> --state <ban|blacklist|none>` →
`control.peers.setBan`; `peers pool-config --max-connections <n>` → `control.peers.setPoolConfig`.

The management verbs + the extended per-peer `control.peerStatus` payload are a **known node-side
gap** (the same gap the extension documents): the node today implements only the running flag +
connected count. Until it ships the management RPCs the `list` view degrades honestly (count only)
and the management verbs surface the node's METHOD_NOT_FOUND. The CLI verbs exist now so the surface
reaches parity and lights up with NO CLI change once the node implements them (tracked cross-repo
follow-up).

### 8.5. `open` — the OS scheme handler (#389)

`dig-node open <link>` is the target the installer registers for the OS `chia://` and
Expand Down
42 changes: 42 additions & 0 deletions crates/dig-node-service/src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,48 @@ pub fn is_control_method(method: &str) -> bool {
method.starts_with("control.")
}

/// The canonical set of `control.*` methods the node's control plane RESOLVES — the
/// union of the methods this shell owns ([`dispatch_control`]) and the ones it delegates
/// to the embedded node's own control surface (`control.peerStatus` +
/// `control.subscribe`/`unsubscribe`/`listSubscriptions`). This is the SINGLE source of
/// truth for "what can be controlled", consumed by:
///
/// * the CLI-parity drift test (#426) — every method here MUST have a `dig-node` CLI verb
/// (see `crate::control_cli::cli_covered_control_methods`), so the CLI never silently
/// falls behind the WS control surface the extension drives;
/// * introspection — a stable list a machine can enumerate.
///
/// Keep it in lockstep with [`dispatch_control`]: a new `control.*` method added there MUST
/// be added here (and given a CLI verb), or the drift test fails.
pub const CONTROL_METHODS: &[&str] = &[
// Owned by this shell (dispatch_control).
"control.status",
"control.config.get",
"control.config.setUpstream",
"control.cache.get",
"control.cache.setCap",
"control.cache.clear",
"control.hostedStores.list",
"control.hostedStores.pin",
"control.hostedStores.unpin",
"control.hostedStores.status",
"control.sync.status",
"control.sync.trigger",
"control.updater.status",
"control.updater.setChannel",
"control.updater.pause",
"control.updater.resume",
"control.updater.checkNow",
"control.pairing.list",
"control.pairing.approve",
"control.pairing.revoke",
// Delegated to the embedded node's own control surface.
"control.peerStatus",
"control.subscribe",
"control.unsubscribe",
"control.listSubscriptions",
];

/// Is this a PAIRING-ADMINISTRATION control method (#280)? PURE.
///
/// These manage the pairing lifecycle — list pending requests, approve one (minting
Expand Down
Loading
Loading