FrontierJS

The loop

A framework is the easy part.

Most of a developer's life with a stack is not writing the app. It is scaffolding, running, migrating, deploying, watching, and eventually extending it — usually across six tools that have never heard of each other. FrontierJS treats that whole loop as one surface, with one vocabulary, and the same nouns at every stop.

↻  and extending brings you back to the schema

fli project:new my-app  ✓ Junction API + Sierra/Mesa UI  ✓ db/schema.lite seeded  ✓ auth wired (optional) fli make:model Lead  ✓ appended to db/schema.lite  ✓ api/src/services/leads.ts  ✓ src/routes/leads/ # make:model, :service, :resource, :route,# :component, :schema, :command, :scaffold

Start

shipped

One command puts a Junction API, a Sierra/Mesa UI and a seeded schema on disk — then generators keep appending to the same seed rather than to a pile of boilerplate.

make:modelappends to the schema and optionally scaffolds the service and resource with it
One seedgenerators write into db/schema.lite, so the file stays the single source
clilitestonejunctionsierra
fli ports:claim  claimed port session for my-app  api  → http://localhost:3200  web  → http://localhost:5173 fli db:studio  browse and edit rows in the browser litestone types ./types.d.ts  Lead, LeadCreate, LeadUpdate written

Build

shipped

A port broker claims a session per project, so two apps on one machine stop fighting over 3000. Litestone Studio gives you the data; typegen keeps the editor honest.

Port brokera claimed session per project — no more "address already in use" across repos
Litestone Studioa local web UI over the live database
DevtoolsSierra ships an in-page overlay; job and request telemetry feeds it
clilitestonesierramesa
fli db:migrate  diffing db/schema.lite against app.db  + ALTER TABLE leads ADD COLUMN company TEXT  ✓ migrations/0002_company.sql applied fli db:status  ✓ live database matches the schema bun run test  createTestApp() + request(app).post(…) fli deploy:doctor  is the target host actually ready?

Verify

shipped

Migrations are diffed, not written — and the same command can tell you whether the live database has drifted from the file. The test kit boots a real app rather than a mock.

Drift is checkabledb:status compares the live database to the schema, so drift is a command rather than a surprise
Real apps in testscreateTestApp, request(), createStubAuth — the hook pipeline runs for real
clilitestonejunction
fli env:set RESEND_API_KEY=… --env production fli deploy  branch main → environment production  build · upload · migrate · cut over  ✓ release 2026-08-02T14:22 live fli deploy:status  containers, web release, disk, last deploy fli deploy:rollback  ✓ web and API back to the previous release

Ship

shipped

Deploy over SSH, with the environment inferred from the git branch you are on. Releases are versioned, so rolling back is a command and not an incident.

Branch → environmentthe deploy target is inferred rather than remembered
Rollbackweb and API return to the previous release together
TargetsSSH hosts today, with CapRover and Cloudflare command groups alongside
cli
curl https://my-app.com/health  { "status": "ok", "uptime": 84213 } curl https://my-app.com/metrics  { "requests": …,    "jobs":     { … },   ← caravan registered this    "conduit":  { … } }  ← and conduit this curl https://my-app.com/manifest  migrations applied, pending, drift fli deploy:logs --followfli db:backup

Operate

shipped

Every app answers the same three questions the same way — because the endpoints are contributed by plugins rather than written per app. This is the surface Basecamp is built on.

Extensible metricsa plugin registers a provider and its stats appear — no per-app wiring
Manifestmigration state is served, so drift is observable in production
NextBasecamp reads exactly these endpoints — see below
clijunctioncaravanconduit
fli make:command reports:weekly  ✓ commands/reports/weekly.md # ---# title: reports:weekly# description: Email the weekly rollup# flags: { since: { type: string } }# ---# <script> … </script> fli reports:weekly --since 7d # then back to step 01: add a model,# and the whole loop runs again.

Extend

shipped

A CLI command is a markdown file — frontmatter for flags, body for the script. So extending the tooling uses the same skill as documenting it, and the two cannot drift apart.

Commands are documentsthe help text and the implementation are the same file
App extensionplugins attach to Junction; providers swap implementations behind a contract
Then it loopsa new model restarts the cycle at Start
clijunction

The control plane

Basecamp — where you watch your stuff

Every app above already answers the questions an operator asks. Basecamp is the FrontierJS app that asks them, for every app you run — and it is built with FrontierJS, which is the point. If the framework cannot comfortably build its own control plane, it is not ready for yours.

The plumbing already exists

These endpoints ship today, on every app, and they are what Basecamp reads — it assembles surfaces rather than inventing them.

/healthliveness, from health
/metricsplugin-contributed — caravan and conduit register their own providers
/manifestmigration state, from litestone status()
/openapi.jsonthe full API surface, derived

What it shows

One place for the things currently spread across a terminal, a dashboard and an SSH session.

Releaseswhat is deployed where, and the roll-back button
Migrationsschema drift between your file and the live database
Jobscaravan queues, failures, retries, cron
Audit@@trail(audit) trails, per model
Egressevery conduit target — the complete outbound surface
Tenantsdb-per-tenant registries and their status

Why it is the honest test

Basecamp is multi-tenant, realtime, permission-heavy and admin-shaped — the exact application profile this framework claims to be good at. Building it in FrontierJS means every rough edge in gates, policies, jobs and channels gets found by the people who wrote them, before it is found by you.

The design-system package has already been through this: its first consuming app surfaced eight shipped bugs on day one. That is what dogfooding is for.

Beyond the browser tab

One component set, several places to put it

A Mesa component already renders to HTML, to email, to an isolated fragment, or to a JS module. The remaining question is not can it render — it is which shells the router knows how to build for.

TargetWhat it producesState
spaThe default Sierra build — file-tree routing, client routershipped
staticPrerendered HTML for routes declaring render: staticshipped
emailCSS inlined into attributes, plain-text fallback, subject extractionshipped
extensionBrowser-extension container — Mesa UI plus a service-worker relay to Junctionjetty, pre-production
widgetEmbeddable island for a page you do not ownconfig shape, no build loop
desktop · mobileThe same routes in a native shelldirection only

The structural bet

These should be modes of one build, not separate packages. Today the extension container is its own package, and it pays for that with a hand-copied resources layer that has already drifted from Sierra's. Folding it onto the target axis is what makes desktop and mobile an entry on a list rather than a third copy of the same code — and it is tracked as such rather than quietly hoped for.

The through-line

Three file types. That is the whole surface.

The reason the loop feels like one tool is that there is almost nothing to learn between its stops. Every artifact you author is one of three things, and each is readable by a person who has never seen this framework.

.lite — the seed

Models, rules, gates, policies, indexes, audit. Everything in the Data realm and everything derived from it.

model Lead {  email String @email  @@gate(0.4.4.5)}

.mesa — the component

A page, a component, an email, a widget. Markdown works as source too, so content-shaped pages stay content-shaped.

<script> … </script>{#each leads.data as l}  <tr>{l.name}</tr>{/each}

.md — the command

A CLI command is a markdown file: frontmatter declares flags and examples, the body is the script. Documentation and implementation cannot drift apart, because they are the same file.

---title: reports:weeklyflags: { since: … }---<script> … </script>

Where this is honest about itself

The loop above is real and runs today — 186 commands across 24 groups, a deploy pipeline with rollback, a port broker, Litestone Studio. Basecamp does not exist yet. Desktop and mobile targets do not exist yet. Sierra does not yet derive forms from a schema, which is the largest single gap in the framework. All three are tracked in the open rather than implied by omission, and the project is alpha: the ideas are settled, the surface still moves.