# 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 ``` ## `
` Progressive enhancement wrapper for HTML forms. ```tsx {/* inputs */}
``` ## 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 (
{actionData?.error &&

{actionData.error}

}
); } ``` ## 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 `
` component that works just like a standard HTML ``, but with superpowers. ## The `` Component ```tsx import { Form } from "@neutron-build/core"; export default function Login() { return (
); } ``` ## Progressive Enhancement The beauty of `
` is that it works **without JavaScript**. 1. **No JS**: The browser submits a standard POST request. The server runs the `action`, handles the logic, and returns a new page (SSR). 2. **With JS**: Neutron intercepts the submit event. It uses `fetch` to send the data to the action. When the response comes back, it updates the page without a full reload. ## Pending States To provide a great user experience, you often want to show a loading state while the form is submitting. You can use `useNavigation`. ```tsx import { Form, useNavigation } from "@neutron-build/core"; export default function Login() { const navigation = useNavigation(); const isSubmitting = navigation.state === "submitting"; return ( {/* ... inputs ... */}
); } ``` --- ## 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 `
`. 2. The `action` runs on the server and completes successfully. 3. Neutron assumes that *something* might have changed on the server. 4. Neutron automatically re-runs **all active loaders** on the page. 5. The UI updates with the fresh data. ## Why This Matters You don't need to manually manage local state or caches. You don't need to append the new item to a list in your React state. Just write your loader to fetch the source of truth from the database. When you mutate the database in an action, the loader re-runs, fetches the new list, and your component updates. **The Loop:** `Database` → `Loader` → `Component` → `Action` → `Database` → `(Auto Revalidate)` ## Manual Revalidation If you need to trigger a revalidation without a form submission (e.g., after a WebSocket event), you can use `useRevalidator`. ```tsx import { useRevalidator } from "@neutron-build/core"; export function RealtimeComponent() { const revalidator = useRevalidator(); useEffect(() => { socket.on("update", () => { revalidator.revalidate(); }); }, []); // ... } ``` --- ## Type Safety Source: https://neutron.build/docs/data/type-safety End-to-end type safety without codegen. Neutron leverages TypeScript's inference capabilities to provide complete type safety across the network boundary, without needing code generation steps or separate DTO files. ## Loaders When you use `useLoaderData()`, the type of the data is inferred directly from the return type of your loader function. ```tsx // 1. Define return type automatically export async function loader() { return { user: { name: "Alice", age: 30, roles: ["admin", "editor"] // inferred as string[] } }; } export default function Page() { // 2. Data is typed! const data = useLoaderData(); // TypeScript knows data.user.name is a string // TypeScript knows data.user.age is a number // TypeScript knows data.user.roles is string[] return
{data.user.name}
; } ``` ## Date Objects Note that because data is serialized over the network (JSON), rich objects like `Date` will be converted to strings. ```tsx export async function loader() { return { createdAt: new Date() }; } export default function Page() { const { createdAt } = useLoaderData(); // createdAt is a string (ISO format), not a Date object! } ``` If you need `Date` objects, standard practice is to construct them in the component: `new Date(createdAt)`. --- ## Adapters Source: https://neutron.build/docs/deployment/adapters Deploying Neutron to supported platforms. Neutron uses an **Adapter** system to build your application for different deployment targets. By changing a few lines of configuration, you can switch from a Node.js server to a Cloudflare Worker or a Docker container. ## How Adapters Work When you run `neutron-ts build`, the framework compiles your code. The final step is the "Adapt" phase, where the chosen adapter takes that compiled code and wraps it in the necessary entry points and configuration for your specific host. ## Selecting an Adapter Adapters ship **bundled inside `@neutron-build/core`** — there's no separate package to install. Select one at build time with the CLI's `--preset` flag: ```bash npx @neutron-build/cli build --preset vercel ``` ## Available Presets - `--preset docker`: long-running Node server (produces `dist/server.mjs` + Dockerfile). - `--preset cloudflare`: Cloudflare Workers / Pages. - `--preset vercel`: Vercel Serverless / Edge Functions. - `--preset netlify`: Netlify Functions v2 + static publish. - `--preset static`: pure static-site output (SSG). --- ## Cloudflare Source: https://neutron.build/docs/deployment/cloudflare Deploying to Cloudflare Workers or Pages. Neutron supports deploying to Cloudflare's edge network, giving your application global low-latency performance. ## Usage The Cloudflare adapter ships inside `@neutron-build/core` and is selected at build time via the CLI's `--preset` flag — no separate install needed. 1. Build with the Cloudflare preset: ```bash npx @neutron-build/cli build --preset cloudflare ``` 2. Deploy (using Wrangler): ```bash npx wrangler pages deploy dist ``` ## Platform Proxy The adapter automatically provides the Cloudflare `env` object (KV, R2, D1) in your loader/action context. ```ts export async function loader({ context }) { const kv = context.cloudflare.env.MY_KV_NAMESPACE; const value = await kv.get("key"); // ... } ``` --- ## Docker Source: https://neutron.build/docs/deployment/docker Containerizing your Neutron app. You can containerize your Neutron application using the Node.js adapter and a standard Dockerfile. ## Dockerfile Create a `Dockerfile` in your project root: ```dockerfile # 1. Build Stage FROM node:20-alpine AS builder WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN npm install -g pnpm && pnpm install --frozen-lockfile COPY . . RUN pnpm run build # 2. Production Stage FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production COPY --from=builder /app/dist ./dist COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/node_modules ./node_modules EXPOSE 3000 CMD ["node", "dist/server/index.js"] ``` ## Build and Run ```bash docker build -t my-neutron-app . docker run -p 3000:3000 my-neutron-app ``` --- ## Netlify Source: https://neutron.build/docs/deployment/netlify Deploying to Netlify. Deploying to Netlify uses the bundled Netlify adapter — no separate install needed; the adapter ships inside `@neutron-build/core` and is selected at build time via the CLI's `--preset` flag. ## Usage 1. Build with the Netlify preset: ```bash npx @neutron-build/cli build --preset netlify ``` 2. Deploy with the Netlify CLI (publishes `dist/` and its functions): ```bash npx netlify deploy --prod --dir dist --functions dist/functions ``` The publish directory carries its own routing (`_redirects`) and headers (`_headers`) files, so no other configuration is required for this flow. ## Git integration If you deploy through Netlify's git integration instead of the CLI, copy `dist/netlify.toml` to your project root. It sets the build command, the publish directory (`dist`), the functions directory (`dist/functions`), and the SSR fallback redirect. ## How SSR routing works The adapter writes `dist/functions/__neutron.mjs`, a Netlify Functions v2 handler, plus a `_redirects` file with a non-forced rewrite: ``` /* /.netlify/functions/__neutron 200 ``` Netlify serves a static file when one matches the request path; only non-matching requests (your `mode: "app"` routes) fall through to the function. Static-only sites can also use the simpler [static hosting](/docs/deployment/static-hosting) flow. ## Limitations The function runs on Netlify's Node.js runtime and is bundled by Netlify at deploy time. Neutron's build checks the generated artifacts, but only a real deploy exercises Netlify's routing and function bundling — run `npx @neutron-build/cli deploy-check --preset netlify` after building to catch missing artifacts before you push. --- ## Node.js Source: https://neutron.build/docs/deployment/node Deploying to a Node.js server. The Node.js adapter builds your application into a standalone Node.js server. This is suitable for deploying to VPS providers (DigitalOcean, Linode), PaaS (Heroku, Railway, Render), or custom infrastructure. ## Usage The Node adapter ships inside `@neutron-build/core` and is selected at build time via the CLI's `--preset` flag — no separate install needed. For a long-running Node server, use the `docker` preset (which produces a Node `server.mjs` you can run directly without containerizing). 1. Build with the Docker preset (produces a standalone Node server): ```bash npx @neutron-build/cli build --preset docker ``` 2. Run the generated server: ```bash node dist/server.mjs ``` ## `neutron-ts preview` vs. the production artifact `npx neutron-ts preview` starts a **local** preview server that runs SSR through the dev toolchain (Vite + your `src/` files). It is convenient for a quick local check, but it is **not** the artifact you deploy: - **Preview** needs `src/` and your dev dependencies present, and pulls Vite at runtime. - **The deployed artifact** is the preset-generated entry (e.g. `dist/server.mjs` from `--preset docker`), which bundles everything and ships **no Vite** — it runs from `dist/` alone. To validate what actually deploys, build with a preset and run the generated server (the two commands above) rather than relying on `preview`. --- ## Static Hosting Source: https://neutron.build/docs/deployment/static-hosting Deploying as a static site. If your application consists only of **static routes** (no `mode: "app"`), you can deploy it to any static hosting provider (GitHub Pages, Netlify, AWS S3, etc.). If you need SSR on one of those hosts, see the [adapters](/docs/deployment/adapters) — SSR requires a preset for the target platform. ## Usage The static adapter ships inside `@neutron-build/core` and is selected at build time via the CLI's `--preset` flag — no separate install needed. 1. Build with the static preset: ```bash npx @neutron-build/cli build --preset static ``` ## Output The build command will produce a directory (usually `dist`) containing pure `.html`, `.css`, and `.js` (for islands) files. Upload this directory to your host. ## Client-Side Routing? Static builds produce standard HTML files. Navigation between pages is standard browser navigation. If you need SPA-like navigation on a static host, consider using View Transitions. --- ## Vercel Source: https://neutron.build/docs/deployment/vercel Deploying to Vercel. Deploying to Vercel uses the bundled Vercel adapter — no separate install needed; the adapter ships inside `@neutron-build/core` and is selected at build time via the CLI's `--preset` flag. ## Usage 1. Build with the Vercel preset: ```bash npx @neutron-build/cli build --preset vercel ``` 2. Deploy: ```bash npx vercel ``` ## Edge Middleware If you enable `edge: true`, your application will run on Vercel's Edge Runtime. Ensure your dependencies are compatible with the edge (no Node.js built-ins like `fs` or `path`). --- ## Getting Started Source: https://neutron.build/docs/elixir/getting-started Create an Elixir API server with Neutron in minutes. ## Create a Project ```bash neutron new my-api --lang elixir cd my-api ``` Or add to an existing Mix project: ```elixir # mix.exs defp deps do [ {:neutron, "~> 0.1.0", hex: :neutron_ex}, {:neutron_nucleus, "~> 0.1.0"} ] end ``` Then fetch dependencies: ```bash mix deps.get ``` ## Your First App ```elixir defmodule MyApp.Router do use Neutron.Router plug Neutron.Logger plug Neutron.RequestID get "/users" do users = Neutron.Nucleus.query!(MyApp.Pool, "SELECT * FROM users") send_json(conn, 200, users) end post "/users" do {:ok, body} = read_json(conn) user = Neutron.Nucleus.query_one!(MyApp.Pool, "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *", [body["name"], body["email"]] ) send_json(conn, 201, user) end end defmodule MyApp.Application do use Application def start(_type, _args) do children = [ {Neutron.Nucleus.Pool, name: MyApp.Pool, url: System.get_env("DATABASE_URL"), size: 10}, {Bandit, plug: MyApp.Router, port: 4000} ] Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor) end end ``` ## Run ```bash # Development with hot reload neutron dev # Or directly with Mix mix run --no-halt ``` ## Test It ```bash curl http://localhost:4000/health curl http://localhost:4000/users curl -X POST http://localhost:4000/users \ -H "Content-Type: application/json" \ -d '{"name": "Alice", "email": "alice@example.com"}' ``` ## Auto-Generated Endpoints Every Neutron Elixir app includes: | Endpoint | Description | |----------|-------------| | `GET /health` | Health check | | `GET /openapi.json` | OpenAPI 3.1 spec | --- ## Elixir Framework Source: https://neutron.build/docs/elixir/index OTP-based backend framework with Plug, Bandit, and 14 Nucleus data models. Neutron for Elixir is a fault-tolerant backend framework built on OTP. Plug-based routing, Bandit HTTP server, and a full Nucleus client for all 14 data models. > **Status: connects, with gaps in the model coverage.** Postgrex could not > complete a connection to Nucleus at all until 2026-08-15 — its type bootstrap > needed `ARRAY (SELECT ...)`, which the engine rejected, so every Elixir, Ecto > and Phoenix application timed out at connect with no cause named. That is > fixed, and Elixir now runs in the live conformance suite: 32 of 42 cases pass, > none fail. > > The remaining 7 are model methods this SDK does not implement yet — see > [Nucleus Client](/docs/elixir/nucleus) for the list. Verified against a running > engine 2026-08-15. ## Features - **OTP supervisors** — Fault-tolerant process trees with automatic restart - **Plug + Bandit** — Composable middleware on a lightweight HTTP server - **14 data models** — Full Nucleus client (SQL, KV, Vector, Graph, TimeSeries, and more) - **Real-time** — Phoenix-style channels with presence tracking - **OpenAPI 3.1** — Auto-generated from route metadata - **RFC 7807** — Standardized error responses ## Hello World ```elixir defmodule MyApp.Router do use Neutron.Router plug Neutron.Logger plug Neutron.RequestID plug Neutron.Recover get "/" do send_json(conn, 200, %{message: "Hello, Neutron!"}) end get "/health" do send_json(conn, 200, %{status: "ok"}) end end # application.ex defmodule MyApp.Application do use Application def start(_type, _args) do children = [ {Neutron.Nucleus.Pool, url: "postgres://localhost:5432/mydb", size: 10}, {Bandit, plug: MyApp.Router, port: 4000} ] Supervisor.start_link(children, strategy: :one_for_one) end end ``` ## Module Overview | Module | Purpose | |--------|---------| | `Neutron.Router` | Plug-based routing with macro DSL | | `Neutron.Nucleus` | Connection pool and client for all 14 models | | `Neutron.Channels` | WebSocket channels with topic routing | | `Neutron.Presence` | Distributed presence tracking via CRDT | | `Neutron.Auth` | JWT, sessions, API keys, RBAC | | `Neutron.Middleware` | Logger, CORS, rate limit, request ID, recovery | | `Neutron.OpenAPI` | Auto-generated OpenAPI 3.1 spec | ## Why Elixir? OTP supervision trees make Neutron Elixir ideal for real-time systems — chat, IoT dashboards, live collaboration. If a Nucleus connection drops, the supervisor restarts it. If a channel process crashes, clients reconnect automatically. --- ## Nucleus Client Source: https://neutron.build/docs/elixir/nucleus Using all 14 Nucleus data models from Elixir. The Elixir Nucleus client provides access to all 14 data models over the PostgreSQL wire protocol. Connections are managed via a GenServer-backed pool with OTP supervision. > **Status: verified against a running engine, with seven known gaps.** As of > 2026-08-15 Elixir is in the `sdk-live` CI matrix and the connection below > works. It could not connect at all before that date — Postgrex's type > bootstrap needs `ARRAY (SELECT ...)`, which the engine rejected. > > Live results: **32 pass, 0 fail, 7 unsupported, 3 expected-fail.** The seven > unsupported are methods this SDK has not implemented: > > - `Document` — no filter-based update or delete (`update_in/4` and > `delete_in/3` address a document by id), and no `find_one`. > - `Document.path_in/4` takes exactly one key, so nested paths are unreachable. > - `TimeSeries` — no range point retrieval, and no aggregate over a range with > a window (`time_bucket/3` truncates a single timestamp). > - `Blob` — no `exists/2`, and no bucket dimension at all; `store`/`get`/`meta`/ > `delete` take a key only. > - `Vector` — no `count/2`. > > Everything else on this page is exercised on every CI run. > > One caveat specific to Postgrex: **UUID parameters are raw 16-byte binaries**, > not dashed strings. `Ecto.UUID.dump/1` converts. ## Connection ```elixir # In your supervision tree {Neutron.Nucleus.Pool, name: MyApp.Pool, url: "postgres://localhost:5432/mydb", size: 10} # Feature detection features = Neutron.Nucleus.features(MyApp.Pool) features.is_nucleus # true ``` ## SQL (Relational) ```elixir users = Neutron.Nucleus.query!(MyApp.Pool, "SELECT * FROM users WHERE active = $1", [true]) user = Neutron.Nucleus.query_one!(MyApp.Pool, "SELECT * FROM users WHERE id = $1", [42]) # Transactions Neutron.Nucleus.transaction(MyApp.Pool, fn conn -> Neutron.Nucleus.exec!(conn, "UPDATE accounts SET balance = balance - $1 WHERE id = $2", [100, 1]) Neutron.Nucleus.exec!(conn, "UPDATE accounts SET balance = balance + $1 WHERE id = $2", [100, 2]) end) ``` ## Key-Value ```elixir kv = Neutron.Nucleus.kv(MyApp.Pool) Neutron.KV.set(kv, "session:1", "data", ttl: 3600) val = Neutron.KV.get(kv, "session:1") Neutron.KV.delete(kv, "session:1") # Hashes Neutron.KV.hset(kv, "user:1", "name", "Alice") name = Neutron.KV.hget(kv, "user:1", "name") ``` ## Vector Search ```elixir results = Neutron.Nucleus.vector(MyApp.Pool) |> Neutron.Vector.search("documents", query_embedding, limit: 10, metric: :cosine, filter: %{category: "tech"}) Enum.each(results, fn r -> IO.puts("ID: #{r.id}, Score: #{r.score}") end) ``` ## Graph ```elixir graph = Neutron.Nucleus.graph(MyApp.Pool) {:ok, node_id} = Neutron.Graph.add_node(graph, "Person", %{name: "Alice"}) Neutron.Graph.add_edge(graph, "KNOWS", node_id, other_id, %{since: 2024}) results = Neutron.Graph.query(graph, "MATCH (p:Person)-[:KNOWS]->(q) RETURN p.name, q.name") ``` ## All 14 Models | Model | Accessor | Key Functions | |-------|----------|---------------| | SQL | `query!/2` | query, exec, transaction | | Key-Value | `kv/1` | get, set, delete, hset, lpush, zadd | | Vector | `vector/1` | search | | TimeSeries | `timeseries/1` | insert, range_avg, last | | Document | `document/1` | insert, find, get | | Graph | `graph/1` | add_node, add_edge, query | | FTS | `fts/1` | index, search | | Geo | `geo/1` | distance, within | | Blob | `blob/1` | put, get, delete | | Streams | `streams/1` | xadd, xrange, xread_group | | Columnar | `columnar/1` | insert, sum, avg | | Datalog | `datalog/1` | assert, query | | CDC | `cdc/1` | subscribe, get_changes | | PubSub | `pubsub/1` | publish, subscribe | --- ## Real-Time Source: https://neutron.build/docs/elixir/realtime Channels, presence tracking, and WebSocket support. The `Neutron.Channels` module provides Phoenix-style real-time communication with topic-based routing, presence tracking, and Nucleus-backed multi-instance sync. ## Channels Define a channel to handle WebSocket messages by topic: ```elixir defmodule MyApp.ChatChannel do use Neutron.Channel def join("chat:" <> room_id, _params, socket) do {:ok, assign(socket, :room_id, room_id)} end def handle_in("message", %{"body" => body}, socket) do room = socket.assigns.room_id user_id = socket.assigns.user_id {:ok, msg} = Neutron.Nucleus.query_one!(MyApp.Pool, "INSERT INTO messages (room, user_id, body) VALUES ($1, $2, $3) RETURNING *", [room, user_id, body]) broadcast!(socket, "message", msg) {:noreply, socket} end end ``` ## Socket Setup Mount channels on your router: ```elixir defmodule MyApp.Socket do use Neutron.Socket channel "chat:*", MyApp.ChatChannel channel "notifications:*", MyApp.NotificationChannel def connect(%{"token" => token}, socket) do case Neutron.Auth.verify_token(token) do {:ok, user_id} -> {:ok, assign(socket, :user_id, user_id)} _ -> :error end end end # In your router websocket "/ws", MyApp.Socket ``` ## Presence Track who is online in each topic with CRDT-based presence: ```elixir defmodule MyApp.ChatChannel do use Neutron.Channel def join("chat:" <> room_id, _params, socket) do Neutron.Presence.track(socket, socket.assigns.user_id, %{ name: socket.assigns.user_name, online_at: System.system_time(:second) }) presences = Neutron.Presence.list(socket) {:ok, %{presences: presences}, socket} end end ``` Clients receive `presence_diff` events automatically when users join or leave. ## Multi-Instance Sync Use Nucleus PubSub to sync broadcasts across multiple Elixir nodes: ```elixir # config/config.exs config :neutron, :pubsub, adapter: Neutron.PubSub.Nucleus, pool: MyApp.Pool # Broadcasts automatically fan out to all connected nodes broadcast!(socket, "message", payload) ``` ## Server-Sent Events For one-way streaming, use SSE: ```elixir get "/events" do conn = Neutron.SSE.start(conn) Neutron.Nucleus.pubsub(MyApp.Pool) |> Neutron.PubSub.subscribe("events:*", fn channel, payload -> Neutron.SSE.send(conn, channel, payload) end) end ``` --- ## Installation Source: https://neutron.build/docs/getting-started/installation Create and run a Neutron TypeScript project. ## Create a project ```bash npm create @neutron-build@latest my-app cd my-app npm install npm run dev ``` The scaffolder is published as `@neutron-build/create`. The previous `create-neutron` package is deprecated; use the scoped command above. The development server prints its local URL when it starts. The default scaffold uses Preact and the `basic` template. No interactive questions are asked. ## Choose a template Pass `--template` when you need a different starting point: ```bash npm create @neutron-build@latest my-docs -- --template docs ``` Available templates are `basic`, `marketing`, `app`, `full`, and `docs`. To use React compatibility instead of the default Preact runtime: ```bash npm create @neutron-build@latest my-app -- --runtime react-compat ``` Run `npm create @neutron-build@latest -- --help` for the complete option list. ## What the scaffold installs The generated project depends on `@neutron-build/core`, `@neutron-build/cli`, Preact, and `preact-render-to-string`. Its main scripts are: ```json { "scripts": { "dev": "neutron-ts dev", "build": "neutron-ts build", "start": "neutron-ts start", "preview": "neutron-ts preview" } } ``` ## Next - [Project structure](/docs/getting-started/project-structure) - [Your first route](/docs/getting-started/your-first-route) --- ## Project Structure Source: https://neutron.build/docs/getting-started/project-structure The files in a basic Neutron TypeScript project. The `basic` template starts with this structure: ```text my-app/ ├── public/ │ └── favicon.svg ├── src/ │ ├── routes/ │ │ ├── _layout.tsx │ │ ├── index.tsx │ │ └── users/ │ │ └── [id].tsx │ ├── main.tsx │ └── neutron-env.d.ts ├── neutron.config.ts ├── package.json ├── tsconfig.json └── vite.config.ts ``` ## `src/routes` Files define URLs: - `index.tsx` maps to the directory root. - `[id].tsx` creates a dynamic segment. - `[...slug].tsx` creates a catch-all segment. - `_layout.tsx` wraps routes in its directory and all child directories. A route can export a component, a `loader`, an `action`, metadata through `head`, middleware, and route configuration. Import framework APIs from `@neutron-build/core`. ## `src/main.tsx` This is the browser entry point. The generated file registers the virtual route manifest and initializes Neutron. Most applications do not need to change it. ## `neutron.config.ts` The scaffold records the rendering runtime here: ```ts import { defineConfig } from "@neutron-build/core"; export default defineConfig({ runtime: "preact", }); ``` Deployment presets are selected by the CLI, for example `neutron-ts build --preset vercel`. ## Next - [Your first route](/docs/getting-started/your-first-route) - [File conventions](/docs/routing/file-conventions) - [Configuration](/docs/configuration/neutron-config) --- ## Your First Route Source: https://neutron.build/docs/getting-started/your-first-route Add static and interactive routes to a Neutron project. Neutron maps files in `src/routes` to URLs. ## Static route Static mode is the default. Create `src/routes/about.tsx`: ```tsx export default function About() { return (

About

This route is rendered to HTML at build time.

); } ``` The file is available at `/about`. Add `export const config = { mode: "static" }` when you want the mode to be explicit. ## App route Use app mode for client-side navigation and hydration: ```tsx import { useLoaderData } from "@neutron-build/core"; export const config = { mode: "app" }; export async function loader() { return { user: "Alice" }; } export default function Dashboard() { const data = useLoaderData(); return

Welcome, {data.user}

; } ``` The loader runs on the server. `useLoaderData()` carries its return type into the component without a separate interface. ## Next - [Loaders](/docs/data/loaders) - [Actions](/docs/data/actions) - [Route conventions](/docs/routing/file-conventions) --- ## Authentication Source: https://neutron.build/docs/go/authentication JWT, sessions, API keys, and role-based access control. The `neutronauth` package provides authentication and authorization middleware for Neutron Go applications. ## JWT ### Generate Tokens ```go import "github.com/neutron-build/neutron/go/neutronauth" token, err := neutronauth.GenerateToken( map[string]any{ "sub": "user_42", "role": "admin", "email": "alice@example.com", }, []byte("your-secret-key"), time.Hour * 24, // expires in 24h ) ``` Tokens are signed with HMAC-SHA256 (HS256). ### Verify Tokens ```go claims, err := neutronauth.ParseToken(tokenString, []byte("your-secret-key")) if err != nil { // Invalid signature or expired } fmt.Println(claims["sub"]) // "user_42" ``` ### JWT Middleware ```go app := neutron.New() // Protect all routes under /api api := app.Group("/api") api.Use(neutronauth.JWTMiddleware([]byte("your-secret-key"))) api.Get("/profile", func(w http.ResponseWriter, r *http.Request) { claims := neutronauth.ClaimsFromContext(r.Context()) neutron.WriteJSON(w, claims) }) ``` ### Options ```go neutronauth.JWTMiddleware(secret, neutronauth.WithHeaderName("X-Auth-Token"), // Custom header (default: Authorization) neutronauth.WithScheme("Token"), // Custom scheme (default: Bearer) neutronauth.WithSkipPaths("/health", "/docs"), // Skip auth on these paths ) ``` ## Sessions Interface-based session storage with cookie management. ### Memory Store (Development) ```go store := neutronauth.NewMemorySessionStore() app.Use(neutronauth.SessionMiddleware(store)) ``` ### Nucleus Store (Production) Stores session data in Nucleus KV with prefix `session:`: ```go store := neutronauth.NewNucleusSessionStore(nucleusClient) app.Use(neutronauth.SessionMiddleware(store)) ``` ### Usage ```go app.Post("/login", func(w http.ResponseWriter, r *http.Request) { session := neutronauth.SessionFromContext(r.Context()) session.Data["user_id"] = "42" session.Data["role"] = "admin" session.Save() // Writes to store + sets cookie }) app.Get("/dashboard", func(w http.ResponseWriter, r *http.Request) { session := neutronauth.SessionFromContext(r.Context()) userID := session.Data["user_id"] // ... }) app.Post("/logout", func(w http.ResponseWriter, r *http.Request) { session := neutronauth.SessionFromContext(r.Context()) session.Destroy() // Removes from store + clears cookie }) ``` ## API Keys Validate API keys from headers or query parameters: ```go app.Use(neutronauth.APIKeyMiddleware(func(key string) (bool, error) { // Look up key in database valid, err := db.ValidateAPIKey(key) return valid, err })) ``` Reads from `X-API-Key` header or `Authorization: ApiKey `. ## Role-Based Access Control ### Require Roles ```go // Only admins can access admin := app.Group("/admin") admin.Use(neutronauth.JWTMiddleware(secret)) admin.Use(neutronauth.RequireRole("admin")) // Multiple roles (any match) admin.Use(neutronauth.RequireRole("admin", "superadmin")) ``` Checks `claims["role"]` (string) or `claims["roles"]` (array). ### Require Permissions ```go // Fine-grained permission checks api.Use(neutronauth.RequirePermission("users:read", "users:write")) ``` Checks `claims["permissions"]` — all listed permissions must be present. ### Response Unauthorized (missing/invalid token) returns `401`: ```json { "type": "https://neutron.build/errors/unauthorized", "title": "Unauthorized", "status": 401, "detail": "Missing or invalid authentication token" } ``` Forbidden (valid token, insufficient role) returns `403`: ```json { "type": "https://neutron.build/errors/forbidden", "title": "Forbidden", "status": 403, "detail": "Insufficient permissions" } ``` --- ## Database Source: https://neutron.build/docs/go/database Nucleus client with all 14 data models. The Go Nucleus client provides type-safe access to all 14 data models over the PostgreSQL wire protocol. ## Connection ```go import "github.com/neutron-build/neutron/go/nucleus" db, err := nucleus.Connect(ctx, "postgres://localhost:5432/mydb") defer db.Close() // Auto-detect Nucleus features fmt.Println(db.Features().IsNucleus) // true ``` ## SQL (Relational) ```go // Type-safe queries with struct scanning type User struct { ID int64 `db:"id"` Name string `db:"name"` Email string `db:"email"` } users, err := nucleus.Query[User](ctx, db.SQL(), "SELECT id, name, email FROM users WHERE active = $1", true) user, err := nucleus.QueryOne[User](ctx, db.SQL(), "SELECT * FROM users WHERE id = $1", 42) ``` ## Transactions ```go tx, err := db.Begin(ctx) _, err = tx.SQL().Exec(ctx, "UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1) _, err = tx.SQL().Exec(ctx, "UPDATE accounts SET balance = balance + $1 WHERE id = $2", 100, 2) err = tx.Commit(ctx) // or tx.Rollback(ctx) ``` ## Key-Value ```go kv := db.KV() await kv.Set(ctx, "session:1", "data", nucleus.WithTTL(3600)) val, err := kv.Get(ctx, "session:1") kv.Delete(ctx, "session:1") // Lists kv.LPush(ctx, "queue", "item1") item, _ := kv.RPop(ctx, "queue") // Hashes kv.HSet(ctx, "user:1", "name", "Alice") name, _ := kv.HGet(ctx, "user:1", "name") // Sorted sets kv.ZAdd(ctx, "leaderboard", 100.0, "player1") top, _ := kv.ZRange(ctx, "leaderboard", 0, 9) ``` ## Vector Search ```go results, err := db.Vector().Search(ctx, "documents", queryEmbedding, nucleus.WithLimit(10), nucleus.WithMetric(nucleus.Cosine), nucleus.WithFilter(map[string]any{"category": "tech"}), ) for _, r := range results { fmt.Printf("ID: %s, Score: %.4f\n", r.ID, r.Score) } ``` ## Full-Text Search ```go results, err := db.FTS().Search(ctx, "articles", "machine learning", nucleus.WithFTSLimit(10), nucleus.WithFuzzy(2), nucleus.WithHighlight(true), ) ``` ## Graph ```go graph := db.Graph() nodeID, _ := graph.AddNode(ctx, "Person", map[string]any{"name": "Alice"}) graph.AddEdge(ctx, "KNOWS", nodeID, otherID, map[string]any{"since": 2024}) result, _ := graph.Query(ctx, "MATCH (p:Person)-[:KNOWS]->(q) RETURN p.name, q.name") ``` ## Document ```go doc := db.Document() id, _ := doc.Insert(ctx, "posts", map[string]any{ "title": "Hello", "body": "World", }) post, _ := doc.Get(ctx, id) posts, _ := doc.Find(ctx, "posts", map[string]any{"author": "alice"}) ``` ## Time Series ```go ts := db.TimeSeries() ts.Insert(ctx, "cpu_usage", time.Now(), 72.5, nucleus.WithTags(map[string]string{"host": "web-01"})) avg, _ := ts.RangeAvg(ctx, "cpu_usage", start, end) last, _ := ts.Last(ctx, "cpu_usage") ``` ## Streams ```go streams := db.Streams() id, _ := streams.XAdd(ctx, "events", map[string]string{"action": "login", "user": "alice"}) entries, _ := streams.XRange(ctx, "events", 0, -1, 100) // Consumer groups streams.XGroupCreate(ctx, "events", "workers", 0) entries, _ = streams.XReadGroup(ctx, "events", "workers", "worker-1", 10) streams.XAck(ctx, "events", "workers", entryID) ``` ## All 14 Models | Model | Accessor | Key Methods | |-------|----------|-------------| | SQL | `db.SQL()` | Query, Exec | | Key-Value | `db.KV()` | Get, Set, Del, HSet, LPush, ZAdd | | Vector | `db.Vector()` | Search | | TimeSeries | `db.TimeSeries()` | Insert, RangeAvg, Last | | Document | `db.Document()` | Insert, Find, Get | | Graph | `db.Graph()` | AddNode, AddEdge, Query | | FTS | `db.FTS()` | Index, Search | | Geo | `db.Geo()` | Distance, Within | | Blob | `db.Blob()` | Put, Get, Delete | | Streams | `db.Streams()` | XAdd, XRange, XReadGroup | | Columnar | `db.Columnar()` | Insert, Sum, Avg | | Datalog | `db.Datalog()` | Assert, Query | | CDC | `db.CDC()` | Subscribe, GetChanges | | PubSub | `db.PubSub()` | Publish, Subscribe | --- ## Deployment Source: https://neutron.build/docs/go/deployment Production deployment with lifecycle hooks, health checks, and Docker. ## Production Server ```go import "github.com/neutron-build/neutron/go/neutron" func main() { app := neutron.New( neutron.WithConfig(neutron.LoadConfig[ServerConfig]("APP")), ) app.Use(neutron.Chain( neutron.RequestID(), neutron.Logger(slog.Default()), neutron.Recover(), neutron.CORS(neutron.CORSOptions{AllowOrigins: []string{"*"}}), neutron.Compress(5), neutron.Timeout(30 * time.Second), )) setupRoutes(app) app.Run(":8080") } ``` ## Configuration Struct-based config with environment variable loading: ```go type ServerConfig struct { Server struct { Addr string `env:"ADDR" default:":8080"` ReadTimeout time.Duration `env:"READ_TIMEOUT" default:"5s"` WriteTimeout time.Duration `env:"WRITE_TIMEOUT" default:"10s"` ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" default:"30s"` } Database struct { URL string `env:"URL" required:"true"` MaxConns int `env:"MAX_CONNS" default:"25"` MinConns int `env:"MIN_CONNS" default:"5"` } Log struct { Level string `env:"LEVEL" default:"info"` Format string `env:"FORMAT" default:"json"` } } cfg, err := neutron.LoadConfig[ServerConfig]("APP") // Reads: APP_SERVER_ADDR, APP_DATABASE_URL, APP_LOG_LEVEL, etc. ``` ## Graceful Shutdown `app.Run()` handles `SIGINT`/`SIGTERM` automatically with a 30-second drain timeout. ### Lifecycle Hooks ```go app := neutron.New( neutron.WithLifecycle( neutron.LifecycleHook{ Name: "database", OnStart: func(ctx context.Context) error { return db.Connect(ctx) }, OnStop: func(ctx context.Context) error { return db.Close() }, }, neutron.LifecycleHook{ Name: "cache", OnStart: func(ctx context.Context) error { return cache.Warmup(ctx) }, OnStop: func(ctx context.Context) error { return cache.Flush() }, }, ), ) ``` - `OnStart` hooks run in registration order during startup - `OnStop` hooks run in **reverse** order during shutdown ## Health Check Built-in `GET /health` endpoint registered automatically: ```json { "status": "ok", "nucleus": true, "version": "0.1.0" } ``` Nucleus detection uses the `NucleusChecker` interface (optional). ## Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `APP_SERVER_ADDR` | `:8080` | Listen address | | `APP_SERVER_READ_TIMEOUT` | `5s` | HTTP read timeout | | `APP_SERVER_WRITE_TIMEOUT` | `10s` | HTTP write timeout | | `APP_SERVER_SHUTDOWN_TIMEOUT` | `30s` | Graceful shutdown timeout | | `APP_DATABASE_URL` | — | Connection string (required) | | `APP_LOG_LEVEL` | `info` | Log level | | `APP_LOG_FORMAT` | `json` | Log format | ## Docker ```dockerfile # Build stage FROM golang:1.22-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o /app/server ./cmd/server # Runtime stage FROM alpine:3.19 RUN apk add --no-cache ca-certificates COPY --from=builder /app/server /usr/local/bin/ EXPOSE 8080 CMD ["server"] ``` ### Docker Compose ```yaml services: app: build: . ports: - "8080:8080" environment: APP_DATABASE_URL: postgres://nucleus:5432/mydb APP_LOG_LEVEL: info depends_on: nucleus: condition: service_healthy nucleus: image: ghcr.io/neutron-build/nucleus:latest ports: - "5432:5432" volumes: - nucleus_data:/var/lib/nucleus healthcheck: test: ["CMD", "pg_isready", "-h", "localhost"] interval: 5s volumes: nucleus_data: ``` ## systemd ```ini [Unit] Description=My Neutron Go App After=network.target [Service] Type=simple User=myapp ExecStart=/usr/local/bin/server Environment=APP_DATABASE_URL=postgres://localhost:5432/mydb Restart=on-failure RestartSec=5 TimeoutStopSec=30 [Install] WantedBy=multi-user.target ``` ## Auto-Generated Endpoints Every Neutron Go app exposes: | Endpoint | Description | |----------|-------------| | `GET /health` | Health + Nucleus detection | | `GET /openapi.json` | OpenAPI 3.1 specification | | `GET /docs` | Swagger UI | --- ## Middleware Source: https://neutron.build/docs/go/middleware Built-in middleware and custom middleware patterns. Neutron Go middleware uses the standard `func(http.Handler) http.Handler` signature, compatible with the entire `net/http` ecosystem. ## Built-In Middleware ```go app := neutron.New( neutron.WithMiddleware( neutron.Logger(logger), // Structured logging neutron.Recover(), // Panic recovery neutron.RequestID(), // X-Request-ID neutron.CORS(neutron.CORSOptions{ // CORS headers AllowOrigins: []string{"https://example.com"}, AllowMethods: []string{"GET", "POST"}, AllowHeaders: []string{"Authorization"}, AllowCredentials: true, MaxAge: 3600, }), neutron.RateLimit(100, 1000), // 100 RPS, burst 1000 neutron.Timeout(30 * time.Second), // Request timeout neutron.Compress(gzip.DefaultCompression), // Gzip compression neutron.OTel(neutron.OTelOptions{ // OpenTelemetry ServiceName: "my-api", }), ), ) ``` | Middleware | Description | |-----------|-------------| | `Logger` | Method, path, status, duration, request_id | | `Recover` | Catches panics, returns 500 | | `RequestID` | Generates X-Request-Id header | | `CORS` | Cross-origin resource sharing | | `RateLimit` | Token bucket rate limiting | | `Timeout` | Per-request deadline | | `Compress` | Gzip response compression | | `OTel` | OpenTelemetry trace context | ## Custom Middleware ```go func TimingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() next.ServeHTTP(w, r) w.Header().Set("X-Response-Time", fmt.Sprintf("%dms", time.Since(start).Milliseconds())) }) } app := neutron.New( neutron.WithMiddleware(TimingMiddleware), ) ``` ## Scoped Middleware Apply middleware to specific route groups: ```go // Public routes — no auth public := router.Group("/public") // API routes — JWT required api := router.Group("/api", neutronauth.JWTMiddleware(secret), ) // Admin routes — JWT + RBAC admin := router.Group("/admin", neutronauth.JWTMiddleware(secret), neutronauth.RBACMiddleware("admin"), ) ``` ## Composing Middleware ```go authStack := neutron.Chain( neutronauth.JWTMiddleware(secret), neutronauth.RBACMiddleware("admin"), ) admin := router.Group("/admin", authStack) ``` ## Context Values Access middleware-injected values: ```go func handler(ctx context.Context, _ neutron.Empty) (string, error) { requestID := neutron.RequestIDFromContext(ctx) traceID := neutron.TraceIDFromContext(ctx) return fmt.Sprintf("request=%s trace=%s", requestID, traceID), nil } ``` --- ## Go Framework Source: https://neutron.build/docs/go/overview Net/http-based framework with typed handlers and Nucleus integration. Neutron for Go is a lightweight web framework built on `net/http`. Type-safe generic handlers, composable middleware, and first-class Nucleus integration. ## Features - **stdlib-based** — 100% compatible with `net/http` ecosystem - **Generic handlers** — `HandlerFunc[In, Out any]` with automatic extraction - **OpenAPI 3.1** — Auto-generated from route metadata - **RFC 7807** — Standardized error responses - **14 data models** — Full Nucleus client for all database models - **Real-time** — WebSocket hub + SSE broadcasting ## Hello World ```go package main import ( "context" "github.com/neutron-build/neutron/go/neutron" ) func main() { app := neutron.New( neutron.WithMiddleware( neutron.Logger(nil), neutron.Recover(), neutron.RequestID(), ), ) neutron.Get(app.Router(), "/", func(ctx context.Context, _ neutron.Empty) (string, error) { return "Hello, Neutron!", nil }) app.Run(":8080") } ``` ## Package Structure | Package | Purpose | |---------|---------| | `neutron` | Core framework (router, handlers, middleware, config) | | `nucleus` | Database client for all 14 Nucleus data models | | `neutronauth` | JWT, sessions, RBAC, API keys | | `neutroncache` | L1 (LRU) + L2 (Nucleus KV) tiered cache | | `neutronjobs` | Background job queue + cron scheduling | | `neutronrealtime` | WebSocket hub + SSE | | `neutrontest` | Test helpers | | `neutroncli` | Dev server, code generation, scaffolding | ## App Configuration ```go app := neutron.New( neutron.WithLogger(slog.Default()), neutron.WithLifecycle(db.LifecycleHook()), neutron.WithOpenAPIInfo("My API", "1.0.0"), neutron.WithMiddleware( neutron.Logger(logger), neutron.Recover(), neutron.RequestID(), neutron.CORS(neutron.CORSOptions{AllowOrigins: []string{"*"}}), neutron.RateLimit(100, 1000), neutron.Timeout(30 * time.Second), neutron.Compress(gzip.DefaultCompression), ), ) app.Run(":8080") // Graceful shutdown on SIGTERM/SIGINT ``` Auto-provides: - `GET /health` — Health check endpoint - `GET /openapi.json` — OpenAPI 3.1 spec - `GET /docs` — Swagger UI --- ## Quickstart Source: https://neutron.build/docs/go/quickstart Build a Go API server in minutes. ## Create a Project ```bash neutron new my-api --lang go cd my-api ``` Or add to an existing project: ```bash go get github.com/neutron-build/neutron/go@latest ``` ## Your First App ```go package main import ( "context" "log/slog" "time" "github.com/neutron-build/neutron/go/neutron" "github.com/neutron-build/neutron/go/nucleus" ) type User struct { ID int64 `db:"id" json:"id"` Name string `db:"name" json:"name"` Email string `db:"email" json:"email"` } type CreateUserInput struct { Name string `json:"name"` Email string `json:"email"` } type GetUserInput struct { ID int64 `path:"id"` } func main() { ctx := context.Background() logger := slog.Default() // Connect to Nucleus db, err := nucleus.Connect(ctx, "postgres://localhost:5432/mydb") if err != nil { panic(err) } // Create app app := neutron.New( neutron.WithLogger(logger), neutron.WithLifecycle(db.LifecycleHook()), neutron.WithOpenAPIInfo("My API", "1.0.0"), neutron.WithMiddleware( neutron.Logger(logger), neutron.Recover(), neutron.RequestID(), neutron.Timeout(30 * time.Second), ), ) // Routes router := app.Router() neutron.Get(router, "/users", func(ctx context.Context, _ neutron.Empty) ([]User, error) { return nucleus.Query[User](ctx, db.SQL(), "SELECT * FROM users") }) neutron.Get(router, "/users/{id}", func(ctx context.Context, input GetUserInput) (User, error) { return nucleus.QueryOne[User](ctx, db.SQL(), "SELECT * FROM users WHERE id = $1", input.ID) }) neutron.Post(router, "/users", func(ctx context.Context, input CreateUserInput) (User, error) { return nucleus.QueryOne[User](ctx, db.SQL(), "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *", input.Name, input.Email) }) app.Run(":8080") } ``` ## Run ```bash # Development neutron dev # Or directly go run . ``` ## Test It ```bash curl http://localhost:8080/health curl http://localhost:8080/users curl -X POST http://localhost:8080/users \ -H "Content-Type: application/json" \ -d '{"name": "Alice", "email": "alice@example.com"}' curl http://localhost:8080/users/1 ``` ## Auto-Generated Endpoints Every Neutron Go app includes: | Endpoint | Description | |----------|-------------| | `GET /health` | Health check | | `GET /openapi.json` | OpenAPI 3.1 spec | | `GET /docs` | Swagger UI | --- ## Real-Time Source: https://neutron.build/docs/go/realtime WebSocket hub and Server-Sent Events. The `neutronrealtime` package provides room-based WebSocket broadcasting and SSE streaming. ## WebSocket Hub Room-based message broadcasting: ```go import "github.com/neutron-build/neutron/go/neutronrealtime" hub := neutronrealtime.NewHub() // Mount WebSocket handler router.Handle("GET /ws/{room}", neutronrealtime.WebSocketHandlerWithRoom(hub, "default", upgrader)) ``` ### Hub API ```go // Connection management hub.Register(conn) hub.Unregister(conn) // Room operations hub.Subscribe("chatroom", conn) hub.Unsubscribe("chatroom", conn) // Broadcasting hub.Broadcast("chatroom", []byte("hello")) // To room hub.BroadcastAll([]byte("announcement")) // To all ``` ### Connection Each connection has a buffered send channel: ```go type Conn struct { ID string // Unique connection ID Send chan []byte // Buffered (256) } ``` ## Server-Sent Events ```go router.Handle("GET /events", neutronrealtime.SSEHandler( func(ctx interface{ Done() <-chan struct{} }, send func(string, []byte) error) error { ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case t := <-ticker.C: send("tick", []byte(t.Format(time.RFC3339))) } } }, )) ``` ## Chat Example ```go // POST /messages — send and broadcast neutron.Post(api, "/messages", func(ctx context.Context, input SendInput) (Message, error) { msg, err := nucleus.QueryOne[Message](ctx, db.SQL(), "INSERT INTO messages (room, user_id, body) VALUES ($1, $2, $3) RETURNING *", input.Room, userID, input.Body) // Broadcast to WebSocket clients data, _ := json.Marshal(msg) hub.Broadcast(input.Room, data) return msg, nil }) ``` ## Multi-Instance Sync Use Nucleus LISTEN/NOTIFY to sync across multiple server instances: ```go // Instance A: broadcast via NOTIFY db.Notify(ctx, "chat:general", messageJSON) // Instance B: listen and forward to local hub db.Listen(ctx, "chat:*", func(channel, payload string) { room := strings.TrimPrefix(channel, "chat:") hub.Broadcast(room, []byte(payload)) }) ``` --- ## Routing & Handlers Source: https://neutron.build/docs/go/routing Typed generic handlers with automatic parameter extraction. ## Defining Routes ```go router := app.Router() neutron.Get(router, "/users", listUsers) neutron.Post(router, "/users", createUser) neutron.Put(router, "/users/{id}", updateUser) neutron.Patch(router, "/users/{id}", patchUser) neutron.Delete(router, "/users/{id}", deleteUser) ``` ## Typed Handlers Handlers are generic functions with automatic input extraction and output serialization: ```go type HandlerFunc[In, Out any] func(ctx context.Context, input In) (Out, error) ``` ### Path Parameters ```go type GetUserInput struct { ID int64 `path:"id"` } type User struct { ID int64 `json:"id"` Name string `json:"name"` } func getUser(ctx context.Context, input GetUserInput) (User, error) { // input.ID extracted from /users/{id} return User{ID: input.ID, Name: "Alice"}, nil } neutron.Get(router, "/users/{id}", getUser) ``` ### Request Body ```go type CreateUserInput struct { Name string `json:"name"` Email string `json:"email"` } func createUser(ctx context.Context, input CreateUserInput) (User, error) { // input.Name and input.Email parsed from JSON body return User{ID: 1, Name: input.Name}, nil } neutron.Post(router, "/users", createUser) ``` ### No Input / No Output ```go // No input func health(ctx context.Context, _ neutron.Empty) (map[string]string, error) { return map[string]string{"status": "ok"}, nil } // No output (204 No Content) func deleteUser(ctx context.Context, input DeleteInput) (neutron.Empty, error) { return neutron.Empty{}, nil } ``` ## Route Groups Group routes with shared middleware: ```go api := router.Group("/api", neutronauth.JWTMiddleware(secret), ) neutron.Get(api, "/items", listItems) neutron.Post(api, "/items", createItem) admin := router.Group("/admin", neutronauth.RBACMiddleware("admin"), ) neutron.Get(admin, "/users", listAllUsers) ``` ## OpenAPI Metadata Annotate routes for auto-generated OpenAPI docs: ```go neutron.Get(router, "/todos", listTodos, neutron.WithSummary("List all todos"), neutron.WithDescription("Returns paginated todo items"), neutron.WithTags("todos"), neutron.WithOperationID("listTodos"), ) ``` ## Error Handling Return RFC 7807 Problem Details errors: ```go func getUser(ctx context.Context, input GetUserInput) (User, error) { user, err := db.FindUser(input.ID) if err != nil { return User{}, neutron.ErrNotFound("user not found") } return user, nil } ``` Error helpers: `ErrBadRequest`, `ErrUnauthorized`, `ErrForbidden`, `ErrNotFound`, `ErrConflict`, `ErrValidation`, `ErrRateLimited`, `ErrInternal`. ## Middleware Standard `net/http` middleware signature: ```go func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token == "" { neutron.WriteError(w, r, neutron.ErrUnauthorized("missing token")) return } next.ServeHTTP(w, r) }) } ``` Built-in: `Logger`, `Recover`, `RequestID`, `CORS`, `RateLimit`, `Timeout`, `Compress`, `OTel`. ## Static Files ```go router.Mount("/static", http.FileServer(http.Dir("./public"))) ``` --- ## Ecosystem Extensions Source: https://neutron.build/docs/julia/ecosystem Bridges to DataFrames, DifferentialEquations, Flux, CUDA, Makie, and more. Neutron for Julia integrates with the scientific-computing ecosystem through package extensions. Each loads automatically when you import both `NeutronJulia` and the target package (the Julia weak-dependency pattern) — no extra setup. ## DataFrames ```julia using NeutronJulia, DataFrames rows = query(sql(client), "SELECT * FROM sales") df = DataFrame(rows) # Automatic conversion ``` ## DifferentialEquations Store ODE/SDE solutions directly in Nucleus TimeSeries: ```julia using NeutronJulia, DifferentialEquations # Solve a system prob = ODEProblem(lorenz!, u0, tspan, p) sol = solve(prob, Tsit5(), saveat=0.01) # Store solution — each variable becomes a separate series ts = timeseries(client) store!(ts, sol, "lorenz:run1"; variable_names=["x", "y", "z"]) # Retrieve later t, u = load_solution(ts, "lorenz:run1", ["x", "y", "z"]) # t::Vector{Float64} (seconds) # u::Matrix{Float64} (n_vars x n_points) ``` ## ModelingToolkit Symbolic variable names flow through to TimeSeries storage: ```julia using NeutronJulia, ModelingToolkit # Variable names from an MTK system are used as series names automatically ``` ## Flux (ML) Generate embeddings with Flux models and store them in Nucleus Vector: ```julia using NeutronJulia, Flux model = Chain(Dense(768, 384), relu, Dense(384, 128)) embedding = model(input_data) v = vector(client) # Store and search embeddings ``` ## CUDA GPU-accelerated vector similarity search: ```julia using NeutronJulia, CUDA # Vector operations accelerated on NVIDIA GPUs ``` ## Makie Plot TimeSeries data directly: ```julia using NeutronJulia, CairoMakie ts = timeseries(client) fig = plot_timeseries!(ts, "cpu_usage", start_ms, end_ms) save("cpu.png", fig) ``` Each extension lives in `ext/` (`NeutronJuliaDataFramesExt.jl`, `NeutronJuliaDiffEqExt.jl`, `NeutronJuliaMTKExt.jl`, `NeutronJuliaGraphsExt.jl`, `NeutronJuliaFluxExt.jl`, `NeutronJuliaCUDAExt.jl`, `NeutronJuliaMakieExt.jl`) and requires Julia 1.9+. --- ## Nucleus Client Source: https://neutron.build/docs/julia/nucleus-client The typed Julia client for all 14 Nucleus data models. Neutron for Julia accesses all 14 Nucleus data models through typed accessors, using multiple dispatch so each model exposes an idiomatic Julia API. Connect once, then reach any model. > **Status: not covered by the live conformance suite.** The `sdk-live` CI matrix > runs python, go, typescript and elixir against a running engine; julia is not > in it. The signatures below are taken from the SDK source, not from executed > calls against Nucleus. When that suite was first pointed at an SDK it found > breakage in eight methods on day one — and a source audit of this page on > 2026-08-15 found four documented calls that could not have worked. Checked > against source 2026-08-15. ```julia client = NeutronJulia.connect(url) sql(client) # SQL queries kv(client) # Key-Value (TTL, lists, hashes, sets, sorted sets, HyperLogLog) vector(client) # Vector search (HNSW, IVFFlat) timeseries(client) # Time series (Gorilla compression) document(client) # Document store (JSONB) graph(client) # Graph (Cypher, adjacency) fts(client) # Full-text search (BM25) geo(client) # Geospatial (R-tree) blob(client) # Blob storage (BLAKE3) streams(client) # Streams (consumer groups) columnar(client) # Columnar analytics datalog(client) # Datalog queries cdc(client) # Change Data Capture pubsub(client) # PubSub (LISTEN/NOTIFY) ``` ## SQL Parameters are **varargs, not an array**. `query(s, sql, [true])` binds a one-element array to `$1`; `query(s, sql, true)` binds the boolean: ```julia s = sql(client) rows = query(s, "SELECT id, name, email FROM users WHERE active = \$1", true) row = query_one(s, "SELECT * FROM users WHERE id = \$1", 42) n = execute!(s, "INSERT INTO users (name, email) VALUES (\$1, \$2)", "Alice", "alice@example.com") ``` `query` returns a column table (`Tables.columntable`), so it drops straight into `DataFrame(rows)`. `query_one` returns a single `NamedTuple` and **throws a `NucleusError` with status 404 when there are no rows** — it does not return `nothing`, so a lookup that may legitimately miss belongs in a `try` or should go through `query` and check for empty. `execute!` returns the affected-row count. ## Key-Value ```julia k = kv(client) kv_set!(k, "key", "value") # or the alias set! kv_get(k, "key") # "value", or nothing when absent kv_delete!(k, "key") kv_set!(k, "session", "abc", ttl=3600) # ttl in seconds, keyword argument # Collections lpush!(k, "queue", "item1") hset!(k, "user:1", "name", "Alice") sadd!(k, "tags", "rust", "julia") zadd!(k, "leaderboard", 100.0, "player1") pfadd!(k, "visitors", "user1", "user2") # HyperLogLog ``` **On the `kv_` prefix.** Every scalar KV operation is exported twice: as `kv_get` / `kv_set!` / `kv_delete!` / `kv_exists` / `kv_incr!` / `kv_ttl` / `kv_expire!`, and — where the short name does not collide with Julia's `Base` — as an alias (`set!`, `setnx!`, `exists`, `incr!`, `ttl`, `expire!`, `dbsize`, `flushdb!`). There is deliberately **no `get` and no `delete!` alias**: those would clash with `Base.get` and `Base.delete!` and silently change dispatch for the whole session. Reach for the `kv_` form when in doubt — it always exists. The collection operations (`lpush!`, `hset!`, `sadd!`, `zadd!`, `pfadd!`, …) have no prefix, because none of them collide. ## Vector Search ```julia v = vector(client) # The dimension is taken from the column, not passed in. `column` defaults to # "embedding"; `ef` and `m_param` tune HNSW build quality. create_index!(v, "embeddings"; column="embedding", metric=Cosine, ef=200, m_param=16) results = search(v, "embeddings", query_vec; k=10, metric=Cosine) d = dims(v, "embeddings") ``` `metric` takes the exported `DistanceMetric` values `L2`, `Cosine` and `InnerProduct` — not strings. There is no `index_type` keyword: `create_index!` builds an HNSW index, and IvfFlat is created through SQL directly. ## TimeSeries ```julia ts = timeseries(client) insert!(ts, "cpu_usage", [now_ms, now_ms + 1000], [0.75, 0.82]) last = last_value(ts, "cpu_usage") count = ts_count(ts, "cpu_usage") avg = range_avg(ts, "cpu_usage", start_ms, end_ms) ``` ## Graph ```julia g = graph(client) # `properties` is a KEYWORD argument — passing the Dict positionally is a # MethodError, not a silently ignored extra. alice = add_node!(g, "Person"; properties=Dict("name" => "Alice")) bob = add_node!(g, "Person"; properties=Dict("name" => "Bob")) add_edge!(g, alice, bob, "KNOWS"; properties=Dict("since" => 2024)) neighbors(g, alice) # Vector{Int64} path = shortest_path(g, alice, bob) # Vector{Int64} ``` Node and edge ids are `Int64` and are what every other graph call takes — `add_edge!`, `neighbors`, `shortest_path`, `delete_node!` and `delete_edge!` all address nodes by the id `add_node!` returned. Cypher is available too, through `graph_query(g, "MATCH ...")`. ## Transactions `transaction` takes a function, so it is used as a `do` block. COMMIT and ROLLBACK are automatic — the block committing on normal return and rolling back on any exception, which is then rethrown: ```julia transaction(client) do tx execute!(sql(tx), "UPDATE accounts SET balance = balance - 100 WHERE id = 1") execute!(sql(tx), "UPDATE accounts SET balance = balance + 100 WHERE id = 2") end ``` The block receives a `NucleusTransaction`, and **every model accessor works on it** — `sql(tx)`, `kv(tx)`, `vector(tx)`, and so on — so a transaction can span models. Use `tx` rather than `client` inside the block; reaching for the outer `client` issues statements on the same connection but outside the reader's intent, which is the kind of thing that reads correct and is not. There is no `commit!` or `rollback!` to call: neither is exported, and the lifecycle is the block's. The return value of the block is the return value of `transaction`: ```julia balance = transaction(client) do tx query_one(sql(tx), "SELECT balance FROM accounts WHERE id = \$1", 1) end ``` --- ## Overview Source: https://neutron.build/docs/julia/overview Scientific computing SDK with Nucleus client and ecosystem extensions. Neutron for Julia provides a typed Nucleus client and integrations with the Julia scientific-computing ecosystem — DifferentialEquations.jl, Flux.jl, CUDA.jl, and Makie.jl. It connects over the PostgreSQL wire protocol via LibPQ.jl. - [Nucleus client](/docs/julia/nucleus-client) — the typed client for all 14 data models, plus transactions. - [Ecosystem extensions](/docs/julia/ecosystem) — DataFrames, DifferentialEquations, Flux, CUDA, Makie bridges. ## Quick Start ```julia using NeutronJulia # Connect to Nucleus client = NeutronJulia.connect("postgres://localhost:5432/mydb") # SQL queries rows = query(sql(client), "SELECT * FROM users WHERE active = true") # Key-Value set!(kv(client), "session:abc", "user_42") val = get(kv(client), "session:abc") # Vector search results = search(vector(client), "embeddings", [0.1, 0.2, 0.3]; k=10, metric=Cosine) close(client) ``` Transport: LibPQ.jl (PostgreSQL wire protocol). Min Julia: **1.9**. ## Connection Pool ```julia pool = ConnectionPool("postgres://localhost:5432/mydb", size=4) with_pool(pool) do client rows = query(sql(client), "SELECT * FROM users") set!(kv(client), "cache:result", JSON3.write(rows)) end # Pool stats idle_count(pool) # Available connections close(pool) ``` ## Project Structure ``` julia/ ├── Project.toml # Package manifest ├── src/ │ ├── NeutronJulia.jl # Main module (exports all 14 models) │ ├── client.jl # Connection management │ ├── pool.jl # Connection pooling │ └── model/ # Data model accessors │ ├── sql.jl │ ├── kv.jl │ ├── vector.jl │ └── ... # 14 model files ├── ext/ # Ecosystem extensions │ ├── NeutronJuliaDataFramesExt.jl │ ├── NeutronJuliaDiffEqExt.jl │ ├── NeutronJuliaMTKExt.jl │ ├── NeutronJuliaGraphsExt.jl │ ├── NeutronJuliaFluxExt.jl │ ├── NeutronJuliaCUDAExt.jl │ └── NeutronJuliaMakieExt.jl └── test/ ``` --- ## Global Middleware Source: https://neutron.build/docs/middleware/global-middleware Intercepting requests across the application. Middleware allows you to run code before a request reaches your route handlers. It is built on web standards (Request/Response). ## Creating Middleware Create a `middleware.ts` file in your `src` directory. ```ts // src/middleware.ts import type { MiddlewareFn } from "@neutron-build/core"; export const middleware: MiddlewareFn[] = [ async (request, context, next) => { console.log(`[${request.method}] ${request.url}`); const response = await next(); response.headers.set("X-Powered-By", "Neutron"); return response; } ]; ``` ## Middleware Signature ```ts type MiddlewareFn = ( request: Request, context: Record, next: () => Promise ) => Promise; ``` - **request**: The incoming request. - **context**: A shared object for passing data to other middleware or loaders. - **next**: A function that calls the next middleware (or the route handler). --- ## Request Context Source: https://neutron.build/docs/middleware/request-context Sharing data between middleware and loaders. The `context` object is a shared mutable object that passes through the middleware chain and eventually arrives at your `loader` or `action`. ## Populating Context Middleware can add data to the context. ```ts // src/middleware.ts export const middleware = [ async (request, context, next) => { const user = await getUserFromSession(request); // Add user to context context.user = user; return next(); } ]; ``` ## Consuming Context Your route handlers can then access this data. ```tsx // src/routes/dashboard.tsx export async function loader({ context }: LoaderArgs) { // Access data set by middleware const user = context.user; return { user }; } ``` This pattern is ideal for authentication (passing the user object) or dependency injection (passing a database client). --- ## Route Middleware Source: https://neutron.build/docs/middleware/route-middleware Scoped middleware for specific routes. In addition to global middleware, you can define middleware that only applies to a specific subdirectory of your application. ## Usage Place a `middleware.ts` file inside a route directory. ``` src/routes/ app/ middleware.ts <-- Runs for all /app/* routes dashboard.tsx settings.tsx ``` ## Execution Order Middleware runs in a top-down order: 1. Global `src/middleware.ts` 2. Route `src/routes/app/middleware.ts` 3. Nested `src/routes/app/admin/middleware.ts` 4. Route Loader/Action ## Common Use Cases - **Authentication**: Checking if a user is logged in before accessing `/app`. - **Logging**: Tracking requests to specific API endpoints. - **Headers**: Adding CORS headers to API routes. --- ## Modeling and Verification Architecture Source: https://neutron.build/docs/modeling/architecture How Lean, Quint, and physical simulation fit Neutron, with current boundaries and a proposed integration architecture. Neutron is a modular, multi-language ecosystem. Application SDKs share observable behavior through the framework contract, and Nucleus provides a common database protocol. Specialist components contribute models, computation, or verification without needing to implement an HTTP framework. ## Current structure | Component | Present in the repository | Integration boundary still missing | |---|---|---| | Lean | A Lake library of hand-written Nucleus models, specifications, proofs, and an axiom audit | Reusable application libraries and a checked connection to application implementations | | Quint | Protocol specs, scenario tests, random simulation, and a Rust model harness | Trace replay against production services; the Rust harness currently mirrors the models | | Modelica | Python modeling and SciPy simulation, optional FMI/Julia integrations, result storage and visualization | Complete distribution of the source packages and independently validated executable FMU export | These are useful foundations. They do not yet form a single application-facing modeling workflow. The architecture below is a **proposed direction**, not an API available today. ## Keep each tool responsible for its own semantics Lean should own pure models, invariants, and deductive proofs. Separate reusable application libraries from the existing Nucleus-specific library, preserving its module names and proof history. Check declared assumptions as well as successful compilation. Keep I/O at explicit boundaries so a theorem’s statement makes clear which part of the application it covers. Quint should own state transitions, concurrent actions, and failure scenarios. Keep model checking distinct from sampled simulation. Record the checker, bounds, constants, seed, and property for each run. Connect replayable traces to real implementations through small adapters that compare observable state and results. Modelica integration should own numerical simulation workflows. Keep Python orchestration, Julia numerical backends, external Modelica compilation, and FMI execution behind explicit interfaces. Preserve solver tolerances, units, parameters, initial conditions, and backend versions with results. Use an external Modelica compiler for Modelica-language source instead of assuming the Python modeling API implements that language. A single universal intermediate representation for proofs, protocol models, and differential equations would lose important distinctions. Prefer small, versioned contracts at the points where these components actually exchange data. ## Connect through artifacts and adapters The proposed shared workflow would record a model identifier and revision, inputs, tool versions, assumptions, outputs, and evidence files. Tool-specific fields stay intact: proof dependencies for Lean, traces and bounds for Quint, and solver configuration and trajectories for simulation. Language adapters should translate explicit operations and observations. For example, a Quint reservation action can invoke a real Go endpoint and compare its response and resulting state with the model. Passing those checks is conformance evidence for the tested traces, not a proof of all Go executions. Nucleus can store results for comparison and presentation. Local files should remain usable for development and CI without requiring a database. Shared storage does not establish semantic equivalence between two implementations. ## Validate one complete example before generalizing 1. **Lean:** model an inventory reservation rule, prove preservation of its invariant, and connect an implementation test to the same inputs and expected outcomes. Make the boundary between theorem and implementation test visible. 2. **Quint:** replay a session or circuit-breaker trace against the existing Rust implementation. Include a deliberately faulty implementation that the comparison must reject. Preserve failing traces as regression fixtures. 3. **Simulation:** run a small physical model against an analytical reference, store and reload its results, and render them in a TypeScript application. Validate an imported FMU using its external runtime; validate any portable export in an independent FMI tool. After those examples work, add project templates and a common CLI entry point that delegates to the tools. Build automation should report missing dependencies as unavailable or skipped, and required release checks should fail if their backend did not run. ## Release criteria - A clean consumer installation works outside the monorepo. - Examples exercise real SDK implementations and actual optional backends where required. - Reports distinguish proofs, bounded checks, sampled tests, and numerical results. - Negative controls demonstrate that a broken invariant or implementation is detected. - Changes to a model, schema, toolchain, or solver make previous evidence identifiable as belonging to the earlier revision. For Lean, retain the axiom audit and document any future extraction toolchain. For Quint, do not treat a mirrored Rust model as a live-engine adapter. For simulation, round-tripping a Python object does not establish executable FMI interoperability. ## References - [Neutron Lean](/lean) and the [existing proof suite](/docs/verification/lean4) - [Quint specifications and checks](/docs/verification/quint) - [Simulation implementation and limitations](/docs/modeling/modelica) - [Lean’s elaboration and kernel](https://lean-lang.org/doc/reference/latest/Elaboration-and-Compilation/) - [Quint model checkers](https://quint.sh/docs/model-checkers) - [FMI specification](https://fmi-standard.org/docs/main/) --- ## Physical Simulation Source: https://neutron.build/docs/modeling/modelica The current Neutron Modelica implementation, optional integrations, and interoperability boundaries. [Neutron Modelica](/modelica) is Neutron’s physical-simulation component. Its implementation lives in `modelica/`; the Python distribution is named `neutron-sim`. ## What exists today The `neutron_sim` package exposes `Variable`, `Parameter`, `Equation`, `der`, `Connector`, `Component`, `System`, `connect`, and `simulate`. Domain libraries cover electrical, mechanical, thermal, and fluid components. SciPy provides numerical integration. Optional modules add FMI import and orchestration, a `juliacall` bridge, Nucleus result storage, plotting, MCP tools, and scikit-learn surrogate models. These require their own dependencies and, for live integrations, their runtimes or services. The separate `neutron_modelica` source package contains FMI runtime, Kirchhoff circuit, and Julia bridge helpers. The current `pyproject.toml` includes only `neutron_sim` in the wheel. Source-tree availability does not establish that both packages are installed from a built distribution. ## Develop from the repository From `modelica/`: ```bash python -m pip install -e ".[dev]" python -m pytest ``` Select extras such as `fmi`, `julia`, and `ai` for the integrations you use. Julia also needs a working Julia runtime and its numerical packages. Nucleus integration tests require a configured database. Review skipped tests before treating a local test run as validation of those integrations. ## Modelica and FMI boundaries Neutron’s Python modeling API is not a general compiler for `.mo` source. Use an external Modelica compiler for Modelica-language models and validate the resulting FMU against the selected importer. The current export function creates `modelDescription.xml` and serialized Python resources for Neutron round trips. It does not supply a native executable FMI implementation. General interchange requires a compatible implementation as well as metadata; see the [FMI specification](https://fmi-standard.org/docs/main/). ## Recommended integration direction Keep model authoring, solving, and result storage independently usable. Validate numerical results against analytical references where available, document solver tolerances, and test optional backends explicitly. A portable FMU export needs an independent import-and-execute test, not only a Neutron round trip. See the [proposed modeling architecture](/docs/modeling/architecture) for the shared artifact and application-integration approach. These additions are future work. --- ## Inference Source: https://neutron.build/docs/mojo/inference Neural network layers, the generation pipeline, graph fusion, and serving. Neutron for Mojo ships pre-built model architectures, an end-to-end generation pipeline, an algebraic graph optimizer, and serving surfaces. See the [overview](/docs/mojo/overview) for the project's preview status. ## Neural Network Layers ### Models Pre-built model architectures: | Model | Description | |-------|-------------| | LLaMA | Meta's LLaMA family | | Phi | Microsoft Phi series | | Mistral | Mistral AI models | | GPT | GPT-2/NeoX variants | ### Key Components ```mojo from neutron.nn import Attention, KVCache, RoPE, BPETokenizer # Attention with KV cache let cache = KVCache(max_seq_len=4096, n_heads=32, head_dim=128) let output = Attention(Q, K, V, cache) # Rotary position embeddings let Q_rot, K_rot = RoPE(Q, K, position) # Tokenization let tokenizer = BPETokenizer.load("tokenizer.json") let tokens = tokenizer.encode("Hello, world!") let text = tokenizer.decode(tokens) ``` ## Inference Pipeline End-to-end text generation from quantized models: ```mojo from neutron.nn import Q4Model, q4_pipeline_generate, PipelineConfig let model = Q4Model.load("model.gguf") let tokenizer = BPETokenizer.load("tokenizer.json") let config = PipelineConfig( max_tokens=512, temperature=0.7, top_p=0.9, chat_template="llama", # or "chatml" ) let response = q4_pipeline_generate(model, tokenizer, "What is Rust?", config) ``` ### Pipeline Steps 1. Apply chat template (LLaMA, ChatML, etc.) 2. Encode prompt with BPE tokenizer 3. Create KV cache (quantized to Q8 for memory efficiency) 4. Prefill all prompt tokens 5. Autoregressive decode with sampling (temperature, top-p, repetition penalty) 6. Decode output tokens back to text ## E-Graph Optimizer Algebraic rewrite engine for compute graph fusion — 30+ rules: | Category | Examples | |----------|---------| | Identity | `x + 0 → x`, `x * 1 → x` | | Idempotence | `relu(relu(x)) → relu(x)` | | Involution | `transpose(transpose(x)) → x` | | Translation invariance | `softmax(x + c) → softmax(x)` | | Operator fusion | `gelu(linear(x)) → fused_linear_gelu(x)` | | Reassociation | `matmul(A, matmul(B, C)) → matmul(matmul(A, B), C)` | The optimizer represents the compute graph as an e-graph (equality saturation) and applies rewrite rules until no more simplifications are possible. ## Serving ### Text Protocol Lightweight stdin/stdout protocol for local inference: ``` REQUEST prompt=What is Rust? max_tokens=256 temperature=0.7 RESPONSE text=Rust is a systems programming language... tokens=42 time_ms=1523 ``` ### HTTP Server OpenAI-compatible API: ``` POST /v1/completions POST /v1/chat/completions ``` Supports streaming responses. ## Model I/O | Format | Read | Write | Description | |--------|------|-------|-------------| | GGUF | Yes | Yes | llama.cpp quantized models | | SafeTensors | Yes | Yes | HuggingFace format | | Checkpoints | Yes | Yes | Training checkpoints | --- ## Overview Source: https://neutron.build/docs/mojo/overview High-performance ML inference with SIMD kernels and quantization. Neutron for Mojo is an ML inference library targeting Mojo 1.0. It provides tensor operations, quantization formats, neural-network layers, and an inference serving pipeline — all with SIMD-accelerated kernels. > **Status: preview.** The implementation is complete for pre-1.0 Mojo syntax and is awaiting the Mojo 1.0 compiler release for testing and migration. Treat this as a preview, not a production dependency. - [Tensors & kernels](/docs/mojo/tensors) — typed tensors and SIMD-accelerated operations. - [Quantization](/docs/mojo/quantization) — the supported quantization formats. - [Inference](/docs/mojo/inference) — model layers, the generation pipeline, graph fusion, and serving. ## Project Structure ``` mojo/ ├── tensor/ # Multi-dtype tensor ops, SIMD kernels ├── quant/ # Quantization (NF4, Q4_K, Q8_0, FP8) ├── nn/ # Neural network layers, models, pipelines ├── serve/ # HTTP + text protocol inference server ├── train/ # Training loops, optimizers ├── optim/ # Adam, SGD, AdamW, schedules ├── autograd/ # Automatic differentiation ├── fusion/ # E-graph algebraic optimization ├── data/ # Tokenizers, datasets, data loaders ├── io/ # GGUF, SafeTensors, checkpoints ├── model/ # LLaMA, Phi, Mistral, GPT ├── python/ # Python interop bindings ├── dlpack/ # DLPack tensor exchange └── cli/ # Inference + benchmark CLI ``` --- ## Quantization Source: https://neutron.build/docs/mojo/quantization Quantization formats for production LLM deployment. Neutron for Mojo supports the quantization formats used in production LLM deployment, from basic 4-bit to FP8. See the [overview](/docs/mojo/overview) for the project's preview status. | Format | Bits | Block Size | Use Case | |--------|------|------------|----------| | `Q4_0` | 4 | 32 | Basic 4-bit | | `Q4_1` | 4+min | 32 | 4-bit with offset | | `Q8_0` | 8 | 32 | High-quality 8-bit | | `Q4_K_S` | 4 | K-quant | Small K-quant | | `Q4_K_M` | 4 | K-quant | Most common (best quality/size) | | `NF4` | 4 | NormalFloat | QLoRA fine-tuning | | `FP8_E4M3` | 8 | — | Training (4 exp, 3 mantissa) | | `FP8_E5M2` | 8 | — | Inference (5 exp, 2 mantissa) | ```mojo from neutron.quant import QuantType let qt = QuantType.Q4_K_M qt.bits_per_element() # 4 qt.block_size() # 32 ``` --- ## Tensors & Kernels Source: https://neutron.build/docs/mojo/tensors Type-safe tensors with compile-time dimension checking and SIMD-accelerated kernels. Neutron for Mojo provides type-safe tensors with compile-time dimension checking, backed by SIMD-accelerated kernels on the hot path. See the [overview](/docs/mojo/overview) for the project's preview status. ## Tensor Operations ```mojo from neutron.tensor import Tensor, Dim, matmul, softmax, rmsnorm # Typed dimensions (compile-time shape safety) alias Batch = Dim[0] alias Seq = Dim[1] alias Hidden = Dim[2] # Core operations let output = matmul(weights, input) # Matrix multiply let probs = softmax(logits, axis=-1) # Softmax let normed = rmsnorm(x, weight, 1e-5) # RMS normalization let activated = silu(x) # SiLU activation ``` ## SIMD Kernels Hot-path operations use SIMD intrinsics for maximum throughput: | Function | Description | |----------|-------------| | `simd_dot(a, b)` | Dot product | | `simd_matvec(A, v)` | Matrix-vector multiply | | `simd_rmsnorm(x, w, eps)` | RMS layer normalization | | `simd_attention_scores(Q, K, scale)` | Attention score computation | | `simd_online_softmax_attention(Q, K, V)` | Fused attention (FlashAttention-style) | ## Additional Operations ```mojo layernorm(x, weight, bias) # Layer normalization gelu(x) # GELU activation swiglu(x, w1, w2, w3) # SwiGLU (used in LLaMA) ``` --- ## Components Source: https://neutron.build/docs/native/components Native UI components for iOS and Android. Neutron Native provides two tiers of components: universal components that work on web and native, and native-only components. ## Tier 1 — Universal These components work on both web and native platforms: ```tsx import { View, Text, Image, Pressable, TextInput, Link } from '@neutron-build/native'; ``` ### View The fundamental layout component (maps to `RCTView`): ```tsx Content ``` ### Text Display text (maps to `RCTText`): ```tsx Hello World ``` ### Image Display images (maps to `RCTImage`): ```tsx ``` ### Pressable Touchable wrapper (maps to `RCTTouchableOpacity`): ```tsx alert('Pressed!')}> Tap Me ``` ### TextInput Text input field (maps to `RCTTextInput`): ```tsx ``` ### Other Tier 1 | Component | Description | |-----------|-------------| | `Link` | Navigation wrapper (uses router) | | `ActivityIndicator` | Loading spinner | | `Switch` | Toggle switch | | `Slider` | Range picker | | `KeyboardAvoidingView` | Keyboard-safe layout | | `RefreshControl` | Pull-to-refresh | ## Tier 3 — Native Only Import from the native subpath: ```tsx import { ScrollView, FlatList, Modal, StatusBar, SafeAreaView } from '@neutron-build/native/native'; ``` ### ScrollView Virtualized scrollable container: ```tsx {items.map(item => )} ``` ### FlatList Recycled virtualized list for large datasets: ```tsx } keyExtractor={item => item.id} ItemSeparatorComponent={() => } ListHeaderComponent={Header} ListFooterComponent={Footer} refreshing={isRefreshing} onRefresh={handleRefresh} /> ``` ### Modal Overlay dialog: ```tsx setShowModal(false)}> Modal Content ``` ### StatusBar Control the system status bar: ```tsx ``` ### SafeAreaView Respect device notches and safe areas: ```tsx Safe from notches ``` ## Props All components accept: - `style` — React Native StyleSheet object (numbers for dimensions, no CSS units) - `testID` — For end-to-end testing - `accessible` + `accessibilityLabel` — Accessibility support --- ## Native (iOS/Android) Source: https://neutron.build/docs/native/overview Preact components rendered as native views via React Native Fabric. Neutron Native lets you build iOS and Android apps using Preact, rendered as native views through React Native's Fabric architecture and JSI. > **Preview.** Neutron Native is in active development and not yet published to npm. > The package names below (`@neutron-build/native`, etc.) are the planned published > names; the API may change before release. ## How It Works 1. You write Preact JSX components 2. Preact renders to a virtual DOM tree 3. The Neutron renderer maps components to UIManager JSI calls 4. React Native Fabric renders actual native views (UIView, android.view.View) No WebView — your components become real native views. ## Packages | Package | Purpose | |---------|---------| | `@neutron-build/native` | Core renderer, components, router, navigation | | `@neutron-build/native-styling` | NeutronWind (className → StyleSheet at build time) | | `@neutron-build/native-cli` | CLI for dev, build, run | ## Quick Start ```bash # Create a new app neutron-native new my-app cd my-app # Install dependencies npm install # Start development neutron-native dev --ios neutron-native dev --android ``` ## App Entry Point ```tsx // index.js import { NeutronApp } from '@neutron-build/native'; import App from './app/_layout'; NeutronApp({ component: App, appName: 'MyApp' }); ``` ## Basic Component ```tsx import { View, Text, Pressable } from '@neutron-build/native'; function HomeScreen() { return ( Hello, Neutron! console.log('pressed')} style={{ padding: 12, backgroundColor: '#3b82f6', borderRadius: 8 }} > Press Me ); } ``` ## CLI Commands ```bash # Development server neutron-native dev [--ios | --android] [--port 8081] # Production build neutron-native build [--ios | --android] [--release] # Run on simulator/device neutron-native run-ios [--simulator "iPhone 16"] neutron-native run-android [--device ] # Scaffold new project neutron-native new ``` ## React Compatibility For libraries that import from `react`: ```js import { preactCompatAliases } from '@neutron-build/native/compat'; // In rspack.config.ts export default { resolve: { alias: preactCompatAliases(), }, }; ``` --- ## Routing Source: https://neutron.build/docs/native/routing File-based routing with Stack, Tabs, and Drawer navigation. Neutron Native uses file-based routing with the `app/` directory convention, similar to Next.js and Expo Router. ## File Structure ``` app/ ├── _layout.tsx # Root layout (Stack, Tabs, or Drawer) ├── index.tsx # Home screen (/) ├── about.tsx # About screen (/about) ├── settings/ │ ├── _layout.tsx # Nested layout │ ├── index.tsx # /settings │ └── profile.tsx # /settings/profile └── users/ └── [id].tsx # Dynamic route (/users/42) ``` ## Navigation ```tsx import { useRouter, useParams, usePathname } from '@neutron-build/native/router'; function HomeScreen() { const router = useRouter(); return ( router.navigate('/about')}> Go to About ); } function UserScreen() { const { id } = useParams(); // URL params const pathname = usePathname(); // Current path return User {id}; } ``` ### Router Methods | Method | Description | |--------|-------------| | `navigate(path, { params })` | Push a new screen | | `goBack()` | Pop the current screen | | `replace(path)` | Replace without adding to history | ### Hooks | Hook | Returns | |------|---------| | `useRouter()` | Router state + methods | | `useParams()` | URL-decoded route parameters | | `usePathname()` | Current path as signal | | `useRoute()` | Current route record | | `useSearchParams()` | Query parameters | ## Navigation Layouts ### Stack Push/pop navigation with slide animations: ```tsx // app/_layout.tsx import { Stack } from '@neutron-build/native/navigation'; export default function Layout() { return ( ); } ``` Features: - Auto back button (hidden on root screen) - Configurable header title, tint color, style - `Stack.push(name, params)` / `Stack.pop()` imperative API ### Tabs Bottom tab bar navigation: ```tsx import { Tabs } from '@neutron-build/native/navigation'; export default function Layout() { return ( ); } ``` Features: - Icons and labels - Badge counts - Lazy-load screens on first tab press - Persistent state across tab switches ### Drawer Slide-in sidebar navigation: ```tsx import { Drawer } from '@neutron-build/native/navigation'; export default function Layout() { return ( ); } ``` Features: - Left or right position - Swipe gesture to open/close - Overlay with backdrop - Hamburger icon in header ## Deep Linking Full support for iOS URL schemes and Android intent filters: ```tsx import { initDeepLinks, handleDeepLink } from '@neutron-build/native/router'; // Register listener on app startup initDeepLinks(); // Or handle manually handleDeepLink('myapp://users/42'); ``` ## Link Component Navigate declaratively: ```tsx import { Link } from '@neutron-build/native'; Go to About ``` --- ## NeutronWind Source: https://neutron.build/docs/native/styling Tailwind-like styling compiled to native StyleSheets at build time. NeutronWind transforms Tailwind-like `className` strings into React Native StyleSheet objects at build time. Zero runtime parsing. ## How It Works ```tsx // You write: Hello // Build plugin transforms to: Hello ``` The transformation happens at build time via Babel or Rspack — no runtime CSS parsing on the device. ## Setup ### Babel (Metro / Expo) ```js // babel.config.js module.exports = { plugins: ['@neutron-build/native-styling/babel-plugin'], }; ``` ### Rspack (Re.Pack) ```js // rspack.config.ts module.exports = { module: { rules: [{ test: /\.[jt]sx?$/, use: ['@neutron-build/native-styling/rspack-loader'], }], }, }; ``` ## Token Reference ### Spacing Base unit: 4px. Scale: 0–96. | Class | Value | Class | Value | |-------|-------|-------|-------| | `p-0` | 0 | `m-0` | 0 | | `p-1` | 4px | `m-1` | 4px | | `p-2` | 8px | `m-2` | 8px | | `p-4` | 16px | `m-4` | 16px | | `p-8` | 32px | `m-8` | 32px | | `p-16` | 64px | `m-16` | 64px | Directional variants: `px-*`, `py-*`, `pt-*`, `pb-*`, `pl-*`, `pr-*`, `mx-*`, `my-*`, `mt-*`, `mb-*`, `ml-*`, `mr-*`. ### Flexbox | Class | Style | |-------|-------| | `flex` | `{ display: 'flex' }` | | `flex-1` | `{ flex: 1 }` | | `flex-row` | `{ flexDirection: 'row' }` | | `flex-col` | `{ flexDirection: 'column' }` | | `flex-wrap` | `{ flexWrap: 'wrap' }` | | `items-center` | `{ alignItems: 'center' }` | | `items-start` | `{ alignItems: 'flex-start' }` | | `justify-center` | `{ justifyContent: 'center' }` | | `justify-between` | `{ justifyContent: 'space-between' }` | | `self-center` | `{ alignSelf: 'center' }` | ### Typography | Class | Size | |-------|------| | `text-xs` | 12px | | `text-sm` | 14px | | `text-base` | 16px | | `text-lg` | 18px | | `text-xl` | 20px | | `text-2xl` | 24px | | `text-3xl` | 30px | | `text-4xl` | 36px | Weight: `font-thin`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-black`. ### Colors Full Tailwind color palette with `text-*`, `bg-*`, and `border-*` prefixes: ```tsx Blue background, white text Card ``` ### Borders & Rounding | Class | Style | |-------|-------| | `border` | 1px border | | `border-2` | 2px border | | `rounded` | borderRadius: 4 | | `rounded-lg` | borderRadius: 8 | | `rounded-xl` | borderRadius: 12 | | `rounded-full` | borderRadius: 9999 | Directional: `rounded-t-*`, `rounded-b-*`, `rounded-l-*`, `rounded-r-*`. ### Sizing | Class | Style | |-------|-------| | `w-full` | width: '100%' | | `h-full` | height: '100%' | | `w-16` | width: 64 | | `h-16` | height: 64 | ## Platform Variants Apply styles per platform: ```tsx Platform-specific sizing Platform-specific padding ``` The `ios:` and `android:` prefixes are resolved at build time — only the relevant platform's styles are included in the bundle. --- ## TurboModules & Device APIs Source: https://neutron.build/docs/native/turbomodules JSI-based native module bindings for camera, location, biometrics, and more. Neutron Native provides two layers for accessing native device capabilities: **TurboModules** (low-level JSI bindings) and **Device Modules** (high-level wrappers around community packages). Both are accessed through `@neutron-build/native`. ## How TurboModules Work TurboModules use React Native's JSI (JavaScript Interface) to call native code without the legacy JSON bridge. Synchronous methods execute from the JavaScript thread; asynchronous methods return promises backed by native dispatch. ``` ┌─────────────┐ JSI (C++) ┌──────────────────┐ │ JavaScript │ ◄────────────► │ Native (ObjC/Kt) │ │ useCamera() │ direct call │ AVCaptureSession │ └─────────────┘ └──────────────────┘ ``` Compared to the old React Native bridge (JSON encode → async queue → JSON decode), TurboModules are: - **Synchronous** — sync methods return instantly, no round-trip - **Type-safe** — TypeScript interfaces match native method signatures - **Lazy-loaded** — modules are instantiated on first access, not at startup ## Two API Layers | Layer | Import | Use when... | |-------|--------|-------------| | **TurboModule hooks** | `@neutron-build/native/turbomodule` | You want direct JSI access with `NativeResult` wrappers | | **Device modules** | `@neutron-build/native/device` | You want high-level functions that auto-detect Expo or bare RN packages | The TurboModule hooks (`useCamera`, `useLocation`, etc.) talk directly to native code via JSI. The device modules (`camera.takePicture`, `location.getCurrentPosition`, etc.) wrap popular community packages (expo-camera, react-native-image-picker, etc.) behind a unified API. ## Built-in Device Modules All 10 device modules are available from `@neutron-build/native/turbomodule` (hooks) and `@neutron-build/native/device` (functions). ### Camera Photo and video capture via native camera APIs. - **iOS:** AVCaptureSession - **Android:** CameraX - **Peer deps:** `expo-camera` or `react-native-image-picker` ```tsx import { useCamera } from '@neutron-build/native/turbomodule'; function PhotoButton() { const camera = useCamera(); async function handleCapture() { const result = await camera.capture({ facing: 'back', quality: 0.9 }); if (result.ok) { console.log(result.value.uri); // file:///path/to/photo.jpg console.log(result.value.width); // 4032 } } return Take Photo; } ``` Or use the device module for auto-detection of installed packages: ```ts import { camera } from '@neutron-build/native/device'; const photo = await camera.takePicture({ quality: 0.9, facing: 'back' }); if (photo) console.log(photo.uri); const picks = await camera.pickFromGallery({ multiple: true, selectionLimit: 5 }); ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `capture(options?)` | async | Open native camera UI, return captured media | | `pickFromGallery(options?)` | async | Pick from photo library | | `isAvailable()` | sync | Check if camera hardware exists | | `checkPermission()` | async | Check permission without prompting | ### Location GPS and network-based geolocation. - **iOS:** CoreLocation (CLLocationManager) - **Android:** FusedLocationProviderClient - **Peer deps:** `expo-location` or `@react-native-community/geolocation` ```tsx import { useLocation } from '@neutron-build/native/turbomodule'; function LocationTracker() { const location = useLocation(); async function getPosition() { const pos = await location.getCurrentPosition({ accuracy: 'best', timeout: 10000 }); if (pos.ok) { console.log(pos.value.latitude, pos.value.longitude); } } // Continuous tracking const sub = location.watchPosition( (coord) => console.log(coord.latitude, coord.longitude), { distanceFilter: 10 }, ); // later: sub.remove() } ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `getCurrentPosition(options?)` | async | One-shot position fix | | `watchPosition(callback, options?)` | sync | Continuous position updates, returns subscription | | `isServicesEnabled()` | async | Check if location services are on at OS level | | `requestPermission(level)` | async | Request `'when-in-use'` or `'always'` permission | ### Notifications Local and push notification management. - **iOS:** UNUserNotificationCenter + APNS - **Android:** FCM + NotificationManager - **Peer deps:** `expo-notifications` or `@react-native-firebase/messaging` ```tsx import { useNotifications } from '@neutron-build/native/turbomodule'; const notif = useNotifications(); // Request permission await notif.requestPermission(); // Get push token const token = await notif.getToken(); if (token.ok) sendTokenToServer(token.value); // Schedule a local notification await notif.scheduleLocal({ title: 'Reminder', body: 'Check in with your team!', fireAfter: 3600, // 1 hour from now repeat: 'day', }); // Listen for foreground notifications const sub = notif.onReceived((payload) => { console.log('Received:', payload.title); }); ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `requestPermission()` | async | Request notification permission | | `getToken()` | async | Get APNS/FCM push token | | `scheduleLocal(payload)` | async | Schedule a local notification | | `cancel(id)` / `cancelAll()` | async | Cancel scheduled notifications | | `getBadgeCount()` / `setBadgeCount(n)` | async | Badge management (iOS) | | `onReceived(callback)` | sync | Listen for foreground notifications | | `onResponse(callback)` | sync | Listen for notification taps | ### Biometrics Fingerprint and face authentication. - **iOS:** LocalAuthentication (LAContext) — Face ID / Touch ID - **Android:** BiometricPrompt (AndroidX) - **Peer deps:** `expo-local-authentication` or `react-native-biometrics` ```tsx import { useBiometrics } from '@neutron-build/native/turbomodule'; const bio = useBiometrics(); // Check hardware capability const { available, biometryType } = await bio.isAvailable(); // biometryType: 'FaceID' | 'TouchID' | 'Fingerprint' | 'Iris' | 'none' if (available) { const result = await bio.authenticate({ reason: 'Confirm payment', title: 'Authentication Required', allowDeviceCredential: true, }); if (result.success) proceedWithPayment(); } ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `isAvailable()` | async | Check hardware and get biometry type | | `authenticate(options?)` | async | Prompt user for biometric auth | | `isEnrolled()` | async | Check if biometric data is enrolled | ### Haptics Tactile feedback using the device vibration motor. - **iOS:** UIImpactFeedbackGenerator / UINotificationFeedbackGenerator (Taptic Engine) - **Android:** Vibrator / VibrationEffect (API 26+) - **Peer deps:** `expo-haptics` (optional; falls back to `Vibration` API) ```tsx import { useHaptics } from '@neutron-build/native/turbomodule'; const haptics = useHaptics(); // Impact feedback — use on button presses haptics.impact('medium'); // 'light' | 'medium' | 'heavy' // Notification feedback — use for success/failure states haptics.notification('success'); // 'success' | 'warning' | 'error' // Selection feedback — use on picker/toggle changes haptics.selection(); // Raw vibration (ms) haptics.vibrate(100); ``` All haptics methods are **synchronous** — they fire instantly on the JS thread. **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `impact(style)` | sync | Impact feedback: `'light'`, `'medium'`, `'heavy'`, `'success'`, `'warning'`, `'error'`, `'selection'` | | `notification(type)` | sync | Notification feedback: `'success'`, `'warning'`, `'error'` | | `selection()` | sync | Selection change tap | | `vibrate(duration?)` | sync | Raw vibration in ms | | `isAvailable()` | sync | Check if haptics hardware exists | ### Clipboard System clipboard read/write. - **iOS:** UIPasteboard.general - **Android:** ClipboardManager - **Peer deps:** `@react-native-clipboard/clipboard` or `expo-clipboard` ```tsx import { useClipboard } from '@neutron-build/native/turbomodule'; const clipboard = useClipboard(); // Copy text clipboard.setString('https://neutron.build'); // Paste text const text = await clipboard.getString(); // Check before reading if (await clipboard.hasString()) { const content = await clipboard.getString(); } // Listen for changes const sub = clipboard.onChange(({ content }) => { console.log('Clipboard changed:', content); }); // later: sub.remove() ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `getString()` | async | Get clipboard text | | `setString(text)` | sync | Set clipboard text | | `hasString()` | async | Check if clipboard has text | | `getImage()` / `setImage(base64)` | async/sync | Image clipboard (base64) | | `onChange(callback)` | sync | Listen for clipboard changes | ### AsyncStorage Persistent key-value storage that survives app restarts. - **iOS:** UserDefaults (small values) / filesystem (large values) - **Android:** SharedPreferences (small values) / filesystem (large values) - **Peer deps:** `@react-native-async-storage/async-storage` or `expo-secure-store` ```tsx import { useAsyncStorage } from '@neutron-build/native/turbomodule'; const storage = useAsyncStorage(); // Basic CRUD await storage.setItem('user.token', 'abc123'); const token = await storage.getItem('user.token'); // 'abc123' await storage.removeItem('user.token'); // Batch operations await storage.multiSet([ ['theme', 'dark'], ['locale', 'en-US'], ['onboarded', 'true'], ]); const values = await storage.multiGet(['theme', 'locale']); // [['theme', 'dark'], ['locale', 'en-US']] // List all keys const keys = await storage.getAllKeys(); // Nuclear option await storage.clear(); ``` When no native module is linked (e.g., in tests or web), AsyncStorage falls back to an in-memory `Map`. **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `getItem(key)` | async | Get value (returns `null` if missing) | | `setItem(key, value)` | async | Set a key-value pair | | `removeItem(key)` | async | Delete a key | | `multiGet(keys)` | async | Batch get | | `multiSet(pairs)` | async | Batch set | | `getAllKeys()` | async | List all keys | | `clear()` | async | Delete everything | ### NetInfo Network connectivity monitoring. - **iOS:** NWPathMonitor (Network framework) - **Android:** ConnectivityManager - **Peer deps:** `@react-native-community/netinfo` ```tsx import { useNetInfo } from '@neutron-build/native/turbomodule'; const netInfo = useNetInfo(); // One-shot check const state = await netInfo.fetch(); console.log(state.type); // 'wifi' | 'cellular' | 'ethernet' | ... console.log(state.isConnected); // true console.log(state.details?.ssid); // 'MyNetwork' console.log(state.details?.cellularGeneration); // '5g' // Continuous monitoring const sub = netInfo.addEventListener((state) => { if (!state.isConnected) showOfflineBanner(); }); // later: sub.remove() // Simple boolean check const online = await netInfo.isConnected(); ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `fetch()` | async | Get current network state snapshot | | `addEventListener(callback)` | sync | Subscribe to network changes | | `isConnected()` | async | Simple boolean connectivity check | ### DeviceInfo Device hardware and software metadata. - **iOS:** UIDevice, ProcessInfo - **Android:** Build, ActivityManager - **Peer deps:** `react-native-device-info` or `expo-device` ```tsx import { useDeviceInfo } from '@neutron-build/native/turbomodule'; const device = useDeviceInfo(); // Full snapshot const info = device.getInfo(); // { brand: 'Apple', model: 'iPhone 16 Pro', systemName: 'iOS', // systemVersion: '18.0', isTablet: false, isEmulator: false, ... } // Individual queries (synchronous — cached) const version = device.getVersion(); // '1.0.0' const build = device.getBuildNumber(); // '42' const bundleId = device.getBundleId(); // 'com.myapp' const locale = device.getLocale(); // 'en-US' const tz = device.getTimezone(); // 'America/New_York' // Async queries const battery = await device.getBatteryLevel(); // 0.85 const lowPower = await device.isLowPowerMode(); // false ``` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `getInfo()` | sync | Full device snapshot (cached) | | `getDeviceId()` | sync | Vendor ID (iOS) / ANDROID_ID | | `getVersion()` / `getBuildNumber()` | sync | App version strings | | `isEmulator()` / `isTablet()` | sync | Device type checks | | `getBatteryLevel()` | async | Battery (0-1) | | `getLocale()` / `getTimezone()` | sync | User locale and timezone | ### Permissions Unified permission management across all device capabilities. - **iOS:** Info.plist keys + runtime requests - **Android:** AndroidManifest.xml + ActivityCompat - **Peer deps:** `react-native-permissions` or Expo modules (each handles its own) ```tsx import { usePermissions } from '@neutron-build/native/turbomodule'; const perms = usePermissions(); // Check without prompting const status = await perms.check('camera'); // 'granted' | 'denied' | 'blocked' | 'unavailable' | 'limited' // Request permission const result = await perms.request('camera'); // If blocked, send user to Settings if (result === 'blocked') { await perms.openSettings(); } // Batch check const statuses = await perms.checkMultiple(['camera', 'microphone', 'location']); // Batch request (Android: single dialog; iOS: sequential prompts) const results = await perms.requestMultiple(['camera', 'microphone']); ``` **Available permission names:** `camera`, `location`, `location-always`, `microphone`, `contacts`, `calendar`, `photo-library`, `notifications`, `bluetooth`, `face-id` **Key methods:** | Method | Kind | Description | |--------|------|-------------| | `check(permission)` | async | Check status without prompting | | `request(permission)` | async | Request a single permission | | `checkMultiple(permissions)` | async | Batch check | | `requestMultiple(permissions)` | async | Batch request | | `openSettings()` | async | Open app settings page | ## Creating Custom TurboModules Register your own native modules with `registerModule`. The registry resolves modules in order: cache, native JSI, then JS factory. ### Step 1 — Define the Interface ```ts import type { TurboModule, ModuleMethod } from '@neutron-build/native/turbomodule'; export interface AnalyticsModule extends TurboModule { moduleName: 'MyAnalytics'; track(event: string, properties?: Record): void; identify(userId: string): Promise; flush(): Promise; isEnabled(): boolean; } ``` ### Step 2 — Register with a JS Fallback ```ts import { registerModule, getModule } from '@neutron-build/native/turbomodule'; const METHODS: readonly ModuleMethod[] = [ { name: 'track', kind: 'sync' }, { name: 'identify', kind: 'async' }, { name: 'flush', kind: 'async' }, { name: 'isEnabled', kind: 'sync' }, ] as const; // JS fallback — used when native module isn't linked registerModule('MyAnalytics', () => ({ moduleName: 'MyAnalytics', methods: METHODS, track(event, properties) { console.log('[analytics stub]', event, properties); }, async identify() {}, async flush() {}, isEnabled() { return false }, })); ``` ### Step 3 — Create a Hook ```ts export function useAnalytics(): AnalyticsModule { const mod = getModule('MyAnalytics'); if (!mod) throw new Error('MyAnalytics module not available'); return mod; } ``` ### Step 4 — Use It ```tsx import { useAnalytics } from './analytics'; function CheckoutScreen() { const analytics = useAnalytics(); function handlePurchase() { analytics.track('purchase', { amount: '29.99', currency: 'USD' }); } return Buy; } ``` When the native module is linked and registered under the same name (`MyAnalytics`), the registry will find it via JSI and use the real native implementation. The JS factory is only used as a fallback. ## Lazy Loading TurboModules are lazy by design. No module code executes until you call `getModule()` or a `use*` hook for the first time. The resolution order for `getModule('NeutronCamera')`: 1. **Cache** — return immediately if already resolved 2. **Native JSI registry** — check `__turboModuleProxy` (set by React Native's C++ runtime) 3. **JS factory** — call the function registered via `registerModule()` ```ts import { hasModule, listModules, requireModule } from '@neutron-build/native/turbomodule'; // Check before using if (hasModule('NeutronCamera')) { const camera = requireModule('NeutronCamera'); // ... } // List all registered modules console.log(listModules()); // ['NeutronCamera', 'NeutronLocation', 'NeutronBiometrics', ...] ``` Use `clearCache()` during hot reload or in test teardown to reset the module cache: ```ts import { clearCache } from '@neutron-build/native/turbomodule'; afterEach(() => { clearCache(); }); ``` ## Expo Go Compatibility The device modules (`@neutron-build/native/device`) are designed to work in both Expo Go and bare React Native. Each module lazily probes for installed packages at runtime: | Module | Expo Package | Bare RN Package | |--------|-------------|-----------------| | Camera | `expo-camera`, `expo-image-picker` | `react-native-image-picker` | | Location | `expo-location` | `@react-native-community/geolocation` | | Notifications | `expo-notifications` | `@react-native-firebase/messaging` | | Biometrics | `expo-local-authentication` | `react-native-biometrics` | | Haptics | `expo-haptics` | Built-in `Vibration` API | | Clipboard | `expo-clipboard` | `@react-native-clipboard/clipboard` | | AsyncStorage | `expo-secure-store` | `@react-native-async-storage/async-storage` | | NetInfo | `@react-native-community/netinfo` | `@react-native-community/netinfo` | | DeviceInfo | `expo-device`, `expo-application` | `react-native-device-info` | | Permissions | Expo modules (per-feature) | `react-native-permissions` | The detection is lazy — packages are `require()`'d on first use, not at import time. If a package is missing, you get a clear error message: ``` [neutron-native/device/camera] No camera package found. Install one of: expo-camera, react-native-image-picker ``` In **Expo Go**, install the Expo-flavored packages: ```bash npx expo install expo-camera expo-location expo-haptics expo-local-authentication expo-notifications expo-clipboard expo-secure-store expo-device ``` In **bare React Native**, install community packages: ```bash npm install react-native-image-picker @react-native-community/geolocation react-native-biometrics @react-native-clipboard/clipboard @react-native-async-storage/async-storage @react-native-community/netinfo react-native-device-info react-native-permissions ``` ## Platform Capabilities Detection Check what is available on the current device at runtime: ```tsx import { hasModule } from '@neutron-build/native/turbomodule'; import { usePermissions, useCamera, useBiometrics } from '@neutron-build/native/turbomodule'; // Check if a module is linked at all const hasCameraModule = hasModule('NeutronCamera'); // Check hardware availability const camera = useCamera(); const cameraAvailable = camera.isAvailable(); const bio = useBiometrics(); const { available, biometryType } = await bio.isAvailable(); // Check permission status before requesting const perms = usePermissions(); const cameraStatus = await perms.check('camera'); const locationStatus = await perms.check('location'); // Full capabilities probe async function getDeviceCapabilities() { const perms = usePermissions(); const statuses = await perms.checkMultiple([ 'camera', 'location', 'microphone', 'notifications', 'bluetooth', ]); return { camera: statuses.camera !== 'unavailable', location: statuses.location !== 'unavailable', microphone: statuses.microphone !== 'unavailable', notifications: statuses.notifications !== 'unavailable', bluetooth: statuses.bluetooth !== 'unavailable', }; } ``` ### NativeResult Pattern All async TurboModule methods return a `NativeResult` wrapper: ```ts interface NativeResult { ok: boolean; value?: T; error?: { code: string; message: string }; } ``` Always check `result.ok` before accessing `result.value`: ```tsx const result = await camera.capture(); if (result.ok) { uploadPhoto(result.value.uri); } else { console.error(result.error.code, result.error.message); } ``` ### Event Subscriptions Modules that emit events return a `NativeSubscription` handle. Always clean up in your component teardown: ```tsx import { useEffect } from 'preact/hooks'; import { useNetInfo } from '@neutron-build/native/turbomodule'; function NetworkBanner() { const netInfo = useNetInfo(); useEffect(() => { const sub = netInfo.addEventListener((state) => { if (!state.isConnected) showBanner('You are offline'); }); return () => sub.remove(); }, []); } ``` --- ## Blob Storage Source: https://neutron.build/docs/nucleus/blob Content-addressed binary storage with deduplication. Nucleus includes built-in blob storage for binary data — files, images, videos, and backups. Content-addressed with automatic deduplication, no S3 or MinIO needed. ## Storing Blobs ```sql -- Store binary data (hex-encoded) with a key SELECT BLOB_STORE('avatar/user-1.png', '89504e470d0a1a0a...', 'image/png'); -- Store without content type SELECT BLOB_STORE('backup/db-2026-03-08.bak', 'cafebabe...'); ``` ## Retrieving Blobs ```sql -- Get blob data (hex-encoded) SELECT BLOB_GET('avatar/user-1.png'); -- Get metadata (size, hash, content type, tags) SELECT BLOB_META('avatar/user-1.png'); -- → {"size":4096,"hash":"a1b2c3...","content_type":"image/png","tags":{}} ``` ## Listing & Counting ```sql -- List all blob keys SELECT BLOB_LIST(); -- → ["avatar/user-1.png","backup/db-2026-03-08.bak"] -- List by prefix SELECT BLOB_LIST('avatar/'); -- → ["avatar/user-1.png"] -- Total blob count SELECT BLOB_COUNT(); -- → 2 ``` ## Tagging Add custom metadata tags to blobs: ```sql SELECT BLOB_TAG('avatar/user-1.png', 'uploaded_by', 'user-1'); SELECT BLOB_TAG('avatar/user-1.png', 'resize', '128x128'); ``` ## Deleting ```sql SELECT BLOB_DELETE('avatar/user-1.png'); -- → true ``` ## Deduplication Nucleus uses BLAKE3 content hashing. Identical data is stored only once, regardless of key: ```sql -- Check deduplication ratio SELECT BLOB_DEDUP_RATIO(); -- → 2.5 (logical bytes / physical bytes) ``` A ratio of 2.5 means you're storing 2.5x more logical data than actual disk usage. ## How It Works - **Content-addressed** — BLAKE3 (256-bit) cryptographic hashing - **Chunked storage** — Large objects split into 1MB chunks - **Deduplication** — Identical chunks stored once across all keys - **Byte-range index** — O(log N) access for partial reads - **WAL-backed** — Crash-safe persistence ## Use Cases - **User uploads** — Avatars, attachments, documents - **Media storage** — Images, videos, audio files - **Backups** — Database snapshots with deduplication - **Artifacts** — Build outputs, logs, reports - **Content delivery** — Serve static files directly from the database --- ## Change Data Capture Source: https://neutron.build/docs/nucleus/cdc Subscribe to row-level changes and query-level diffs. Nucleus provides built-in Change Data Capture (CDC) — no external connectors required. Changes are captured at the executor level and delivered through broadcast channels. ## Change Events Every INSERT, UPDATE, and DELETE emits a `ChangeEvent`: ``` ChangeEvent { table: "orders" change_type: Insert | Update | Delete new_row: [42, "shipped", 99.99] // present on Insert/Update old_row: [42, "pending", 99.99] // present on Update/Delete timestamp: 1709827200000 } ``` ### How It Works 1. DML operations (INSERT, UPDATE, DELETE) execute normally 2. After committing, the executor calls `notify_change` with the affected rows 3. A per-table `ChangeNotifier` broadcasts events to all subscribers 4. Subscribers receive events through async channels ## Row-Level Subscriptions Subscribe to changes on a specific table: ### Rust ```rust let mut rx = client.subscribe_changes("orders").await?; while let Some(event) = rx.recv().await { match event.change_type { ChangeType::Insert => println!("New order: {:?}", event.new_row), ChangeType::Update => println!("Updated: {:?} -> {:?}", event.old_row, event.new_row), ChangeType::Delete => println!("Deleted: {:?}", event.old_row), } } ``` ### Go ```go cdc := client.CDC() ch := cdc.Subscribe("orders") for event := range ch { fmt.Printf("Change: %s on %s\n", event.Type, event.Table) } ``` ### Python ```python async for event in client.cdc.subscribe("orders"): if event.change_type == "insert": print(f"New row: {event.new_row}") ``` ## Query-Level Subscriptions Subscribe to the **result set** of a SQL query. Nucleus tracks which tables the query depends on and pushes diffs when results change. ```sql -- Subscribe to a query (returns subscription_id) SUBSCRIBE SELECT * FROM orders WHERE status = 'pending'; ``` When any row in `orders` changes, Nucleus re-evaluates the query and returns a `QueryDiff`: ``` QueryDiff { subscription_id: 1 added_rows: [[43, "pending", 29.99]] // rows that now match removed_rows: [[42, "pending", 99.99]] // rows that no longer match } ``` ### Polling for Diffs ```sql FETCH SUBSCRIPTION 1; ``` Returns up to 1000 buffered diffs per call. ## Scheduled Tasks Nucleus includes a cron-like task scheduler for periodic CDC processing: ```sql -- Run every 60 seconds SCHEDULE 'archive_old_orders' EVERY 60 SECONDS AS 'INSERT INTO orders_archive SELECT * FROM orders WHERE created_at < now() - interval 30 day'; ``` ### Schedule Types | Type | Example | |------|---------| | `EVERY N SECONDS` | Poll for changes frequently | | `EVERY N MINUTES` | Periodic aggregation | | `EVERY N HOURS` | Daily summaries | | `ONCE AFTER N` | One-time delayed task | Tasks track `last_run`, `run_count`, and `last_error` for observability. ## Use Cases - **Event sourcing** — Capture all state changes as an immutable log - **Cache invalidation** — Bust caches when underlying data changes - **Search indexing** — Keep FTS/vector indexes in sync with source tables - **Audit trails** — Record who changed what and when - **Real-time dashboards** — Push diffs to WebSocket clients via PubSub --- ## Columnar Analytics Source: https://neutron.build/docs/nucleus/columnar Typed SQL analytics and the native columnar ingestion boundary. Nucleus can store a typed SQL table in its columnar engine. Use ordinary SQL aggregates over declared numeric columns. The separate `COLUMNAR_*` ingestion functions accept untyped key-value data and have a narrower aggregation contract. ## Typed SQL Tables ```sql CREATE TABLE analytics_events ( user_id INT, action TEXT, duration DOUBLE PRECISION ) WITH (engine='columnar'); INSERT INTO analytics_events VALUES (42, 'click', 1.5), (43, 'view', 3.2), (42, 'purchase', 12.0); SELECT COUNT(*), SUM(duration), AVG(duration), MIN(duration), MAX(duration) FROM analytics_events; ``` Typed columns give SQL aggregation a numeric schema. This path differs from native untyped ingestion; it does not infer numbers from text values. ## Native Untyped Ingestion ```sql SELECT COLUMNAR_INSERT('raw_events', 'user_id', '42', 'duration', '1.5'); SELECT COLUMNAR_INSERT('raw_events', 'user_id', '43', 'duration', '3.2'); SELECT COLUMNAR_COUNT('raw_events'); -- Counts the two ingested rows. ``` `COLUMNAR_SUM`, `COLUMNAR_AVG`, `COLUMNAR_MIN`, and `COLUMNAR_MAX` refuse untyped values with SQLSTATE `0A000`. Use a typed SQL table for numeric analytics. ## Storage and Failures A declared columnar SQL table uses disk-backed storage when the database has a data directory, and memory storage in memory mode. A failed durable open refuses the operation or startup instead of silently selecting memory storage. Native ingestion is durable only when its WAL is attached and the selected commit mode synchronizes it. WAL failures propagate to the caller. An I/O error can leave an uncertain outcome that requires recovery; an error is not a durable acknowledgement. These boundaries do not establish power-loss survival from a successful process-restart test. --- ## Configuration Source: https://neutron.build/docs/nucleus/configuration Server configuration and deployment options. Nucleus runs as a single binary with sensible defaults. Configure via command-line flags, environment variables, or a configuration file. ## Quick Start ```bash # Default: listens on 0.0.0.0:5432 (PostgreSQL wire protocol) nucleus # Custom port nucleus --port 5433 # With data directory nucleus --data-dir /var/lib/nucleus ``` ## Connection Connect using any PostgreSQL client: ```bash # psql psql -h localhost -p 5432 # Connection string postgresql://localhost:5432/nucleus ``` All standard PostgreSQL drivers work — psycopg2, pgx, node-postgres, diesel, sqlx, etc. ## Data Directory Nucleus stores all data in a single directory: ``` data/ ├── meta.json # Table schemas, views, sequences, roles ├── sequences.json # Sequence current values ├── pages/ # B-tree page files ├── wal/ # Write-ahead log ├── fts.wal # Full-text search WAL ├── doc.wal # Document store WAL ├── graph.wal # Graph WAL ├── blob/ # Blob chunk storage ├── ts/ # Time-series data └── kv/ # Key-value WAL ``` ## Wire Protocol Nucleus implements the PostgreSQL wire protocol (v3), supporting: - **Simple query** — Text-based queries - **Extended query** — Prepared statements with parameters - **COPY** — Bulk data import/export - **SSL/TLS** — Encrypted connections - **Authentication** — Password, MD5, SCRAM-SHA-256 ## Storage Engines Nucleus supports multiple storage backends: | Engine | Description | Best For | |--------|-------------|----------| | **Disk** (default) | B-tree pages on disk with buffer pool | Production | | **Buffered** | In-memory with periodic flush | Development | | **LSM** | Log-structured merge tree | Write-heavy workloads | | **Columnar** | Column-oriented storage | Analytics | ## MVCC Multi-Version Concurrency Control provides snapshot isolation: - Readers never block writers - Writers never block readers - Each transaction sees a consistent snapshot - Automatic conflict detection on write-write conflicts ## Backup & Recovery Most data models use write-ahead logging (WAL) for crash recovery — 8 of the 14 are WAL-durable today, with the rest in progress: 1. Operations are written to the WAL before applying 2. On startup, any incomplete operations are replayed 3. Periodic checkpointing compacts the WAL ## Performance Tuning ### Buffer Pool The buffer pool caches frequently accessed B-tree pages in memory. Larger pools reduce disk I/O for read-heavy workloads. ### SIMD Acceleration Nucleus automatically uses SIMD instructions (SSE4.2, AVX2, NEON) when available for: - Vector distance calculations - Time-series aggregations - Columnar scan operations ### Specialty Index Rebuild On startup, specialty indexes (HNSW, IVFFlat, GIN, R-tree) are rebuilt from table data. This ensures consistency after any crash but adds startup time proportional to indexed data. ## Monitoring ```sql -- Check server version SELECT VERSION(); -- Database statistics SELECT GRAPH_NODE_COUNT(); SELECT GRAPH_EDGE_COUNT(); SELECT FTS_DOC_COUNT(); SELECT BLOB_COUNT(); SELECT TS_COUNT('series_name'); SELECT COLUMNAR_COUNT('table_name'); SELECT DOC_COUNT(); ``` ## License Nucleus is licensed under the **Business Source License 1.1**. Its Additional Use Grant permits production use unless Nucleus is offered to third parties on a hosted or embedded basis as a database service or database engine. Uses outside that grant require a commercial license. Each version converts to MIT on its applicable change date; the current license lists January 1, 2046 or the fourth anniversary of that version's first public distribution, whichever comes first. The language frameworks are separately MIT-licensed. See each component's license file for its terms. --- ## Datalog Source: https://neutron.build/docs/nucleus/datalog Logic programming with semi-naive evaluation. Nucleus includes a built-in Datalog engine for recursive queries and logic programming. Define facts and rules, then query with automatic fixed-point evaluation — integrated directly with your relational and graph data. ## Facts Assert ground truth: ```sql SELECT DATALOG_ASSERT('parent(alice, bob).'); SELECT DATALOG_ASSERT('parent(bob, charlie).'); SELECT DATALOG_ASSERT('parent(charlie, diana).'); ``` ## Rules Define derived relations: ```sql -- Direct parent relationship SELECT DATALOG_RULE('ancestor(X, Y) :- parent(X, Y).'); -- Transitive closure (recursive) SELECT DATALOG_RULE('ancestor(X, Z) :- ancestor(X, Y), parent(Y, Z).'); ``` ## Queries ```sql -- Who are Alice's descendants? SELECT DATALOG_QUERY('?- ancestor(alice, Who).'); -- → [{"Who":"bob"}, {"Who":"charlie"}, {"Who":"diana"}] -- Is Alice an ancestor of Diana? SELECT DATALOG_QUERY('?- ancestor(alice, diana).'); -- → [{}] (empty binding = true) ``` ## Retracting Facts ```sql -- Remove a specific fact SELECT DATALOG_RETRACT('parent(bob, charlie).'); -- Clear all facts for a predicate SELECT DATALOG_CLEAR('parent'); ``` ## Cross-Model Integration Import data from relational tables and the graph engine: ```sql -- Import rows from a SQL table as facts -- Each row becomes: predicate(col1, col2, ...) SELECT DATALOG_IMPORT('employees', 'employee'); -- Import graph edges as facts -- Each edge becomes: predicate(from_id, edge_type, to_id) SELECT DATALOG_IMPORT_GRAPH('edge'); -- Import graph nodes -- Each node becomes: predicate(node_id, label) SELECT DATALOG_IMPORT_NODES('node'); ``` Then query across models: ```sql -- Find all employees reachable through the management graph SELECT DATALOG_RULE('manages(X, Y) :- edge(X, "MANAGES", Y).'); SELECT DATALOG_RULE('manages(X, Z) :- manages(X, Y), edge(Y, "MANAGES", Z).'); SELECT DATALOG_QUERY('?- manages(1, Who).'); ``` ## Negation Stratified negation using `\+`: ```sql SELECT DATALOG_RULE('orphan(X) :- person(X), \\+ parent(_, X).'); SELECT DATALOG_QUERY('?- orphan(Who).'); ``` ## Aggregates Built-in aggregate functions in rule heads: ```sql -- Count children per parent SELECT DATALOG_RULE('child_count(X, count) :- parent(X, Y).'); SELECT DATALOG_QUERY('?- child_count(Who, Count).'); ``` Supported aggregates: `count`, `sum`, `min`, `max`. ## How It Works - **Semi-naive evaluation** — Only new facts from the previous iteration are used to derive new facts, avoiding redundant computation - **Indexed EDB** — O(1) fact lookups for efficient joins - **Stratified negation** — Safe handling of negated predicates - **WAL-backed** — Crash-safe fact persistence ## Use Cases - **Access control** — Recursive permission inheritance - **Graph analytics** — Transitive closure, reachability - **Data lineage** — Track data provenance and dependencies - **Rule engines** — Business rules and compliance checks - **Ontologies** — Taxonomic reasoning and classification --- ## Document Store Source: https://neutron.build/docs/nucleus/document Schemaless JSON documents with GIN indexing. Nucleus includes a built-in document store for schemaless JSON data. Store, query, and index documents alongside your relational tables — no separate database needed. ## Document Operations ### Insert & Retrieve ```sql -- Insert a document, get back its ID SELECT DOC_INSERT('{"name": "Alice", "age": 30, "tags": ["admin", "dev"]}'); -- → 1 -- Get document by ID SELECT DOC_GET(1); -- → {"name":"Alice","age":30,"tags":["admin","dev"]} -- Count all documents SELECT DOC_COUNT(); ``` ### Query by Containment Find documents matching a JSON pattern using the `@>` containment operator: ```sql -- Find all documents where age = 30 SELECT DOC_QUERY('{"age": 30}'); -- → "1" (comma-separated IDs) -- Nested containment SELECT DOC_QUERY('{"tags": ["admin"]}'); ``` ### Extract Nested Values ```sql -- Get a value at a path SELECT DOC_PATH(1, 'name'); -- → "Alice" -- Nested paths SELECT DOC_PATH(1, 'address', 'city'); ``` ## JSONB Columns Store JSON directly in relational tables using the `JSONB` type: ```sql CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT, metadata JSONB ); INSERT INTO products (name, metadata) VALUES ('Widget', '{"color": "blue", "weight": 2.5, "dimensions": {"w": 10, "h": 5}}'); ``` ## JSONB Operators PostgreSQL-compatible arrow operators for JSON traversal: ```sql -- Get field as JSONB SELECT metadata -> 'color' FROM products; -- → "blue" -- Get field as text SELECT metadata ->> 'color' FROM products; -- → blue -- Nested path as JSONB SELECT metadata #> '{dimensions,w}' FROM products; -- → 10 -- Nested path as text SELECT metadata #>> '{dimensions,w}' FROM products; -- → 10 ``` ## JSONB Functions ### Construction ```sql -- Build an object from key-value pairs SELECT JSON_BUILD_OBJECT('name', 'Alice', 'age', 30); -- → {"name":"Alice","age":30} -- Build an array SELECT JSON_BUILD_ARRAY(1, 'two', true, null); -- → [1,"two",true,null] -- Convert any value to JSON SELECT TO_JSONB(42); ``` ### Inspection ```sql -- Get the JSON type SELECT JSON_TYPEOF('{"a":1}'); -- → "object" SELECT JSON_TYPEOF('[1,2]'); -- → "array" SELECT JSON_TYPEOF('"hello"'); -- → "string" -- Array length SELECT JSON_ARRAY_LENGTH('[1,2,3]'); -- → 3 -- Object keys SELECT JSON_OBJECT_KEYS('{"a":1,"b":2}'); -- → ["a","b"] ``` ### Transformation ```sql -- Set a value at a path SELECT JSON_SET('{"a":1}', 'b', '2'); -- → {"a":1,"b":2} -- Pretty print SELECT JSON_PRETTY('{"a":1,"b":[2,3]}'); -- Strip null values SELECT JSON_STRIP_NULLS('{"a":1,"b":null,"c":3}'); -- → {"a":1,"c":3} ``` ### Path Extraction ```sql -- Extract nested value as JSONB SELECT JSON_EXTRACT_PATH('{"a":{"b":{"c":42}}}', 'a', 'b', 'c'); -- → 42 -- Extract as text SELECT JSON_EXTRACT_PATH_TEXT('{"a":{"b":"hello"}}', 'a', 'b'); -- → hello ``` ## GIN Index GIN (Generalized Inverted Index) accelerates containment queries on JSONB columns: ```sql CREATE INDEX idx_products_metadata ON products USING gin (metadata); -- This query uses the GIN index SELECT * FROM products WHERE metadata @> '{"color": "blue"}'; ``` The GIN index extracts all path/value pairs from each document, enabling fast lookups without scanning every row. ## Use Cases - **User profiles** — Flexible schema for varying user attributes - **Product catalogs** — Different products have different fields - **Event logging** — Store arbitrary event payloads - **Configuration** — Store app settings as JSON - **API responses** — Cache and query external API data --- ## Full-Text Search Source: https://neutron.build/docs/nucleus/fulltext BM25-ranked search as an index on your table, and hybrid search in one query. Full-text search in Nucleus is an index on a table column, not a separate store. Rows are matched with the `@@` operator and ranked with `BM25()`, so search results are ordinary rows: joinable, filterable, and covered by the same transactions and row-level security policies as everything else. Because keyword search and vector search both return rows from the same table, hybrid search is a plain SQL query — no fusion built-in, no second system to keep in sync. ## Creating an Index ```sql CREATE TABLE articles ( id INT PRIMARY KEY, title TEXT, body TEXT, category TEXT, embedding VECTOR(384) ); CREATE INDEX articles_body_fts ON articles USING FTS (body); ``` `USING BM25` is accepted as a synonym. The index is maintained by `INSERT`, `UPDATE`, and `DELETE` like any other index, and is rebuilt from committed rows if a transaction aborts. There is nothing to re-synchronise by hand. **Requirement:** the table needs an integer `PRIMARY KEY`. Documents are keyed on it so that maintenance survives deletes, which shift physical row positions. Tables without one can still use `@@` — they just don't get an index. ## Matching `column @@ 'query'` is true when the column contains **every** term in the query, after stemming and stopword removal. ```sql SELECT id, title FROM articles WHERE body @@ 'machine learning' AND category = 'tech' ORDER BY published_at DESC LIMIT 10; ``` `@@` is defined on the row's own text, so it returns the same rows whether or not an index exists. The index makes it faster; it never changes the answer. ## Ranking `BM25(column, 'query')` scores a row against the corpus statistics of that column's index. ```sql SELECT id, title, BM25(body, 'machine learning') AS score FROM articles WHERE body @@ 'machine learning' ORDER BY score DESC LIMIT 10; ``` Okapi BM25 with the standard parameters — `k1 = 1.2` (term frequency saturation) and `b = 0.75` (document length normalisation). Shorter documents containing more occurrences of rarer terms score higher. `BM25()` requires an FTS index on the column, because inverse document frequency and average document length are properties of the corpus, not of one row. Without an index it reports that, rather than scoring against an empty corpus. ## Hybrid Search Reciprocal Rank Fusion combines a keyword ranking and a vector ranking by their positions rather than their scores, which sidesteps the problem that BM25 scores and cosine distances are not on a comparable scale. ```sql WITH kw AS ( SELECT id, ROW_NUMBER() OVER (ORDER BY BM25(body, 'machine learning') DESC) AS r FROM articles WHERE body @@ 'machine learning' LIMIT 50 ), sem AS ( SELECT id, ROW_NUMBER() OVER ( ORDER BY VECTOR_DISTANCE(embedding, VECTOR('[0.1, 0.2, ...]'), 'cosine') ) AS r FROM articles LIMIT 50 ) SELECT COALESCE(kw.id, sem.id) AS id, COALESCE(1.0 / (60 + kw.r), 0) + COALESCE(1.0 / (60 + sem.r), 0) AS score FROM kw FULL OUTER JOIN sem ON kw.id = sem.id ORDER BY score DESC LIMIT 10; ``` Both halves read the same table in the same snapshot, through the same policies. The constant `60` is the conventional RRF damping factor; raising it flattens the contribution of top ranks. Because fusion is expressed in the query rather than hidden in a built-in, you can weight the two halves differently, add a third ranking, or replace the fusion entirely without waiting for a new function. ## PostgreSQL Compatibility The PostgreSQL spelling works and returns the same rows: ```sql SELECT * FROM articles WHERE TO_TSVECTOR(body) @@ PLAINTO_TSQUERY('machine learning'); ``` ```sql -- Boolean match test SELECT * FROM articles WHERE TS_MATCH(body, 'machine learning'); -- Highlight matching terms with tags SELECT TS_HEADLINE(body, 'rust') FROM articles; -- → "Rust is a systems programming language" -- Convert text to a stemmed query SELECT PLAINTO_TSQUERY('machine learning'); -- → "machine & learn" ``` Two differences worth knowing: - **`TS_RANK` is not BM25.** Like PostgreSQL's `ts_rank`, it scores a single `(document, query)` pair with no corpus, so it has no inverse document frequency and no length normalisation. It is fine for a rough within-document signal, and it will order results differently from `BM25()`. Use `BM25()` for relevance ranking. - **`TO_TSVECTOR` returns text**, a normalised term string, not a distinct `tsvector` type with lexeme positions. It composes correctly with `@@` and `PLAINTO_TSQUERY`; it does not render like PostgreSQL's `'learn':2 'machin':1`. ## Behaviour to Expect - **Corpus statistics are not snapshot-isolated.** `N`, average document length, and document frequencies come from the current index rather than from your transaction's snapshot, so a score can shift as other sessions write. This matches how Lucene, Elasticsearch, and PostgreSQL statistics behave; the *set of rows* you get back is snapshot-exact, only the ranking weights are shared. - **Under row-level security**, `@@` and `BM25()` remain available and filter through policy like any other predicate. Index acceleration is skipped, so search falls back to a scan; results are unchanged. Corpus statistics are aggregate and are not partitioned by policy — a document frequency counts rows the querying role cannot read, the same way PostgreSQL's planner statistics do. - **Inside an open transaction**, index acceleration is likewise skipped so that uncommitted rows cannot leak into another session's candidate set. `@@` still evaluates correctly against your own uncommitted rows. ## Stemming Six built-in language stemmers normalise words to their root form: | Language | Examples | |----------|----------| | English (default) | running → run, learning → learn | | German | Übungen → Übung | | French | étudiantes → étudiant | | Spanish | corriendo → corr | | Italian | velocemente → veloce | | Portuguese | correndo → corr | ## Tokenization Pipeline 1. Split on non-alphanumeric characters 2. Lowercase 3. Filter stopwords (48 common English words) 4. Apply language-specific stemming Both sides of a comparison run through the same pipeline, so a query term matches a document term whenever their stems agree. ## Document Store Surface (Legacy) Nucleus also exposes the inverted index directly, keyed by a document id you supply: ```sql SELECT FTS_INDEX(1, 'Rust is a systems programming language'); SELECT FTS_SEARCH('systems programming', 10); -- → [{"doc_id":1,"score":2.45}] SELECT FTS_FUZZY_SEARCH('systms programing', 2, 10); SELECT FTS_REMOVE(1); ``` This store is independent of your tables. Nothing ties `doc_id` 1 to any row, so keeping it consistent with a table is the application's job — a missed `FTS_REMOVE` leaves a deleted row searchable. It is also unavailable while row-level security is active, because it has no policy-aware access path. Prefer `CREATE INDEX ... USING FTS` for anything that indexes table data. The document store remains for corpora that genuinely have no table behind them, and for fuzzy and faceted search, which the table-attached index does not yet expose. ## Use Cases - **Site search** — full-text search across pages and posts - **Product search** — find products by description, ranked by relevance - **Log analysis** — search through structured log messages - **Knowledge base** — search documentation and articles - **Retrieval for AI** — hybrid keyword + vector retrieval in one query, one snapshot --- ## Geospatial Source: https://neutron.build/docs/nucleus/geo R-tree spatial indexing with distance and area functions. Nucleus includes built-in geospatial support with R-tree indexing, Haversine distance, and polygon operations — no PostGIS extension needed. ## Distance Calculations ### Geographic Distance (Haversine) Calculate great-circle distance between two points on Earth: ```sql -- Distance between San Francisco and New York (in meters) SELECT GEO_DISTANCE(37.7749, -122.4194, 40.7128, -74.0060); -- → 4,129,086.0 (~4,129 km) -- PostGIS-compatible alias SELECT ST_DISTANCE(37.7749, -122.4194, 40.7128, -74.0060); ``` Parameters: `(lat1, lon1, lat2, lon2)` — coordinates in degrees. ### Euclidean Distance For Cartesian coordinate systems: ```sql SELECT GEO_DISTANCE_EUCLIDEAN(0, 0, 3, 4); -- → 5.0 -- PostGIS-compatible alias SELECT ST_DISTANCE_EUCLIDEAN(0, 0, 3, 4); ``` ## Proximity Queries Check if two points are within a given distance: ```sql -- Are these two points within 5,000,000 meters (5,000 km)? SELECT GEO_WITHIN(37.7749, -122.4194, 40.7128, -74.0060, 5000000); -- → true -- PostGIS-compatible alias SELECT ST_DWITHIN(37.7749, -122.4194, 40.7128, -74.0060, 5000000); ``` ### Finding Nearby Locations Combine with SQL queries to find nearby points: ```sql -- Find all stores within 10km of a user SELECT name, address, GEO_DISTANCE(lat, lon, 37.7749, -122.4194) AS distance_m FROM stores WHERE GEO_WITHIN(lat, lon, 37.7749, -122.4194, 10000) ORDER BY distance_m LIMIT 10; ``` ## Polygon Area Calculate the area of a polygon using the Shoelace formula: ```sql -- Area of a triangle SELECT GEO_AREA(0, 0, 4, 0, 2, 3); -- → 6.0 -- Area of a quadrilateral SELECT ST_AREA(0, 0, 4, 0, 4, 3, 0, 3); -- → 12.0 ``` Pass coordinate pairs as alternating x, y values. Requires at least 3 points (6 arguments). ## R-Tree Index The spatial engine uses a custom R-tree index for efficient spatial queries: - **Max 16 entries per node** — balanced tree structure - **Bounding box queries** — O(log N) for intersection and containment tests - **Point-in-polygon** — Ray casting algorithm ## PostGIS Compatibility All functions have PostGIS-compatible aliases: | Nucleus | PostGIS Alias | |---------|--------------| | `GEO_DISTANCE` | `ST_DISTANCE` | | `GEO_DISTANCE_EUCLIDEAN` | `ST_DISTANCE_EUCLIDEAN` | | `GEO_WITHIN` | `ST_DWITHIN` | | `GEO_AREA` | `ST_AREA` | ## Use Cases - **Store locators** — Find nearest locations - **Delivery zones** — Check if an address is within a service area - **Fleet tracking** — Monitor vehicle positions and distances - **Geofencing** — Trigger actions when entering/leaving areas - **Mapping** — Calculate routes and distances --- ## Graph Database Source: https://neutron.build/docs/nucleus/graph Property graph with Cypher queries and built-in algorithms. Nucleus includes a native property graph engine with Cypher query support, graph algorithms, and CSR-optimized traversals — no separate graph database needed. ## Creating Nodes & Edges ```sql -- Add nodes with labels and properties SELECT GRAPH_ADD_NODE('Person', '{"name": "Alice", "age": 30}'); -- → 1 SELECT GRAPH_ADD_NODE('Person', '{"name": "Bob", "age": 25}'); -- → 2 SELECT GRAPH_ADD_NODE('Company', '{"name": "Acme", "industry": "Tech"}'); -- → 3 -- Add directed edges with types and properties SELECT GRAPH_ADD_EDGE(1, 2, 'KNOWS', '{"since": 2020}'); SELECT GRAPH_ADD_EDGE(1, 3, 'WORKS_AT', '{"role": "Engineer"}'); SELECT GRAPH_ADD_EDGE(2, 3, 'WORKS_AT', '{"role": "Designer"}'); ``` ## Querying the Graph ### Neighbors ```sql -- Outgoing neighbors (default) SELECT GRAPH_NEIGHBORS(1, 'out'); -- → [{"neighbor_id":2,"edge_id":1,"edge_type":"KNOWS"}, ...] -- Incoming neighbors SELECT GRAPH_NEIGHBORS(3, 'in'); -- Both directions SELECT GRAPH_NEIGHBORS(1, 'both'); ``` ### Shortest Path ```sql -- Find shortest path between two nodes SELECT GRAPH_SHORTEST_PATH(1, 3); -- → [1, 3] (direct edge exists) ``` ### Statistics ```sql SELECT GRAPH_NODE_COUNT(); -- → 3 SELECT GRAPH_EDGE_COUNT(); -- → 3 ``` ## Cypher Queries Use `GRAPH_QUERY` for pattern matching with the Cypher query language: ```sql -- Find all people SELECT GRAPH_QUERY('MATCH (p:Person) RETURN p.name, p.age'); -- Traverse relationships SELECT GRAPH_QUERY(' MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name '); -- Filter with WHERE SELECT GRAPH_QUERY(' MATCH (p:Person)-[r:KNOWS]->(q:Person) WHERE p.age > 25 RETURN p.name, q.name '); -- Create nodes and edges SELECT GRAPH_QUERY(' CREATE (n:City {name: "Portland", state: "OR"}) '); SELECT GRAPH_QUERY(' CREATE (a:Person {name: "Charlie"})-[:KNOWS]->(b:Person {name: "Diana"}) '); ``` ### Supported Cypher Features | Feature | Example | |---------|---------| | Pattern matching | `MATCH (a)-[r]->(b)` | | Labels | `(n:Person)` | | Properties | `{name: "Alice"}` | | Edge types | `-[:KNOWS]->` | | Directions | `->`, `<-`, `--` (undirected) | | Variable-length paths | `-[*1..3]->` | | WHERE | `WHERE n.age > 25 AND n.name = "Alice"` | | OPTIONAL MATCH | Returns NULL if no match | | WITH | Intermediate projection | | DELETE | `DELETE n, r` | | COUNT | `RETURN COUNT(*)` | ## Graph Algorithms Built-in algorithms accessible through the graph engine: | Algorithm | Purpose | |-----------|---------| | **BFS** | Breadth-first traversal | | **DFS** | Depth-first traversal | | **Shortest Path** | Unweighted (BFS-based) | | **Dijkstra** | Weighted shortest path using edge properties | | **Connected Components** | Find isolated subgraphs | | **PageRank** | Rank nodes by importance | | **Label Propagation** | Community detection | | **Louvain** | Modularity-optimized community detection | ## Deleting Graph Data ```sql -- Delete a node (cascades connected edges) SELECT GRAPH_DELETE_NODE(1); -- Delete an edge SELECT GRAPH_DELETE_EDGE(1); -- Delete via Cypher SELECT GRAPH_QUERY(' MATCH (n:Person {name: "Bob"}) DELETE n '); ``` ## Performance - **CSR (Compressed Sparse Row)** format for cache-friendly analytical traversals - **Property indexes** (B-tree) per label/property for fast lookups - **Label and type indexes** for O(1) filtered traversals - **Parallel BFS** for multi-source traversals - **Tiered storage** with hot/cold separation (100K node threshold) - **WAL-backed** durability with crash recovery ## Use Cases - **Social networks** — Friends, followers, mutual connections - **Knowledge graphs** — Entity relationships, ontologies - **Fraud detection** — Transaction paths, ring detection - **Recommendation engines** — User-item-category graphs - **Network topology** — Infrastructure dependencies --- ## Key-Value Source: https://neutron.build/docs/nucleus/key-value Redis-like key-value operations with TTL support. Nucleus provides a built-in key-value store accessible through SQL functions. It supports strings, lists, hashes, sets, sorted sets, and HyperLogLog — similar to Redis, but without a separate server. ## Basic Operations ```sql -- Set a key with optional TTL (seconds) SELECT KV_SET('user:1:session', 'abc123', 3600); -- Get a value SELECT KV_GET('user:1:session'); -- Delete a key SELECT KV_DEL('user:1:session'); -- Check if a key exists SELECT KV_EXISTS('user:1:session'); ``` ## TTL Management ```sql -- Set with TTL (seconds) SELECT KV_SET('cache:homepage', '...', 300); -- Get remaining TTL SELECT KV_TTL('cache:homepage'); -- Set TTL on existing key SELECT KV_EXPIRE('cache:homepage', 600); ``` ## Lists ```sql -- Push to list (left/right) SELECT KV_LPUSH('queue:emails', 'msg1'); SELECT KV_RPUSH('queue:emails', 'msg2'); -- Pop from list SELECT KV_LPOP('queue:emails'); SELECT KV_RPOP('queue:emails'); -- Get list length SELECT KV_LLEN('queue:emails'); -- Get range SELECT KV_LRANGE('queue:emails', 0, -1); ``` ## Hashes ```sql -- Set hash field SELECT KV_HSET('user:1', 'name', 'Alice'); SELECT KV_HSET('user:1', 'email', 'alice@example.com'); -- Get hash field SELECT KV_HGET('user:1', 'name'); -- Get all fields SELECT KV_HGETALL('user:1'); -- Delete hash field SELECT KV_HDEL('user:1', 'email'); ``` ## Sets ```sql -- Add members SELECT KV_SADD('tags:post:1', 'rust'); SELECT KV_SADD('tags:post:1', 'database'); SELECT KV_SADD('tags:post:1', 'performance'); -- Check membership SELECT KV_SISMEMBER('tags:post:1', 'rust'); -- Get all members SELECT KV_SMEMBERS('tags:post:1'); -- Remove a member SELECT KV_SREM('tags:post:1', 'rust'); -- Cardinality SELECT KV_SCARD('tags:post:1'); ``` Set-to-set operations (`SINTER`/`SUNION`/`SDIFF`) and TTL removal (`PERSIST`) are not implemented. Intersect two tag sets in SQL against `KV_SMEMBERS` until they are. ## Sorted Sets ```sql -- Add with score SELECT KV_ZADD('leaderboard', 100.0, 'player1'); SELECT KV_ZADD('leaderboard', 250.0, 'player2'); SELECT KV_ZADD('leaderboard', 175.0, 'player3'); -- Get by rank (top N) SELECT KV_ZRANGE('leaderboard', 0, 9); -- Get by score range SELECT KV_ZRANGEBYSCORE('leaderboard', 100.0, 200.0); -- Count members SELECT KV_ZCARD('leaderboard'); -- Remove a member SELECT KV_ZREM('leaderboard', 'player2'); ``` ## HyperLogLog Probabilistic cardinality estimation — count unique items using minimal memory. ```sql -- Add elements SELECT KV_PFADD('visitors:2026-03-08', 'user1'); SELECT KV_PFADD('visitors:2026-03-08', 'user2'); SELECT KV_PFADD('visitors:2026-03-08', 'user1'); -- duplicate, not counted -- Estimate count SELECT KV_PFCOUNT('visitors:2026-03-08'); -- ~2 -- Merge multiple HLLs SELECT KV_PFMERGE('visitors:march', 'visitors:2026-03-01', 'visitors:2026-03-02'); ``` ## Use Cases - **Session storage** — `KV_SET` with TTL for user sessions - **Rate limiting** — Increment counters with expiry - **Caching** — Cache expensive query results - **Queues** — LPUSH/RPOP for simple job queues - **Leaderboards** — Sorted sets for ranked data - **Analytics** — HyperLogLog for unique counts --- ## What is Nucleus? Source: https://neutron.build/docs/nucleus/overview A multi-model database engine that speaks PostgreSQL. Nucleus is a multi-model database engine written in Rust. It provides **14 data models** through a single server, accessed via the standard PostgreSQL wire protocol. Any PostgreSQL client, driver, or ORM works out of the box. ## Why Nucleus? Most applications need more than one type of database. A typical stack might include PostgreSQL for relational data, Redis for caching, Elasticsearch for search, a vector database for AI, and InfluxDB for metrics. Nucleus combines all of these into one. - **One connection string** — No more managing 5 database connections - **One query language** — SQL extended with model-specific functions - **One operational surface** — One backup, one monitor, one deploy - **PostgreSQL compatible** — Use `psql`, `pgx`, `asyncpg`, or any PostgreSQL driver ## Data Models | Model | Use Case | Access Pattern | |-------|----------|---------------| | **SQL** | Relational data, joins, transactions | Standard SQL | | **Key-Value** | Caching, sessions, counters | `KV_SET`, `KV_GET`, TTL | | **Vector** | Similarity search, RAG, embeddings | `VECTOR_DISTANCE`, HNSW/IVFFlat | | **Document** | Flexible schemas, nested data | JSONB, `->`, `->>`, GIN | | **Graph** | Relationships, networks, paths | Cypher queries | | **Full-Text Search** | Search, autocomplete, facets | `FTS_SEARCH`, BM25 | | **Time Series** | Metrics, IoT, logs | Gorilla compression, continuous aggs | | **Columnar** | Analytics, aggregations | Vectorized execution, LZ4/Zstd | | **Blob** | Files, images, media | Chunked, content-addressed, dedup | | **Datalog** | Logic programming, rules | Semi-naive evaluation | | **Streams** | Event sourcing, queues | Append-only, consumer groups | | **Geospatial** | Maps, location queries | R-tree, point-in-radius | | **CDC** | Change tracking | Change Data Capture | | **Pub/Sub** | Real-time notifications | LISTEN/NOTIFY | ## Connecting Nucleus speaks the PostgreSQL wire protocol. Connect with any PostgreSQL client: ```bash # psql psql -h localhost -p 5432 -U neutron # Connection string postgresql://neutron:password@localhost:5432/mydb ``` From any language framework: ```typescript // TypeScript (@neutron-build/nucleus) import { createClient, withSQL } from "@neutron-build/nucleus"; const db = await createClient({ url: process.env.DATABASE_URL! }) .use(withSQL) .connect(); const users = await db.sql.query("SELECT * FROM users"); ``` ```rust // Rust (sqlx, diesel, or raw pgx) let rows = sqlx::query("SELECT * FROM users") .fetch_all(&pool).await?; ``` ```go // Go (pgx/v5) rows, err := pool.Query(ctx, "SELECT * FROM users") ``` ```python # Python (asyncpg) rows = await conn.fetch("SELECT * FROM users") ``` ## Architecture Nucleus is a single statically-linked binary. No JVM, no runtime dependencies. - **Storage**: Pluggable engines (in-memory, disk, buffered, columnar, LSM) - **MVCC**: Snapshot isolation with row-level versioning - **WAL**: Write-ahead log for crash recovery - **Indexes**: B-tree, hash, GIN, R-tree, HNSW, IVFFlat - **Compression**: LZ4, Zstd, Gorilla (time series) ## License Nucleus uses the **Business Source License 1.1** (BSL). Its Additional Use Grant permits production use unless Nucleus is offered to third parties on a hosted or embedded basis as a database service or database engine. Uses outside that grant require a commercial license. Each version converts to MIT on its applicable change date; the current license lists 2046-01-01 or the fourth anniversary of that version's first public distribution, whichever comes first. --- ## PubSub Source: https://neutron.build/docs/nucleus/pubsub Real-time LISTEN/NOTIFY messaging and stream processing. Nucleus supports PostgreSQL-compatible `LISTEN`/`NOTIFY` for real-time messaging, plus Redis-compatible streams for durable event processing. ## LISTEN / NOTIFY ### Subscribe to a Channel ```sql LISTEN order_events; ``` The connection receives all messages published to `order_events` until `UNLISTEN` is called. ### Publish a Message ```sql NOTIFY order_events, '{"order_id": 42, "status": "shipped"}'; ``` All connections listening on `order_events` receive the payload. ### Unsubscribe ```sql UNLISTEN order_events; ``` ### Cluster-Wide Delivery In clustered deployments, `NOTIFY` on one node delivers to `LISTEN` on all nodes. Messages propagate via gossip-based subscription routing — no configuration required. ## PubSub Hub Internally, Nucleus uses a `PubSubHub` backed by Tokio broadcast channels. Each channel gets an independent sender, so high-throughput topics don't block unrelated channels. ### Distributed Routing When running in `PrimaryReplica` or `MultiRaft` mode, the `DistributedPubSubRouter` automatically: 1. Propagates subscription state across nodes via gossip 2. Routes `NOTIFY` messages to all nodes with active listeners 3. Delivers locally through in-process broadcast channels ## Job Queue Nucleus includes a built-in priority job queue accessible via PubSub infrastructure: ### Priority Levels | Level | Value | Use Case | |-------|-------|----------| | Low | 0 | Background cleanup | | Normal | 1 | Standard processing | | High | 2 | User-facing tasks | | Critical | 3 | Payment processing, alerts | ### Features - **Auto-retry** — Failed jobs re-enqueue up to `max_retries` - **Dead-letter queue** — Jobs exceeding retry limit are moved to DLQ - **Priority ordering** — Higher priority jobs dequeue first - **At-least-once delivery** — Jobs must be explicitly completed ## Client Integration ### Rust ```rust use neutron::nucleus::NucleusPool; let pool = NucleusPool::connect("postgres://localhost:5432/mydb").await?; // Subscribe pool.execute("LISTEN order_events").await?; // Publish pool.execute("NOTIFY order_events, 'hello'").await?; ``` ### Go ```go client := nucleus.Connect("postgres://localhost:5432/mydb") // PubSub model ps := client.PubSub() ps.Listen("order_events") ps.Notify("order_events", "hello") ``` ### Python ```python client = await nucleus_connect("postgres://localhost:5432/mydb") await client.execute("LISTEN order_events") await client.execute("NOTIFY order_events, 'hello'") ``` ## Streams For durable, ordered event processing, see [Streams](/docs/nucleus/streams) — Nucleus's Redis-compatible append-only log with consumer groups. --- ## Quick Start Source: https://neutron.build/docs/nucleus/quickstart Get Nucleus running in 60 seconds. ## Install Download and extract the binary for your platform (each release publishes checksums alongside; verify with `sha256sum -c checksums.txt` from the same release page): ```bash # macOS (Apple Silicon) curl -L https://github.com/neutron-build/neutron/releases/latest/download/nucleus-darwin-arm64.tar.gz \ | tar xz && chmod +x nucleus # macOS (Intel) curl -L https://github.com/neutron-build/neutron/releases/latest/download/nucleus-darwin-amd64.tar.gz \ | tar xz && chmod +x nucleus # Linux (x86_64) curl -L https://github.com/neutron-build/neutron/releases/latest/download/nucleus-linux-amd64.tar.gz \ | tar xz && chmod +x nucleus # Linux (aarch64) curl -L https://github.com/neutron-build/neutron/releases/latest/download/nucleus-linux-arm64.tar.gz \ | tar xz && chmod +x nucleus ``` Or use the Neutron CLI: ```bash neutron db start ``` ## Start the Server ```bash ./nucleus --port 5432 --data-dir ./data ``` Nucleus is now listening on port 5432 with the PostgreSQL wire protocol. ## Connect ```bash psql -h localhost -p 5432 -U neutron ``` ## Create a Table ```sql CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE, created_at TIMESTAMP DEFAULT NOW() ); INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'); SELECT * FROM users; ``` ## Try Multi-Model Once connected, you can use any of the 14 data models through SQL extensions: ```sql -- Key-Value SELECT KV_SET('session:abc', '{"user_id": 1}', 3600); SELECT KV_GET('session:abc'); -- Vector search CREATE TABLE embeddings (id SERIAL, vec Vector(3)); INSERT INTO embeddings (vec) VALUES (VECTOR('[1.0, 0.5, 0.3]')); SELECT * FROM embeddings ORDER BY VECTOR_DISTANCE(vec, VECTOR('[1.0, 0.4, 0.2]'), 'l2') LIMIT 5; -- Full-text search SELECT FTS_SEARCH('users', 'alice', 10); -- Document (JSONB) CREATE TABLE profiles (id SERIAL, data JSONB); INSERT INTO profiles (data) VALUES ('{"name": "Alice", "tags": ["admin", "dev"]}'); SELECT data->>'name' FROM profiles WHERE data->'tags' ? 'admin'; ``` ## Next Steps - [SQL Reference](/docs/nucleus/sql) — Full SQL syntax - [Key-Value](/docs/nucleus/key-value) — Redis-like operations - [Vector Search](/docs/nucleus/vector) — Similarity search for AI - [Configuration](/docs/nucleus/configuration) — Tuning and options --- ## Replication Source: https://neutron.build/docs/nucleus/replication Design of the primary-replica, multi-Raft and failover modes. Not shipping — see the status notice. > **Status: this page describes a DESIGN, not a shipped feature. Do not deploy > it.** Everything below the Cluster Modes table is the intended architecture. > In the shipping engine, as of 2026-08-21: > > - **A replica never applies streamed records to its storage.** The receive > loop appends into an in-memory structure whose own comment says it > "simulates durability". The function that would write into real replica > storage has no callers outside its tests. > - **A replica is fully writable and nothing consults replication state.** There > is no read-only gate and no role check, so pointing a client at a "replica" > gives you an empty, writable database while the startup banner says it is > replicating. > - **Automatic failover is not wired.** `check_health` and `promote` have no > production callers. > - **Synchronous mode does not wait for anything.** The durability it advertises > is not enforced. > - **Multi-Raft does not gate writes.** Joining N nodes yields N independent > writable databases running background elections, not one cluster. > > Single-node Nucleus is unaffected — MVCC, WAL durability and crash recovery are > real and tested. If you need high availability today, replicate at the storage > or infrastructure layer, not with this. > > This notice will be removed when there is a `probe_replication` harness proving > a replica converges and survives a crash. Until it exists, treat the tables > below as a roadmap. Nucleus defines three cluster modes. Only Standalone is implemented. ## Cluster Modes | Mode | Nodes | Consensus | Status | |------|-------|-----------|--------| | **Standalone** | 1 | None | **Implemented.** The supported way to run Nucleus. | | **PrimaryReplica** | 2 | WAL streaming | **Design only.** Streams, never applies. Not HA. | | **MultiRaft** | 3+ | Raft per shard | **Design only.** Elections run; writes are not gated. | ## Primary-Replica Two-node setup where the primary streams WAL records to a replica. ### Replication Modes | Mode | Intended behavior | Actual behavior today | |------|-------------------|-----------------------| | **Synchronous** | Primary waits for replica ACK before committing | Does not wait. The advertised durability is not enforced. | | **Asynchronous** | Primary streams WAL in background | Streams, but the replica never applies what it receives. | ### WAL Streaming The primary's Write-Ahead Log is streamed to the replica in batches (default 64 records per batch): ``` Primary Replica │ │ │── WAL Batch (records 1-64) ──►│ │ │── Apply records │◄── ACK (confirmed_lsn=64) ────│ │ │ │── WAL Batch (records 65-128) ─►│ │ │ ``` ### WAL Payload Types | Type | Content | |------|---------| | `PageWrite` | Page ID + data bytes | | `Commit` | Transaction ID | | `Abort` | Transaction ID | | `Checkpoint` | Marks a consistent point | ### Monitoring ```sql SELECT * FROM nucleus_replication_status; ``` Returns: | Field | Description | |-------|-------------| | `node_id` | This node's ID | | `role` | `primary` or `replica` | | `wal_lsn` | Latest WAL sequence number | | `applied_lsn` | Last applied sequence number | | `replication_lag` | Records behind primary | | `peer_connected` | Whether peer is reachable | ## Automatic Failover > Design only. `check_health` and `promote` have no production callers. The `FailoverManager` monitors heartbeats and promotes the replica if the primary is unreachable. ### Failover Timeline 1. **Primary stops responding** — Heartbeat timeout (default 5 seconds) 2. **PrimaryDown detected** — Failover manager triggers promotion 3. **Replica promoted** — Becomes new primary, starts accepting writes 4. **Old primary rejoins** — Demoted to replica, resyncs from new primary ### Failover Events | Event | Description | |-------|-------------| | `PrimaryDown` | Heartbeat timeout exceeded | | `ReplicaPromoted` | Replica took over as primary | | `OldPrimaryRejoined` | Previous primary reconnected | | `ReplicationResumed` | Streaming resumed after failover | ## Multi-Raft (3+ Nodes) > Design only. The DML gate never opens, so joined nodes accept writes independently. For horizontal scaling, Nucleus uses **one Raft consensus group per shard**. Each shard independently elects a leader and replicates its log. ### Architecture ``` Node 1 Node 2 Node 3 ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Shard A │◄────────►│ Shard A │◄────────►│ Shard A │ │ (Leader) │ │(Follower)│ │(Follower)│ ├──────────┤ ├──────────┤ ├──────────┤ │ Shard B │ │ Shard B │ │ Shard B │ │(Follower)│◄────────►│ (Leader) │◄────────►│(Follower)│ ├──────────┤ ├──────────┤ ├──────────┤ │ Shard C │ │ Shard C │ │ Shard C │ │(Follower)│◄────────►│(Follower)│◄────────►│ (Leader) │ └──────────┘ └──────────┘ └──────────┘ ``` ### Raft Consensus Each Raft group follows the standard protocol: 1. **Leader election** — Candidates increment term, request votes from majority 2. **Log replication** — Leader sends AppendEntries RPCs to followers 3. **Commitment** — Entry committed when majority acknowledges 4. **Snapshot transfer** — Slow followers receive full state snapshot ### Raft Messages | Message | Purpose | |---------|---------| | `RequestVote` | Candidate requests vote with term + log position | | `VoteResponse` | Follower grants or denies vote | | `AppendEntries` | Leader sends log entries + commit index | | `Heartbeat` | Leader maintains authority (empty AppendEntries) | | `InstallSnapshot` | Full state transfer to lagging follower | ### Operations The Raft log carries these operation types: ``` Put { key, value } — Write a key-value pair Delete { key } — Remove a key Sql(String) — Execute SQL statement TxnPrepare { txn_id } — Phase 1 of 2PC TxnCommit { txn_id } — Phase 2 commit TxnAbort { txn_id } — Phase 2 abort Noop — Leader confirmation ``` ## Distributed Transactions > Design only. Depends on the Raft mode above. Cross-shard transactions use two-phase commit (2PC) coordinated through Raft: ### Transaction Phases ``` Coordinator Shard A Leader Shard B Leader │ │ │ │── TxnPrepare ────────►│ │ │── TxnPrepare ─────────────────────────────────►│ │ │ │ │◄── Prepared ──────────│ │ │◄── Prepared ──────────────────────────────────│ │ │ │ │── TxnCommit ─────────►│ │ │── TxnCommit ──────────────────────────────────►│ │ │ │ ``` ### Transaction Lifecycle | Phase | State | Description | |-------|-------|-------------| | 1 | `Active` | Transaction executing, writes buffered | | 2 | `Preparing` | Prepare sent to all participating shards | | 3 | `Committing` | All shards prepared, commit in progress | | 4 | `Committed` | All shards committed | | — | `Aborting` | Any shard failed to prepare | | — | `Aborted` | All shards rolled back | ## Configuration ```toml [cluster] mode = "multi_raft" # standalone | primary_replica | multi_raft node_id = 1 peers = ["node2:5433", "node3:5434"] [cluster.replication] mode = "synchronous" # synchronous | asynchronous batch_size = 64 # WAL records per batch [cluster.failover] heartbeat_timeout_ms = 5000 # Promote replica after this timeout [cluster.tls] cert = "/path/to/node.crt" key = "/path/to/node.key" ca = "/path/to/ca.crt" ``` --- ## Security Source: https://neutron.build/docs/nucleus/security Row-level security, encryption, and audit logging. Nucleus provides defense-in-depth security: row-level security (RLS), tenant-aware encryption, and immutable audit logging. ## Row-Level Security (RLS) Control which rows each user can see and modify. ### Enable RLS ```sql ENABLE RLS ON patients; ``` ### Create Policies ```sql -- Doctors can see all patients CREATE POLICY doctor_read ON patients FOR SELECT TO doctor USING (true); -- Patients can only see their own records CREATE POLICY patient_read ON patients FOR SELECT TO patient USING (patient_id = current_user_id()); -- Nurses can only see patients in their department CREATE POLICY nurse_read ON patients FOR SELECT TO nurse USING (department = current_tenant()); ``` ### Policy Types | Type | Behavior | |------|----------| | **Permissive** (default) | At least one permissive policy must pass | | **Restrictive** | All restrictive policies must pass | Policies combine: `(any permissive passes) AND (all restrictive pass)`. ### Predicate Types | Predicate | Description | |-----------|-------------| | `column = 'value'` | Column equals constant | | `column = current_user()` | Column matches session user | | `column = current_tenant()` | Column matches session tenant | | `has_role('admin')` | Session must have role | | Combined with `AND` / `OR` | Compose predicates | ### Superuser Bypass Users with the `superuser` role bypass all RLS policies. ## Column-Level Data Masking Mask a column's value for a role, without changing what is stored: ```sql CREATE MASKING POLICY ON people (ssn) TO analyst USING REDACT '***'; CREATE MASKING POLICY ON people (email) TO analyst USING EMAIL; CREATE MASKING POLICY ON cards (pan) TO support USING PARTIAL (0, 4, '*'); CREATE MASKING POLICY ON people (name) TO analyst USING HASH; SHOW MASKING POLICIES; DROP MASKING POLICY ON people (ssn) TO analyst; ``` | Rule | Effect | |------|--------| | `REDACT ''` | Replace the value with a constant | | `EMAIL` | `ada@example.com` → `a**@example.com` | | `PARTIAL (first, last [, 'char'])` | Keep the first and last N characters, mask the middle | | `HASH` | Replace with a stable digest, for pseudonymisation | | `NONE` | Pass through | A policy is identified by `(table, column, role)`; creating one for the same triple replaces it. Masking DDL requires superuser authority, participates in transactions with the rest of the security catalog, and survives restart. Masks apply to a role the session **has**, including through role membership, and they apply on every path a row leaves by — not just plain `SELECT`. Superusers are not masked. Two limits worth knowing. Masking is not a substitute for RLS: it changes the value returned, so a masked column can still be used in a `WHERE` clause to narrow rows. And there is no column-level `GRANT` — to remove a column from a role's reach entirely, expose a view that omits it. ## Encryption ### Tenant-Aware Key Management Nucleus supports per-tenant encryption keys with rotation: ```sql -- Register a tenant key SELECT register_tenant_key('tenant_42', decode('...base64key...', 'base64')); -- Rotate to a new key (old key archived for decryption) SELECT rotate_key('tenant_42', decode('...newkey...', 'base64')); ``` ### Key Rotation Key rotation is a managed process: 1. **Begin** — New key registered, old key marked for archival 2. **Re-encrypt** — Pages re-encrypted in background (progress tracked) 3. **Complete** — Old key archived, new key active Old keys are retained for decrypting data written before rotation. ### Encrypted Indexes Encrypted index modes are unavailable. `CREATE INDEX ... USING ENCRYPTED`, `ENCRYPTED_OPE`, `ENCRYPTED_RANDOM`, and `ENCRYPTED_LOOKUP` refuse with SQLSTATE `0A000`. The retired prototype did not provide secure cryptography. Recovery keeps historical catalog definitions and base rows but does not rebuild those sidecars. Base rows remain plaintext; this boundary is separate from page-level encryption at rest. ## Audit Logging Immutable, append-only audit log for compliance and forensics. ### What's Logged Every query execution records: | Field | Description | |-------|-------------| | `timestamp` | When the query ran | | `user` | Who ran it | | `action` | Query type (SELECT, INSERT, UPDATE, DELETE, DDL) | | `table` | Affected table | | `query` | Full SQL text | | `rows_affected` | Number of rows modified | | `success` | Whether the query succeeded | ### Querying the Audit Log ```sql -- Recent activity by user SELECT * FROM nucleus_audit_log WHERE user = 'alice' ORDER BY timestamp DESC LIMIT 100; -- All changes to a table SELECT * FROM nucleus_audit_log WHERE table_name = 'patients' AND action IN ('INSERT', 'UPDATE', 'DELETE'); ``` Audit entries are immutable — they cannot be updated or deleted. ## Session Context Security features use session context to evaluate policies: ```sql -- Set session properties SET SESSION user = 'alice'; SET SESSION role = 'doctor'; SET SESSION tenant_id = 'hospital_42'; ``` Context properties available to RLS predicates and masking rules: | Function | Returns | |----------|---------| | `current_user()` | Session user | | `current_tenant()` | Session tenant ID | | `has_role('name')` | Whether session has role | ## Cluster Security ### Inter-Node TLS In clustered deployments, node-to-node traffic — Raft and replication — uses **mutual** TLS: each node presents its certificate and requires its peer to present one signed by the cluster CA, in both directions. A node without a valid certificate cannot join, and a node will not talk to a peer whose certificate the CA did not sign. Configure it with environment variables (there is no `[cluster.tls]` TOML section — this page previously showed one that does not exist): ```bash NUCLEUS_INTERNAL_TLS=1 NUCLEUS_INTERNAL_TLS_CERT=/path/to/node.crt NUCLEUS_INTERNAL_TLS_KEY=/path/to/node.key NUCLEUS_INTERNAL_TLS_CA=/path/to/ca.crt NUCLEUS_INTERNAL_TLS_SERVER_NAME=nucleus.internal # optional, default "localhost" ``` `NUCLEUS_CLUSTER_TOKEN` is still enforced at connection setup and is a second factor rather than a replacement. Node identity within an authenticated cluster is not yet bound to the certificate: a peer that completes the handshake is trusted for the node id it claims. All Raft messages, WAL streaming, and gossip use encrypted channels. --- ## SQL Reference Source: https://neutron.build/docs/nucleus/sql Standard SQL with multi-model extensions. Nucleus supports standard SQL (PostgreSQL dialect) with extensions for multi-model operations. Any valid PostgreSQL query works. ## Data Types | Type | Description | |------|-------------| | `INTEGER` / `INT` | 64-bit integer | | `FLOAT` / `REAL` | 64-bit floating point | | `TEXT` / `VARCHAR` | UTF-8 string | | `BOOLEAN` / `BOOL` | true / false | | `TIMESTAMP` | Date and time | | `JSONB` | Binary JSON | | `BLOB` | Binary data | | `Vector(N)` | N-dimensional float vector | ## Tables ```sql -- Create CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, price FLOAT DEFAULT 0.0, metadata JSONB, embedding Vector(384), created_at TIMESTAMP DEFAULT NOW() ); -- Drop DROP TABLE products; -- Alter ALTER TABLE products ADD COLUMN category TEXT; ALTER TABLE products DROP COLUMN category; ``` ## Queries ```sql -- Select SELECT name, price FROM products WHERE price > 10.0; -- Aliases SELECT name AS product_name, price * 1.1 AS with_tax FROM products; -- Ordering SELECT * FROM products ORDER BY price DESC LIMIT 10 OFFSET 20; -- Distinct SELECT DISTINCT category FROM products; ``` ## Joins ```sql -- Inner join SELECT o.id, u.name, o.total FROM orders o JOIN users u ON o.user_id = u.id; -- Left join SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.name; ``` ## Aggregations ```sql SELECT category, COUNT(*) AS total, AVG(price) AS avg_price, MIN(price) AS cheapest, MAX(price) AS most_expensive, SUM(price) AS revenue FROM products GROUP BY category HAVING COUNT(*) > 5; ``` ## Subqueries ```sql -- In WHERE SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > 100); -- In FROM SELECT avg_total FROM ( SELECT user_id, AVG(total) AS avg_total FROM orders GROUP BY user_id ) AS user_avgs WHERE avg_total > 50; ``` ## Views ```sql CREATE VIEW active_users AS SELECT * FROM users WHERE last_login > NOW() - INTERVAL '30 days'; SELECT * FROM active_users; ``` ## Indexes ```sql -- B-tree (default) CREATE INDEX idx_users_email ON users (email); -- Unique CREATE UNIQUE INDEX idx_users_email_unique ON users (email); -- HNSW (vector) CREATE INDEX idx_embeddings_hnsw ON embeddings USING hnsw (vec); -- IVFFlat (vector) CREATE INDEX idx_embeddings_ivf ON embeddings USING ivfflat (vec); ``` ## Transactions ```sql BEGIN; INSERT INTO accounts (user_id, balance) VALUES (1, 1000); UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; COMMIT; -- Or rollback BEGIN; DELETE FROM users WHERE id = 1; ROLLBACK; ``` ## Functions ```sql -- String SELECT UPPER(name), LOWER(email), LENGTH(name) FROM users; -- Math SELECT ABS(-5), ROUND(3.14159, 2), CEIL(3.2), FLOOR(3.8); -- Date SELECT NOW(), EXTRACT(YEAR FROM created_at) FROM users; -- Conditional SELECT COALESCE(nickname, name) AS display_name FROM users; SELECT CASE WHEN price > 100 THEN 'expensive' ELSE 'cheap' END FROM products; ``` ## Sequences ```sql CREATE SEQUENCE order_seq START 1000; SELECT NEXTVAL('order_seq'); SELECT CURRVAL('order_seq'); ``` ## Triggers `CREATE TRIGGER` and `DROP TRIGGER` are accepted and the definition is stored and matched against the right table, timing and event: ```sql CREATE TRIGGER audit_writes AFTER INSERT ON users FOR EACH ROW EXECUTE FUNCTION audit_users(); ``` **Trigger bodies do not execute yet.** A trigger fires at the right moment and its body is not run, so nothing observable happens — verified against a live engine on 2026-08-17. Do not use a trigger to enforce an invariant, write an audit row, or maintain a derived table; use the application, or a statement that does the work explicitly. The `SET NEW.col = …` body form shown here previously does not parse at all. --- ## Streams Source: https://neutron.build/docs/nucleus/streams Append-only logs with consumer groups. Nucleus includes Redis-compatible streams for event sourcing, message queues, and real-time data pipelines. Append-only with consumer groups for reliable processing. ## Adding Entries ```sql -- Add an entry with field-value pairs SELECT STREAM_XADD('events', 'user', 'alice', 'action', 'login', 'ip', '192.168.1.1'); -- → "1709856000000-0" SELECT STREAM_XADD('events', 'user', 'bob', 'action', 'purchase', 'amount', '49.99'); -- → "1709856000001-0" ``` Each entry gets an auto-generated ID in the format `-`. ## Reading Entries ```sql -- Get entries in a time range (start_ms, end_ms, count) SELECT STREAM_XRANGE('events', 0, 9999999999999, 100); -- Read entries after a specific timestamp SELECT STREAM_XREAD('events', 1709856000000, 10); -- Stream length SELECT STREAM_XLEN('events'); -- → 2 ``` ## Consumer Groups Consumer groups let multiple consumers process a stream cooperatively, with exactly-once delivery guarantees: ```sql -- Create a consumer group starting from the beginning SELECT STREAM_XGROUP_CREATE('events', 'processors', 0); -- Read as a consumer (entries are assigned to this consumer) SELECT STREAM_XREADGROUP('events', 'processors', 'worker-1', 10); -- Acknowledge processing is complete SELECT STREAM_XACK('events', 'processors', 1709856000000, 0); ``` ### How Consumer Groups Work 1. **Create** a group on a stream with a starting position 2. **Read** entries as a named consumer — entries are tracked per consumer 3. **Acknowledge** entries after processing — removes from pending list 4. Unacknowledged entries can be reclaimed if a consumer fails ## Entry ID Format IDs follow the Redis convention: ``` - ``` Examples: - `1709856000000-0` — First entry at that millisecond - `1709856000000-1` — Second entry at the same millisecond - `*` — Auto-generate with current timestamp ## Use Cases - **Event sourcing** — Immutable event log for state reconstruction - **Message queues** — Producer/consumer with acknowledgment - **Activity feeds** — User actions, notifications, audit trails - **Change data capture** — Stream database changes to consumers - **Real-time pipelines** — Multi-stage data processing - **Task distribution** — Fan out work to multiple workers via consumer groups --- ## Time Series Source: https://neutron.build/docs/nucleus/timeseries Gorilla-compressed metrics with continuous aggregation. Nucleus includes a built-in time-series engine with Gorilla compression, continuous aggregation, and retention policies — no InfluxDB or TimescaleDB needed. ## Inserting Data ```sql -- Insert a data point (series_name, timestamp_ms, value) SELECT TS_INSERT('cpu_usage', 1709856000000, 72.5); SELECT TS_INSERT('cpu_usage', 1709856001000, 68.3); SELECT TS_INSERT('cpu_usage', 1709856002000, 75.1); SELECT TS_INSERT('memory_mb', 1709856000000, 4096); SELECT TS_INSERT('memory_mb', 1709856001000, 4120); ``` ## Querying ```sql -- Get the latest value SELECT TS_LAST('cpu_usage'); -- → 75.1 -- Count total points SELECT TS_COUNT('cpu_usage'); -- → 3 -- Count points in a time range SELECT TS_RANGE_COUNT('cpu_usage', 1709856000000, 1709856002000); -- Average in a time range SELECT TS_RANGE_AVG('cpu_usage', 1709856000000, 1709856002000); ``` ## Time Bucketing Group data points into fixed time windows: ```sql -- Bucket by hour SELECT TIME_BUCKET(3600000, timestamp_col) AS hour, AVG(value) AS avg_cpu FROM metrics GROUP BY hour ORDER BY hour; -- Using named intervals SELECT DATE_BIN('1 hour', timestamp_col) AS hour, AVG(value) FROM metrics GROUP BY hour; ``` ### Bucket Sizes | Name | Aliases | Milliseconds | |------|---------|-------------| | Second | `s`, `sec`, `seconds` | 1,000 | | Minute | `m`, `min`, `minutes` | 60,000 | | Hour | `h`, `hr`, `hours` | 3,600,000 | | Day | `d`, `days` | 86,400,000 | | Week | `w`, `weeks` | 604,800,000 | | Month | `mon`, `months` | ~2,592,000,000 | ## Continuous Aggregation Pre-compute rollups that materialize automatically as data arrives: ```sql -- Create a continuous aggregate (via the API) -- Materializes hourly averages from the cpu_usage series -- Supports: Avg, Sum, Min, Max, Count, First, Last ``` Continuous aggregates use watermark-based incremental materialization — only closed time buckets are computed, avoiding partial results. ## Retention Policies Automatically delete old data: ```sql -- Set retention: delete points older than 30 days SELECT TS_RETENTION(2592000000); ``` ## Gorilla Compression Nucleus uses Facebook's Gorilla compression algorithm for time-series data: **Timestamps** — Delta-of-delta encoding: - First timestamp: 64 bits - Subsequent: variable-length (0-12 bits for typical monotonic timestamps) **Values** — XOR-based compression: - Similar consecutive values compress to just a few bits - Typical compression ratio: ~1.37 bytes/point (vs 16 bytes uncompressed) Compression is automatic and transparent — no configuration needed. ## Performance - **SIMD-accelerated** aggregations (sum, min, max) on supported hardware - **Parallel range queries** for sum, count, avg, min, max - **Parallel bulk insert** for batch ingestion - **Partition index** (B-tree on time windows) for O(log P + K) range scans - **Running statistics** — count, sum, min, max maintained incrementally - **WAL-backed** durability with crash recovery ## Use Cases - **Application metrics** — CPU, memory, request latency - **IoT telemetry** — Sensor readings, device status - **Financial data** — Stock prices, trading volumes - **Infrastructure monitoring** — Server health, network traffic - **Analytics** — User activity over time, conversion funnels --- ## Vector Search Source: https://neutron.build/docs/nucleus/vector Similarity search for AI, RAG, and embeddings. Nucleus includes built-in vector search with HNSW and IVFFlat indexes. Store embeddings alongside your relational data — no separate vector database needed. ## The Vector Type Declare a fixed-dimension vector column: ```sql CREATE TABLE documents ( id SERIAL PRIMARY KEY, title TEXT, content TEXT, embedding Vector(384) ); ``` ## Inserting Vectors Use the `VECTOR()` function to create vector values: ```sql INSERT INTO documents (title, content, embedding) VALUES ( 'Getting Started', 'Learn how to use Neutron...', VECTOR('[0.1, 0.5, 0.3, ...]') ); ``` **Important:** Always use `VECTOR('...')` — a bare string `'[1,0,0]'` stores as text, not as a vector. ## Similarity Search Use `VECTOR_DISTANCE` to find similar vectors: ```sql SELECT title, VECTOR_DISTANCE(embedding, VECTOR('[0.1, 0.4, 0.2, ...]'), 'l2') AS distance FROM documents ORDER BY distance LIMIT 10; ``` ## Distance Metrics | Metric | Function | Best For | |--------|----------|----------| | `l2` | Euclidean distance | General purpose | | `cosine` | Cosine similarity | Text embeddings | | `dot` | Inner product | Normalized vectors | ```sql -- Cosine similarity SELECT * FROM documents ORDER BY VECTOR_DISTANCE(embedding, VECTOR('[...]'), 'cosine') LIMIT 5; -- Inner product SELECT * FROM documents ORDER BY VECTOR_DISTANCE(embedding, VECTOR('[...]'), 'dot') LIMIT 5; ``` ## Indexes ### HNSW (Hierarchical Navigable Small World) Best for high recall with moderate memory usage. Recommended for most use cases. ```sql CREATE INDEX idx_docs_hnsw ON documents USING hnsw (embedding); ``` ### IVFFlat (Inverted File with Flat) Best for large datasets where you can trade some recall for speed. ```sql CREATE INDEX idx_docs_ivf ON documents USING ivfflat (embedding); ``` ## Filtering with Vectors Combine vector search with SQL filters: ```sql -- Find similar documents in a specific category SELECT title, VECTOR_DISTANCE(embedding, VECTOR('[...]'), 'cosine') AS score FROM documents WHERE category = 'tutorials' ORDER BY score LIMIT 10; -- Join with other tables SELECT d.title, u.name AS author, VECTOR_DISTANCE(d.embedding, VECTOR('[...]'), 'l2') AS dist FROM documents d JOIN users u ON d.author_id = u.id ORDER BY dist LIMIT 5; ``` ## RAG Pattern A common pattern for Retrieval-Augmented Generation: ```sql -- 1. Store document chunks with embeddings CREATE TABLE chunks ( id SERIAL PRIMARY KEY, doc_id INTEGER REFERENCES documents(id), text TEXT, embedding Vector(1536) ); -- 2. Create HNSW index CREATE INDEX idx_chunks_hnsw ON chunks USING hnsw (embedding); -- 3. Retrieve relevant chunks for a query SELECT text FROM chunks ORDER BY VECTOR_DISTANCE(embedding, VECTOR('[query_embedding...]'), 'cosine') LIMIT 5; -- 4. Pass retrieved chunks to your LLM as context ``` ## Index choice - **HNSW** provides approximate nearest-neighbor search with a graph index. - **IVFFlat** trades recall for a smaller candidate set controlled by its probe configuration. - **Brute force** compares every candidate and returns exact results without an approximate index. Measure index build time, recall, memory use, and query latency with your own dimensions and dataset before choosing an index. --- ## Database Source: https://neutron.build/docs/python/database Async Nucleus client with all 14 data models. The Python Nucleus client provides async access to all 14 data models using asyncpg. ## Connection ```python from neutron.nucleus import NucleusClient db = await NucleusClient.connect( "postgres://localhost:5432/mydb", min_size=5, max_size=25, ) # Auto-detect features print(db.features.is_nucleus) # True print(db.features.version) # "0.1.0" await db.close() ``` ## SQL ```python from pydantic import BaseModel class User(BaseModel): id: int name: str email: str # Query multiple rows users = await db.sql.query(User, "SELECT * FROM users") # Query one row user = await db.sql.query_one(User, "SELECT * FROM users WHERE id = $1", 42) # Query one or None user = await db.sql.query_one_or_none(User, "SELECT * FROM users WHERE id = $1", 99) # Execute (returns affected count) count = await db.sql.execute( "INSERT INTO users (name, email) VALUES ($1, $2)", "Alice", "alice@example.com") # Scalar value total = await db.sql.fetchval("SELECT COUNT(*) FROM users") ``` ## Key-Value ```python kv = db.kv await kv.set("session:1", "data", ttl=3600) val = await kv.get("session:1") await kv.delete("session:1") # Typed get/set (JSON serialization) await kv.set_typed("prefs:1", PrefsModel(theme="dark"), ttl=3600) prefs = await kv.get_typed("prefs:1", PrefsModel) # Lists await kv.lpush("queue", "item") item = await kv.rpop("queue") # Hashes await kv.hset("user:1", "name", "Alice") name = await kv.hget("user:1", "name") all_fields = await kv.hgetall("user:1") # Sorted sets await kv.zadd("leaderboard", 100, "alice") top = await kv.zrange("leaderboard", 0, 9) # HyperLogLog await kv.pfadd("visitors", "user1") count = await kv.pfcount("visitors") ``` ## Vector Search ```python results = await db.vector.search( "documents", query_embedding, k=10, metric="cosine", filter={"category": "tech"}, ) for r in results: print(f"ID: {r.id}, Score: {r.score:.4f}") ``` ## Document ```python doc_id = await db.document.insert("posts", { "title": "Hello", "body": "World", "tags": ["python"] }) post = await db.document.find_one("posts", {"author": "alice"}) posts = await db.document.find("posts", {"author": "alice"}, limit=10) # Typed queries post = await db.document.find_one_typed("posts", {"author": "alice"}, BlogPost) ``` ## Graph ```python node_id = await db.graph.add_node(["Person"], {"name": "Alice", "age": 30}) await db.graph.add_edge("KNOWS", node_id, other_id, {"since": "2024"}) result = await db.graph.query( "MATCH (p:Person)-[:KNOWS]->(q) RETURN p.name, q.name") ``` ## Full-Text Search ```python results = await db.fts.search("articles", "machine learning", limit=10, fuzzy=2, highlight=True) ``` ## Time Series ```python from neutron.nucleus import TimeSeriesPoint from datetime import datetime points = [TimeSeriesPoint(timestamp=datetime.now(), value=72.5, tags={"host": "web-01"})] await db.timeseries.write("cpu_usage", points) latest = await db.timeseries.last("cpu_usage") points = await db.timeseries.query("cpu_usage", start, end, buckets=60) ``` ## All 14 Models | Model | Accessor | Key Methods | |-------|----------|-------------| | SQL | `db.sql` | query, execute, fetchval | | Key-Value | `db.kv` | get, set, hset, lpush, zadd | | Vector | `db.vector` | search | | TimeSeries | `db.timeseries` | write, query, last | | Document | `db.document` | insert, find, get | | Graph | `db.graph` | add_node, add_edge, query | | FTS | `db.fts` | search | | Geo | `db.geo` | distance, within_radius | | Blob | `db.blob` | put, get, delete | | Streams | `db.streams` | xadd, xrange, xreadgroup | | Columnar | `db.columnar` | insert, sum, avg | | Datalog | `db.datalog` | assert_rule, query | | CDC | `db.cdc` | subscribe | | PubSub | `db.pubsub` | publish, subscribe | --- ## Deployment Source: https://neutron.build/docs/python/deployment Production deployment with uvicorn, granian, and Docker. ## Running the Server ```python from neutron import App app = App(title="My API", version="1.0.0") # Development app.run(host="127.0.0.1", port=8000) # Production app.run(host="0.0.0.0", port=8000, server="uvicorn", workers=4) ``` ### Server Backends | Backend | Install | Features | |---------|---------|----------| | **uvicorn** (default) | Included | Asyncio, HTTP/1.1, WebSocket | | **granian** | `pip install granian` | Rust/Tokio, HTTP/2, faster | ```python # Use granian for better performance app.run(server="granian", workers=4) ``` ## Configuration Pydantic Settings with `NEUTRON_` prefix: | Variable | Default | Description | |----------|---------|-------------| | `NEUTRON_HOST` | `0.0.0.0` | Bind address | | `NEUTRON_PORT` | `8000` | Listen port | | `NEUTRON_WORKERS` | `1` | Worker processes | | `NEUTRON_DEBUG` | `false` | Debug mode | | `NEUTRON_DATABASE_URL` | — | Connection string (required) | | `NEUTRON_DB_POOL_MIN` | `5` | Min pool connections | | `NEUTRON_DB_POOL_MAX` | `25` | Max pool connections | | `NEUTRON_LOG_LEVEL` | `info` | Log level | | `NEUTRON_LOG_FORMAT` | `json` | Log format (`json` or `pretty`) | ## Built-in Endpoints Every app exposes: | Endpoint | Description | |----------|-------------| | `GET /health` | `{"status": "ok", "nucleus": true, "version": "1.0.0"}` | | `GET /openapi.json` | OpenAPI 3.1 spec | | `GET /docs` | Swagger UI | ## Lifespan Events ```python from contextlib import asynccontextmanager @asynccontextmanager async def lifespan(app): # Startup await db.connect() await cache.warmup() yield # Shutdown await cache.flush() await db.close() app = App(lifespan=lifespan) ``` ## Docker ```dockerfile FROM python:3.12-slim WORKDIR /app COPY pyproject.toml . RUN pip install --no-cache-dir . COPY . . EXPOSE 8000 CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"] ``` ### With granian (faster) ```dockerfile FROM python:3.12-slim WORKDIR /app COPY pyproject.toml . RUN pip install --no-cache-dir ".[fast]" COPY . . EXPOSE 8000 CMD ["granian", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4", "--interface", "asgi"] ``` ### Docker Compose ```yaml services: app: build: . ports: - "8000:8000" environment: NEUTRON_DATABASE_URL: postgres://nucleus:5432/mydb NEUTRON_LOG_LEVEL: info NEUTRON_WORKERS: 4 depends_on: nucleus: condition: service_healthy nucleus: image: ghcr.io/neutron-build/nucleus:latest ports: - "5432:5432" volumes: - nucleus_data:/var/lib/nucleus healthcheck: test: ["CMD", "pg_isready", "-h", "localhost"] interval: 5s volumes: nucleus_data: ``` ## Dependencies ```toml # pyproject.toml [project] dependencies = [ "starlette>=0.38.0", "pydantic>=2.0", "uvicorn[standard]>=0.30.0", "asyncpg>=0.29.0", "structlog>=24.0.0", ] [project.optional-dependencies] fast = ["granian>=2.0.0"] auth = ["PyJWT[crypto]>=2.8.0"] ``` ## systemd ```ini [Unit] Description=My Neutron Python App After=network.target [Service] Type=simple User=myapp WorkingDirectory=/opt/myapp ExecStart=/opt/myapp/.venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 Environment=NEUTRON_DATABASE_URL=postgres://localhost:5432/mydb Restart=on-failure RestartSec=5 TimeoutStopSec=30 [Install] WantedBy=multi-user.target ``` --- ## Middleware Source: https://neutron.build/docs/python/middleware Request pipeline with 7 built-in middleware components. Neutron Python uses ASGI middleware built on Starlette. Each middleware wraps the next, forming a pipeline. ## Adding Middleware ```python from neutron import App from neutron.middleware import ( RequestIDMiddleware, LoggingMiddleware, CORSMiddleware, CompressionMiddleware, RateLimitMiddleware, TimeoutMiddleware, OTelMiddleware, ) app = App( middleware=[ RequestIDMiddleware(), LoggingMiddleware(), OTelMiddleware(service_name="my-api"), CORSMiddleware(allow_origins=["https://example.com"]), CompressionMiddleware(minimum_size=500), TimeoutMiddleware(timeout=30.0), RateLimitMiddleware(rps=100, burst=200), ] ) ``` Middleware executes in registration order for requests, reverse order for responses. ## Built-in Middleware ### RequestID Generates a UUID for each request, available in `request.state.request_id` and injected as the `x-request-id` response header. ```python RequestIDMiddleware() ``` ### Logging Structured access logging via `structlog`: ```python LoggingMiddleware() ``` Logs: method, path, status code, duration (ms). Status-based levels: 5xx = ERROR, 4xx = WARN, 2xx/3xx = INFO. ### CORS Cross-Origin Resource Sharing: ```python CORSMiddleware( allow_origins=["https://example.com"], allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Authorization", "Content-Type"], allow_credentials=True, max_age=3600, ) ``` ### Compression Gzip response compression: ```python CompressionMiddleware(minimum_size=500) # Only compress responses > 500 bytes ``` ### Rate Limiting Token bucket rate limiter: ```python RateLimitMiddleware(rps=100, burst=200) ``` Returns `429 Too Many Requests` with RFC 7807 body: ```json { "type": "https://neutron.build/errors/rate-limited", "title": "Rate Limited", "status": 429, "detail": "Too many requests" } ``` ### Timeout Request timeout using `asyncio.wait_for`: ```python TimeoutMiddleware(timeout=30.0) # seconds ``` Returns `504 Gateway Timeout` with RFC 7807 body on timeout. ### OpenTelemetry Distributed tracing: ```python OTelMiddleware(service_name="my-api") ``` - Generates trace ID and span ID per request - Injects `traceparent` and `x-trace-id` response headers - Logs trace context via structlog ## Custom Middleware Inherit from `_NeutronMiddleware`: ```python from neutron.middleware import _NeutronMiddleware class AuthMiddleware(_NeutronMiddleware): def __init__(self, secret: str): self.secret = secret async def __call__(self, scope, receive, send): if scope["type"] == "http": headers = dict(scope.get("headers", [])) token = headers.get(b"authorization", b"").decode() if not self.validate(token): response = JSONResponse({"detail": "Unauthorized"}, status_code=401) await response(scope, receive, send) return await self.app(scope, receive, send) def validate(self, token: str) -> bool: # Your validation logic ... ``` Register like any other middleware: ```python app = App(middleware=[ RequestIDMiddleware(), AuthMiddleware(secret="my-secret"), ]) ``` ## Error Format All middleware error responses follow RFC 7807 Problem Details: ```json { "type": "https://neutron.build/errors/", "title": "Human-Readable Title", "status": 429, "detail": "Detailed explanation" } ``` --- ## Python Framework Source: https://neutron.build/docs/python/overview Async web framework with Starlette, Pydantic, and Nucleus. Neutron for Python is an async web framework built on Starlette and Pydantic v2. Typed handlers with automatic parameter extraction, structured middleware, and a full Nucleus client for all 14 data models. ## Features - **Async-first** — Built on Starlette ASGI + asyncpg - **Type-safe** — Pydantic v2 validation for requests and responses - **Auto-extract** — Path, query, header, and body parameters from type annotations - **RFC 7807** — Standardized error responses - **OpenAPI 3.1** — Auto-generated from handler signatures - **14 data models** — Full Nucleus client ## Hello World ```python from neutron import App, Router app = App(title="My API", version="1.0.0") router = Router() @router.get("/") async def hello() -> dict: return {"message": "Hello, Neutron!"} @router.get("/users/{user_id}") async def get_user(user_id: int) -> dict: return {"id": user_id, "name": "Alice"} app.include_router(router) if __name__ == "__main__": app.run(port=8000) ``` ## Package Structure | Module | Purpose | |--------|---------| | `neutron.app` | ASGI application, middleware, lifecycle | | `neutron.router` | Typed route registration | | `neutron.handler` | Parameter extraction engine | | `neutron.middleware` | 7 built-in middleware | | `neutron.error` | RFC 7807 error responses | | `neutron.config` | Environment-based config | | `neutron.depends` | Dependency injection | | `neutron.nucleus` | Database client (all 14 models) | ## Auto-Generated Endpoints | Endpoint | Description | |----------|-------------| | `GET /health` | Health check | | `GET /openapi.json` | OpenAPI 3.1 spec | | `GET /docs` | Swagger UI | ## Servers ```python # Uvicorn (default, HTTP/1.1) app.run(port=8000) # Granian (Rust/Tokio, HTTP/2) app.run(port=8000, server="granian") ``` --- ## Quickstart Source: https://neutron.build/docs/python/quickstart Build a Python API server in minutes. ## Install ```bash pip install neutron-framework ``` Or with extras: ```bash pip install "neutron-framework[ai]" # AI providers, agents, RAG pip install "neutron-framework[all]" # every extra ``` The full set is `ai`, `crypto`, `granian`, `rich`, `all` — plus `test` for development. There is no `redis` or `otel` extra; this block advertised both until 2026-08-17. ## Create a Project ```bash neutron new my-api --lang python cd my-api python3 -m venv .venv .venv/bin/pip install -e . neutron dev ``` The generated `pyproject.toml` installs `neutron-framework` from PyPI. `neutron dev` runs `python -m neutron dev app.main:app` with the project's `.venv`, on `NEUTRON_PORT` (default 8000). ## Your First App ```python from pydantic import BaseModel from neutron import App, Router from neutron.nucleus import NucleusClient app = App(title="My API", version="1.0.0") router = Router() class CreateUser(BaseModel): name: str email: str class User(BaseModel): id: int name: str email: str @router.get("/users/{user_id}") async def get_user(user_id: int) -> User: user = await app.state.db.sql.query_one(User, "SELECT * FROM users WHERE id = $1", user_id) return user @router.post("/users") async def create_user(input: CreateUser) -> User: user = await app.state.db.sql.query_one(User, "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *", input.name, input.email) return user @router.get("/users") async def list_users() -> list[User]: return await app.state.db.sql.query(User, "SELECT * FROM users") app.include_router(router, prefix="/api") @app.on_event("startup") async def startup(): app.state.db = await NucleusClient.connect("postgres://localhost:5432/mydb") @app.on_event("shutdown") async def shutdown(): await app.state.db.close() if __name__ == "__main__": app.run(port=8000) ``` ## Run ```bash # Development neutron dev # Or directly python -m neutron run # or uvicorn app.main:app --reload --port 8000 ``` ## Test It ```bash curl http://localhost:8000/health curl http://localhost:8000/api/users curl -X POST http://localhost:8000/api/users \ -H "Content-Type: application/json" \ -d '{"name": "Alice", "email": "alice@example.com"}' ``` ## Middleware Stack ```python from neutron.middleware import ( RequestIDMiddleware, LoggingMiddleware, CORSMiddleware, CompressionMiddleware, RateLimitMiddleware, TimeoutMiddleware, ) app = App( title="My API", middleware=[ RequestIDMiddleware(), LoggingMiddleware(), CORSMiddleware(allow_origins=["*"]), CompressionMiddleware(minimum_size=500), RateLimitMiddleware(rps=100, burst=200), TimeoutMiddleware(timeout=30), ], ) ``` --- ## Real-time Source: https://neutron.build/docs/python/realtime WebSocket hub and Server-Sent Events for live updates. Neutron Python provides two real-time transports: WebSocket hub for bidirectional messaging and SSE for server-push streaming. ## WebSocket Hub Room-based broadcasting system for real-time applications. ### Setup ```python from neutron.realtime import WebSocketHub hub = WebSocketHub() @app.websocket("/ws") async def websocket_endpoint(websocket): await hub.handle(websocket) ``` ### Client Protocol Clients send JSON messages to join rooms, leave rooms, and broadcast: ```json // Join a room {"action": "join", "room": "chat:general"} // Leave a room {"action": "leave", "room": "chat:general"} // Send a message to a room {"action": "message", "room": "chat:general", "data": {"text": "Hello!"}} ``` ### Server Responses ```json // Confirmation {"action": "joined", "room": "chat:general"} {"action": "left", "room": "chat:general"} ``` ### Server-Side Broadcasting Push messages from any handler: ```python @app.post("/notifications/send") async def send_notification(request): data = await request.json() # Broadcast to all connections in a room count = await hub.broadcast( room="notifications", data={"type": "alert", "message": data["message"]}, exclude=None, # Optional: exclude a specific connection ) return {"sent_to": count} ``` ### Hub Stats ```python hub.connection_count # Total active WebSocket connections hub.room_count("chat:general") # Connections in a specific room hub.rooms # List of all active room names ``` ## Server-Sent Events (SSE) One-way server-push for event streaming. ### Basic Usage ```python from neutron.realtime import sse_response @app.get("/events") async def events(request): async def generate(): for i in range(100): yield {"data": f"Event {i}", "event": "update", "id": str(i)} await asyncio.sleep(1) return sse_response(generate()) ``` ### SSE Stream Class For more control, use `SSEStream` directly: ```python from neutron.realtime import SSEStream, sse_response stream = SSEStream() # Push events programmatically await stream.send(data="Hello", event="greeting", id="1") await stream.send(data={"count": 42}, event="metric") await stream.close() ``` ### Wire Format Events are formatted as standard SSE: ``` event: update id: 1 data: Event 1 event: metric data: {"count": 42} ``` Multiline data is split across multiple `data:` lines. ### Response Headers SSE responses automatically set: | Header | Value | |--------|-------| | `Content-Type` | `text/event-stream` | | `Cache-Control` | `no-cache` | | `Connection` | `keep-alive` | ### Custom Headers ```python return sse_response(generate(), headers={"X-Stream-ID": "abc123"}) ``` ## Nucleus Integration ### Live Query Results via SSE Stream database changes to clients using PubSub + SSE: ```python @app.get("/orders/live") async def live_orders(request): async def stream(): async for event in db.pubsub.listen("order_events"): yield {"event": "order_update", "data": event} return sse_response(stream()) ``` ### Chat with WebSocket Hub ```python hub = WebSocketHub() @app.websocket("/chat") async def chat(websocket): await hub.handle(websocket) # Server-side message injection @app.post("/chat/announce") async def announce(request): data = await request.json() await hub.broadcast("chat:general", { "type": "system", "text": data["message"], }) return {"status": "sent"} ``` --- ## Routing Source: https://neutron.build/docs/python/routing Typed routes with automatic parameter extraction. ## Route Registration ```python from neutron import Router router = Router() @router.get("/users") async def list_users() -> list[User]: ... @router.post("/users") async def create_user(input: CreateUser) -> User: ... @router.put("/users/{user_id}") async def update_user(user_id: int, input: UpdateUser) -> User: ... @router.patch("/users/{user_id}") async def patch_user(user_id: int, input: PatchUser) -> User: ... @router.delete("/users/{user_id}") async def delete_user(user_id: int) -> None: ... ``` ## Parameter Extraction Parameters are automatically extracted based on type annotations: ### Path Parameters ```python @router.get("/users/{user_id}/posts/{post_id}") async def get_post(user_id: int, post_id: int) -> Post: # Extracted from URL path ... ``` ### Request Body ```python from pydantic import BaseModel class CreateUser(BaseModel): name: str email: str age: int | None = None @router.post("/users") async def create_user(input: CreateUser) -> User: # Parsed from JSON body, validated by Pydantic ... ``` ### Query Parameters ```python from neutron import Query class Filters(BaseModel): page: int = 1 per_page: int = 20 q: str | None = None @router.get("/users") async def list_users(query: Query[Filters]) -> list[User]: # ?page=2&per_page=50&q=alice ... ``` ### Headers ```python from neutron import Header class AuthHeaders(BaseModel): authorization: str @router.get("/protected") async def protected(headers: Header[AuthHeaders]) -> dict: ... ``` ### Raw Request ```python from starlette.requests import Request @router.get("/custom") async def custom(request: Request) -> dict: body = await request.json() ... ``` ## Route Groups ```python api = router.group("/api") @api.get("/items") async def list_items() -> list[Item]: ... @api.post("/items") async def create_item(input: CreateItem) -> Item: ... ``` ## Dependency Injection ```python from neutron import Depends async def get_db(): return request.app.state.db async def get_current_user(db = Depends(get_db)): # Resolved recursively ... @router.get("/me") async def me(user = Depends(get_current_user)) -> User: return user ``` ## Response Serialization Return types are automatically serialized: | Return Type | Response | |-------------|----------| | Pydantic model | JSON (200) | | `list[Model]` | JSON array (200) | | `dict` | JSON (200) | | `None` | 204 No Content | | `str` | text/plain (200) | | Starlette `Response` | passed through | ## Error Handling ```python from neutron.error import not_found, bad_request, validation_error @router.get("/users/{user_id}") async def get_user(user_id: int) -> User: user = await db.find(user_id) if not user: raise not_found("User not found") return user ``` RFC 7807 errors: `bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `validation_error`, `rate_limited`, `internal_error`. --- ## Islands Source: https://neutron.build/docs/rendering/islands Hydrate individual components on a static page. An island is an interactive Preact component inside an otherwise static page. Neutron emits the island runtime and component chunk without hydrating the surrounding route. ## Add an island ```tsx import { Island } from "@neutron-build/core/client"; import Counter from "../components/Counter"; export default function Page() { return (

Static content

); } ``` Props are serialized into the generated HTML, so pass values that can be represented as JSON. ## Hydration timing | Value | Hydration begins | | --- | --- | | `client="load"` | When the island runtime starts | | `client="visible"` | When the element enters the viewport | | `client="idle"` | During browser idle time, with a timeout fallback | | `client="media"` | When the query passed through `media` matches | | `client="only"` | Immediately, rendering a fresh tree if no server markup exists | For a media-controlled island: ```tsx ``` ## Prerendered markup is not reconciled Hydration attaches listeners to the markup the build produced; it does not diff attributes against the first client render. Whatever an island rendered at build time sticks until state changes it, and a static build has no URL, no `localStorage`, and no viewport. Anything derived from those must start unresolved on the server and on the first client render alike, then fill in after mount: ```tsx function Nav() { // "" at build time and on the first client render; the real path after mount. const [path, setPath] = useState(""); useEffect(() => setPath(location.pathname), []); return Docs; } ``` Reading `location.pathname` in the initial state keeps the build-time value on every page (the same prerendered `active` class everywhere). This does not show up in `neutron dev`, where nothing is prerendered, so check a production build. Use app mode instead when navigation state, loaders, and actions belong to the whole route rather than a few isolated controls. --- ## Server-Side Rendering Source: https://neutron.build/docs/rendering/server-side-rendering Request-time rendering for app routes. Routes with `config.mode` set to `"app"` render on the server. Use them when middleware, authentication, request data, or mutations must run at request time. ## Request lifecycle For a matched app route, Neutron: 1. Runs the configured middleware chain. 2. Runs matching layout and page loaders in parallel. 3. Renders their component tree into the document shell. 4. Streams the response when the selected renderer supports streaming. 5. Hydrates the document and starts the client router in the browser. The initial response contains rendered HTML; it is not an empty client-side application shell. ## Enable app mode ```tsx export const config = { mode: "app" }; ``` The mode is declared per route, so static documentation or marketing pages can live beside authenticated application routes in the same project. ## Cache a rendered response `cache.maxAge` enables Neutron's route response cache: ```tsx export const config = { mode: "app", cache: { maxAge: 60 }, }; ``` Use request-specific caching rules carefully for personalized responses. Route headers can also control downstream browser or CDN caching: ```tsx export function headers() { return { "Cache-Control": "public, max-age=60, s-maxage=3600" }; } ``` See [App Routes](/docs/routing/app-routes) for loaders, actions, and the client entry. --- ## Static Rendering Source: https://neutron.build/docs/rendering/static-rendering How Neutron generates build-time HTML. Static rendering is the default for Neutron routes. It is intended for pages whose output can be known at build time. ## Build process For each static route, `neutron-ts build`: 1. Loads the matching layouts and page. 2. Runs their loaders, if present. 3. Renders the component tree to HTML. 4. Writes the HTML and links it to the built stylesheet assets. Dynamic routes are generated once for every parameter set returned by `getStaticPaths`. ## Client JavaScript A plain static page does not receive Neutron's client router or page-shell hydration. Static pages can still contain authored scripts, native browser features such as view transitions, and independently hydrated [islands](/docs/rendering/islands). This distinction matters: `static` describes when the page HTML is produced, not a blanket prohibition on JavaScript. ## Build-time data Static loaders run during the build: ```tsx import { useLoaderData } from "@neutron-build/core"; export async function loader() { return { posts: await listPublishedPosts() }; } export default function Posts() { const { posts } = useLoaderData(); return ; } ``` Rebuild to publish changes in that data. If the response must vary by user or request, use [server-rendered app mode](/docs/rendering/server-side-rendering). --- ## View Transitions Source: https://neutron.build/docs/rendering/view-transitions Native browser page transitions. Neutron's `ViewTransitions` component enables the browser's View Transitions API for supported same-origin navigations. Unsupported browsers keep normal navigation behavior. ## Enabling View Transitions To enable view transitions, add the `` component to your root layout. ```tsx // src/routes/_layout.tsx import type { ComponentChildren } from "preact"; import { ViewTransitions } from "@neutron-build/core/client"; export default function RootLayout({ children }: { children?: ComponentChildren }) { return (
{children}
); } ``` ## How It Works 1. **Browser navigation:** Plain links and static routes remain browser-owned. The component opts the document into cross-document transitions with CSS and adds lightweight prefetching. 2. **Client navigation:** `Link` and `navigate()` use the client router's transition support on app routes. ## Customizing Animations You can name specific elements to animate them distinctly during the transition using the `view-transition-name` CSS property. ```tsx // Page 1: List // Page 2: Detail ``` Supporting browsers can animate the named element between the two views. --- ## App Routes Source: https://neutron.build/docs/routing/app-routes Render per request with hydration and client navigation. App mode is for routes whose response depends on the request or whose interface needs the client router. Neutron runs matching middleware and loaders on the server, renders HTML, then hydrates the route in the browser. ```tsx export const config = { mode: "app" }; ``` After the initial document load, links between app routes use client-side navigation. A project can mix app routes with [static routes](/docs/routing/static-routes); navigation to a static route remains a browser-native document navigation. ## Load server data Matching layout and page loaders run in parallel. Their return values are serialized for the route components: ```tsx import { useLoaderData } from "@neutron-build/core"; export const config = { mode: "app" }; export async function loader({ request }) { return { user: await getUser(request) }; } export default function Profile() { const { user } = useLoaderData(); return

