Commit ad24450
* feat(server): local plaintext content-serve surface + local-first cache (#289, #290)
Add a NEW loopback-only HTTP surface, `GET /s/<storeId>[:<root>]/<path>`, that
decrypts store content SERVER-SIDE and returns the real website to a same-machine
browser — distinct from the blind-ciphertext JSON-RPC `POST /` (which stays blind
for node<->node + mTLS node-class clients). A browser cannot present a client cert
to get plaintext from the public gateway, so the trusted local node is the only
place a plaintext read can legitimately happen; plaintext only ever crosses loopback.
#289 — serve surface:
- dig-node-core `content_serve`: `Node::serve_content_plaintext` resolves the
chain-anchored root (pinned #127, fail-closed), derives `retrieval_key =
SHA-256(canonical rootless URN)` (empty -> index.html), fetches ciphertext
local-first -> peer -> public RPC, then verifies inclusion vs the anchored root and
AES-256-GCM-SIV-decrypts, reusing the ONE digstore-core read-crypto every DIG
client shares. A local module's DECOY for a missing key (proof does not fold to the
anchored root) is treated as a miss and falls through, never a served/garbage
result. `Node::manifest_paths` exposes the PublicManifest paths for the SPA
decision. Plus `derive_retrieval_key` + `ServeSource`/`PlaintextOutcome`.
- dig-node-service `content` (pure helpers): `/s/...` route parsing, store-root
`<base>` + `<meta referrer=same-origin>` injection, Referer rerooting of
root-absolute subresources, the ecosystem ext->MIME map + `is_static_asset_path`
classifier (ported from the on.dig.net loader), and the hardened store CSP.
- server: `GET /s/*path` + a fallback that reroots via `Referer`; SPA history-fallback
(route miss -> the store's index.html, known-file/asset miss -> honest 404, #144
MIME rule); content-type + nosniff; store CSP on HTML; and per-serve
`X-Dig-Verified` / `X-Dig-Root` / `X-Dig-Source` provenance headers (#292).
#290 — local-first cache: a synced+verified .dig serves local with no network (the
default once cached); an unsynced read serves the immediate resource from the
peer/RPC tier AND triggers the single-flight background whole-.dig sync-down
(`maybe_backfill_capsule`, chain-anchored-root pinned) so the next read is local.
Freshness is inherent to the anchored-root pin (a stale generation is never served
as current). `X-Dig-Source` reflects the tier that served the main resource.
Verified: `cargo fmt --all --check`, `cargo clippy --all-targets --locked -D warnings`
(all crates), and the full workspace test suite (0 failed) — including new unit tests
(verify/decrypt round-trip + tamper/wrong-root/wrong-key fail-closed, retrieval-key
derivation, route parse, Referer reroot, content-type, base injection) and end-to-end
HTTP integration tests (decrypted HTML with injected base + headers, JS asset, SPA
route fallback, asset 404, Referer reroot).
Blast radius: purely ADDITIVE. New `content_serve` module + two `Node` methods in
dig-node-core (no existing symbol changed); new `content` module + additive routes
(`/s/*path` + fallback) in the service `router()` (the only modified existing symbol;
`POST /` / `/health` / discovery / control unchanged). No `.dig` format or wire
change (store-format read via the existing digstore libs; §5.1 preserved). Deps: added
digstore-stage/core/crypto + tempfile as dig-node-service DEV-deps (the integration
fixture) at the same rev as dig-node-core.
Bump: minor (0.14.0 -> 0.15.0 workspace; dig-node-core 0.3.0 -> 0.4.0) — a new
backwards-compatible capability (feat), no breaking change.
Closes #289
Closes #290
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(server): use `?` for the Referer path parse (clippy question_mark)
The newer stable clippy CI runs flags the `match Some/None -> return None`
pattern in `reroot_via_referer` under `-D clippy::question_mark`; rewrite it to
the `?` operator (identical behavior, covered by the existing reroot tests).
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent dd2d746 commit ad24450
11 files changed
Lines changed: 1681 additions & 7 deletions
File tree
- crates
- dig-node-core
- src
- dig-node-service
- src
- tests
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
71 | 74 | | |
72 | 75 | | |
73 | 76 | | |
| |||
294 | 297 | | |
295 | 298 | | |
296 | 299 | | |
| 300 | + | |
| 301 | + | |
297 | 302 | | |
298 | 303 | | |
299 | 304 | | |
| |||
346 | 351 | | |
347 | 352 | | |
348 | 353 | | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
349 | 419 | | |
350 | 420 | | |
351 | 421 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
0 commit comments