The big idea
Voyant is a set of headless domain modules that you compose into an application. Each module owns one slice of the travel business, such as catalog, bookings, or finance. A module brings its own database schema, services, and HTTP routes, plus an optional React package for its UI. You pick the modules you need, wire them together with links, and deploy the result as a single application per organization. The platform keeps a strict boundary:- Packages hold reusable business logic, schemas, services, routes, and adapters. They are framework-agnostic.
- Starters and app shells own UI, auth wiring, deployment shape, and runtime configuration.
The stack
Voyant runs as a resident Node process against Postgres. The deployable is a container image:
Core packages do not depend on any of these choices. They expose plain services and an HTTP contract, so you can host them differently if you need to.
Modules
A module is a package such as@voyant-travel/bookings or @voyant-travel/catalog. Each one is self-contained:
- A Drizzle schema for its tables.
- Domain services that enforce the rules of that subdomain.
- HTTP routes exposing those services over the API.
- A matching
-reactpackage (for example@voyant-travel/bookings-react) with hooks, clients, query keys, and reusable UI. - A contracts package (for example
@voyant-travel/bookings-contracts) describing the wire types shared between server and client.
Links
Modules stay decoupled, but real travel data is relational: a person belongs to organizations, a booking references a quote, a product references a place. Voyant models these cross-module relationships with links. A link is a declarative relationship between an entity in one module and an entity in another. You declare it once and the platform can emit the link table DDL for you:voyant db sync-links command emits the SQL or a generated Drizzle schema for all declared links.
Transport and contracts
Every module exposes its services over HTTP using Hono. The wire shape of those routes lives in a dedicated-contracts package, so the server implementation and the client SDK share one source of truth for request and response types. The -react packages consume those contracts to give you typed hooks instead of hand-written fetch calls.
This contract-first approach is the same idea behind the public SDKs: a typed client generated against a stable API surface.
Jobs and subscribers
Subscribers react to domain events. Package-owned jobs handle scheduled or wakeable background work and call the same domain services as routes and subscribers. The host supplies cadence, leases, retries, and health while durable domain records remain authoritative. Selecting a package invoyant.config.ts selects its closed job inventory. See Jobs.
Surfaces: storefront and admin
Two surfaces sit on top of the modules, and they are not the same shape:- The Public API is the customer-facing HTTP contract under
/v1/public: discovery, product detail, checkout, and self-service. The platform owns the contract; you own the frontend that consumes it, whether that is a Theme, the managed booking engine, or something you host yourself. - The admin is the staff-facing operations console: managing quotes, bookings, inventory, finance, and the rest of the back office. It ships as a ready-made React surface assembled from the domain modules and their React packages.
The deployment and security boundary
Voyant’s tenancy model is deliberately simple: one Postgres database and one runtime per organization. Isolation is enforced at the deployment boundary, not by in-process middleware that filters rows by tenant id. This is a foundational decision. It removes a whole class of cross-tenant data-leak bugs, keeps queries simple, and makes each customer’s data physically separate. When you deploy to Voyant, the platform provisions and manages that per-organization runtime and database for you.Where to go next
Glossary
The shared travel vocabulary every module uses.
Project structure
What a scaffolded Voyant project actually contains.
Module catalog
Every domain module and what it owns.
Jobs
Understand package-owned background work.