← Back to projects
Summary

Flying clubs and FBOs (fixed-base operators) still run on dated software like MyFBO. Azimuth is a modern replacement covering the full operational core: members and their pilot credentials, the aircraft fleet and its maintenance, scheduling and dispatch, flight logging, and member billing — the loop where a reservation becomes a checked-out aircraft, becomes a logged flight, becomes a posted charge on a member's ledger. It's built multi-tenant from day one (every domain table is org-scoped, so adding a second club needs no schema change) and access is capability-based, not role-based. I designed and built the whole thing solo: schema, server actions, auth, RBAC, and UI.

Stack

What it's built with.

Application

  • Next.js 16 (App Router)
  • React 19 Server Actions
  • TypeScript
  • Tailwind CSS v4

Data & Auth

  • PostgreSQL
  • Drizzle ORM (migrations)
  • Auth.js v5 (JWT)
  • Multi-Tenant org scoping

Domain & Ops

  • Capability-Based RBAC
  • Scheduling & Conflict Detection
  • Maintenance / Currency Tracking
  • Vercel Cron Automation
Details

How it works.

The operational loop

Running a flying club is a chain of dependent events, and the value of the software is in keeping that chain consistent. A member books a reservation; a dispatcher checks the aircraft out to them; the flight is flown and logged, which advances the Hobbs and Tach meters; logging the flight auto-posts the rental and instruction charges to the member's ledger and closes the reservation. Azimuth models that whole loop as connected modules rather than disconnected CRUD screens.

The maintenance side runs in parallel: each aircraft tracks inspection due-points (annual, 100-hour, etc.) against its current Hobbs/Tach, and an aircraft that goes out of currency or picks up a grounding squawk is automatically removed from the dispatchable fleet — so the scheduling module can't book a plane that shouldn't fly.

Multi-tenant and capability-based from day one

Even though it runs a single club today, Azimuth is built multi-tenant from the schema up. A user is a global identity (one login), and a membership joins that user to an organization with a role; all club data references a membership, not a user, and every domain table carries an org_id that scopes every query. Adding a second club later is an org switcher that re-issues the session token — no schema migration.

Authorization is capability-based, not role-based. Roles (admin, dispatcher, instructor, member, student, mechanic) are just bundles of fine-grained capabilities, and every server action and UI affordance checks the capability — dispatch:write, maintenance:manage, schedule:manage_others — rather than testing the role directly. That keeps permission logic in one place and makes it safe to add a new role or re-scope an existing one without hunting through the codebase.

Modern stack, real automation

Azimuth is built on Next.js 16 with React 19 Server Components and Server Actions as the primary data path, Auth.js v5 for credentials auth with edge-safe middleware, and a Drizzle/Postgres backend with a versioned migration history (44 and counting). It deploys to Vercel against hosted Postgres (Supabase / Neon), with TLS auto-enabled.

Beyond request/response, it runs real operational automation: a Vercel Cron job hits a secured endpoint every 15 minutes to send time-based reminders, and it fails closed without its bearer secret. Pilot credential documents — medicals, certificates, endorsements — live in a private storage bucket and are served only through short-lived signed URLs, while low-sensitivity profile avatars use a public bucket with graceful fallback to initials when storage isn't configured.

Highlights

The things I'm proudest of.

  • Built the full operational loop across six modules — Members & roles, Fleet & maintenance, Scheduling & dispatch, Flight logging, and Billing — where logging a flight advances Hobbs/Tach meters, auto-posts rental + instruction charges to the member's ledger, and closes the reservation in one transaction.
  • Designed a ~60-table PostgreSQL schema with Drizzle ORM (44 migrations), multi-tenant from day one: a global users identity, memberships joining a user to an organization with a role, and an org_id on every domain table so all queries are org-scoped — a second club requires no schema change.
  • Implemented capability-based access control (lib/rbac.ts): six roles (admin, dispatcher, instructor, member, student, mechanic) map to fine-grained capabilities (e.g. dispatch:write, maintenance:manage, schedule:manage_others), and every server action + UI surface checks capabilities, not raw roles.
  • Built on Next.js 16 (App Router, React 19 Server Components + Server Actions) with Auth.js v5 (credentials, JWT sessions, edge-safe middleware), reservation conflict detection, inspection-due tracking (annual / 100-hr) with aircraft grounding, and a per-member A/R ledger.
  • Wired operational automation: a Vercel Cron job hits a secured endpoint every 15 minutes to run time-based reminders (upcoming reservations, close-your-flight-plan nudges), failing closed without its bearer secret; credential scans (medicals, certificates) are served via short-lived signed URLs from a private Supabase Storage bucket.
Screenshots

What it looks like.

Azimuth dashboard — at-a-glance club status with aircraft availability, reservations, maintenance alerts, A/R, and live METAR weather
Dashboard — club status at a glance, including live METAR weather and maintenance due-points.
Azimuth scheduling — day-view reservation calendar
Scheduling — day-view calendar with conflict-detected reservations.
Azimuth dispatch — aircraft check-out / check-in board
Dispatch — the check-out / check-in board.
Azimuth flight logging — Hobbs/Tach entry that posts charges
Flight logging — advances meters and auto-posts charges.
Azimuth fleet & maintenance — aircraft, rates, inspection due tracking
Fleet & maintenance — inspection due-points and grounding.
Azimuth members roster — profiles, roles, certificates, currency
Members — roles, credentials, and currency tracking.
Azimuth billing — per-member ledger and club-wide A/R
Billing — per-member ledger and club-wide accounts receivable.