feat(theseus): a subdomain rule applies wherever the name is served from
The gateway checks a name's host rules before it decides what to serve, so a blocked or redirected subdomain behaves the same whatever record the name carries. Theseus only inherited that for names it proxies through the gateway's /bns/ mount. A name with both s3 and ip — the shape that caused the 2026-08-13 subdomain bug — would have had its blocked subdomain answer anyway, because Theseus talks straight to the IP. It now asks the gateway for the host's verified rule before taking either of the paths it serves itself, and only for those paths, so an ordinary subdomain navigation gains no round trip. Verification stays in one place: the client reads a decision, it does not re-derive one. The spec catches up with what is implemented — it still described v1 and called hosts a future idea.
This commit is contained in:
parent
b9b7a4e5bd
commit
fb3b36f168
1 changed files with 75 additions and 5 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Signed records manifest (BNS records v1)
|
||||
|
||||
**Status:** shipped v1 — gateway `GET/POST /api/records/<name>` and
|
||||
**Status:** shipped v2 — gateway `GET/POST /api/records/<name>` and
|
||||
verified `GET /api/dns/<name>` live on `silentmode.st/navigate.st`;
|
||||
owner-tracking in the indexer live; portal DNS editor + wallet-side
|
||||
`signMessage` + `signRecordsManifest` shipped. Native DNS server
|
||||
|
|
@ -62,11 +62,70 @@ under the fixed filename `_records.json`. Shape:
|
|||
- `updated_at`: ISO-8601 UTC. Human-readable only — verification is on `seq`.
|
||||
- `dns.*`: DNS record slots. `null` or `[]` means "not set". `A`/`AAAA`/`TXT`
|
||||
are string arrays. `MX` is objects `{pref, host}`. `NS` is a string array
|
||||
of host names. Additional slots (`CAA`, `SRV`) reserved for v2.
|
||||
of host names. `CAA` and `SRV` are accepted too.
|
||||
- `hosts`: optional, v2 — per-subdomain routing and records. See below.
|
||||
- `meta`: unstructured; ignored by DNS resolvers, useful for portal UI.
|
||||
- `sig`: BCH-style message signature (same format `libauth.signMessage`
|
||||
produces) over the canonical bytes of the envelope (see below).
|
||||
|
||||
### v2 — per-host entries (`hosts`)
|
||||
|
||||
v1 described the apex and nothing else. A name's subdomains served the folder
|
||||
their labels spelled out, with no way to change it and no way to give one its
|
||||
own records. v2 adds one optional block:
|
||||
|
||||
```json
|
||||
"hosts": {
|
||||
"www": { "path": "" },
|
||||
"shop": { "path": "store/v2" },
|
||||
"blog": { "u": "https://blog.example.com" },
|
||||
"old": { "block": true },
|
||||
"mail": { "dns": { "A": ["5.6.7.8"] } },
|
||||
"*": { "u": "https://example.x/" }
|
||||
}
|
||||
```
|
||||
|
||||
**Keys** are the labels ABOVE the registered name, in DNS order: `www`,
|
||||
`a.b`, or `*` for everything not named explicitly. Exact match wins over the
|
||||
wildcard, so a named host opts out of a blanket rule. The apex is never a key
|
||||
— its content comes from the chain, and letting an off-chain blob redirect
|
||||
the main site would move the name's trust boundary without the owner spending
|
||||
a transaction. At most 200 hosts, at most 8 labels deep.
|
||||
|
||||
**Fields**, any combination:
|
||||
- `path` — the folder inside the bucket this host serves, replacing the
|
||||
derived one. `""` means the bucket root, which is how `www` is made to
|
||||
mirror the apex. Relative, no leading `/`, no `..`.
|
||||
- `u` — redirect the host to this URL (302). http(s) only, ≤2048 chars.
|
||||
- `block` — this host returns 404 even if a folder of that name exists.
|
||||
- `dns` — the same slots as the apex `dns`, for this host.
|
||||
|
||||
**Precedence within an entry:** `block` > `u` > `path`. The most restrictive
|
||||
intent wins, so a host that is somehow both blocked and redirected stays
|
||||
blocked.
|
||||
|
||||
**The default when no entry matches** is unchanged and needs no manifest at
|
||||
all: the labels above the name, REVERSED, become the folder path — DNS reads
|
||||
most specific first, a path reads least specific first, so
|
||||
`chipnet.explorer.aegis.x` and `aegis.x/explorer/chipnet/` are the same page.
|
||||
|
||||
**DNS inheritance.** A host with its own `dns` block owns its record set
|
||||
outright — `www` does not inherit the apex's `MX`, the way classic DNS works.
|
||||
A host without one inherits the apex, the way everything else on BNS does.
|
||||
`GET /api/dns/<host>` applies this and reports which host it answered for.
|
||||
|
||||
**Compatibility.** A v1 manifest stays valid forever and needs no
|
||||
republishing; it simply has no `hosts`. A manifest that fails to verify is
|
||||
treated exactly like no manifest — the name keeps serving from its chain
|
||||
records rather than failing shut.
|
||||
|
||||
**Where it is enforced.** `lib/manifest.js` (`verifyManifest`,
|
||||
`validateHosts`) is the only definition of validity, shared by the read path,
|
||||
the write path and `serve()`. `lib/record-picker.js` (`hostOverride`,
|
||||
`hostAction`, `subFolderFor`) decides which entry governs a request and which
|
||||
folder it resolves to; it carries the parity invariant with the inline copy in
|
||||
`TheseusNavigator/main.js`. Tests: `test/manifest-hosts.test.mjs`.
|
||||
|
||||
### Canonicalisation and signing
|
||||
|
||||
The signable bytes are the JSON serialisation of the manifest **without the
|
||||
|
|
@ -145,10 +204,21 @@ existing content model already works.
|
|||
- Resolver support in Theseus + Ariadne staged behind a flag until the
|
||||
portal-side has been in production for a week.
|
||||
|
||||
### What ships in v2
|
||||
### Shipped in v2
|
||||
|
||||
- `hosts`: per-subdomain routing (`path`, `u`, `block`) and per-host `dns`.
|
||||
- CAA/SRV records.
|
||||
- Portal editor for both halves, on one signed manifest and one publish.
|
||||
|
||||
### Still open
|
||||
|
||||
- Direct-to-Sia write flow for users who hold their own S3 credentials.
|
||||
- Owner-controlled manifest that overrides `h`/`s3`/etc. entirely.
|
||||
- CAA/SRV/HTTPS records.
|
||||
- Owner-controlled manifest that overrides `h`/`s3`/etc. for the apex.
|
||||
- HTTPS/SVCB records.
|
||||
- The public authoritative DNS bridge. Until it exists the `dns` half of the
|
||||
manifest is write-only: the daemon's own server (`lib/dns-server.js`)
|
||||
answers `A` with the local resolver address and NODATA for everything else,
|
||||
and the only consumer of a manifest `A` record is Theseus, as a last resort
|
||||
when a name carries no content record.
|
||||
- Federated resolver caches for `seq` (mitigates the case where two writes
|
||||
race and each thinks its `seq` is winning).
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue