Carrier
Architecture

.carrier → lexer → parser → AST → semantic → IR → rust → cargo → binary.

Carrier keeps a real compiler pipeline. Rust is the backend, not the language you write. Each stage lives in its own crate with a single responsibility.

Pipeline

Stages and responsibilities

01
.carrier
source
02
lexer
tokens
03
parser
AST
04
semantic
types · validation
05
IR
lowering
06
codegen
rust
07
cargo / rustc
build
08
service
native binary
crates/carrier-*.carrier/build/<binary>
Crates

Eight crates. One job each.

The compiler workspace is a plain Cargo workspace. Reading the code is the fastest way to understand how Carrier behaves.

carrier-lexerTokenizer

Converts .carrier source into tokens with spans. Handles comments, doc comments, string literals, identifiers, and attribute annotations.

carrier-parserParser

Produces the AST for declarations, statements, and expressions. Preserves spans for diagnostic-quality error messages.

carrier-astAST nodes

Span-carrying syntax tree. The canonical shape every later stage consumes.

carrier-semanticSemantic analysis

Name resolution, type checking, CRUD and auth validation, policy checks, query defaults, idempotency scope rules, built-in call signatures.

carrier-irIntermediate form

Lowers the semantic model into a codegen-oriented program. Decouples language surface from generation.

carrier-codegen-rustCode generation

Generates a standalone Rust service project, plus OpenAPI, manifest, and migration artifacts.

carrier-runtimeRuntime helpers

The internal runtime depended on by generated services: auth, Postgres, cache, jobs, schedules, events, idempotency, outbound HTTP.

carriercCompiler CLI

Project discovery, generation, build orchestration, dev watch, OpenAPI export, and migration commands.

Runtime

What the generated service gets for free.

Generated Rust services link against carrier-runtime. You never write against it directly — it powers the constructs you declare in .carrier source.

Async server
Tokio async runtime. Axum routing. Request body size limits. Graceful shutdown.
Standard endpoints
/health · /ready · /openapi.json · /docs.
Auth
JWT issue/verify, role enforcement, auth.register, auth.login, auth.current_user.
Postgres
Connection pooling, bootstrap database creation, generated migration application.
Policy session context
Sets carrier.current_roles and carrier.current_tenant per request, before any DB work.
Idempotency
Persistent response replay keyed by scope + Idempotency-Key. Scope: {METHOD} {PATH}:{user.id|public}.
Cache
Redis when REDIS_URL is healthy, otherwise an in-memory fallback. Keys are tenant-scoped by the runtime.
Jobs · schedules · events
Durable Postgres-backed job queue, schedule poller, and events table. Audit records persisted across generated CRUD and explicit calls.
Outbound HTTP
JSON HTTP helpers backing client declarations.
Artifacts

Every build emits the same shape

Deterministic, machine-readable outputs — not debug artifacts. Tools can treat these as the ground truth of a Carrier project.

.carrier/
  • .carrier/generated/generated Rust service project
  • .carrier/generated/migrations/SQL migrations & schema snapshots
  • .carrier/manifest.jsondeclarations · routes · models · policies · jobs
  • .carrier/build/<binary>native executable
At runtime
  • /openapi.jsonserved by the binary
  • /docsAPI explorer UI
  • /health · /readyliveness · readiness
  • JSON logs to stdoutstructured metadata for downstream collectors
Data access ladder

Escape hatches are intentional, not afterthoughts

Carrier defines a clear sequence for how you reach for Postgres. Most code lives at the top. Escape hatches are explicit and typed.

  1. 1
    Generated CRUD
    standard list · get · create · update · delete · restore
  2. 2
    Model helpers
    Model.get, Model.search, count_by_*, exists_by_*, get_by_*, list_by_*
  3. 3
    Actions + transactions
    auth context, row locks via get_for_update, cache, jobs, audit
  4. 4
    Raw SQL
    sql.list_as, sql.one_as, sql.scalar_as, sql.exec — typed results
  5. 5
    DB functions
    db.call_as, db.fn_one_as, db.fn_scalar_as — stored functions & builtins
Honest

Current limits

Carrier ships what the compiler actually implements. These are the places where it deliberately stops short today.

Imports
Validated metadata. Every file under src/ still compiles together — imports are not yet selective loaders.
Migration diffing
Structural. Renames are not inferred automatically. Migrations are explicit SQL artifacts under .carrier/generated/migrations/.
Scheduled payloads
Scheduled jobs currently enqueue the target job with an empty JSON object payload. Use Json payload types so the job body handles an empty input.
transaction + return
return from inside a transaction { } block is not supported yet. Do work in the block; return after.
Telemetry
JSON logs, events, audit records, and job/schedule state are durable. Native Sentry, OpenTelemetry, Datadog, or ELK integrations are intentionally not built in.
Generated Rust
Generated Rust can still produce warnings in some app shapes. The service builds and runs; lint cleanliness is progressive.