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
64 changes: 38 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,20 @@ chia://<storeID>/index.html?salt=<hex> # private store

### How `chia://` → `rpc.dig.net` works (the native read path)

The native handler (`net/url_request/dig_protocol_handler.cc`) mirrors the
reference extension exactly, but in C++ with BoringSSL instead of the WASM
crypto module:
The native handler mirrors the reference extension exactly, currently in C++
with BoringSSL (`net/url_request/dig_crypto.*`). **Planned (#44 item 2, build-
gated):** route verify+decrypt through the shared `digstore` `dig_client` **wasm**
— the byte-identical artifact the extension/hub/SDK use — so there is exactly ONE
trustless read-crypto implementation (client-side decrypt stays; only the
duplicate C++ copy is retired). The pipeline:

1. **Parse the URN** (`dig_urn.cc`) — full form or shorthand, plus `?salt=`.
2. **Retrieval key** — `retrieval_key = SHA-256(canonical rootless URN)` where the
canonical rootless URN is `urn:dig:chia:<storeID>/<resourceKey>`
(`dig_crypto.cc`, SYSTEM.md "Retrieval key"). The URN itself is never sent.
3. **Fetch** — `POST` JSON-RPC 2.0 `dig.getContent` to the configured DIG RPC
endpoint (default `https://rpc.dig.net/`), streaming 3 MiB windows and
3. **Fetch** — `POST` JSON-RPC 2.0 `dig.getContent` to an **external** dig-node
resolved by the §5.3 ladder (custom endpoint > `dig.local` > `localhost:8080`
> `rpc.dig.net`; see "source resolution" below), streaming windows and
reassembling `{ ciphertext, chunk_lens, inclusion_proof, total_length,
complete, next_offset }` (SYSTEM.md "JSON-RPC 2.0 read methods").
4. **Verify** — recompute the leaf `SHA-256(ciphertext)` and fold the Merkle
Expand All @@ -73,46 +77,54 @@ error page — content is never shown unless it verifies *and* decrypts.

### Where `chia://` content is read from (source resolution)

The DIG Browser is a **consumer** in the DIG serve/consume split (`SYSTEM.md` →
"Roles — serving vs consuming"). For every `chia://` read it picks a source **in
order**:
The DIG Browser is a **pure RPC consumer** in the DIG serve/consume split
(`SYSTEM.md` → "Roles — serving vs consuming"): it runs **no in-process node**.
For every `chia://` read it resolves an **external** dig-node **in order** (§5.3):

