backend update notification API live and mounted (ZUP 45); dashboard UI that would surface it not yet built — see body
Sourced from: docs/DECISIONS.md D5.8 D8.19, docs/08-PLATFORM-SPEC.md §7.3, apps/platform/src/server/releases/routes.ts, apps/platform/src/server/releases/notifications.ts, apps/platform/src/server/releases/catalog.ts, apps/platform/src/server/releases/semver.ts, apps/platform/src/nodes/routes.ts, apps/platform/src/app.ts, apps/node/src/health/discovery.ts

Template updates

Your treasury node runs from a versioned Railway template. New template versions ship bug fixes, security hardening, and new features. This page covers how you're expected to find out about updates and apply them — updates are always merchant-initiated; ZuPayments never pushes code to your infrastructure.

Why updates are never automatic

Self-custody means your Railway project is the root of trust for your node. Nothing about ZuPayments' design lets the platform reach into your infrastructure and change what's running there — not for updates, not for anything. If it could, that would be a hole in the exact guarantee the whole product is built on: that ZuPayments structurally cannot touch your keys or your code. So template updates are something you pull, on your own schedule, never something pushed to you.

How you'll find out about an update

Your node exposes its own template version at a small discovery endpoint (/.well-known/zupay-node) that reports {node_id, template_version, pubkey_fingerprint}. This part is live and stable (D8.19, apps/node/src/health/discovery.ts).

What's now built on the platform side (ZUP-45, not yet true when this page was first drafted):

  • A template_versions catalog table records each released Railway template version — its semver, an optional changelog, a Railway template ref for building a redeploy link, and whether it's flagged security-critical (apps/platform/src/server/releases/catalog.ts).
  • GET /dashboard/template-updates is a real, mounted, authenticated route (session + onboarded, same gate as the rest of /dashboard/*apps/platform/src/app.ts). For each node you've registered, it returns whether an update is available and, if so, the target version, changelog, a Railway redeploy deep-link, and a severity of "routine" or "security". The comparison is pure semver — major.minor.patch only, hand-rolled rather than pulling in a dependency (apps/platform/src/server/releases/semver.ts) — and a node with no reported version, or one already at or ahead of the latest published version, correctly gets no nudge rather than a false "out of date" claim (notifications.ts, unit-tested independent of Postgres).
  • Severity is elevated to "security" whenever any published version strictly newer than your node's current version is flagged security-critical — not only when the very latest release happens to be the security one — so you can't miss an in-between security fix by jumping straight to the newest release.
  • This surface is read-only and self-custody-safe by construction: it computes a notification and a Railway deep-link; it never calls your node or triggers a redeploy itself (D5.8, golden rule 5).

What's still missing: there is no dashboard UI to display any of this. The platform (apps/platform) is currently an API-only backend with no merchant-facing frontend built — GET /dashboard/template-updates works if you call it directly with a valid session, but there is nowhere in a browser UI today where you'd see an "update available" badge. Treat this the same way as before: check the ZuPayments changelog/release notes directly rather than expecting an in-dashboard prompt, until a dashboard frontend exists to render this endpoint.

One more accuracy note: your node's recorded template_version is only refreshed when you (re-)register the node with the platform — the platform does not poll your node's /.well-known/zupay-node on any schedule (apps/platform/src/nodes/routes.ts, POST /register is the only write path to nodes.templateVersion, on both first registration and re-registration). If you update your node's template without re-registering it, the platform's record of your version can lag behind what's actually deployed until you next register.

Applying an update

Until a guided in-dashboard flow ships, updating means redeploying your node service from the newer template version through Railway directly — the same mechanism as the initial deploy in deploy.md, just targeting a newer template version instead of a fresh project. Your data (vault, wallets, audit log, settings) lives in your Postgres service and is untouched by a template redeploy to the app service alone.

Before redeploying:

  • Read the changelog for the version you're moving to — check specifically for anything marked security-critical or for any migration notes. (If a dashboard UI isn't available to you yet, GET /dashboard/template-updates will return the changelog text and severity for your node directly.)
  • Confirm you don't have anything time-sensitive in flight (an active sweep, consolidation, or outbound transfer) — a redeploy restarts the process, which triggers the same crash-recovery pass described in incidents.md. That pass is designed to handle an in-flight transfer safely, but there's no reason to create unnecessary work for it if you can just wait a few minutes for things to settle first.
  • After the redeploy, confirm /health/ready returns 200 and spot-check the admin dashboard loads normally before considering the update complete.
  • Re-register the node with the platform afterward if you want its recorded template_version to reflect the new version promptly (see the note above — there's no automatic re-poll yet).

What updating does not affect

A template update changes the application code your node runs. It does not:

  • Rotate or touch any wallet key (see key-rotation.md for why wallet keys never rotate at all).
  • Change your admin credentials or TOTP enrollment.
  • Require re-registering with the hosted dashboard for the node to keep working — registration only affects whether the platform's recorded template version (and therefore the update-notification API above) is current; your node's identity (its discovery fingerprint) is stable across template versions either way.