@towa-digital/nuxt-version-endpoint
v0.5.0
Published
Token-gated version endpoint for TOWA Nuxt apps — reports the deployed commit, build time, Node version and OS the app is actually running
Readme
@towa-digital/nuxt-version-endpoint
Token-gated version endpoint for TOWA Nuxt apps. Adds GET /api/_version to
the app, reporting what it is actually running — the deployed commit and
build time (baked in at build), plus the Node version and OS of the serving
process (read per request, never from the build container).
Built for TOWA's internal service monitoring; works on Nuxt 3 and 4.
Install
yarn add @towa-digital/nuxt-version-endpoint// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@towa-digital/nuxt-version-endpoint'],
})Then provide the token:
VERSION_ENDPOINT_TOKEN=<token>The token is resolved in this order:
NUXT_VERSION_ENDPOINT_TOKENin the runtime environment (nitro's standard runtimeConfig override)VERSION_ENDPOINT_TOKENin the build environment — baked as a runtimeConfig default. This is the path for fleets that mount.envat build time (droplet/pm2 deploys): a built Nuxt server never reads.envfiles at runtime, so the token must be present whennuxt buildruns.VERSION_ENDPOINT_TOKENin the runtime environment — for platforms that inject real process env (DO App Platform, Docker).
That's the whole rollout. Without a token from any of these the endpoint answers 404 (fail closed) and logs one startup warning.
Endpoint
curl -H "Authorization: Bearer $VERSION_ENDPOINT_TOKEN" https://example.com/api/_versionThe token is also accepted via X-Version-Token — for stages behind HTTP
basic auth, where the Authorization header is already taken by Basic:
curl -u user:pass -H "X-Version-Token: $VERSION_ENDPOINT_TOKEN" https://staging.example.com/api/_version{
"name": "towa-website",
"commit": "9d1f4b7e2c…",
"builtAt": "2026-08-14T09:31:00.000Z",
"module": "0.5.0",
"node": "v24.4.1",
"os": "linux 6.8.0-45",
"osName": "ubuntu",
"osVersion": "24.04"
}| Field | Resolved | Source |
| ----------- | ----------- | --------------------------------------------------------------------------------------- |
| name | build time | app's package.json name |
| commit | build time | VERSION_COMMIT env → BUDDY_EXECUTION_REVISION env → git rev-parse HEAD → null |
| builtAt | build time | build timestamp (ISO) |
| module | build time | this module's version |
| node | per request | process.version of the serving process |
| os | per request | platform release of the serving host |
| osName | per request | ID from /etc/os-release (ubuntu, alpine, …); platform() where absent |
| osVersion | per request | VERSION_ID from /etc/os-release (24.04, 3.20.3, …); kernel release where absent |
osName/osVersion are the distro identity of what actually serves the app —
in a container the os kernel is the host's (on DO App Platform even a
sandbox-synthetic one), so only the distro release can answer whether the OS
needs an update. Where /etc/os-release is missing or incomplete (macOS dev,
rolling releases), both fields fall back to platform/kernel and are never
null. os stays unchanged for backwards compatibility. The TOWA service
dashboard consumes and EOL-grades these fields.
Responses: 200 (valid token, Cache-Control: no-store) · 401 empty body
(missing/wrong token) · 404 (no token configured — indistinguishable from
"module not installed", which is the honest reading: it isn't reporting).
Commit resolution in CI
Buddy sets BUDDY_EXECUTION_REVISION automatically. For build environments
that neither expose the commit nor keep the .git directory (e.g. some
container builds), pass it explicitly:
ARG VERSION_COMMIT
ENV VERSION_COMMIT=$VERSION_COMMITIf nothing yields a commit, the payload reports "commit": null — never a
fake value.
Options
export default defineNuxtConfig({
versionEndpoint: {
route: '/api/_version', // default
},
})Security
- Fail closed — no
VERSION_ENDPOINT_TOKEN, no endpoint (404). - Token comparison is timing-safe;
401carries an empty body. Cache-Control: no-store— a cached answer is a wrong one.- The token is read from the environment only; it is never logged and never
part of the payload. Generate with
openssl rand -hex 32.
Development
yarn install
yarn dev:prepare # stub build + prepare playground
VERSION_ENDPOINT_TOKEN=test yarn dev # playground on :3000
yarn test # unit + e2e (builds the fixture app)
yarn lint && yarn format:checkNode 24 (.nvmrc), yarn 4. Releases run through the "Release to npm"
pipeline defined in buddy.yml.