Hello, {user.name}

; } ``` Keep credentials and private services inside loaders and actions; those exports are stripped from the client build. ## Handle mutations An `action` handles non-GET submissions for the matched route: ```tsx import { Form } from "@neutron-build/core"; export const config = { mode: "app" }; export async function action({ request }) { const form = await request.formData(); await updateName(form.get("name")); return { saved: true }; } export default function Settings() { return ( ); } ``` See [Loaders](/docs/data/loaders), [Actions](/docs/data/actions), and [Forms](/docs/data/forms). ## Client entry The generated `src/main.tsx` registers the shared route table and starts hydration: ```tsx import { init, registerRoutes } from "@neutron-build/core/client"; import { routes } from "virtual:neutron/routes"; registerRoutes(routes); void init(); ``` The starter creates this file. Add browser-only setup before `init()` when needed; removing the entry disables hydration and client navigation. ## Runtime Preact is the default. Projects that need React package compatibility can use the `react-compat` runtime when they are created: ```bash npm create @neutron-build@latest my-app -- --runtime react-compat ``` The runtime setting is project-wide; the static/app decision is per route. --- ## Dynamic Routes Source: https://neutron.build/docs/routing/dynamic-routes Handling variable path segments. Dynamic routes allow you to match variable segments of a URL. ## Basic Parameters Create a file with square brackets `[]` to capture a parameter. **File**: `src/routes/users/[id].tsx` ```tsx import { useLoaderData } from "@neutron-build/core"; export async function loader({ params }: LoaderArgs) { // Access the parameter via params.id console.log(params.id); return { userId: params.id }; } export default function UserPage() { const { userId } = useLoaderData(); return
User ID: {userId}
; } ``` Visiting `/users/123` will render the page with `userId` as "123". ## Catch-all Parameters To match multiple path segments (or the rest of the path), use the spread syntax `[...]`. **File**: `src/routes/docs/[...slug].tsx` Matches: - `/docs/getting-started` (`params.slug` = "getting-started") - `/docs/api/reference/v1` (`params.slug` = "api/reference/v1") ```tsx export async function loader({ params }: LoaderArgs) { const slug = params.slug; // Fetch content based on slug... } ``` ## Static vs App Mode - **App Mode**: Dynamic parameters are available immediately in the `loader` via `params`. - **Static Mode**: You must use `getStaticPaths` to define all possible values for the parameters at build time. ```tsx // Static mode example export async function getStaticPaths() { return [ { params: { id: "1" } }, { params: { id: "2" } }, ]; } ``` --- ## Error Boundaries Source: https://neutron.build/docs/routing/error-boundaries Handling errors gracefully per route. In Neutron, errors don't have to crash your entire application. You can define Error Boundaries to catch errors within specific routes or layouts. ## The `ErrorBoundary` Component Any route file can export an `ErrorBoundary` component. ```tsx // src/routes/app/dashboard.tsx import type { ErrorBoundaryProps } from "@neutron-build/core"; export default function Dashboard() { throw new Error("Something broke!"); } // The nearest ErrorBoundary catches the error. It receives the thrown error // as a prop, plus an optional `reset` to retry rendering. export function ErrorBoundary({ error, reset }: ErrorBoundaryProps) { return (

Oops!

{error.message}

{reset && }
); } ``` ## Granular Error Handling Error boundaries operate hierarchically. If a route throws an error, Neutron looks for the nearest Error Boundary. 1. **Route level**: `src/routes/app/dashboard.tsx` (exports `ErrorBoundary`) 2. **Sibling level**: `src/routes/app/_error.tsx` (if the route file doesn't export one) 3. **Parent Layout**: `src/routes/app/_layout.tsx` 4. **Root**: `src/routes/_error.tsx` This means if a specific part of your page crashes (e.g., a widget in the dashboard), the surrounding layout (sidebar, header) remains interactive. Only the broken part is replaced by the Error Boundary. ## Expected Errors (Throwing Responses) To short-circuit a request with a specific HTTP response — a 404, a redirect, a permission error — throw a `Response` from a loader or action (the `notFound()` and `redirect()` helpers return one). Neutron serves it **directly as the HTTP response**; it does not render the `ErrorBoundary`. ```tsx import { notFound, type LoaderArgs } from "@neutron-build/core"; export async function loader({ params }: LoaderArgs) { const project = await getProject(params.id); if (!project) { throw notFound(); // → 404 response, served as-is } return { project }; } ``` Throwing (or letting propagate) an `Error` — rather than a `Response` — is what triggers the nearest `ErrorBoundary`. --- ## File Conventions Source: https://neutron.build/docs/routing/file-conventions How files in src/routes map to URLs. Neutron uses a file-system based router. The files you create in `src/routes` directly determine the routes in your application. ## Basic Mapping | File Path | URL Path | | :--- | :--- | | `src/routes/index.tsx` | `/` | | `src/routes/about.tsx` | `/about` | | `src/routes/blog/index.tsx` | `/blog` | | `src/routes/blog/post.tsx` | `/blog/post` | ## Special Files - **`index.tsx`**: The default route for a directory. - **`_layout.tsx`**: A layout component that wraps all child routes in its directory. - **`_error.tsx`**: An error boundary component that catches errors from child routes. - **`_*.tsx`** (any other file starting with `_`): Private files. These are ignored by the router. Use them for co-located components, utilities, or styles. ## Dynamic Routes You can create dynamic route segments by wrapping the filename in brackets `[]`. | File Path | URL Path | Params | | :--- | :--- | :--- | | `src/routes/users/[id].tsx` | `/users/123` | `{ id: "123" }` | | `src/routes/posts/[slug].tsx` | `/posts/hello-world` | `{ slug: "hello-world" }` | ## Catch-all Routes To match all remaining path segments, use the rest syntax `[...]`. | File Path | URL Path | Params | | :--- | :--- | :--- | | `src/routes/docs/[...slug].tsx` | `/docs/guides/setup` | `{ slug: "guides/setup" }` | | `src/routes/[...404].tsx` | `/any/unmatched/path` | `{ 404: "any/unmatched/path" }` | ## Escaping If you need a route to actually include brackets in the URL, you likely need to rethink your URL structure, as this is reserved syntax. --- ## Internationalization Source: https://neutron.build/docs/routing/internationalization Locale-prefixed URLs — resolution, redirects, and link helpers. Neutron's i18n utilities handle locale-prefixed URLs: resolving the locale from a pathname, redirecting to canonical paths, and building locale-aware links. This is routing only. Neutron does not include a translation system — there is no message catalogue, no translation lookup, and no formatting or pluralisation helpers. Locale resolution is URL-based; the `Accept-Language` header is not consulted. ## Setup Add the i18n middleware in your `src/middleware.ts` file. ```ts // src/middleware.ts import { createI18nMiddleware } from "@neutron-build/core"; export const middleware = [ createI18nMiddleware({ locales: ["en", "es", "fr"], defaultLocale: "en", }), ]; ``` The middleware resolves the locale from the URL on every request and puts two values on the request context: - `context.locale` — the resolved locale (for example `"es"`) - `context.pathWithoutLocale` — the pathname with the locale prefix removed (for example `/docs`) ## Strategies The default strategy is `prefix-except-default`: every locale is prefixed except the default one. | URL | Resolved locale | Redirect | | -------------- | --------------- | ---------------- | | `/pricing` | `en` | — | | `/es/pricing` | `es` | — | | `/en/pricing` | `en` | 307 → `/pricing` | Set `strategy: "prefix"` to prefix every locale, including the default: ```ts createI18nMiddleware({ locales: ["en", "es"], defaultLocale: "en", strategy: "prefix", }); ``` | URL | Resolved locale | Redirect | | -------------- | --------------- | ------------------------- | | `/pricing` | `en` | 307 → `/en/pricing` | | `/en/pricing` | `en` | — | | `/es/pricing` | `es` | — | Redirects are issued for `GET` and `HEAD` requests only. The middleware also runs before prebuilt (static) pages are served, so the canonical redirects apply to them too. ## Reading the Locale in a Loader The context values set by the middleware arrive in your `loader` (and `action`). ```tsx // src/routes/[...slug].tsx import type { LoaderArgs } from "@neutron-build/core"; export async function loader({ context }: LoaderArgs) { const locale = context.locale as string; const path = context.pathWithoutLocale as string; return { locale, path }; } ``` The example uses a catch-all route because of the next section. ## Route Matching Is Not Rewritten The middleware annotates the context and issues redirects — it does not rewrite the URL before routing. Route matching sees the full pathname, prefix included, so `/es/pricing` needs a route that matches it (a `[locale]` path segment or a catch-all such as `[...slug]`) or the request 404s. ## Building Locale-Aware Links `withLocalePath` prefixes a path with a locale according to your strategy. In `prefix-except-default`, the default locale gets no prefix. ```ts import { withLocalePath } from "@neutron-build/core"; const options = { locales: ["en", "es", "fr"], defaultLocale: "en", }; withLocalePath("/pricing", "es", options); // "/es/pricing" withLocalePath("/pricing", "en", options); // "/pricing" withLocalePath("/", "es", options); // "/es" ``` It throws if the locale is not in `locales`. ## Resolving Paths Directly `resolveLocalePath` returns everything the middleware derives from a pathname. ```ts import { resolveLocalePath, stripLocalePrefix } from "@neutron-build/core"; const options = { locales: ["en", "es", "fr"], defaultLocale: "en", }; resolveLocalePath("/es/pricing", options); // { // locale: "es", // pathname: "/es/pricing", // pathWithoutLocale: "/pricing", // hasLocalePrefix: true // } stripLocalePrefix("/es/pricing", options); // "/pricing" ``` Both `resolveLocalePath` and `withLocalePath` throw if `defaultLocale` is not in `locales`. ## What Is Not Included - No message catalogue or translation lookup — there is no `t()` function. - No formatting or pluralisation for dates, numbers, or plurals. - No locale negotiation from request headers — resolution is purely URL-based. - No URL rewriting for route matching — see [Route Matching Is Not Rewritten](#route-matching-is-not-rewritten). If you need translated content, pair this module with a translation library and look messages up by `context.locale` in your loaders. --- ## Nested Layouts Source: https://neutron.build/docs/routing/nested-layouts Using layouts to share UI across routes. Nested layouts share UI across a directory of routes while preserving the route hierarchy. ## Creating a Layout A layout is a special file named `_layout.tsx`. It wraps all sibling routes and their children. **File**: `src/routes/_layout.tsx` (Root layout) ```tsx import type { ComponentChildren } from "preact"; export default function RootLayout({ children }: { children?: ComponentChildren }) { return (
{children}
{/* Child routes render here */}
© 2024
); } ``` Layouts render as fragments — Neutron owns the surrounding ``/``/``, so a layout that returns a full document is rejected. ## Nesting You can nest layouts as deeply as you need. **Structure**: ``` src/routes/ _layout.tsx (Root layout: ,