# Neutron Documentation (full text)
> The complete Neutron documentation, concatenated for LLM ingestion. Neutron
> is a multi-language full-stack framework backed by Nucleus, a multi-model
> database. The curated link index is at https://neutron.build/llms.txt. Each section below
> is one documentation page; its canonical URL is given under the heading.
---
## Components
Source: https://neutron.build/docs/api/components
API reference for Neutron components.
Components available from `@neutron-build/core`.
## ``
Navigate between pages without a full reload (in app mode).
```tsx
About
```
| Prop | Type | Description |
| :--- | :--- | :--- |
| `to` | `string` | The URL path. |
| `prefetch` | `"intent" \| "render" \| "none"` | When to prefetch data. |
## ``
A special version of `` that knows when it is active.
```tsx
isActive ? "active" : ""}
>
Dashboard
```
## `
```
## Layout `children`
A layout renders its child route via the standard `children` prop — Neutron has no `` component.
```tsx
// _layout.tsx
import type { ComponentChildren } from "preact";
export default function Layout({ children }: { children?: ComponentChildren }) {
return (
{children}
);
}
```
## ``
Renders an interactive component inside a static route.
```tsx
```
## ``
Enables View Transitions API support. Place in the `` of your root layout.
```tsx
```
---
## Content API
Source: https://neutron.build/docs/api/content-api
API reference for content collections.
Available from `@neutron-build/core/content`.
## `defineCollection`
Defines a collection schema in `src/content/config.ts`.
```ts
import { defineCollection, z } from "@neutron-build/core/content";
const blog = defineCollection({
schema: z.object({ /* ... */ })
});
```
## `getCollection`
Retrieves all entries in a collection.
```ts
const posts = await getCollection("blog");
// With filter
const posts = await getCollection("blog", ({ data }) => !data.draft);
```
## `getEntry`
Retrieves a single entry by slug.
```ts
const post = await getEntry("blog", "hello-world");
```
## `z` (Zod)
Re-export of the Zod library for schema definition.
```ts
import { z } from "@neutron-build/core/content";
```
---
## Hooks
Source: https://neutron.build/docs/api/hooks
API reference for Neutron hooks.
Hooks available from `@neutron-build/core`.
## `useLoaderData`
Returns the data returned from the route's `loader`.
```tsx
const data = useLoaderData();
```
## `useActionData`
Returns the data returned from the route's `action`. Undefined if the action hasn't run yet.
```tsx
const data = useActionData();
```
## `useNavigation`
Returns the current navigation state.
```tsx
const navigation = useNavigation();
// navigation.state: "idle" | "loading" | "submitting"
// navigation.formData: FormData (during submission)
```
## `useParams`
Returns an object of dynamic route parameters.
```tsx
const params = useParams();
// params.id
```
## `useRevalidator`
Allows manual revalidation of loaders.
```tsx
const { revalidate, state } = useRevalidator();
```
## Handling errors
There is no `useRouteError` hook. Errors are handled by exporting an `ErrorBoundary` component, which receives the error as a prop:
```tsx
import type { ErrorBoundaryProps } from "@neutron-build/core";
export function ErrorBoundary({ error, reset }: ErrorBoundaryProps) {
return
{error.message}
;
}
```
See [Error Boundaries](/docs/routing/error-boundaries) for details.
> Additional client hooks exported from `@neutron-build/core/client` — `useNavigate`, `useSubmit`, `useSearchParams`, `useBlocker`, `useMatches` — follow the same signatures as their React Router equivalents.
---
## Route Exports
Source: https://neutron.build/docs/api/route-exports
API reference for route modules.
Route files in `src/routes` can export the following named exports.
## `default` (Component)
**Required**. The React/Preact component to render for this route.
```tsx
export default function Page() {
return
Hello
;
}
```
## `loader`
**Optional**. Async function to load data on the server.
```tsx
export async function loader(args: LoaderArgs): Promise;
```
## `action`
**Optional**. Async function to handle non-GET requests.
```tsx
export async function action(args: ActionArgs): Promise;
```
## `config`
**Optional**. Configuration object for the route.
```tsx
export const config = {
mode: "static" | "app" // default: "static"
};
```
## `ErrorBoundary`
**Optional**. Component to render when an error occurs in this route or its children.
```tsx
export function ErrorBoundary() {
return
Something went wrong
;
}
```
## `headers`
**Optional**. Function to set HTTP headers for the response.
```tsx
export function headers({ loaderHeaders, parentHeaders }) {
return {
"Cache-Control": "max-age=3600",
};
}
```
## `head`
**Optional**. Function to set the document `` — title, meta tags, and
`` tags. It receives the route's `data` (plus `params`, `pathname`,
`request`, `context`) and returns an SEO object.
```tsx
export function head({ data }) {
return {
title: data.post.title,
description: data.post.excerpt,
canonical: `https://example.com/blog/${data.post.slug}`,
openGraph: { image: data.post.cover },
// Arbitrary tags: favicon, preconnect, manifest, alternate…
link: { rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
};
}
```
Supported fields: `title`, `titleTemplate` (e.g. `"%s — My Site"`, usually set on
a layout so child routes inherit it), `description`, `canonical`, `keywords`,
`noindex`, `openGraph`, `twitter`, `jsonLd`, `link`, `headScripts`, `htmlAttrs`,
`bodyAttrs`. A layout's `head()` and a route's `head()` merge — the route wins
per field, while `link`, `headScripts`, and `jsonLd` are concatenated.
---
## Server Utilities
Source: https://neutron.build/docs/api/server-utilities
Helpers for server-side code.
Utilities available from `@neutron-build/core`. These should generally only be used in `loader` and `action` functions.
## `redirect`
Throws a redirect response.
```tsx
import { redirect } from "@neutron-build/core";
export async function action() {
throw redirect("/login");
}
```
## Response Helpers
Neutron uses standard [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. You can construct them directly.
```tsx
export async function loader() {
if (!found) {
throw new Response("Not Found", { status: 404 });
}
}
```
## Keeping code server-only
Two conventions keep server code out of the browser bundle:
### `.server.ts` files
Any module whose filename ends in `.server` (e.g. `db.server.ts`,
`auth.server.ts`) is **server-only**. Its imports are stripped from the
client build, so it is safe to use secrets, database drivers, or Node
built-ins there:
```ts
// src/lib/db.server.ts
import { Pool } from "pg";
export const db = new Pool({ connectionString: process.env.DATABASE_URL });
```
```tsx
// src/routes/users.tsx
import { db } from "../lib/db.server.ts"; // stripped from the client bundle
export async function loader() {
return { users: (await db.query("SELECT * FROM users")).rows };
}
```
### Node built-ins in route modules
You can import Node built-ins (`node:fs`, `path`, `crypto`, …) directly in a
route module as long as you only use them in server exports (`loader`,
`action`, HTTP handlers). When Neutron builds the client half of the route it
removes those exports **and** their now-unused built-in imports, so they never
reach the browser:
```tsx
import { readFileSync } from "node:fs"; // removed from the client bundle
export const config = { mode: "app" };
export async function loader() {
return { version: readFileSync("VERSION", "utf8") };
}
export default function Page({ data }) {
return
Version {data.version}
;
}
```
Look-alike packages that are **not** built-ins (`fs-extra`, `path-browserify`)
are left untouched — only real Node built-ins are stripped.
---
## Multi-service applications
Source: https://neutron.build/docs/cli/applications
Run several native services and finite tasks from one neutron.toml. Experimental; macOS and Linux.
**Experimental.** The manifest format may still change. Service supervision and tasks run on macOS and Linux; on Windows only `project check` and `project plan` work.
An `[application]` table in `neutron.toml` describes services that keep running (an API, a web app) and tasks that finish (a build, a test run, a proof check). Each keeps its own native toolchain: Neutron runs the command you declare, as an argument list without a shell, and does not compile or wrap it. Projects without the table keep the single-language `neutron dev` behavior.
```toml
[application]
version = 1
name = "inventory"
[application.services.api]
path = "services/api"
command = ["go", "run", "./cmd/server"]
contract = "neutron/v1" # a Neutron SDK service; port assigned at start
[application.services.web]
path = "apps/web"
command = ["pnpm", "dev"]
ports = [3000]
depends_on = ["api"] # web receives NEUTRON_SERVICE_API_URL
ready = { http = "http://127.0.0.1:3000/", timeout = "30s" }
[application.tasks.test]
path = "services/api"
command = ["go", "test", "./..."]
timeout = "10m"
```
## Commands
| Command | What it does |
|---|---|
| `neutron project check` | Validates the manifest without running anything. |
| `neutron project plan [--json] [--service NAME]` | Shows services, tasks, ports and injected environment keys (never values). |
| `neutron dev [--service NAME]` | Starts the selected service and its dependencies in dependency order, in the foreground. |
| `neutron project run TASK... [--jobs N] [--json]` | Runs tasks and their dependencies; exits nonzero unless all succeed. |
| `neutron project spec --write` or `--check` `[--service NAME]` | Snapshots each Neutron SDK service's OpenAPI document, or fails if a snapshot is out of date. |
## Services
A service starts once each of its `depends_on` services is ready. Readiness is one of `http` (a URL that must return 2xx, no redirects), `tcp` (`host:port` accepting connections), or `path` (an HTTP path on the service's own single port), each with a `timeout`. A service that others depend on must be able to report readiness.
If any service fails to start, fails readiness, or exits, even with status 0, the whole session stops. There is no automatic restart; your tool's own watcher handles reloads.
**Stopping.** Interrupt once: dependents stop before their dependencies, each gets SIGTERM, then SIGKILL after its grace period. Interrupt again to skip the remaining grace. Services also stop if the terminal closes or the `neutron` process itself is killed; each service runs under a small supervisor process that stops its whole process tree when `neutron` goes away.
**Ports.** Declared ports are checked before anything starts, including listeners on `0.0.0.0`, `::` and `::1`, so a stale server cannot pass as the new one.
**Finding other services.** Each service receives `NEUTRON_SERVICE__URL` (for example `NEUTRON_SERVICE_API_URL=http://127.0.0.1:52811`) for every direct dependency with exactly one port. Explicit `env` entries always win.
## Neutron SDK services
`contract = "neutron/v1"` marks a service built on a Neutron SDK that follows the [framework contract](https://github.com/neutron-build/neutron/blob/main/FRAMEWORK_CONTRACT.md). For such a service the coordinator:
- assigns a free loopback port when `ports` is omitted, and passes `NEUTRON_HOST` and `NEUTRON_PORT`,
- treats `GET /health` as readiness unless `ready` says otherwise,
- allows the contract's 30-second drain before SIGKILL,
- reports, once ready, a degraded `/health`, a missing `/openapi.json`, or a spec that is not OpenAPI 3.1. These are warnings; the session keeps running.
The SDK must read `NEUTRON_HOST` and `NEUTRON_PORT` for an assigned port to work. The Go, TypeScript and Python SDKs do so from the current repository; declare `ports` explicitly with older releases.
## Typed clients between services
A Neutron SDK service describes its API at `/openapi.json`. `neutron project spec --write` starts the Neutron SDK services, waits until they are ready, and saves each document canonically as `openapi.json` in the service's directory. Commit that file: it is the reviewed interface, so an API change appears as a diff. `neutron project spec --check` fails when a running service no longer matches its snapshot, which makes it a useful CI step.
Clients are generated from the snapshot by an ordinary task using an existing generator, for example a TypeScript client from a Go API:
```toml
[application.tasks.web-api-types]
path = "apps/web"
command = ["./node_modules/.bin/openapi-typescript", "../../services/api/openapi.json", "--output", "src/api.d.ts"]
timeout = "2m"
[application.tasks.web-typecheck]
path = "apps/web"
command = ["./node_modules/.bin/tsc", "--noEmit"]
depends_on = ["web-api-types"]
timeout = "5m"
```
Renaming a response field in the API then fails `spec --check`, and after `spec --write` fails the typecheck wherever the old field was used.
## Tasks
A task succeeds when its command exits 0. `timeout` is required. `depends_on` may name only other tasks. `outputs` lists paths the task should produce; they appear in the result but are not cached or checked. Native tools keep their own caches.
`project run` is fail-fast: after a failure, timeout or interrupt, nothing new starts, running tasks are stopped, and the rest are reported as `skipped`. `--jobs` bounds concurrency (default: CPU count). With `--json`, a versioned result goes to stdout and task output to stderr:
```json
{
"version": 1,
"tasks": [
{ "name": "build", "status": "succeeded", "exit_code": 0, "duration_ms": 1840, "outputs": ["bin/api"] },
{ "name": "test", "status": "failed", "exit_code": 1, "duration_ms": 3120, "error": "exit status 1" }
]
}
```
Statuses are `succeeded`, `failed`, `timed_out`, `cancelled` and `skipped`. Verification and simulation tools such as `lake build`, `quint verify`, or a Modelica run are ordinary tasks. A passing task means the tool exited 0, not that a proof covers your implementation.
## Limits
- Services and tasks cannot depend on each other yet.
- An assigned port is free when chosen; another process could take it before the service binds it.
- A readiness probe proves something answered on the port, not which process answered.
- Child output is shown as written; secrets printed by a service are not redacted.
Runnable examples: [`cli/examples/application`](https://github.com/neutron-build/neutron/tree/main/cli/examples/application) (plain Go and Node services) and [`cli/examples/neutron-application`](https://github.com/neutron-build/neutron/tree/main/cli/examples/neutron-application) (a Go SDK API and a Neutron TypeScript app with a generated typed client).
---
## Command Reference
Source: https://neutron.build/docs/cli/commands
Every CLI command with flags and examples.
## Project Management
### `neutron new `
Create a new project from templates.
```bash
neutron new my-api --lang python
neutron new my-app --lang typescript
neutron new my-service --lang go
```
**Flags:** `--lang, -l` — Project language (python, typescript, go, rust, zig, julia)
### `neutron init`
Initialize Neutron in an existing project.
```bash
neutron init # auto-detect language
neutron init --lang go # explicit
```
Creates `neutron.toml` and `migrations/` directory.
### `neutron dev`
Start the development server with auto-restart.
```bash
neutron dev
```
Delegates to the language-specific dev server based on auto-detection.
If `neutron.toml` has an `[application]` table, `neutron dev` instead starts its services in dependency order; `--service NAME` selects one service plus its dependencies. See [Multi-service applications](/docs/cli/applications), which also covers `neutron project check`, `plan` and `run`.
Application projects (`neutron.toml` with an `[application]` table) also have `neutron project check`, `plan`, `run` and `spec`; see [Multi-service applications](/docs/cli/applications).
### `neutron generate`
Generate typed models from one database table or every table in a schema.
```bash
neutron generate --table users --lang ts --out src/models/users.ts
neutron generate --all --schema public --lang go --out internal/models/
```
Supported targets are Go, TypeScript (`ts`), Rust, Python, Elixir, and Zig.
**Flags:** `--table`, `--all`, `--schema` (default `public`), `--lang` (`go`, `ts`, `rust`, `python`, `elixir`, `zig`), `--out` (file or directory; `-` for stdout, the default).
## Database
### `neutron db start`
Download Nucleus (if needed) and start a local instance.
```bash
neutron db start # persistent, port 5432
neutron db start --port 5433 # custom port
neutron db start --memory # ephemeral (no persistence)
neutron db start --data-dir ./db # custom data directory
```
### `neutron db stop`
Stop the local Nucleus instance.
```bash
neutron db stop
```
### `neutron db status`
Check database connectivity and version.
```bash
neutron db status
```
### `neutron db reset`
Drop all tables and recreate the database.
```bash
neutron db reset # interactive confirmation
neutron db reset --force # skip confirmation
```
### `neutron db push`
Apply a schema document straight to the database, without migration files, for prototyping.
```bash
neutron db push --schema neutron.schema.json --dry-run
neutron db push --schema neutron.schema.json
```
The plan runs in one transaction, under the migration lock, with one exception: PostgreSQL cannot use an enum value inside the transaction that adds it (SQLSTATE 55P04), so when a plan adds enum values and also changes anything else, push runs two reported transactions. The enum value additions commit first, then the rest of the plan runs in one transaction; `--dry-run` shows the boundary. If the second transaction fails it rolls back as a whole, the added values stay (PostgreSQL cannot remove them), and a re-run converges.
Statements the connected server cannot run are refused at plan time, before anything is applied: changing a generated column's expression needs `ALTER COLUMN ... SET EXPRESSION`, PostgreSQL 17+.
Push refuses a database with migration history unless `--force`, and drops nothing absent from the schema without `--allow-destructive`; neither flag touches `_neutron_*` metadata or extension-owned objects. Column order is informational: a column declared between existing ones is added last, and an order difference plans nothing.
**Flags:** `--schema` (schema document, default `neutron.schema.json`), `--dry-run`, `--force`, `--allow-destructive`, `--rename table.old>table.new` (repeatable; quote it, `>` is a shell redirect), `--timeout` (default 1m).
### `neutron schema`
Work with schema documents (the cross-language contract in `contracts/data/`).
```bash
neutron schema export # run the compiled export module, write neutron.schema.json
neutron schema pull # introspect the database into a document (read-only)
neutron schema check # desired document vs the snapshot chain (offline)
neutron schema check --live # database vs the applied snapshots: drift
neutron schema baseline # record an existing database as the chain root (read-only)
```
**Flags:**
- `schema export` — `--module` (compiled schema module, default `export-schema.mjs` or `[migrations].module`), `--out` (default `neutron.schema.json` or `[migrations].schema`), `--timeout` (default 2m). The module runs as a direct `node` child process, prints one schema document v2 to stdout, and the result is written atomically.
- `schema pull` — `--out` (default `neutron.schema.pulled.json`), `--timeout` (default 30s). A user table with a foreign key into a `_neutron_*` table cannot be described by a schema document: pull refuses and names it.
- `schema check` — `--schema`, `--dir`, `--live`, `--timeout` (default 30s). Exits 1 when the document has pending changes, or with `--live` when the database has drifted.
- `schema baseline` — `--dir`, `--timeout` (default 30s). Holds the migration lock while it reads, so it needs a second connection.
A baseline moves the migrations directory to the snapshot workflow, one way: from then on `neutron migrate` refuses any migration without a snapshot ("the snapshot chain is incomplete"), including files from `neutron migrate create` and from `neutron migrate generate` in live mode. Generate later migrations with `--mode snapshot`, or set `[migrations] snapshots = true`. `schema baseline` refuses to run while a baseline exists, while any migration file is unapplied, and while older history is not adopted and migration files exist (with no migration files it writes the baseline and warns that the history is not adopted); it leaves out the `_neutron_*` tables the CLI manages. A baseline from an earlier CLI that recorded `_neutron_migrations` keeps working: leave it in place, because later snapshots chain to its hash. An earlier CLI also let a baseline cover files that were not applied yet; `schema check --live` passes until such a file is applied, then reports drift. Compare the baseline's `covers` list with the pending files in `neutron migrate status`, and if a covered file is pending, re-baseline once as the upgrade steps in the `@neutron-build/sql` README describe.
## Migrations
### `neutron migrate`
Apply all pending migrations.
```bash
neutron migrate
neutron migrate --dir db/migrations # custom directory
```
**Flags:** `--dir` (default `migrations`), `--timeout` (total budget for the batch, default 1m, `0` for none), `--allow-destructive` (acknowledge data loss: drops, `TRUNCATE`, and similar).
Migrations are `.up.sql` files named `{NNN}_{name}.up.sql`. Each runs in a transaction that also records its history row (version, checksum, owner, format), so a migration is either fully applied or not applied at all.
PostgreSQL cannot use an enum value inside the transaction that adds it (SQLSTATE 55P04). A migration that adds an enum value and uses it (in a view, check, default or cast) therefore rolls back, and the error names the fix: move the `alter type ... add value` statements into their own earlier migration. `neutron migrate generate` writes enum value additions that way (`{NNN}_{name}_enum_values`, then `{NNN+1}_{name}`) whenever the plan also changes anything else.
Before any statement runs, migrations that need a newer PostgreSQL than the connected server are refused: a snapshot plan's recorded `minServerMajor`, or a version-gated statement such as `ALTER COLUMN ... SET EXPRESSION` (changing a generated column's expression, PostgreSQL 17+). `neutron migrate resolve --retry` applies the same check, and `--abort` checks the down SQL it would run.
Runners are serialized across processes with a PostgreSQL session-level advisory lock held on a dedicated connection from the history read through the final apply. Transaction-pooled proxies (for example PgBouncer in transaction mode) are unsupported for migration connections — use a direct or session-pooled connection.
Migration files may contain only these statement kinds: `SELECT` and DML (`INSERT`/`UPDATE`/`DELETE`/`MERGE`), `TRUNCATE`, create/alter/drop of schema objects (including `CREATE INDEX CONCURRENTLY`), and `SET LOCAL`. Anything else is refused before the file runs; no flag bypasses this.
Operational migrations (concurrent indexes on large tables, bounded backfills) can opt into the step journal with a `-- neutron:journaled` line before the first statement. Each step then carries a verification, either a built-in structural postcondition or a declared one:
```sql
-- neutron:journaled
-- neutron:step verify="SELECT count(*) FROM t WHERE col IS NULL" expect="0"
UPDATE t SET col = 0 WHERE col IS NULL AND id <= 500;
```
Verified steps are skipped on apply and on `migrate resolve --retry`, so an interrupted backfill resumes from its checkpoint. Steps accept `lock_timeout=` and `statement_timeout=` annotations.
Applied migrations record a SHA-256 checksum of their SQL; a modified applied migration fails the next run before anything new is applied. `neutron migrate` targets PostgreSQL — for Nucleus databases use the language SDK runners (`go/nucleus`, `@neutron-build/nucleus`), which stay experimental.
### `neutron migrate adopt`
Adopt a legacy migration history into the current protocol, in one transaction.
```bash
neutron migrate adopt
```
**Flags:** `--dir`, `--timeout` (default 1m).
Databases with histories written by an older CLI, or by the Go/TS Nucleus SDKs (integer version columns), are refused by `neutron migrate` until adopted once. Adoption matches history rows to files by exact version text (`001` and `1` are never treated as the same migration), verifies what can be proven (a legacy SDK checksum that reproduces from the supplied file), and reports rows with no recorded checksum, or with no file of the same version, as unverified — checksums are never fabricated. A recorded checksum that does not match its file refuses the whole adoption and changes nothing: restore the applied SQL or reconcile the row by hand.
### `neutron migrate status`
Show applied and pending migrations.
```bash
neutron migrate status
```
**Flags:** `--dir`, `--timeout` (default 10s).
### `neutron migrate create `
Generate new migration files.
```bash
neutron migrate create add_role_column
```
Creates `{version}_{name}.up.sql` and `{version}_{name}.down.sql`. **Flags:** `--dir`.
### `neutron migrate down [N]`
Revert the latest migration, or the latest `N` migrations.
```bash
neutron migrate down
neutron migrate down 3
```
Down SQL is not data restoration. Rollbacks run under the same lock and checksum verification as `neutron migrate`, and the command refuses a migration whose plan report marks it irreversible, or whose down file holds only an `IRREVERSIBLE` comment: forward-fix instead. **Flags:** `--dir`, `--timeout` (default 1m).
### `neutron migrate generate`
Write the next `.up.sql`/`.down.sql` pair from a schema document exported by `@neutron-build/sql`.
```bash
neutron migrate generate --schema neutron.schema.json --name add_posts
neutron migrate generate --mode snapshot --schema neutron.schema.json
neutron migrate generate --rename 'users.name>users.full_name'
```
`--mode live` (the default, unless `neutron.toml` sets `[migrations] snapshots = true`) diffs against the database; `--mode snapshot` plans offline from the last accepted snapshot, accounts for pending migrations, and writes a `.plan.json` risk and reversibility report. When a plan adds enum values and also changes anything else, generate writes the additions as their own earlier migration (`{NNN}_{name}_enum_values`, then `{NNN+1}_{name}`); in snapshot mode each gets its own plan report and snapshot. `--mode live` refuses statements the connected server cannot run (changing a generated column's expression needs PostgreSQL 17+). `--mode snapshot` cannot know the target server, so the plan report records the requirement as `minServerMajor` and `neutron migrate` refuses older servers before running anything. Nothing absent from the schema is dropped without `--allow-destructive` (in snapshot mode, what a plan leaves in place stays in its target snapshot, so a later `--allow-destructive` plan drops it), column order differences are noted and never planned (PostgreSQL appends added columns), `_neutron_*` metadata and extension-owned objects are never dropped, and catalog structures the planner cannot represent faithfully are refused instead of planned. Quote `--rename` values: `>` is a shell redirect.
**Flags:** `--schema` (default `neutron.schema.json`), `--name`, `--mode` (`live` or `snapshot`), `--rename` (repeatable; `schema.table.old>schema.table.new` for v2 documents, and `table.old>table.new` when the table name is unambiguous), `--allow-destructive`, `--dir`, `--timeout` (default 30s).
### `neutron migrate resolve `
Inspect and recover a migration that was interrupted outside a transaction.
```bash
neutron migrate resolve 004
neutron migrate resolve 004 --retry
```
Transactional migrations need none of this: a failure rolls back with its history row. A non-transactional migration (`CREATE INDEX CONCURRENTLY`, journaled steps) interrupted mid-file leaves partial effects and no history row. `resolve` checks each statement's postcondition against the catalog and offers exactly one action: `--retry` skips provably applied statements, `--mark-applied` records the row only when every effect is proven, `--abort` runs the down SQL. A statement whose outcome cannot be proven (`ALTER`, DML) is never replayed automatically. With no action it prints the inspection report only. `--abort` runs the down SQL in one transaction and is allowed whenever a clean state cannot be proven. **Flags:** `--retry`, `--mark-applied`, `--abort` (exactly one), `--dir`, `--timeout` (default 1m). Operational patterns (expand, backfill, contract, concurrent indexes) are shown in `cli/examples-src/README.md`.
### Upgrading from earlier CLI builds
- **Migration history.** A `_neutron_migrations` table written by an older CLI or by the Go/TS Nucleus SDKs is refused until `neutron migrate adopt` graduates it once. Adoption never fabricates checksums: rows it cannot prove stay unverified, and a recorded checksum that does not match its file refuses the adoption.
- **Statement allowlist.** Migration files may contain only the statement kinds listed in `neutron migrate --help` (`SELECT` and DML, `TRUNCATE`, create/alter/drop of schema objects, `SET LOCAL`). Functions, triggers, rules, grants, `DO` blocks and database- or role-wide statements are refused before anything in the file runs; no flag bypasses this.
- **Destructive changes.** Drops and other data-losing statements need `--allow-destructive`; `db push --force` only permits pushing onto a database that has migration history.
- **Nucleus.** `neutron migrate` refuses Nucleus databases; the Go and TypeScript SDK runners own those, and remain experimental.
- **MCP.** `query_sql` is read-only (best-effort on Nucleus; see `neutron mcp` below); writes go through `execute_sql`, which exists only with `--allow-writes`. The HTTP transport binds to `127.0.0.1` unless `--host` says otherwise, and writes over HTTP need `NEUTRON_MCP_TOKEN`.
The migration protocol (identity, checksum, locking, adoption) is specified in `contracts/data/MIGRATIONS.md`.
## Data
### `neutron seed`
Execute a SQL seed file.
```bash
neutron seed # default: seeds/seed.sql
neutron seed --file data/demo.sql # custom file (-f)
```
### `neutron repl`
Interactive SQL shell.
```bash
neutron repl
```
Supports multi-line SQL, table-formatted output, and meta-commands (`\dt`, `\q`, `\?`).
## Desktop and Native
### `neutron desktop`
Develop, build, or preview a Tauri desktop application.
```bash
neutron desktop dev
neutron desktop build
neutron desktop preview
```
**Flags:** `dev --port` (Vite port, default 5173); `build --target` (for example `aarch64-apple-darwin`) and `--release` (default true).
### `neutron native`
Scaffold, develop, run, or build an iOS/Android application.
```bash
neutron native init my-app
neutron native dev
neutron native run ios
neutron native build --ios --android
```
**Flags:** `dev --port` (bundler port, default 8081); `build --ios`, `--android`, `--release` (default true).
## Studio
### `neutron studio`
Launch the web-based database manager.
```bash
neutron studio # opens http://localhost:4983
neutron studio --port 8080 # custom port
neutron studio --migrations db/migrations # migrations directory shown in the inspection journey
```
## MCP Server
### `neutron mcp`
Start a Model Context Protocol server for AI assistants.
```bash
# stdio (for Claude Desktop, Cursor, etc.)
neutron mcp --db postgres://localhost:5432/mydb
# HTTP
neutron mcp --transport http --port 7700
# Dump schema
neutron mcp --dump-schema markdown
```
Tools are read-only by default: on PostgreSQL they run inside a rolled-back
`READ ONLY` transaction on a connection that is closed afterwards (advisory
locks released), and functions whose effects escape a rollback are refused;
on Nucleus, database reads also request a rolled-back `READ ONLY` transaction.
The measured engine refuses `NEXTVAL` through views, scalar subqueries and
`WHERE` predicates without advancing the sequence. An additional lexical
guard refuses writes, mutating model functions and write Cypher through
`GRAPH_QUERY`. Name checks cannot see through every user-defined wrapper:
on PostgreSQL connect as a low-privilege role; protection for unverified
Nucleus specialty functions and their wrappers remains best-effort. Use a
trusted principal and do not infer complete specialty protection from the
tested `NEXTVAL` paths. Values
under secret-looking names are redacted, and results carry each touched
model's actual transaction and durability limits. The HTTP transport answers
only requests addressed to localhost, an IP address or the `--host` name.
**Flags:**
- `--transport` — `stdio` (default) or `http`
- `--host` — HTTP bind address (default: 127.0.0.1)
- `--port` — HTTP port (default: 7700)
- `--db` — Database URL
- `--dump-schema` — Print schema and exit (`openai`, `mcp`, or `markdown`)
- `--allow-writes` — Offer the `execute_sql` write tool (over HTTP also requires `NEUTRON_MCP_TOKEN`)
- `--no-redact` — Return values under secret-looking names
- `--migrations` — Migrations directory for `migration_status` and `inspect_table` (default: `migrations`)
- `--log` — Write debug logs to stderr (default: silent)
## Diagnostics
### `neutron doctor`
Check your development environment.
```bash
neutron doctor
```
Verifies language runtimes, database connectivity, and configuration.
### `neutron version`
Show CLI and database versions.
```bash
neutron version
```
### `neutron upgrade`
Self-update to the latest version.
```bash
neutron upgrade
```
### `neutron completion`
Generate shell completions.
```bash
neutron completion bash >> ~/.bashrc
neutron completion zsh > "${fpath[1]}/_neutron"
neutron completion fish > ~/.config/fish/completions/neutron.fish
```
## Global Flags
All commands accept:
| Flag | Description |
|------|-------------|
| `--config` | Path to neutron.toml |
| `--url` | Database URL (overrides config) |
| `--verbose` | Debug logging |
| `--no-color` | Disable colored output |
---
## CLI
Source: https://neutron.build/docs/cli/overview
Universal command-line tool for every Neutron language.
The Neutron CLI is a single binary that works across all languages in the ecosystem. It handles database management, migrations, project scaffolding, and Studio — so you don't need separate tools per language.
## Install
Download a prebuilt Go CLI archive from the
[GitHub releases](https://github.com/neutron-build/neutron/releases/tag/cli%2Fv0.3.0),
or build the current source:
```bash
git clone https://github.com/neutron-build/neutron.git
cd neutron/cli
make install
```
The CLI runs natively on Linux and macOS (amd64 and arm64), the four targets
the release archives are built for. Windows is supported through WSL: install
and run the Linux build inside it. There is no native Windows release.
The TypeScript framework has a separate Node-based CLI whose executable is
`neutron-ts`:
```bash
npm install -D @neutron-build/cli
npx neutron-ts --help
```
## What It Does
| Area | Commands |
|------|----------|
| **Projects** | `new`, `init`, `dev`, `desktop`, `native`, `project` |
| **Database** | `db start`, `db stop`, `db status`, `db reset`, `db push` |
| **Schema** | `schema export`, `schema pull`, `schema check`, `schema baseline` |
| **Migrations** | `migrate`, `migrate status`, `migrate create`, `migrate generate`, `migrate down`, `migrate adopt`, `migrate resolve` |
| **Data** | `seed`, `repl` |
| **Tools** | `generate`, `studio`, `mcp`, `doctor`, `upgrade`, `completion`, `version` |
## Language Auto-Detection
When you run `neutron dev`, the CLI detects your project language and delegates to the right dev server:
| Detected By | Language | Dev Server |
|-------------|----------|-----------|
| `pyproject.toml` | Python | uvicorn |
| `package.json` | TypeScript | npm run dev |
| `go.mod` | Go | air / go run |
| `Cargo.toml` | Rust | cargo watch |
| `build.zig` | Zig | zig build run |
| `Project.toml` | Julia | julia --project |
Override with `--lang` or set `project.lang` in `neutron.toml`.
## Configuration
Configuration is read from (highest priority first):
1. CLI flags (`--url`, `--port`)
2. Environment variables (`DATABASE_URL`, `NUCLEUS_URL`)
3. Project config (`./neutron.toml`)
4. User config (`~/.neutron/config.toml`)
5. Defaults
### neutron.toml
```toml
[database]
url = "postgres://localhost:5432/mydb"
[studio]
port = 4983
[project]
lang = "python"
[nucleus]
port = 5432
data_dir = "nucleus_data"
```
## Quick Start
```bash
# Create a new project
neutron new my-api --lang go
# Start a local database
neutron db start
# Run migrations
neutron migrate
# Seed test data
neutron seed
# Start development
neutron dev
# Open Studio
neutron studio
```
---
## Environment Variables
Source: https://neutron.build/docs/configuration/environment-variables
Managing secrets and config.
Neutron uses Vite's environment variable handling.
## `.env` Files
Place `.env` files in your project root.
```
# .env
DATABASE_URL="postgresql://user:pass@localhost:5432/db"
PUBLIC_API_KEY="12345"
```
## Server vs Client
- **Server**: Variables are available via `process.env` (Node) or specific context providers (Cloudflare).
```ts
export async function loader() {
// Safe to use secrets here
const db = connect(process.env.DATABASE_URL);
}
```
- **Client**: Only variables prefixed with `PUBLIC_` (or `VITE_`) are exposed to the client.
```ts
console.log(import.meta.env.PUBLIC_API_KEY);
```
**Warning**: Never access private secrets (like DB passwords) in code that runs on the client (components), even if you think you are "hiding" it. Only access them in `loader` or `action` functions.
---
## neutron.config.ts
Source: https://neutron.build/docs/configuration/neutron-config
Configuring the framework.
The `neutron.config.ts` file is the entry point for configuring your Neutron application.
## Basic Configuration
```ts
import { defineConfig } from "@neutron-build/core";
export default defineConfig({
// Choice of runtime (default: preact)
runtime: "preact" | "react",
// Deployment adapters are bundled in @neutron-build/core and selected at
// build time with the CLI's --preset flag (e.g. neutron-ts build --preset vercel).
// Data layer configuration
data: {
database: "postgres" | "sqlite",
databaseUrl: process.env.DATABASE_URL,
dragonflyUrl: process.env.DRAGONFLY_URL,
storage: {
provider: "s3",
bucket: "my-bucket",
}
},
// Custom Vite configuration
vite: {
plugins: [],
resolve: {
alias: {
"~": "/src",
},
},
},
// Project paths (optional, defaults shown)
routesDir: "src/routes",
publicDir: "public",
outDir: "dist",
});
```
## Options
| Option | Type | Description |
| :--- | :--- | :--- |
| `adapter` | `NeutronAdapter` | The deployment adapter to use. |
| `vite` | `UserConfig` | Vite configuration object. Merged with Neutron's defaults. |
| `routesDir` | `string` | Path to the routes directory. Default: `"src/routes"`. |
| `features` | `object` | Toggle experimental features. |
---
## TypeScript
Source: https://neutron.build/docs/configuration/typescript
Using TypeScript in Neutron.
Neutron is written in TypeScript and is designed to provide excellent type safety out of the box.
## `tsconfig.json`
A scaffolded project ships a ready-to-use `tsconfig.json`. The options Neutron
relies on are Preact JSX and bundler module resolution:
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"jsx": "react-jsx",
"jsxImportSource": "preact",
"strict": true,
"skipLibCheck": true,
"types": ["vite/client"]
},
"include": ["src", "src/**/.neutron-*.d.ts"]
}
```
The `include` glob picks up Neutron's generated type files
(`.neutron-routes.d.ts`, `.neutron-content.d.ts`), which are written on
`neutron-ts dev` / `neutron-ts build`. Add path aliases under
`compilerOptions.paths` if you want them — e.g. `"~/*": ["src/*"]`.
## Type Inference
Neutron uses TypeScript inference to carry a loader's return type into the route component.
```tsx
// The return type of this function...
export async function loader() {
return { hello: "world" };
}
// ...is inferred here!
const data = useLoaderData();
// data is { hello: string }
```
This works for `useLoaderData`, `useActionData`, and generally means you rarely need to write manual interfaces for your API responses.
---
## Content Collections
Source: https://neutron.build/docs/content/content-collections
Managing typed content with schemas.
Content Collections allow you to manage Markdown or MDX content with strict type safety.
## Directory Structure
Collections live in `src/content`. Each subdirectory is a collection.
```
src/content/
blog/ # "blog" collection
post-1.md
post-2.mdx
docs/ # "docs" collection
guide.md
config.ts # Configuration file
```
## Defining Collections
You define your collections in `src/content/config.ts`.
```ts
import { defineCollection, z } from "@neutron-build/core/content";
const blog = defineCollection({
schema: z.object({
title: z.string(),
date: z.date(),
tags: z.array(z.string()),
draft: z.boolean().default(false),
}),
});
export const collections = {
blog,
};
```
---
## Markdown & MDX
Source: https://neutron.build/docs/content/markdown-mdx
Writing and rendering content.
Neutron supports standard Markdown (`.md`) and MDX (`.mdx`) out of the box.
## Using MDX
MDX allows you to use components inside your Markdown content.
```mdx
---
title: Hello World
---
import Counter from "../../components/Counter.tsx";
# Hello World
Here is an interactive counter:
```
## Querying Content
To use your content, import helper functions from `@neutron-build/core/content`.
```tsx
// src/routes/blog/index.tsx
import { getCollection } from "@neutron-build/core/content";
export async function loader() {
// Get all blog posts that aren't drafts
const posts = await getCollection("blog", ({ data }) => {
return data.draft !== true;
});
return { posts };
}
```
## Rendering Content
```tsx
// src/routes/blog/[slug].tsx
import { getEntry } from "@neutron-build/core/content";
export async function loader({ params }) {
const entry = await getEntry("blog", params.slug);
return { entry };
}
export default function Post() {
const { entry } = useLoaderData();
const { Content } = entry.render();
return (
{entry.data.title}
);
}
```
---
## Schemas
Source: https://neutron.build/docs/content/schemas
Validating content with Zod.
Neutron uses [Zod](https://zod.dev/) to validate the frontmatter of your content files. This ensures your content matches your expected shape and provides TypeScript types for your data.
## Common Validators
```ts
import { z } from "@neutron-build/core/content";
// Strings
z.string()
z.string().email()
z.string().url()
// Numbers
z.number()
z.number().min(1)
// Dates
z.date()
// Arrays
z.array(z.string())
// Enums
z.enum(["red", "green", "blue"])
// Relation (reference to another collection entry)
// (Future feature)
```
## Build Errors
If a content file does not match the schema, `neutron-ts build` will fail with a helpful error message pointing to the specific file and field that is invalid. This prevents broken pages from ever reaching production.
---
## Caching Loaders
Source: https://neutron.build/docs/data-layer/caching
Cache app responses and loader data by route.
App routes can cache a rendered response, loader data, or both. Cache behavior
is declared on the route:
```tsx
export const config = {
mode: "app",
cache: {
maxAge: 60,
loaderMaxAge: 15,
},
};
```
- `maxAge` is the response-cache lifetime in seconds.
- `loaderMaxAge` is the loader-data cache lifetime in seconds.
- A missing or zero value disables that cache.
The cache key includes the request path. Loader keys also include the route and
its parameters.
## Distributed cache
Core includes the cache contracts. For shared cache state across server
instances, use the optional Redis-compatible provider:
```bash
npm install @neutron-build/cache-redis ioredis
```
```ts
import { createServer } from "@neutron-build/core";
import { createRedisNeutronCacheStores } from "@neutron-build/cache-redis";
const cache = await createRedisNeutronCacheStores({
url: process.env.DRAGONFLY_URL ?? process.env.REDIS_URL,
keyPrefix: "my-app:",
});
await createServer({ cache });
```
The provider works with Redis or a compatible Dragonfly endpoint. It stores
app and loader entries separately and supports deleting entries by route path.
Do not cache personalized responses unless the cache policy and keying scheme
provide the required user isolation.
---
## Database and Drizzle
Source: https://neutron.build/docs/data-layer/database
Create a Drizzle client for SQLite, PostgreSQL, or Nucleus.
`createDrizzleDatabase()` from `@neutron-build/data` resolves a database
profile and creates the matching Drizzle client.
## Install a driver
For PostgreSQL or Nucleus SQL:
```bash
npm install @neutron-build/data drizzle-orm postgres
```
For SQLite:
```bash
npm install @neutron-build/data drizzle-orm @libsql/client
```
## Connect
```ts
import { createDrizzleDatabase } from "@neutron-build/data";
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
import * as schema from "./schema";
const database = await createDrizzleDatabase({
config: { database: "postgres" },
schema,
});
const db = database.db as PostgresJsDatabase;
try {
const users = await db.select().from(schema.users);
} finally {
await database.close();
}
```
The returned object contains:
- `profile` — the selected provider and connection string.
- `db` — the Drizzle database instance.
- `client` — the underlying Postgres or libSQL client.
- `nucleus` — the optional multi-model client when using Nucleus.
- `close()` — closes the created clients.
From the root import, `db` and `client` are typed `unknown`; narrow them to the
installed driver's types, as in the example above. The `@neutron-build/data/drizzle`
subpath returns genuine Drizzle types when you pass an explicit `profile`:
```ts
import { createDrizzleDatabase } from "@neutron-build/data/drizzle";
const database = await createDrizzleDatabase({
profile: { provider: "postgres", connectionString: process.env.DATABASE_URL! },
schema,
});
// database.db is a PostgresJsDatabase; database.close() closes it.
```
This package is the Drizzle interop path. For new application SQL, Neutron's own
schema, query and migration surface is `@neutron-build/sql`, whose schema
documents feed `neutron migrate generate` and `neutron db push`.
## Select a provider
Pass a provider explicitly:
```ts
const database = await createDrizzleDatabase({
config: { database: "postgres" },
schema,
});
```
Without an explicit provider, selection checks `NUCLEUS_URL`, then
`DATABASE_URL`, then uses `.neutron/dev.db` with SQLite. Nucleus uses the
PostgreSQL driver for SQL and optionally creates an `@neutron-build/nucleus`
client for its other data models.
---
## Redis and Dragonfly
Source: https://neutron.build/docs/data-layer/dragonfly
Optional Redis-compatible cache, session, queue, and realtime drivers.
Neutron does not require Dragonfly. Its optional Redis integrations accept a
Redis or Dragonfly connection URL because both expose the commands these
drivers use.
## Cache client
```ts
import { createRedisCacheClient } from "@neutron-build/data";
const cache = await createRedisCacheClient({
url: process.env.DRAGONFLY_URL ?? process.env.REDIS_URL,
keyPrefix: "my-app:",
});
await cache.set("status", "ready", 60);
const status = await cache.get("status");
await cache.close();
```
The client implements `get`, `set`, `del`, and `incr`. If no URL is passed, it
checks `DRAGONFLY_URL`, then `REDIS_URL`, then uses
`redis://127.0.0.1:6379`.
## BullMQ queue
BullMQ and `ioredis` are optional peer dependencies:
```bash
npm install @neutron-build/data bullmq ioredis
```
```ts
import { createBullMqQueueDriver } from "@neutron-build/data";
const queue = await createBullMqQueueDriver({ queueName: "media" });
await queue.process("transcode", async (job) => {
await transcode(job.payload);
});
await queue.add("transcode", { assetId: "asset_123" });
```
`@neutron-build/data` also exports Redis-compatible session and realtime
drivers. These are explicit application choices; Neutron does not start or
configure a Dragonfly server automatically.
---
## Data Layer
Source: https://neutron.build/docs/data-layer/overview
Optional database, cache, session, queue, and storage drivers.
`@neutron-build/data` contains optional backend primitives for TypeScript
applications. The framework core does not install a database, cache, or queue
on its own.
## Available drivers
| Area | Included interface and implementations |
| --- | --- |
| Database | Drizzle profiles for SQLite, PostgreSQL, and Nucleus |
| Cache | In-memory, Redis/Dragonfly, and Nucleus KV clients |
| Sessions | Generic cache-backed store and Redis/Dragonfly store |
| Queues | In-memory driver and BullMQ |
| Storage | In-memory, S3-compatible, and Nucleus Blob drivers |
| Realtime | In-memory, Redis/Dragonfly, and Nucleus PubSub buses |
The integrations load their third-party dependencies lazily. Install only the
drivers an application uses:
```bash
npm install @neutron-build/data
npm install drizzle-orm postgres # PostgreSQL or Nucleus SQL
npm install drizzle-orm @libsql/client # SQLite
npm install ioredis bullmq # Redis/Dragonfly and queues
```
## Database selection
`resolveDataConfig()` uses an explicit `database` option when supplied.
Otherwise it checks `NUCLEUS_URL`, then `DATABASE_URL`, and falls back to a
local SQLite file at `.neutron/dev.db`.
```ts
import { resolveDataConfig } from "@neutron-build/data";
const config = resolveDataConfig({ database: "nucleus" });
```
This resolution selects a profile; it does not start local services or
provision production infrastructure.
## In-memory defaults
The cache, queue, storage, and realtime modules include in-memory
implementations for local use and tests. Choose an external driver explicitly
when state must be shared across processes.
Continue with [Database and Drizzle](/docs/data-layer/database),
[Caching loaders](/docs/data-layer/caching), or
[Redis and Dragonfly](/docs/data-layer/dragonfly).
---
## Actions
Source: https://neutron.build/docs/data/actions
Server-side mutations and updates.
Actions are responsible for handling data mutations. Anytime a non-GET request (POST, PUT, PATCH, DELETE) is sent to your route, the `action` function is called.
## The `action` Function
Export an async function named `action` from your route module.
```tsx
import type { ActionArgs } from "@neutron-build/core";
import { redirect } from "@neutron-build/core";
export async function action({ request }: ActionArgs) {
const formData = await request.formData();
const name = formData.get("name");
await db.projects.create({ name });
return redirect("/projects");
}
```
## `useActionData`
Sometimes you want to return data from an action instead of redirecting (e.g., validation errors). You can access this data using `useActionData`.
```tsx
export async function action({ request }: ActionArgs) {
const formData = await request.formData();
if (!formData.get("email")) {
return { error: "Email is required" };
}
// ...
return { success: true };
}
export default function Signup() {
const actionData = useActionData();
return (
);
}
```
## Action Arguments
Similar to loaders, actions receive:
- `request`: The web Request object (contains the body/formData).
- `params`: Route parameters.
- `context`: App context.
## Redirects
It is common to redirect the user after a successful action. Use the `redirect` helper.
```tsx
import { redirect } from "@neutron-build/core";
// ...
throw redirect("/dashboard"); // or return redirect("/dashboard")
```
---
## Forms
Source: https://neutron.build/docs/data/forms
Progressive enhancement with the Form component.
Neutron provides a `
);
}
```
## Progressive Enhancement
The beauty of `
);
}
```
---
## Loaders
Source: https://neutron.build/docs/data/loaders
Server-side data loading.
Loaders are the primary way to get data into your Neutron routes. They run exclusively on the server, which means you can connect directly to your database, access secrets, or call internal APIs.
## The `loader` Function
Export an async function named `loader` from any route module.
```tsx
import type { LoaderArgs } from "@neutron-build/core";
export async function loader({ request, params, context }: LoaderArgs) {
// This code runs on the server!
const users = await db.users.findMany();
return { users };
}
```
## `useLoaderData`
To access the data returned by your loader in your component, use the `useLoaderData` hook.
```tsx
import { useLoaderData } from "@neutron-build/core";
export default function UsersPage() {
const { users } = useLoaderData();
return (
{users.map(user =>
{user.name}
)}
);
}
```
## Loader Arguments
The loader function receives a single object with the following properties:
- `request`: The standard web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object.
- `params`: An object containing the dynamic route parameters (e.g., `{ id: "123" }`).
- `context`: App-specific context passed from middleware.
## Parallel Loading
When you have nested routes (e.g., `_layout.tsx` -> `app/_layout.tsx` -> `dashboard.tsx`), Neutron runs all matching loaders **in parallel**. It does not wait for parent loaders to finish before starting child loaders. This prevents "waterfalls" and ensures your page loads as fast as possible.
---
## Revalidation
Source: https://neutron.build/docs/data/revalidation
Automatic data freshness.
After a successful action, Neutron can re-run the active loaders for the current page.
## How It Works
1. A user submits a `