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.
This build says node-backend is off.
Static adapter: there is no server process, so nothing under /api/ was injected
and every call below should 404 with text/html. The prerendered pages still
work — which is the point of the gate.
/api/node-backendServer 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.
/api/node-backendEcho, 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.
/healthLiveness 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.
/api/forms/contactThe 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.