Files
ThothII/.superpowers/sdd/task-4-report.md
T

4.2 KiB

Task 4 — Unix-socket DWH verification report

Scope

Implemented the standalone Linux verifier at tools/dwh-auth/internal/service and wired the exact command:

dwh-auth serve --registry-root ABSOLUTE_CANONICAL --socket ABSOLUTE_CANONICAL

The service accepts only GET /verify. It returns empty 204 responses with X-DWH-Key-ID for verified v1 or reserved legacy credentials; credential failures are generic empty 401 responses, and registry/integrity faults are empty 503 responses. Other paths/methods return empty 404/405.

Security decisions

  • Exactly one X-API-Key header, maximum 128 bytes.
  • Strict thtdwh_v1. parsing precedes legacy lookup; non-v1 values alone may use the reserved legacy record.
  • Registry integrity is checked before every verification request, so unrelated malformed/unsafe records fail closed with 503.
  • Logs emit only timestamp, decision code, and (when safely parsed or verified) public key ID; test sentinels prove no key, digest, description, or query value is emitted.
  • serve validates canonical absolute paths, performs startup Store.Check, and reports service startup errors as non-secret integrity failure.
  • Socket collisions that are regular files, directories, symlinks, live sockets, or foreign-owned stale sockets are refused. Only an owned stale Unix socket after ECONNREFUSED can be reclaimed.
  • Published sockets are mode 0660; cancellation calls graceful shutdown and removes only a revalidated same-device/same-inode owned socket. A test seam proves a changed path is retained rather than unlinked.

Required supporting security fix

Commit 943f809 (fix: reject duplicate legacy DWH records) tightens the Task 2 registry contract: a synthetically valid active plus revoked legacy pair is now an integrity failure. It is intentionally separate from the Task 4 commit.

TDD evidence

RED was observed for the missing handler, listener/configuration API, CLI wiring, unrelated-registry corruption, active+revoked legacy state, and cleanup replacement race. Each increment was then implemented minimally and rerun GREEN.

Verification

All commands were executed in official golang:1.26.5, with only this worktree mounted:

gofmt -w cmd internal/command internal/service
go test ./internal/service ./internal/command -count=1
go test ./... -count=1
go test -race ./... -count=1
go vet ./...
git diff --check

All passed. A dependency scan also found no third-party Go dependencies.

Scope boundary

No Nginx, systemd, real Unix socket, real registry, credential, legacy stack, or external service was changed. All test data was synthetic and temporary.

Follow-up hardening: runtime read-only registry and socket parent

The Task 5 storage contract uses root:dwh-auth SGID directories (2750) and a service account with read-only group access. The original registry reader path was incompatible because shared locks were opened O_RDWR and lazily created as 0600; secure-directory validation also rejected SGID.

The runtime path now uses registry.OpenReadOnly: it opens only preprovisioned root, active, revoked, and .writer.lock paths, and rejects Add/Revoke. The administrative Open path bootstraps the lock through the exclusive writer path. Shared lock acquisition opens the existing root:dwh-auth 0640 lock O_RDONLY with LOCK_SH; writer acquisition remains O_RDWR with LOCK_EX, preserving cross-process snapshot exclusion. Secure directories allow SGID but still reject setuid, sticky, group-write, and world-write bits.

Task 5 must create .writer.lock as 0640 root:dwh-auth alongside the 2750 root:dwh-auth registry directories before the service starts.

The socket parent must be a canonical non-symlink directory owned by the service EUID and not group/world writable. This removes the bind-to-chmod and path-replacement exposure from other principals. The remaining POSIX path race is bounded to trusted processes sharing the service EUID inside that non-contendible parent.

Additional verification (official golang:1.26.5, worktree only): focused securefile/registry/service/command tests, full tests, full race tests, vet, plus ten race repetitions each for cross-store snapshot readers, OpenReadOnly, and listener tests: all PASS.