Security
Database-enforced rules, the demo guard, safe redirects, the URL guard and signed store grants.
Rules in the database
The most important rules are enforced by Postgres (row-level security, column grants, triggers and check constraints), so a bug in the app or a misbehaving agent cannot break them. In short:
- Billing state is written only by the store.
- Locked, disabled or
nonemodules return no rows. - Launching, approving, aborting and sending vehicle commands need
publish, are recorded as the real caller, and are never possible for agents. - No launch with a grounded unit, a low battery reserve or an altitude above the limit without a waiver.
- Release to service needs a second person (four-eyes).
- Flight logs are append-only; notifications can only be marked read; audit entries can't be forged.
- Only owners and admins change access; agents never go above
edit.
The full list with how each rule is enforced is on Invariants. Every rule has a test that tries to break it.
Demo guard
Demo mode has no sign-in, so it must never serve a production deployment by accident. If DEMO_MODE=true and NODE_ENV=production, the app throws at start-up unless ALLOW_DEMO_IN_PROD=true is set explicitly.
Safe redirects
Any next or return-to parameter goes through safeRedirect. It only accepts same-origin relative paths: one leading slash, no //, no backslash, no scheme and no control characters. Anything else falls back to /app. This prevents open redirects after sign-in.
URL guard
User-supplied URLs that the server would fetch go through a URL guard that refuses private, loopback, link-local and other reserved addresses. In this build the only such URL is the store's push target, which comes from configuration.
Signed store grants
The demo store on the site signs each entitlement with HMAC-SHA256 over a canonical JSON body (order, workspace, modules, tier, AI add-on, period end and issue time), using a shared secret of at least 16 characters. The app:
- checks the signature format,
- refuses grants issued more than 5 minutes before or after now (replay window),
- recomputes the signature and compares it in constant time,
- only then writes the billing columns with the service role.
A grant that fails any step changes nothing.
Secrets
No real keys are needed or used in this build. .env files are git-ignored. The local database password is local only.