milestone 17: real deadlines, validator holes, upstream editor, trusted proxies, contract samples, badvers
This commit is contained in:
@@ -26,7 +26,9 @@ missing tutorial, and prove every instruction by running it.
|
||||
alone: the first real blocklist download aborted the process (commit 35f2324)
|
||||
and a stale SPA bundle crashed the settings page — both would have surfaced if
|
||||
the documented paths had been run. No session may report a page complete on the
|
||||
strength of having read the code.
|
||||
strength of having read the code. This ruling constrains **command lines**;
|
||||
a pasted **output line** may carry a placeholder where the literal text
|
||||
would go stale, as milestone-17 ruling 8 does with `nxdns <version>`.
|
||||
|
||||
4. **Content is verified against `src/`, not copied from the old pages.** Defaults,
|
||||
flags, exit codes, paths and route names come from the code at HEAD. Report any
|
||||
|
||||
+44
-2
@@ -78,6 +78,17 @@ total budget. Decision: the total budget becomes real.
|
||||
file's ceiling-only idiom: `1..1_000_000` with the "must be at most"
|
||||
message shape. No full memory-fit guarantee — out of reach, not needed.
|
||||
|
||||
**Recorded (implementation):** three consequences this ruling did not call
|
||||
out. (a) `cache.size = 0` was a documented "disables caching" mode; the
|
||||
`1..` floor removes it — configuration.md now says there is no off value
|
||||
(use 1). (b) The configuration.md NextDNS-DoT redaction passage lost its
|
||||
premise (a hostname `tls://` URL is now unconfigurable) and was rewritten.
|
||||
(c) Three config fixtures used `tls://dot.example:853` and now use an IP
|
||||
literal with `tls_name`; the back-up-and-restore.md export transcript grew
|
||||
to `head -10` with real re-captured output. Also: `Pool.init` takes a named
|
||||
`pool.Timeouts { attempt, total }` struct, not two positional durations —
|
||||
two same-typed adjacent parameters would be silently swappable.
|
||||
|
||||
### 3. The upstream editor is built
|
||||
|
||||
Milestone 8 ruling 9 requires upstream CRUD in the UI ("the Settings page
|
||||
@@ -179,6 +190,16 @@ address's 3-stream SSE cap. No X-Forwarded-For handling exists anywhere
|
||||
chain → 400); an integration test proving a proxied remote address is
|
||||
rate-limited while the proxy itself stays exempt.
|
||||
|
||||
**Recorded (implementation of ruling 4):** `Request.client_addr` is
|
||||
`std.Io.net.IpAddress`, not `address.NetAddress` — `http_util.zig` is a
|
||||
module root for the fuzz target and cannot import nxdns modules; any future
|
||||
ruling that wants a domain type on `Request` hits this. `clientAddr` reads
|
||||
only the final chain entry, never scanning backwards: an earlier entry is
|
||||
client-controlled, so a fallback would hand the request an attacker-chosen
|
||||
identity. Known residual, out of this ruling's list: the login log line in
|
||||
`handlers/auth.zig` still prints the socket peer, which reads as loopback
|
||||
behind a trusted proxy — carried to milestone 19.
|
||||
|
||||
### 5. A field-level contract guard between server and frontend
|
||||
|
||||
The REST contract lives in three hand-synced copies. The Zig side is
|
||||
@@ -217,6 +238,16 @@ Mechanism — captured samples validated by `tsc`, no new dependencies:
|
||||
checking, so a server field missing from types.ts, a types.ts field
|
||||
missing from the wire, and an out-of-union literal all fail
|
||||
`npm run typecheck` — which already runs in CI's frontend job.
|
||||
**Recorded (implementation):** canonicalization gained two rules beyond
|
||||
"keys sorted, numbers→0": repeated array elements dedupe by canonical text
|
||||
(60 identical zeroed timeseries buckets collapse to one; differing rows all
|
||||
stay), and two build-identity strings (`Version.git_commit`,
|
||||
`Version.zig_version`) redact to `"<build>"` so the golden is not pinned to
|
||||
one machine — the list is the named constant `volatile_string_keys`. The
|
||||
renderer emits prettier's own fixpoint output, because CI runs
|
||||
`format:check` on the tree. 43 samples: 38 endpoint + 5 error classes. The
|
||||
regen command is documented in AGENTS.md.
|
||||
|
||||
- Embedding: an anonymous-import root must be Zig, not TypeScript, so a
|
||||
wrapper `web/src/lib/contract_samples.zig` exports
|
||||
`pub const bytes = @embedFile("contractSamples.gen.ts");` and build.zig
|
||||
@@ -262,6 +293,13 @@ finding.
|
||||
`edns.extendedRcode`) and version 0 in the reply OPT; a version-0
|
||||
query is unaffected.
|
||||
|
||||
**Recorded (implementation):** the write-side splitter is
|
||||
`edns.splitRcode` with `pub const badvers: u12 = 16`; the BADVERS reply
|
||||
echoes the question when `qdcount == 1`, following the neighboring
|
||||
FORMERR sites, so a client can bind the reply to its query; a malformed
|
||||
OPT whose version byte is 1 answers FORMERR, because `parseOpt` fails
|
||||
before the version check can read the byte.
|
||||
|
||||
### 7. The stale phase comments state the as-built contract
|
||||
|
||||
- `rate_limiter.zig:5-6` ("Phase 7 decides the locking"): rewrite to the
|
||||
@@ -404,8 +442,12 @@ Deleted surface: the seven unused `get*` wrappers, `getMetrics`,
|
||||
types.ts type fails `npm run typecheck` (proven, then reverted).
|
||||
- [ ] A version-1 EDNS query answers composite RCODE 16 with version 0;
|
||||
`nxdns_dns_badvers_total` appears in /metrics.
|
||||
- [ ] Grep: "Phase 7" and "Phase 8" appear in no source doc comment;
|
||||
the rewritten headers state the mutex and the maintenance task.
|
||||
- [ ] "Phase 7"/"Phase 8" no longer appear in `rate_limiter.zig`,
|
||||
`shutdown.zig`, or `pool.zig`; the rewritten headers state the
|
||||
mutex and the maintenance task. (Narrowed during implementation:
|
||||
the original repo-wide grep exceeded ruling 7's three files — 24
|
||||
stale phase references remain in files this milestone's sessions
|
||||
do not own; the sweep moved to milestone 19.)
|
||||
- [ ] Grep: `0.1.0-dev` appears in no docs/ page; the new drift test
|
||||
passes and was proven able to fail (reinsert the literal, watch it
|
||||
fail, revert).
|
||||
|
||||
@@ -313,6 +313,29 @@ pinned by two tests. Restructure: `try { await onSubmit(...) } catch (error)
|
||||
{ if (error instanceof ApiError) return; throw error; }`, resets after the
|
||||
try. The page's `<InlineError>` keeps rendering the mutation error as today.
|
||||
|
||||
### Carried in from milestone 17: the stale phase-comment sweep
|
||||
|
||||
Milestone 17 ruling 7 rewrote the "Phase 7/8" headers in three files; its
|
||||
repo-wide acceptance grep then found 24 more stale phase references in
|
||||
files its sessions did not own, recorded here for this milestone:
|
||||
safesearch.zig:58, forward_client.zig:7, dns_cache.zig:10/:42/:435,
|
||||
cli.zig:332, filter_integration_test.zig:1391, logger.zig:178/:303,
|
||||
retention.zig:7/:137/:151, disk_monitor.zig:7, querylog_schema.zig:71,
|
||||
db.zig:272, manager.zig:292/:613/:1145, and the six repository files
|
||||
(groups_repo.zig:7, rules_repo.zig:15, sources_repo.zig:9,
|
||||
upstreams_repo.zig:7, local_repo.zig:3, clients_repo.zig:12-13). Each
|
||||
comment is rewritten to state the as-built truth it gestures at (not
|
||||
deleted, unless it says nothing beyond the phase number). Line numbers
|
||||
are hints from m17-time HEAD; re-grep before acting. Acceptance: "Phase
|
||||
7" and "Phase 8" appear in no source doc comment repo-wide; spec files
|
||||
and TECH_DEBT.md are exempt. The session owning each file in the plan
|
||||
below picks up its share; files owned by no session fall to S4.
|
||||
|
||||
Also carried from milestone 17 (ruling 4 residual): the login log line in
|
||||
`src/web/handlers/auth.zig` prints the socket peer, which reads as
|
||||
loopback for every login behind a trusted proxy; it should print
|
||||
`request.client_addr`.
|
||||
|
||||
## Sessions
|
||||
|
||||
S1-S4 run in parallel; no two sessions write the same file.
|
||||
|
||||
@@ -54,6 +54,14 @@ anything else against /home/mokhtar/app/zig tag 0.16.0.
|
||||
re-materializes). Client prefixes: `GET/PUT /api/client-prefixes` as a whole-list
|
||||
resource (tiny table, atomic replace). No POST for clients — creation is by DNS activity
|
||||
or import (PLAN:540 gives clients no POST deliberately).
|
||||
|
||||
> **Placement deviation (milestone 17 ruling 3).** "The Settings page must
|
||||
> edit them" above names the wrong page. Milestone 9 dropped the obligation
|
||||
> entirely; milestone 17 restored it as a dedicated **Upstreams** page with
|
||||
> its own nav entry (`web/src/features/upstreams/`, route `/upstreams`),
|
||||
> matching the house resource-page pattern that every other collection
|
||||
> follows. The Settings page keeps the scalar settings keys only. The API
|
||||
> contract in this ruling is unchanged.
|
||||
10. **Repo layer**: every list row the API serves carries its row id; each mutated resource
|
||||
gains `getX(db, id)`, `updateX(db, id, item)`, `deleteX(db, id)` (strict: 0 rows touched
|
||||
→ error.NotFound), written in the house repo idiom with prepared statements. Existing
|
||||
|
||||
Reference in New Issue
Block a user