Carrier is a language and toolchain for defining enterprise services in a form that is compact, structured, and directly connected to runtime behavior. It gives teams a way to describe the core shape of a service without scattering that shape across controllers, ORM classes, validation schemas, middleware, migration files, and handwritten API documentation.
At a glance, a Carrier service is built from a small set of architectural constructs:
- service defines the application boundary
- model defines persistent business data
- type defines stable request and response contracts
- fn defines pure helper logic
- action defines reusable business behavior
- route and crud define HTTP-facing behavior
- policy defines access and visibility rules
- healthcare.questionnaire defines typed questionnaire-style inputs for healthcare workflows
These constructs are not just syntax. They are architectural signals. They tell developers, reviewers, platform teams, and enterprise architects what the system is, what it exposes, what it protects, and how it is expected to evolve.
A Real Service in a Page
The clearest way to see Carrier is to read one. The fragment below is the entry file for the booking-service example in the reference repository. It declares a service, an authentication scheme, and an explicit import graph. There is no framework wiring.
service BookingService {openapi {title: "Booking Service API"version: "0.9.0"} server {host: env("HOST", "0.0.0.0")port: env_int("PORT", 4200)}} auth jwt StaffAuth {issuer: env("JWT_ISSUER", "carrier")audience: env("JWT_AUDIENCE", "carrier-users")secret: env("JWT_SECRET", "local-dev-secret")roles_claim: "roles"} import "./00_models/bookings"import "./00_models/slots"import "./10_types/common"import "./10_types/notifications"import "./15_async/jobs"import "./20_actions/notification_actions"import "./20_actions/slot_actions"import "./30_routes/slot_routes"Even before reading the imported files, an architect can see the shape of this service: what it is, how identity is established, and how it is composed from sorted layers (models, types, async, actions, routes). The numeric prefixes are not cosmetic — they keep the project sorted in dependency order.
Services, Models, Actions, Routes, and Policies
A Carrier project starts with a service. The service declaration names the application boundary and establishes the unit being built. In enterprise terms, this is where the implementation begins to align with a bounded context, capability, or product-owned service.
Within that boundary, model declarations describe persistent data. Models are central to schema design, migration planning, reporting impact, and data governance. A model is not just an implementation convenience; it is a declaration that the service owns a particular kind of information.
type declarations define structured values used for request bodies, response bodies, operational envelopes, and reusable data shapes. action declarations hold business behavior — meaningful domain work, especially when reusable, transactional, or auth-aware. route and crud declarations define the HTTP surface. policy declarations express access and visibility rules.
THE PATTERN AT THE HEART OF CARRIER
route → action → transaction
The route owns the HTTP edge. The action owns the business operation. The transaction owns the commit and rollback boundary.
When this pattern is followed, services stay readable, testable, and governable at scale.
API-First and Schema-Aware Development
Carrier is well suited to API-first development because the API shape is part of the source, not an afterthought.
In many conventional stacks, API documentation is either handwritten or generated from framework-specific annotations. Both approaches drift. Carrier's approach is to make route and type declarations central enough that API documentation can be generated directly from the service definition:
carrier openapi > openapi.json
For enterprise architects and platform teams, OpenAPI is not just documentation. It is a governance artifact. It can feed API catalogs, contract review, client generation, security scanning, and compatibility checks.
Carrier is also schema-aware. Models and related declarations affect the database schema, and schema changes can be managed through Carrier's migration workflow:
carrier migrate generate
This connects source-level data modeling to database evolution. Instead of treating schema as a separate manual artifact, Carrier keeps schema impact close to the service definition.
From Carrier Source to Runtime Targets
Carrier source files live under src/, usually rooted at src/main.carrier. When main.carrier exists, Carrier follows the transitive import graph from that file. This gives large projects a controlled way to organize code across folders and modules while keeping the build graph explicit.
During development, the Node target is commonly used:
carrier build --target node
Carrier may support other runtime targets, but enterprise teams should be explicit about which targets are part of their delivery pipeline. The recommended development verification is carrier check followed by carrier build --target node. The Rust target or other heavier targets should be reserved for cases where they are explicitly required.
GENERATED ARTIFACTS ARE OUTPUTS, NOT SOURCE
Carrier writes generated artifacts under .carrier/. The most important governance artifact is .carrier/manifest.json.
Enterprise teams should fix Carrier source under src/, not hand-edit generated files. The Carrier source is the architectural asset; generated artifacts are evidence.
Why Carrier Is Made for the Age of Automation
There is one more reason Carrier matters now, and it deserves to be said directly: Carrier is built for the age of AI-assisted software delivery.
The promise of AI code generation has always been speed. The reality, in conventional stacks, is inconsistency at speed. The same business request produces a controller in one project, a service class in another, a procedure in a third. Field names drift. Validation drifts. Authorization drifts. Each generated codebase needs its own audit before it can be trusted, and the architect's promise of consistency is the first thing to die.
Carrier's narrow construct vocabulary changes the shape of that problem. There are only so many ways to declare a service, a model, an action, a route, or a policy. The compiler rejects the rest. Once a domain is captured — entities, contracts, roles, tenants, regulations, workflows — an AI agent operating within Carrier's rails will produce the same well-shaped service every time, with the same OpenAPI, the same migrations, the same manifest. The agent cannot wander, because there is nowhere to wander to.
Best-practice by default. Tenant scoping, named contracts, thin routes, and explicit policies are the path of least resistance, not optional discipline.
The result is the rare combination an enterprise actually needs: the velocity of automation with the consistency of design. Architects describe the domain. Agents produce the source. The compiler produces the evidence. Reviewers inspect a small surface that is the same every time. This is not luck. It is the language doing what it was designed to do.