
Security & Deployment
Security you can point at, not security in adjective form.
Every control below is implemented in the platform today — with a specific mechanism, not a category label. Choose cloud, on-premise, or hybrid; the security model travels with you.
Last reviewed by the platform team on
The controls
Six layers of control — each one with a specific mechanism.
Items below are shipped and verifiable in the running platform. For compliance review, the deployment team can walk through the underlying code paths, migrations, and audit queries.
Identity + Authentication
How operators prove who they are
JWT with rotating refresh tokens
Short-lived access tokens, long-lived refresh tokens. Refresh rotation detects replay — if a stolen token is reused after rotation, the whole family is invalidated and the session is killed.
Two-step tenant login
Login is company-code first, then credentials. A leaked email + password from one tenant cannot be guessed against another tenant without also knowing the company identifier.
7-day offline grace
Refresh tokens stay accepted for 7 days past expiry so terminals running disconnected re-sync cleanly when the network returns, without losing offline access decisions.
Password policy enforced once
Minimum length is enforced via a single constant in the auth service — every password-set entry point (login, reset, admin create) shares the same rule.
PIN re-authentication for HIGH-risk actions
AI Assistant HIGH-risk tools (revoke credential, disable user) render a PIN re-auth modal before the confirm executes — operator session alone is not enough.
TOTP 2FA — schema in place, enforcement rolling out
The user model carries TOTP secret / backup-code fields and the operator UI exposes the toggle. End-to-end enforcement (enrolment, challenge, backup codes) is rolling out tenant-by-tenant after pilot validation.
Authorization + Multi-tenancy
How blast radius stays small
Explicit tenant scoping
Every query that touches tenant data carries a parameterized WHERE tenant_id = $N. We removed an earlier SQL-rewriting wrapper precisely because string heuristics masked isolation bugs; explicit beats magic.
RBAC + plugin gating
Permissions checked per-request. Plugin-gated modules (Visitor, Parking, VMS, Attendance) are off by default — a tenant has to be entitled before routes register.
Service-side resource validation
Cross-tenant lookups go through ValidateResourceAccess so a forged ID in a URL can't cross a tenant boundary even when a query path forgets to filter.
AI Assistant forwards user JWT
The AI service never holds write credentials. Every action tool call is made on behalf of the logged-in operator, with their RBAC enforced by the downstream service — not by the assistant.
Audit + Forensics
What gets recorded and how long it stays
Immutable audit log
TimescaleDB hypertable. Application user has INSERT and SELECT only — no UPDATE, no DELETE. You can't cover your tracks by editing history.
2-year retention, compressed after 30 days
Time-partitioned with compression policy. Long-tail forensics stay queryable without paying hot-storage cost for old chunks.
Batched, decoupled writer
Services publish audit entries to NATS JetStream; a standalone audit-svc consumes and batch-inserts. A burst of activity can't pause request handling waiting for an audit write.
Every CRUD + auth event
Logins, refreshes, password changes, every CREATE/UPDATE/DELETE across services. Query API at /api/v1/audit/ for system admins and /api/v1/audit/tenant/ for tenant-scoped operators.
Data Residency + AI Boundary
Where data goes when AI is in the loop
Refuses to start cloud LLM in production
ai-asst-svc explicitly refuses to boot when APP_ENV=production and the active LLM profile is DeploymentCloud. A misconfigured tenant cannot silently route operator data to a third-party cloud.
Self-hosted Ollama for residency-bound tenants
For sites under data-residency policy, the LLM provider can be switched to Ollama running inside the deployment — no payload leaves the perimeter.
LLM circuit breaker
Provider failures don't cascade — the assistant degrades cleanly and surfaces "LLM unavailable" instead of hanging operator workflows.
Network + Transport
How traffic enters the platform
CORS allowlist enforced at boot
CORS_ALLOWED_ORIGINS is mandatory in production; the middleware refuses to start if the value is "*" while AllowCredentials is true. No "we'll fix it later" wildcard reaching prod by accident.
Trusted-proxy aware client IP
TRUSTED_PROXIES is a CIDR allowlist. X-Forwarded-For is only honored when the immediate peer is on the list — replaces a previous middleware that trusted client-controlled IP headers.
AES-encrypted RTSP credentials
Camera RTSP credentials are encrypted at rest with a 32-byte VMS_CREDENTIAL_KEY before being persisted, then decrypted on demand for stream setup.
Wall-display kiosk tokens
Public wall-display surfaces use scope-limited, read-only kiosk tokens — they can render assigned dashboards but cannot mutate state, and tokens are individually revocable.
Operational Safety
How upgrades stay safe in production
NOT VALID constraint policy
Any ADD CONSTRAINT against an existing table is written NOT VALID — enforced on new rows, validated out-of-band later. Learned from a 2026-05-16 incident where immediate validation against pre-existing rows blocked every service that depended on migrate.
Documented recovery runbooks
Production incidents become written runbooks (docs/runbooks/*) so the next responder doesn't reinvent the recovery from log archaeology.
Sequential, paired migrations
Every schema change is a numbered .up.sql + .down.sql pair, applied in strict order. No live ALTER, no hand-edited table.
Trust topology
Every module inherits the same governance layer.
Tap any layer to see how it helps a buyer evaluate the platform.
Signal
Doors, visitors, cameras, intercoms, parking, and devices generate live operational signals instead of isolated alerts.
01Signal
Doors, visitors, cameras, intercoms, parking, and devices generate live operational signals instead of isolated alerts.
02Context
Each signal becomes useful because operator, person, role, site, zone, policy, and related module records are shown together.
03Action
Teams approve, deny, review, escalate, annotate, or link evidence without leaving the command surface.
04Audit
Every decision keeps the who, what, when, why, and related evidence needed for handoff, reporting, and investigation.
Hosting choice does not change the trust model — the same controls apply in cloud, on-prem, and hybrid deployments.
Deployment
Three deployment models, same security model.
Cloud
Managed rollout
Faster start, managed platform operations. Default LLM is Anthropic Claude. Best for greenfield deployments and multi-site portfolios without strict residency rules.
On-premise
Site control
Full stack inside your perimeter — TimescaleDB, NATS, EMQX, MediaMTX, MinIO, Ollama. No telemetry, no outbound LLM calls. Required for government / healthcare / residency-bound sites.
Hybrid
Phased modernization
Control plane in cloud, sensitive workloads on-prem. Per-tenant LLM profile lets one portfolio mix cloud and self-hosted inference under one operator UI.
Best fit
For sites where trust is part of the buying decision.
If your security review starts with "show me the audit query, the migration policy, and the LLM data path" — you're in the right place.
Next step
Bring your security review to the deployment team
We will walk through specific code paths, migrations, audit queries, and the LLM data boundary — not just a feature checklist.