1. **A local standalone dig-node**, if one is reachable — preferred, because it
1. **A custom endpoint**, if you set one in `chrome://settings/dig` — it
**overrides** the whole ladder and is used exclusively (your deliberate
choice, no silent fallback).
2. **A local standalone dig-node**, if one is reachable — preferred, because it
is local/offline-capable and contributes to the network. It is addressed
`http://dig.local` **first** (the `dig-installer` maps that name to the
node's privileged `:80` loopback listener), then `http://localhost:8080` (the
node's always-on localhost listener). The browser does a cheap `GET /health`
liveness probe (confirming `status:"ok"` + `mode:"local-node"`) and **memoizes
the verdict for ~5s**, so a page's many subresources never each re-probe a
down node and a single failed probe never stalls a load.
2. **The browser's own in-process dig-node** otherwise — which itself reaches
`rpc.dig.net` when it has no cached capsule.

Either way the served bytes are **always** verified against the on-chain root
and decrypted on your device — the source is never trusted (fail-closed). So the
browser is **fully functional standalone** (no local node needed), and when a
local dig-node *is* present it consumes from it and they share one `.dig` cache.

- **Disable** consuming from a local node (in-process only) with the
3. **The public gateway `https://rpc.dig.net`** otherwise — the final fallback,
so a standalone browser with no local node still resolves every request.

Whichever source serves the bytes, they are **always** verified against the
on-chain root and decrypted on your device — the source is never trusted
(fail-closed). So the browser is **fully functional standalone** (no local node
needed); when a local dig-node *is* present it consumes from it.

- **Set a custom node** (or clear it, back to automatic) in **Settings → DIG
Network** (`chrome://settings/dig`); it persists in the `dig.custom_node_url`
pref and takes precedence over the auto-ladder.
- **Skip the local node** (go straight to `rpc.dig.net`) with the
`--disable-local-dig-node` command-line switch.
- The pure resolution policy (ordering, port, host, probe path, TTL) lives in
- The pure resolution policy (override precedence, ordering, port, host, probe
path, TTL) lives in
[`dig/node/dig_source_resolution.mjs`](dig/node/dig_source_resolution.mjs)
with a Node test harness; the native loader mirrors it in C++.

### Run & manage your node — `dig://node` (My Node)

The DIG Browser is also your node's **controller**. When a local standalone
**dig-node** is running, open **`dig://node`** ("My Node") to manage it: see its
status, the **stores it hosts** (pin / unpin), the **cache** (view / clear / set
cap), **§21 sync** status, and the **upstream** it fetches from. It drives the
node's `control.*` admin RPCs over loopback only.
status, the **stores it hosts** (pin / unpin), **§21 sync** status, and the
**upstream** it fetches from. It drives the node's `control.*` admin RPCs over
loopback only. (The browser is **not** the node's cache-config UI — cache
management lives on the node itself, #44 SoC.)

- **Hidden when you have no local node.** Consumption never needs one, so with no
node present the page shows a **calm, dismissible nudge** instead of an empty
controller: it says the browser already works fully on its own (in-process node
+ `rpc.dig.net`, nothing to install to browse), then invites installing a
**standalone dig-node** to run a full node, contribute to the network, share
your `.dig` cache, and unlock this My Node controller. The **Install dig-node**
controller: it says the browser already works fully on its own (it reads from
the public gateway `rpc.dig.net`, nothing to install to browse), then invites
installing a **standalone dig-node** to run a full node, contribute to the
network, and unlock this My Node controller. The **Install dig-node**
link points at the [`dig-installer` releases](https://github.com/DIG-Network/dig-installer/releases)
(the same target the `dig-chrome-extension` uses). Never alarmist; dismissing it
is remembered (localStorage), and "Check again" re-engages.
Expand Down
73 changes: 48 additions & 25 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ DIG Browser MUST:
- resolve `chia://` and `dig://` requests through an EXTERNAL DIG node's read RPC (§3), following the
§5.3 node-resolution ladder (§4);
- verify every retrieved resource's Merkle inclusion proof against the on-chain-anchored root and
decrypt it client-side, **fail-closed**, using the shared `digstore` `dig_client` read-crypto wasm —
the SAME artifact the hub, extension, and SDK use byte-for-byte (§5);
decrypt it client-side, **fail-closed**, calling the digstore **Rust** read-crypto DIRECTLY via FFI
(`dig_runtime`'s `dig_read_verify_decrypt`) — the SAME Rust the webpage `dig-client-wasm` wraps (§5);
- inject a `window.chia` wallet provider (CHIP-0002) backed by the in-browser wallet (§6);
- surface per-resource proof provenance (Shields, §7) and a page-posture control surface
(`dig://control`, §8);
Expand All @@ -35,8 +35,9 @@ DIG Browser MUST NOT:
- run an in-process/embedded DIG node, any P2P networking, provider records, or content serving;
- keep a user-managed on-disk content cache or expose cache-configuration chrome (no
`chrome://settings/dig` cache section, no cache cap/usage/clear UI, no `DigCacheHandler` Mojo);
- reimplement the read-crypto in native code — there is exactly ONE trustless read-crypto
implementation (the shared wasm).
- carry a SECOND read-crypto implementation — the browser reuses the ONE `digstore` Rust read-crypto
(linked natively via `dig_runtime` FFI). It runs no bespoke native C++ crypto and no wasm; wasm is
the webpage binding of that same Rust (hub/extension/SDK), never the browser's.

The trust model is unconditional: **the source is never trusted.** Whichever node serves the bytes,
the browser independently verifies + decrypts them; a proof failure or decrypt failure yields an error
Expand Down Expand Up @@ -111,11 +112,21 @@ After bytes are retrieved from the chosen node, the browser ALWAYS, in-process:
3. reassembles chunks and serves the plaintext to the renderer only if every step succeeds —
otherwise it fails closed with an error page.

This is implemented by the shared `digstore` `dig_client` read-crypto **wasm** — the byte-identical
artifact (same SHA-256) the hub (`apps/web/lib/dig-client`), the `dig-chrome-extension`, and
`@dignetwork/dig-sdk` consume. There is exactly ONE trustless read-crypto implementation in the
ecosystem; the browser MUST NOT carry a second (native C++) copy. Client-side decryption is
load-bearing (it powers Shields, §7) and is never delegated to the node.
This is implemented by the digstore **Rust** read-crypto, called DIRECTLY via FFI. The browser is a
native application, so it links the digstore read-crypto crate (`digstore-core`, exported over C-ABI by
the `dig_runtime` cdylib as `dig_read_verify_decrypt`) and calls the Rust in-process — **WASM is for
webpages only.** There is exactly ONE trustless read-crypto implementation in the ecosystem — the
`digstore` Rust — with two bindings over it: the native FFI the browser uses here, and the
`dig-client-wasm` binding the hub (`apps/web/lib/dig-client`), the `dig-chrome-extension`, and
`@dignetwork/dig-sdk` (all webpages/JS) use. The browser MUST NOT carry a second copy of the crypto
(no bespoke native C++, no wasm). Client-side decryption is load-bearing (it powers Shields, §7) and is
never delegated to the node.

The C++ URL loader (`dig_url_loader_factory.cc`) owns only the NETWORK side — resolve the endpoint
(§4), fetch the ciphertext + base64 inclusion proof, resolve the chain-anchored root — then hands those
bytes to `dig_read_verify_decrypt`; it contains NO verify/decrypt logic of its own. A nonzero status
from the FFI (bad input / verification failure / decrypt failure) is rendered as a fail-closed error
page, never partial content.

## 6. Wallet — injected `window.chia`

Expand Down Expand Up @@ -144,8 +155,10 @@ configuration lives with the node.

## 9. Conformance

- **Single read-crypto:** the `dig_client` wasm the browser loads MUST be the byte-identical artifact
(same SHA-256) used by the hub, extension, and SDK; a divergent copy is a conformance failure.
- **Single read-crypto:** the browser's verify+decrypt MUST be the digstore Rust read-crypto called via
the `dig_runtime` FFI (`dig_read_verify_decrypt`) — the SAME `digstore-core` Rust the hub, extension,
and SDK consume through the `dig-client-wasm` binding. A bespoke native C++ crypto copy, or loading
the wasm into the browser, is a conformance failure (wasm is the webpage binding, not the browser's).
- **Ladder parity:** `dig/node/dig_source_resolution.mjs` and its native C++ mirror MUST implement the
identical ordering, host/port, probe path, and TTL; any change is made in both, in one unit of work.
- **No node surface:** the shipped browser exposes no node/P2P/cache configuration chrome (§1).
Expand All @@ -155,19 +168,29 @@ configuration lives with the node.

## 10. Transition (#41/#44)

The fork is mid-migration to the pure-RPC-consumer contract above (super-repo #44, per the #41
separation-of-concerns ruling). The remaining code deltas from the previous "browser runs an
in-process node" architecture are:

- **Remove the in-process node** (the `dig-runtime` cdylib loaded at PostBrowserStart) and make the
terminal fallback of the §4 ladder `rpc.dig.net` instead of an in-process node. This ships gated on
the dig-installer reliably delivering a local dig-node (#40) AND a buildable release (#26).
- **Single-source the read-crypto** — drop the native C++ `net::dig` DigURLLoader read-crypto copy in
favor of the shared wasm (§5).
The fork is completing its migration to the pure-RPC-consumer contract above (super-repo #44, per the
#41 separation-of-concerns ruling). Status of the four code deltas from the previous "browser runs an
in-process node" architecture:

- **Remove the in-process node** — LANDED on the consumer read path: `dig/node/dig_source_resolution.mjs`
and the native `dig_url_loader_factory.cc` no longer have an `in-process` terminal; the §4 ladder now
terminates at the public gateway `rpc.dig.net`, and the loader never calls the runtime's node RPC
(`dig_rpc`). The `dig-runtime` DLL is loaded at PostBrowserStart for the in-browser WALLET (§6) and
the read-crypto FFI (§5) only. The companion `dig-node` change that makes this a TRUE zero-node
browser has landed: `dig_runtime_start_wallet()` starts the wallet WITHOUT the node engine (no
`dig_rpc`/P2P/cache) — the browser calls it at startup (dig-node #47). `rpc.dig.net` + the
installer's standalone dig-node (#40) cover content. Ships once verified on a buildable release (#26).
- **Single-source the read-crypto** — LANDED in code (build-gated verify). The former native C++
`net::dig` verify/decrypt (`net/url_request/dig_crypto.*`, `dig_urn.*`) is DELETED; the loader now
calls the digstore **Rust** read-crypto directly via the `dig_runtime` FFI `dig_read_verify_decrypt`
(§5) — the SAME Rust the webpage `dig-client-wasm` wraps, so there is exactly ONE trustless
read-crypto implementation. **WASM is for webpages only; the native browser uses the Rust directly.**
Client-side decrypt STAYS (it powers Shields). Needs a Chromium build (#26) to wire the `dig_runtime`
link + regenerated patches and verify end-to-end.
- **Remove the `chrome://settings/dig` cache section** + the `DigCacheHandler` Mojo + the "My Node"
cache card (§1).
- **Add the custom-node setting** (§4.3).
cache card (§1) — DONE.
- **Add the custom-node setting** (§4.3) — DONE (the `chrome://settings/dig` section now hosts the
persisted custom node endpoint pref, `dig.custom_node_url`).

Until the in-process node is removed, `dig/node/dig_source_resolution.mjs` still lists an `in-process`
terminal step; the target terminal step is `rpc.dig.net`. This section is removed once the migration
lands.
This section is removed once the read-crypto single-sourcing (item 2) lands and the full node removal
is build-verified.
Loading
Loading