Security Architecture
Deployment posture
Riviamigo is not approved for direct Internet exposure. Production Compose binds the application behind a host firewall and authenticated gateway, and every shared deployment must place an authenticated tunnel or identity-aware reverse proxy in front of it. The outer gateway owns public TLS, certificate renewal, identity enforcement, and Internet-facing rate limits; Riviamigo login remains required behind it.
See the secure deployment runbook for the required gateway contract and verification steps.
The current internal audit evidence and release requirements are tracked in
docs/security-audit.md.
Sensitive vulnerability reports must use the repository's private GitHub Security Advisory flow. Do not publish credentials, exploit details, production data, live telemetry, or precise vehicle locations in public issues.
Authentication
- JWT (RS256) with 15-minute access tokens
- 30-day HttpOnly refresh tokens, rotated on use
- API keys are SHA256-hashed, read-only, and bound to exactly one vehicle; keys never authorize dashboard, account, administrative, or vehicle-setting writes
- Argon2 password hashing
- Vehicle membership roles are capability boundaries:
vieweris telemetry and history read-only,managermay run operational changes such as schedules and backfills, andowneralone manages credentials and membership. - Protected-route bootstrap uses
POST /v1/auth/bootstrap, which returns fresh tokens when a valid refresh cookie exists and204 No Contentwhen no resumable session exists, so first-load logged-out state does not depend on a visible refresh 401. - The web app attempts one refresh on protected 401s, then emits a single auth-expired flow: toast, session clear, redirect to
/login, and resume to the original in-app route after successful sign-in.
Transport Security
- Production nginx is an HTTP origin on port 8080, not a public TLS endpoint
- Public HTTPS and HSTS are enforced by the authenticated outer gateway
Securecookie flag enforced (disable only with COOKIE_INSECURE=1 in local dev)
Rate Limiting
- Riviamigo applies class-specific auth, read, write, and heavy-read limits
- The authenticated outer gateway must apply its own client-facing limits
- The internal origin does not trust arbitrary forwarded client-IP headers
Headers
X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: no-referrerContent-Security-Policy: default-src 'self'; ...
Database
- PostgreSQL accessible only on internal Docker network (not exposed to host)
- Parameterized queries via sqlx (compile-time checked)
- Telemetry column names validated against allowlist before interpolation
- Rivian vehicle credentials are encrypted with age before durable storage
Secret Storage
- Durable Rivian credential bundles are encrypted before storage in
riviamigo.vehicle_credentials - Short-lived connect / OTP staging data should stay encrypted at rest in Redis and Redis should remain internal-only
- Production generates
AGE_ENCRYPTION_KEY,JWT_SECRET, andJWT_PUBLIC_KEYon first start and persists them in PostgreSQL; externally managed overrides must supply all three together
Audit Logging
- Security events (login success/failure, key operations) logged to
riviamigo.security_events - Structured
[riviamigo][LEVEL]key-value logs are written to stdout/stderr; Docker supplies the outer timestamp. The production wrapper normalizes Nginx error lines into the same shape.
Dependencies
- Weekly automated dependency audits via Dependabot
cargo audit --deny warningsin CIpnpm audit --prod --audit-level=highin CI- Semgrep SAST is blocking on trusted branches and same-repository pull requests; fork pull requests use a separate secret-free blocking scan.
- Fixable critical and high Trivy findings are blocking after the unified production image builds; unfixed base-image findings remain visible for review and base-digest refreshes.
- Workflow actions are pinned to reviewed commit SHAs.
Release Images
- Standard Compose pulls one public unified image from GitHub Container Registry; source builds use the explicit build overlay only.
- Stable images use immutable Calendar Version tags and provenance attestations;
latestis a moving convenience tag, not a reproducible deployment identifier. - Container images are published only by intentional release workflows from validated
maintags or the currentdevpre-release candidate. Stable and pre-release image tags and digests must be treated as release artifacts. - See the release images runbook for package visibility, tag protection, and recovery requirements.
Production Checklist
-
COOKIE_INSECUREis NOT set -
POSTGRES_PASSWORDchanged from default -
REDIS_PASSWORDis strong and Redis is not host-published - Generated application keys are protected by database backups, or all three explicit key overrides are stored safely
-
ALLOWED_ORIGINSset to exact frontend domain(s) - An authenticated tunnel or identity-aware reverse proxy terminates public HTTPS
- Host firewall rules restrict direct access to port 8080
- Redis is reachable only on a private/internal network
- Firewall blocks API, PostgreSQL, Redis, and origin ports from external access
-
IMAGE_TAGis pinned to an exact Calendar Version when repeatability matters