Multi-tenant Virtual Airline Live Dispatch & ACARS tool.
For the full product guide, architecture, API reference, operating runbooks, and current limitations, see the project Wiki.
- One tenant = one Virtual Airline (first: vSAS)
- API: Hono + TypeScript on Vercel Services (
apps/api) - Web: Next.js pilot portal + dispatcher suite (
apps/web) - ACARS: Hoppie's ACARS with tenant-scoped encrypted ground-station credentials
apps/api — Hono REST API (/api/v1)
apps/web — Next.js frontend (path tenancy at /vsas)
- Node.js 24+ (26.7 recommended locally; Vercel currently builds on 24.x)
- pnpm 11.21+
- Neon Postgres + Clerk (Vercel Marketplace)
pnpm install
cp .env.example apps/api/.env
# configure apps/web/.env.local from apps/web/.env.example
# fill DATABASE_URL, CLERK_SECRET_KEY, etc.
DATABASE_URL='postgresql://...' pnpm db:push
pnpm dev # web :3000 + API :3001Open http://localhost:3000/vsas. Health check: GET http://localhost:3001/health.
The API reference is available through
Swagger UI,
ReDoc, and the raw
OpenAPI document.
The public legal pages are available at /impressum and /privacy. Configure
all required LEGAL_* values from apps/web/.env.example before production;
production requests fail closed rather than publish placeholder operator data.
See docs/privacy-compliance.md for the deployment
and operating checklist.
Guarded retention, export, legal-hold, and verified request procedures are in
docs/privacy-operations.md; the software does
not by itself certify GDPR compliance.
Clerk Organizations must use optional membership, organization slugs must be
enabled, end-user organization creation and Verified Domain enrollment must be
disabled, and the Primary Role Set must contain org:admin, org:dispatcher,
and org:pilot. Production uses Clerk Waitlist mode with email enabled;
self-service new users enter through /vsas/waitlist. Clerk Dashboard approval
emails open the Clerk Account Portal sign-up flow; configure its sign-up
fallback redirect to /vsas/join, where VA Dispatch handles tenant-role
approval. The tenant-branded /vsas/sign-up route remains available for direct
invitation flows that redirect into the application. This flow does not require
Clerk's Invite-only (restricted) sign-up mode or the paid allowlist. The vSAS
Clerk organization slug is
vsas; set VSAS_CLERK_ORG_ID to its immutable ID. Tenant admins then manage
the separate pilot/dispatcher application approval, roles, removal, and tenant
membership settings inside VA Dispatch. See the Wiki configuration reference
for the one-time global Clerk setup.
| Role | Capabilities |
|---|---|
| Pilot | Request schedule, accept / decline / cancel flights |
| Dispatcher | Fulfill requests, flight board, ACARS send/receive |
| Admin | Tenant settings, invitations, approvals, roles/removal, ACARS config |
Production ACARS uses Hoppie exclusively. An administrator opens
/:slug/settings/organization, enters the VA ground-station callsign and Hoppie
logon code, and runs a connection test. The code is encrypted with
TENANT_SECRETS_KEY and is never returned by the API. Until that succeeds,
ACARS is explicitly unconfigured and outbound sends fail safely.
Every member, including dispatchers who also fly, saves their aircraft callsign
under /:slug/settings. Their personal Hoppie logon remains in their simulator
ACARS client; this application never asks for or stores it. The member and VA
ground-station Hoppie accounts must use the same network affiliation.
Hoppie registration is free and self-service; no separate API approval is required: https://www.hoppie.nl/acars/system/register.html.
Local development and automated tests can use the internal DB-backed adapter
with ACARS_PROVIDER=mock. Its POST /api/v1/acars/simulate fixture is never
enabled in production, even if a stale production variable says mock.
| Layer | Choice | Idle behavior |
|---|---|---|
| Postgres | Neon Free / scale-to-zero | Suspends when unused → $0 idle |
| Auth | Clerk + B2B Authentication | Production custom roles require the add-on |
| API | Vercel Fluid Compute | Active CPU pricing; no charge when not invoked |
| ACARS cron | Every 1 min, configured tenants only | Required for a live Hoppie ground station |
Do not add Redis/queues for v1 — they would add idle or minimum footprint.
The production poll cron selects only tenants with an encrypted Hoppie logon.
Set ACARS_PROVIDER=hoppie in production for an explicit, self-documenting
configuration; the runtime enforces Hoppie there regardless. Vercel Pro is
required for the one-minute schedule; slower cron schedules delay inbound
messages but do not affect outbound sends.
Vercel Services (vercel.ts): web + api, rewrites /api/* → API. If the
Services private beta is unavailable, deploy apps/web and apps/api as two
projects and set API_ORIGIN on the web project to the API project's public
origin; the Next.js rewrite keeps browser calls on same-origin /api/*.
Normal releases are automatic: CI validates an internal pull request before a
separate credentialed workflow deploys its preview; a successful main CI run
deploys Production. Builds do not modify the database; /api/ready must confirm
that the selected environment already has the required tenant/membership
schema. See
docs/maintainer-setup.md for the one-time GitHub
and Vercel configuration.
Initial local provisioning remains manual:
vercel login
vercel link
# Prefer Neon Free / Launch (autosuspend). Avoid always-on compute plans.
vercel integration add neon
vercel integration add clerk
vercel env pull apps/api/.env.local --yes
# copy DATABASE_URL + CLERK_* into apps/api/.env for local
DATABASE_URL='postgresql://...' pnpm db:push| Script | Description |
|---|---|
pnpm dev:api |
Run API locally |
pnpm dev:web |
Run the Next.js app locally |
pnpm dev |
Run API and web together |
pnpm test:api |
API unit and isolation tests |
pnpm test:web |
Frontend unit and component tests |
pnpm test:coverage |
Full-source tests and coverage |
pnpm security:audit |
High-severity dependency audit |
pnpm --filter @va-dispatch/web test:e2e |
Deterministic browser smoke tests |
pnpm test:e2e:integrated |
Real web/API/PostgreSQL journeys |
pnpm db:push |
Apply the canonical schema to a fresh database |
pnpm typecheck |
TypeScript check |
This Shiftbloom project is pre-production and uses schema.ts as its canonical
database definition. Recreate an empty database and run db:push; never point
that command at a database containing user data. If the product later becomes
long-lived production software, define a new data-evolution policy first.
The integrated browser suite requires a separately confirmed disposable
PostgreSQL database. See
docs/integrated-e2e.md for its guarded local command,
provider isolation, and deployed-browser acceptance checklist.
Contributions are welcome. Read CONTRIBUTING.md for setup,
quality checks, and pull request expectations. Participation is governed by the
Code of Conduct, and support guidance is available in
SUPPORT.md. Repository administrators should also complete the
maintainer setup checklist after these files land on
main.
Do not report vulnerabilities in public issues. Follow the private reporting process in SECURITY.md.
Copyright is held by the respective VA Dispatch contributors. The project is
free software licensed under the GNU Affero General Public License version 3
or any later version (AGPL-3.0-or-later). Operators who make a
modified version available over a network must offer its corresponding source
to users as required by the license. Hosted forks must set
NEXT_PUBLIC_SOURCE_URL to the corresponding source for their deployed version;
the application exposes this link in its legal notice.