Repository navigation
Description
Objective
Take the registry HTTP service from working prototype to something safe to run as a long-lived service: typed at its boundaries, linted, validating what it accepts, observable when it fails, and able to start and stop cleanly.
Rationale
A deep review of the service found the same class of problem repeatedly: the code was correct but undefended. strict: true was defeated by pervasive any at the JSON boundary, so a field-name drift like the instrumentId versus id confusion would surface at runtime rather than at compile time. Nothing validated incoming requests, so a malformed body reached the ledger. The terminal error handler returned a clean 500 but logged nothing, leaving an operator blind to a ledger outage. None of these are bugs a test would catch, and all of them get much more expensive once the service is deployed.
Scope
In scope:
- Typed payloads and
unknownat the JSON boundary, removinganyfromsrc/ - Lint and format tooling matching the team's reference repo
- Runtime request validation against the vendored OpenAPI specs
- Structured logging, 5xx logging, and clean startup and shutdown
- Error responses tested against the specs'
ErrorResponseschemas - A ledger readiness probe distinct from the liveness probe
- Layering cleanup, keeping the pure mapping module free of Express
Out of scope:
- New endpoints or any change to the API contract
- Live-node verification, which belongs to the conformance epic
- Authentication and rate limiting
Architecture & technical considerations
- Biome is used for lint, format, and import organization, matching
BootNodeDev/dAppBooster. Alias imports were deliberately skipped: they work there only because Vite bundles, and this package is plaintsctodist/under NodeNext, where an unrewritten alias throwsERR_MODULE_NOT_FOUNDat runtime. - Request validation runs one
express-openapi-validatorinstance per vendored spec withignoreUndocumented: true, so the admin and health routes pass through untouched. - The vendored
openapi/directory stays byte-pristine and is excluded from formatting. - Shutdown is hand-rolled rather than delegated to a library: idle connections are released first so
close()cannot hang, with an unref'd timer backstop that force-closes at a configurable deadline. - The undocumented 409 on standard transfer and allocation endpoints is a deliberate spec extension and is kept, documented at its single site.
Dependencies
Depends on the registry HTTP service epic. Every change is additive and self-contained within registry/.
Issue breakdown
-
Type the ledger boundary and remove
anyfrom the registry #31 Type the ledger boundary and removeanyfrom the registry (refactor(registry): type the ledger boundary and removeany#11) -
Move resolveOrRespond out of the pure mapping module #32 Move resolveOrRespond out of the pure mapping module (refactor(registry): move resolveOrRespond out of the pure mapping module #12)
-
Adopt Biome for linting and formatting in the registry #33 Adopt Biome for linting and formatting in the registry (chore(registry): adopt biome for linting and formatting #14)
-
Validate incoming requests against the vendored OpenAPI specs #34 Validate incoming requests against the vendored OpenAPI specs (feat(registry): validate requests against the vendored openapi specs #15)
-
Add structured logging and graceful shutdown #35 Add structured logging and graceful shutdown (feat(registry): structured logging and graceful shutdown #16)
-
Validate error responses against the ErrorResponse schemas #36 Validate error responses against the ErrorResponse schemas (test(registry): validate error responses against the ErrorResponse schemas #17)
-
Add a ledger readiness probe #37 Add a ledger readiness probe (feat(registry): add a ledger readiness probe #18)
-
SHUTDOWN_TIMEOUT_MS accepts 0 and unbounded values, defeating connection draining #38 SHUTDOWN_TIMEOUT_MS accepts 0 and unbounded values (chore(registry): production-harden the HTTP service #19)
-
Admin accept and reject handlers duplicate the same lookup block #50 Admin accept and reject handlers duplicate the same lookup block
-
Four request validators run on every request #51 Four request validators run on every request
Every task through #37 is merged into the aggregate branch, and #38 lands with #19.
The last two are deferred polish, filed after the whole-branch review.
Acceptance criteria
- No
anyremains inregistry/src -
npm run lintis clean and the vendoredopenapi/directory is untouched by formatting - Malformed requests are rejected before they reach the ledger
- Every 5xx is logged with structure, and the process exits non-zero on a config error
- The service drains in-flight connections on
SIGTERMandSIGINT - Liveness and readiness are separate endpoints
- The full vitest suite is green
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
anyfrom the registry #31