Skip to main content
Announcement bar — session-dismissible, edit or remove in home.ts

Node backend

Every page here is prerendered, so the server half is easy to lose and hard to notice losing. These four calls are how you tell a Node artifact from a static one at run time. Adapter choice is a feature, not an environment variable — see AGENTS.md §15.

GET
/api/node-backend

Server facts: runtime, deploy flags, which secrets are configured.

This foundation's diagnostic route. Reports the Node version, the DEPLOY_BRANCH flags baked in at build time by src/lib/flags.ts, and whether each access: 'secret' variable is set. Never a value: there is no authentication in front of this.

POST
/api/node-backend

Echo, plus the origin comparison that explains a 403 behind a proxy.

Echoes the body, and reports what the server believes its own origin to be. If serverOrigin and the origin header disagree on a deployed host, that is what turns Astro's CSRF check into a 403 on POST that reads like an application bug (AGENTS.md §9). clientAddress sits next to x-forwarded-for because it does not trust that header — behind a proxy every caller looks local, which quietly makes the form rate limit global.

GET
/health

Liveness probe — must be a live 200, not a file.

The liveness probe the deployment agent polls after installing an artifact, failing the deploy after repeated misses. It runs per request on purpose: a 200 proves the server is executing code, where a prerendered /health would answer 200 from a file while the application itself was wedged.

POST
/api/forms/contact

The one endpoint that exists for the application rather than for diagnostics.

The real form-intake endpoint, sent a deliberately INVALID payload so it proves the route runs and validates without delivering an enquiry anywhere. Expect 422 and a fields object; HTML would mean the route is not in this artifact at all.

The payload carries ft: "9000" on purpose. The fill-timer runs before the schema, and a submission that looks too fast is dropped with a success-shaped { ok: true, deliveries: [] } so a bot gets no tuning feedback. Drop ft and you get that 200 instead of this 422 — a form that appears to work while discarding everything.

Changing the adapter is a feature choice: set node-backend in src/features/features.config.json, run npm run update-features, and commit what it writes. npm run smoke makes the same distinction from the command line against a built artifact.