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
267 changes: 263 additions & 4 deletions docs/run-a-node/manage.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
sidebar_position: 7
title: Manage your node
description: "Operate a running dig-node: the control.* admin RPCs (status, cache, peers) and the DIG Browser's My Node UI that drives them."
description: "Operate a running dig-node: the control.* admin RPCs (status, cache, peers, wallet chain reads) and the DIG Browser's My Node UI that drives them."
keywords:
- dig-node admin
- control RPC
Expand All @@ -25,6 +25,7 @@ The `control.*` namespace exposes operator actions that are **not** part of the
- **status** — health, version, uptime, and the resolved listeners.
- **cache** — inspect the `.dig` cache ([capsules](../concepts.md#capsule) held), and prune/evict.
- **peers / upstream** — the upstream the node blind-fetches from and any peer state.
- **wallet** — read chain state for a public address or coin, and broadcast an already-signed spend ([below](#control-wallet-balance)).

These are admin-scoped: a remote reader hitting the public dig RPC can never call them.

Expand All @@ -34,9 +35,25 @@ After publishing a store with `digs commit`, seed your own node so it immediatel

**Advanced — `DIG_NODE_PUSH_OPEN=true`** lets remote authorized writers push to your node; only a caller holding the store's publisher key can (a signed request signature is required), so it stays safe against cache-poisoning. Leave it off unless you intend to accept remote seed-pushes.

## `control.wallet.balance` — read a public address's balance {#control-wallet-balance}
## `control.wallet.*` — chain reads and broadcast {#control-wallet-balance}

`control.wallet.balance` returns the on-chain balance of any public address, in XCH or $DIG. Unlike the other `control.*` methods it is an **open read**: it needs only an address — no control token, no wallet seed, no signing key — because it reads a public address's balance from the chain. It is served on the node's loopback control plane.
`control.wallet.*` is the wallet-facing slice of the control plane. Five methods, all on the node's loopback control plane, all working with lowercase 64-hex, unprefixed hashes, and integer amounts in the asset's base unit (mojos for XCH):

| Method | What it does |
|---|---|
| [`control.wallet.balance`](#control-wallet-balance) | Total balance of a public address. |
| [`control.wallet.coins`](#control-wallet-coins) | Unspent coins at a public address. |
| [`control.wallet.peak`](#control-wallet-peak) | The node's current chain peak height. |
| [`control.wallet.coinById`](#control-wallet-coinbyid) | A single coin by id, including a spent one. |
| [`control.wallet.broadcast`](#control-wallet-broadcast) | Push an already-signed spend bundle. |

Four of the five need only public information — an address, or a coin id — so they are **open reads**: no control token, no wallet seed, no signing key. This means a person whose node runs as a service with a control-token file they can't read can still see their own money. `control.wallet.broadcast` is different: it puts bytes on the network, so it is the one wallet method that **requires a control token**, the same as the other admin `control.*` methods.

None of the five wallet methods ever accept a key, a seed, or a mnemonic. The node never signs a spend — it only reads chain state and, for `broadcast`, accepts bytes somebody else already signed.

### `control.wallet.balance` — read a public address's balance {#control-wallet-balance}

`control.wallet.balance` returns the on-chain balance of any public address, in XCH or $DIG. It is an **open read**: it needs only an address — no control token, no wallet seed, no signing key — because it reads a public address's balance from the chain.

**Request**

Expand Down Expand Up @@ -75,7 +92,7 @@ After publishing a store with `digs commit`, seed your own node so it immediatel
| `synced` | boolean | Whether **this answer** came from a fully synced local copy. A `"fallback"` answer is always `false`. |
| `peak_height` | number \| null | The chain height **this answer** reflects, or `null` — including for every `"fallback"` answer. |

### Where the answer came from
#### Where the answer came from

`source` tells you which of two places produced the figure:

Expand Down Expand Up @@ -110,6 +127,248 @@ dig-node wallet balance xch1… --asset dig

The CLI reaches the same loopback method and prints the returned figure.

### `control.wallet.coins` — read a public address's unspent coins {#control-wallet-coins}

`control.wallet.coins` lists the **unspent** coins held by a public address, for one asset. It is an **open read** — only an address is needed.

**Request**

```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "control.wallet.coins",
"params": { "address": "xch1…", "asset": "xch" }
}
```

| Param | Type | Required | Meaning |
|---|---|---|---|
| `address` | string | yes | The bech32m address to read (e.g. `xch1…`). |
| `asset` | string | no | The asset to list coins for: `"xch"` (default) or `"dig"`. Omit for `"xch"`. |

**Result**

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"coins": [
{
"coin_id": "aa11…",
"asset": "xch",
"amount": 1000000000,
"parent_coin_info": "bb22…",
"puzzle_hash": "cc33…",
"created_height": 4200000,
"spent_height": null
}
],
"source": "db", "synced": true, "peak_height": 4200042
}
}
```

| Field | Type | Meaning |
|---|---|---|
| `coins` | array | The address's unspent coins for the requested asset (see the coin shape below). |
| `source` | string \| null | Which source answered — same meaning as [`control.wallet.balance`](#where-the-answer-came-from). |
| `synced` | boolean | Whether this answer came from a fully synced local copy. |
| `peak_height` | number \| null | The chain height this answer reflects. |

Each entry in `coins`:

| Field | Type | Meaning |
|---|---|---|
| `coin_id` | string | The coin's id, lowercase 64-hex. |
| `asset` | string | The asset this coin is denominated in: `"xch"` or `"dig"`. |
| `amount` | number (u64) | The coin's amount in mojos. |
| `parent_coin_info` | string | The parent coin id, lowercase 64-hex. |
| `puzzle_hash` | string | The coin's puzzle hash, lowercase 64-hex. |
| `created_height` | number | The block height this coin was created at. |
| `spent_height` | number \| null | Always `null` from this method — `control.wallet.coins` returns unspent coins only. Use [`control.wallet.coinById`](#control-wallet-coinbyid) to look up a coin that may have been spent. |

`coins: []` is a **success**: a chain was consulted and the address holds no unspent coins for that asset. It is never returned because the chain couldn't be reached — an unreachable chain is one of the errors below instead, so an empty list always means "checked, and there is nothing there."

**Errors**

| Code | Name | Meaning |
|---|---|---|
| `-32602` | Invalid params | `address` is missing, not a valid bech32m address, or `asset` is not `"xch"`/`"dig"`. |
| `-32040` | `WALLET_NO_CHAIN_SOURCE` | No live chain source could answer this read. |
| `-32041` | `WALLET_NOT_SYNCED` | The wallet is still syncing and no fallback is available yet. |
| `-32042` | `WALLET_READ_FAILED` | The read failed at the underlying DB / chain-source layer. |
| `-32043` | `WALLET_RATE_LIMITED` | The open coinset-fallback rate limit is exhausted; back off and retry. |

See the full [error-code reference](../support/error-codes.md#dig-rpc-json-rpc).

### `control.wallet.peak` — the node's current chain peak {#control-wallet-peak}

`control.wallet.peak` returns the chain height this node currently tracks. It is an **open read** and takes no parameters — use it to get a height to compare a coin's `created_height`/`spent_height` against.

**Request**

```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "control.wallet.peak",
"params": {}
}
```

**Result**

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": { "peak_height": 4200042, "synced": true }
}
```

| Field | Type | Meaning |
|---|---|---|
| `peak_height` | number \| null | The chain height this node currently tracks, or `null` if this node tracks no height yet. |
| `synced` | boolean | Whether this node's own copy of the chain is fully synced. |

`peak_height: null` means "this node doesn't yet know a height" — it is **not** a zero. Treat it as unknown, never as height `0`: every real block sits trivially above `0`, so reading `null` as `0` would make an unconfirmed transaction look buried under thousands of blocks it hasn't actually seen.

**Errors**

| Code | Name | Meaning |
|---|---|---|
| `-32040` | `WALLET_NO_CHAIN_SOURCE` | No live chain source could answer this read. |
| `-32041` | `WALLET_NOT_SYNCED` | The wallet is still syncing and no fallback is available yet. |
| `-32042` | `WALLET_READ_FAILED` | The read failed at the underlying DB / chain-source layer. |

See the full [error-code reference](../support/error-codes.md#dig-rpc-json-rpc).

### `control.wallet.coinById` — read one coin, spent or unspent {#control-wallet-coinbyid}

`control.wallet.coinById` looks up a single coin by its coin id — unlike `control.wallet.coins`, it can see a coin that has since been **spent**. It is an **open read**: only a coin id is needed.

This is the method to use to confirm an on-chain mint or payment once you know which coin was consumed to pay for it: check `spent_height` on the funding coin.

**Request**

```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "control.wallet.coinById",
"params": { "coin_id": "aa11…" }
}
```

| Param | Type | Required | Meaning |
|---|---|---|---|
| `coin_id` | string | yes | The coin id to look up, lowercase 64-hex. A `0x` prefix is accepted on input; the node never emits one. |

**Result**

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"coin": {
"coin_id": "aa11…",
"asset": null,
"amount": 1000000000,
"parent_coin_info": "bb22…",
"puzzle_hash": "cc33…",
"created_height": 4200000,
"spent_height": 4200010
},
"source": "fallback", "synced": false, "peak_height": null
}
}
```

| Field | Type | Meaning |
|---|---|---|
| `coin` | object \| null | The coin, or `null` if no such coin exists on chain. See the field table below. |
| `source` | string | Always `"fallback"` for this method. |
| `synced` | boolean | Always `false` for this method. |
| `peak_height` | number \| null | Always `null` for this method — call [`control.wallet.peak`](#control-wallet-peak) for a height to compare against. |

The `coin` object:

| Field | Type | Meaning |
|---|---|---|
| `coin_id` | string | The coin's id, lowercase 64-hex — echoes the request. |
| `asset` | null | Always `null` here: a coin id alone doesn't say what the coin is denominated in, and the node does not guess. |
| `amount` | number (u64) | The coin's amount in mojos. |
| `parent_coin_info` | string | The parent coin id, lowercase 64-hex. |
| `puzzle_hash` | string | The coin's puzzle hash, lowercase 64-hex. |
| `created_height` | number | The block height this coin was created at. |
| `spent_height` | number \| null | The block height this coin was spent at, or `null` if it is still unspent. This is the field `control.wallet.coins` can never show you. |

`coin: null` is a **success**: a chain was consulted and no coin with that id exists. It is never returned because the chain couldn't be reached — an unreachable chain is one of the errors below instead.

**Errors**

| Code | Name | Meaning |
|---|---|---|
| `-32602` | Invalid params | `coin_id` is missing or not 64 lowercase hex characters (with or without a `0x` prefix). |
| `-32040` | `WALLET_NO_CHAIN_SOURCE` | No live chain source could answer this read. |
| `-32042` | `WALLET_READ_FAILED` | The read failed at the underlying DB / chain-source layer. |
| `-32043` | `WALLET_RATE_LIMITED` | The open coinset-fallback rate limit is exhausted; back off and retry. |

See the full [error-code reference](../support/error-codes.md#dig-rpc-json-rpc).

### `control.wallet.broadcast` — push an already-signed spend {#control-wallet-broadcast}

`control.wallet.broadcast` pushes a spend bundle that was **already signed** somewhere else — the node never signs anything, and there is deliberately no parameter through which a key, a seed, or a mnemonic could be passed. Unlike the four reads above, this method **requires a control token**: it puts bytes on the network, so it carries the same admin authority as the other `control.*` write operations.

**Request**

```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "control.wallet.broadcast",
"params": { "signed_bundle_hex": "d34d…" }
}
```

| Param | Type | Required | Meaning |
|---|---|---|---|
| `signed_bundle_hex` | string | yes | The already-signed spend bundle, hex-encoded, lowercase. |

**Result**

```json
{
"jsonrpc": "2.0",
"id": 1,
"result": { "accepted": true, "transaction_id": "ee44…", "rejection": null }
}
```

| Field | Type | Meaning |
|---|---|---|
| `accepted` | boolean | Whether the mempool took the bundle. |
| `transaction_id` | string \| null | The transaction id, if accepted. |
| `rejection` | string \| null | The mempool's reason for refusing the bundle, if `accepted` is `false`. |

Two things worth being precise about:

- A mempool that looks at the bundle and says no is still a **successful call** — `accepted: false` with a `rejection` reason, not an error. Failing to *reach* a mempool at all is an error instead (below). The remedies are opposite: a `rejection` means retry the *same* bundle only after fixing what it complains about (or rebuild it), while a broadcast error means retry the *same* bundle as-is once the node can reach the network again.
- `accepted: true` means the mempool took the bundle — it is **not** evidence that the spend reached a block. Confirm that separately with [`control.wallet.coinById`](#control-wallet-coinbyid) against the coin the spend consumes: `spent_height` becoming non-null is the actual on-chain confirmation.

**Errors**

| Code | Name | Meaning |
|---|---|---|
| `-32602` | Invalid params | `signed_bundle_hex` is missing or not valid hex. |
| `-32030` | Unauthorized (control) | Called without a valid local control token. |
| `-32040` | `WALLET_NO_CHAIN_SOURCE` | No live chain source could reach the network to broadcast. |
| `-32042` | `WALLET_READ_FAILED` | The broadcast failed at the underlying chain-source layer. |

See the full [error-code reference](../support/error-codes.md#dig-rpc-json-rpc).

## The DIG Browser Control Pane

Expand Down
10 changes: 5 additions & 5 deletions docs/support/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ This catalog is also published as [`error-codes.json`](https://docs.dig.net/erro

## dig RPC (JSON-RPC)

The [dig RPC](../protocol/dig-rpc.md) uses the standard [JSON-RPC 2.0](https://www.jsonrpc.org/specification) error codes plus the protocol-specific `-32004`, `-32005`, the node-profile `-32003` / `-32006` / `-32007` / `-32008` / `-32009`, the shell/staging codes `-32010` (upstream) and `-32011`–`-32014` (`dig.stage`), the metadata-response bound `-32015` (`dig.getMetadata`), the push-reassembly bound `-32016` (`cache.pushCapsule`), the [private-retrieval](../protocol/onion-routing.md) codes `-32020` / `-32021` / `-32022`, the local control-plane codes `-32030` / `-32031` / `-32032`, and the wallet-balance read codes `-32040`–`-32043`. A content **miss is never an error** — the capsule returns its own indistinguishable, non-verifying response (there is no `decoy` field on the wire), and the client discovers the miss by inclusion-proof and/or decryption failure (see [the blind host model](../protocol/blind-host-model.md)). For any well-formed body the HTTP status is `200`; the error is carried in the JSON envelope.
The [dig RPC](../protocol/dig-rpc.md) uses the standard [JSON-RPC 2.0](https://www.jsonrpc.org/specification) error codes plus the protocol-specific `-32004`, `-32005`, the node-profile `-32003` / `-32006` / `-32007` / `-32008` / `-32009`, the shell/staging codes `-32010` (upstream) and `-32011`–`-32014` (`dig.stage`), the metadata-response bound `-32015` (`dig.getMetadata`), the push-reassembly bound `-32016` (`cache.pushCapsule`), the [private-retrieval](../protocol/onion-routing.md) codes `-32020` / `-32021` / `-32022`, the local control-plane codes `-32030` / `-32031` / `-32032`, and the [wallet chain-read/broadcast](../run-a-node/manage.md#control-wallet-balance) codes `-32040`–`-32043`. A content **miss is never an error** — the capsule returns its own indistinguishable, non-verifying response (there is no `decoy` field on the wire), and the client discovers the miss by inclusion-proof and/or decryption failure (see [the blind host model](../protocol/blind-host-model.md)). For any well-formed body the HTTP status is `200`; the error is carried in the JSON envelope.

| Code | Meaning | What to do |
|---|---|---|
Expand Down Expand Up @@ -56,10 +56,10 @@ The [dig RPC](../protocol/dig-rpc.md) uses the standard [JSON-RPC 2.0](https://w
| `-32030` | **Unauthorized (control)** — a `control.*` method was called without a valid local control token. Control methods are loopback-only. (These codes are `-32030`+ so they never collide with the onion codes above.) | Call control methods from the local machine with the node's control token. |
| `-32031` | **Not supported (control)** — a control operation this build/pin cannot perform (e.g. §21 whole-store sync with no loaded identity). | Ensure the required capability/identity is present, or use a build that supports it. |
| `-32032` | **Control error** — a control operation failed at runtime (distinct from bad input or an absent capability). | Retry; check the node's logs; report if it persists. |
| `-32040` | **Wallet: no chain source** (`WALLET_NO_CHAIN_SOURCE`) — a [`control.wallet.balance`](../run-a-node/manage.md#control-wallet-balance) read had no live chain source able to answer this address. Reported instead of a fabricated `0`. | Attach/allow a chain source (a synced local node or the coinset fallback), then retry. |
| `-32041` | **Wallet: not synced** (`WALLET_NOT_SYNCED`) — the wallet is still syncing and no live fallback is available to answer yet. | Wait for the node to finish syncing, then retry. |
| `-32042` | **Wallet: read failed** (`WALLET_READ_FAILED`) — the balance read failed at the underlying DB / chain-source layer. | Retry; check the node's logs; report if it persists. |
| `-32043` | **Wallet: rate limited** (`WALLET_RATE_LIMITED`) — the open coinset-fallback rate limit is exhausted (too many arbitrary-address reads in a short window). | Back off and retry; the cheap local-DB fast path is never rate-limited. |
| `-32040` | **Wallet: no chain source** (`WALLET_NO_CHAIN_SOURCE`) — one of the [`control.wallet.*`](../run-a-node/manage.md#control-wallet-balance) methods ([`balance`](../run-a-node/manage.md#control-wallet-balance), [`coins`](../run-a-node/manage.md#control-wallet-coins), [`peak`](../run-a-node/manage.md#control-wallet-peak), [`coinById`](../run-a-node/manage.md#control-wallet-coinbyid), [`broadcast`](../run-a-node/manage.md#control-wallet-broadcast)) had no live chain source able to answer. Reported instead of a fabricated result. | Attach/allow a chain source (a synced local node or the coinset fallback), then retry. |
| `-32041` | **Wallet: not synced** (`WALLET_NOT_SYNCED`) — a `control.wallet.*` read hit a wallet that is still syncing, with no live fallback available yet. | Wait for the node to finish syncing, then retry. |
| `-32042` | **Wallet: read failed** (`WALLET_READ_FAILED`) — a `control.wallet.*` call failed at the underlying DB / chain-source layer. | Retry; check the node's logs; report if it persists. |
| `-32043` | **Wallet: rate limited** (`WALLET_RATE_LIMITED`) — the open coinset-fallback rate limit is exhausted (too many arbitrary-address/coin reads in a short window). | Back off and retry; the cheap local-DB fast path is never rate-limited. |

## dig-store CLI (exit codes) {#digstore-cli-exit-codes}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "docs-dig-net",
"version": "0.15.0",
"version": "0.16.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
Expand Down
Loading
Loading