The suite that shipped with the implementation pinned the decisions that
were expensive to get wrong. This turns it into the exhaustive matrix the
surface deserves: POST /hosts/<id> is the only publicly writable endpoint
on the service, so a mistake there is an intrusion rather than an outage.
__tests__/hosts.test.js goes from 22 to 155 cases (suite 61 -> 194):
- auth: nine near-miss Authorization headers, the read token refused on
the write route, the ingest token refused on all five read routes, and
the two tokens proven independent — either one unset leaves the other
path working
- rejection ordering: 404 (routing) beats 503 (ingest token unset) beats
403 (outside the allowlist), and a malformed id answers 404 whatever
token it carries, so route existence stays unenumerable
- routing: eleven more malformed ids, every one of them a 404. Widening
HOST_ROUTE_RE to `^/hosts/(.+)$` turns sixteen of them red, which is
what makes the narrow pattern a traversal control rather than a comment
- payload: twenty-two field mutations, eight bodies that are not a JSON
object, an Infinity only a raw body can express, and the 4 KiB ceiling
pinned from both sides — 4096 accepted, 4097 and 8 KiB refused
- persistence: 0600 mode, whitelist rebuild, 128-character cap, loadAvg
sliced to three, the route id winning over an id in the body, an
overwrite leaving no temp debris, and the 500 path when HOSTS_DIR
cannot be created
- freshness: 899/900/901 exact against a frozen Date.now() instead of the
wall clock, plus a custom HOSTS_STALE_SECONDS and its fallbacks
- listing: eight ways a snapshot file can be corrupt, each degrading its
own entry while a healthy neighbour keeps its data
The 4 KiB hostname of the acceptance criteria is covered twice, because
the body ceiling fires before the sanitiser ever sees it: 4096 characters
are refused with 413, and 3500 characters (which fit) are truncated to
128 with the long value absent from the file.
index.js is untouched. No defect surfaced, and the two mutations used to
prove the net bites were reverted.
Resolves#14