
Surrealkit
- 119 installs
- 21 repo stars
- Updated June 16, 2026
- surrealdb/agent-skills
Helps with ai & agent building tasks.
About
surrealkit is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- surrealkit
- AI & Agent Building
- AI-coding skill
Surrealkit by the numbers
- 119 all-time installs (skills.sh)
- +16 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #3,837 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/surrealdb/agent-skills --skill surrealkitAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 119 |
|---|---|
| repo stars | ★ 21 |
| Last updated | June 16, 2026 |
| Repository | surrealdb/agent-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
SurrealKit
A skill for driving SurrealKit, SurrealDB's schema-management and migration CLI.
SurrealKit keeps a SurrealDB database in sync with .surql schema files. It provides two complementary workflows — a fast declarative sync for development and controlled, phased rollouts for shared and production databases — plus seeding, type generation, and a declarative testing framework.
When to use this skill
Reference these guidelines when:
- Scaffolding a new SurrealDB project (
surrealkit init) or authoring templates - Applying schema changes in development (
surrealkit sync) - Planning, executing, or rolling back production migrations (
surrealkit rollout) - Generating JSON or TypeScript types from a live schema (
surrealkit typegen) - Writing or running declarative tests for schemas, permissions, or API endpoints (
surrealkit test)
This skill covers the SurrealKit tool itself. To write the actual schema, seed, and query statements that go in .surql files, use the surrealql skill.
Installation
| Method | Command |
|---|---|
cargo binstall (recommended) | cargo binstall surrealkit |
| Cargo (from source) | cargo install surrealkit |
| Docker | docker pull ghcr.io/surrealdb/surrealkit:latest |
| Prebuilt tarball | GitHub Releases |
Command map
| Command | Purpose | Reference |
|---|---|---|
surrealkit init | Scaffold a project from a template, selecting optional features | init-templates.md |
surrealkit sync | Declaratively reconcile the database to your schema files (dev) | sync-rollouts.md |
surrealkit rollout <sub> | Plan, stage, complete, and roll back migrations (shared/prod) | sync-rollouts.md |
surrealkit typegen | Introspect a live DB and emit JSON / TypeScript types | typegen.md |
surrealkit test | Run declarative TOML test suites | testing.md |
surrealkit seed | Run seeding files in database/seed/ | sync-rollouts.md |
surrealkit apply <path> | Apply a single .surql file directly | — |
surrealkit status | Show sync/rollout state | — |
Run surrealkit <command> --help to confirm available flags for an installed version.
Connection & config
Global flags work on every command and resolve in this order (highest wins): CLI flags > system env vars > `.env` file > defaults.
surrealkit --host http://localhost:8000 --ns my_ns --db my_db \
--user root --pass root --auth-level root sync| Flag | Env var (with fallback) | Default |
|---|---|---|
--host | SURREALDB_HOST (DATABASE_HOST) | http://localhost:8000 |
--ns | SURREALDB_NAMESPACE (DATABASE_NAMESPACE) | db |
--db | SURREALDB_NAME (DATABASE_NAME) | test |
--user | SURREALDB_USER (DATABASE_USER) | root |
--pass | SURREALDB_PASSWORD (DATABASE_PASSWORD) | root |
--auth-level | SURREALDB_AUTH_LEVEL (DATABASE_AUTH_LEVEL) | root (root / namespace / database) |
--folder | SURREALDB_FOLDER | ./database |
The project root holds surrealkit.toml with [variables] and [typegen] sections.
Template variables
Use ${VAR_NAME} tokens in any .surql file (schema, seed, or rollout SQL). Names are case-insensitive. Values resolve in order (highest wins):
1. --var KEY=VALUE CLI flag (repeatable) 2. SURREALKIT_VAR_<KEY> environment variable 3. [variables] section in surrealkit.toml
# surrealkit.toml
[variables]
schema_prefix = "myapp"
talent_username = "talent_rw"surrealkit sync --var schema_prefix=acme --var talent_username=talent_rw- An undefined variable is a hard error — SurrealKit never silently skips it or leaves the token in the SQL.
- Escape a literal
${...}by doubling the dollar sign:$${literal}. - Substitution runs on
sync,seed,apply, androllout start/complete/rollback. It does not run onrollout plan/baseline/status/lint(no user SQL executes there).
Project layout
surrealkit init creates a database/ directory (override the root with --folder / SURREALDB_FOLDER):
database/
├── schema/ # Schema definitions (.surql) — the source of truth
├── rollouts/ # Generated rollout manifests (.toml)
├── snapshots/ # Internal drift tracking
│ ├── schema_snapshot.json
│ └── catalog_snapshot.json
├── seed/ # Seeding files (.surql)
├── tests/
│ ├── suites/ # Test suites (.toml)
│ ├── fixtures/ # Test fixture data (.surql)
│ └── config.toml # Global test config
└── setup.surql # One-time setup script
surrealkit.toml # Project config ([variables], [typegen])Rules & conventions
- Sync vs rollout: use
syncfor local, preview, and other disposable databases where it is safe to match files immediately; userolloutfor shared/production databases that need review, staged execution, rollback, or operator-controlled cutover. - Schema files are the source of truth.
synccreates, updates, and prunes SurrealKit-managed objects to matchdatabase/schema/. - Schema files should contain
DEFINE/REMOVEstatements. Allow other statements (INSERT,UPDATE,CREATE) only with--allow-all-statements, which disables catalog entity tracking. - Store SurrealQL in files with the
.surqlextension. Validate and format generated SurrealQL with the tools described in the surrealql skill (surreal validate,npx @surrealdb/surql-fmt). - SurrealKit is young and evolving; confirm command surfaces against
surrealkit --helpand the README.
References
- Project scaffolding and templates — references/init-templates.md
- Development sync and production rollouts — references/sync-rollouts.md
- Type generation (JSON and TypeScript) — references/typegen.md
- Declarative testing framework — references/testing.md
Project Init & Templates
surrealkit init scaffolds a project from a template and lets you choose which optional features to include. It always writes the base layout first (schema/, rollouts/, snapshots/, seed/, tests/, setup.surql, surrealkit.toml), then adds the files contributed by the features you pick.
Interactive
surrealkit initIn a terminal this shows a checklist of the template's features. Pick the ones you want and SurrealKit writes their schema, seed, and test files into database/.
Non-interactive
When there is no terminal (e.g. CI) or you pass any of these flags, init runs without prompting:
| Flag | Behaviour |
|---|---|
--feature <id> | Enable a feature by id. Repeatable; pulls in what it requires |
-y, --yes | Take the template's default features |
--minimal | Scaffold the base project only, with no features |
--force | Overwrite files that already exist (default is to skip them) |
--template <name> | Use a named bundled template (default: default) |
--from <src> | Use an external template (overrides --template) |
surrealkit init --feature organizations --feature teams
surrealkit init -y
surrealkit init --minimalSelecting a feature also adds any features it requires, and init prints what it added.
Using your own template
Point --from at a local path or a git repository:
surrealkit init --from ./path/to/template
surrealkit init --from https://github.com/your-org/your-template.git
surrealkit init --from https://github.com/your-org/your-template.git#v1.0.0
surrealkit init --from https://github.com/your-org/your-template.git#v1.0.0:subdirGit sources are cloned with git clone --depth 1, so git must be on your PATH. Pin a branch, tag, or commit with #rev, and target a subdirectory with #rev:subdir.
Template layout
A template is a directory with a template.toml manifest plus the files each feature contributes:
schema_version = 1
name = "default"
display_name = "My starter"
description = "Shown above the feature checklist"
[[features]]
id = "organizations"
name = "Organizations"
description = "Shown next to the feature in the checklist"
default = false
schema = ["schema/organization/organization.surql"]
seed = ["seed/organization_permissions.surql"]
suites = ["tests/suites/organization.toml"]
fixtures = ["tests/fixtures/organization_seed.surql"]
[[features]]
id = "teams"
name = "Teams"
requires = ["organizations"]
schema = ["schema/team/team.surql"]Each feature lists the files it adds, grouped by where they land:
schemafiles →database/schema/seedfiles →database/seed/suitesfiles →database/tests/suites/fixturesfiles →database/tests/fixtures/
Set default = true to pre-check a feature in the prompt and include it with -y. Use requires to declare dependencies on other features.
Bundled template
The bundled default template provides an organization and access-control model with four opt-in features:
- Organizations — organizations, roles that bundle permissions, a per-app
permission catalog, employees, and invitations.
- Teams — teams within an organization, with per-member roles.
- Organization units — a department and region hierarchy with unit-scoped
permissions.
- Subsidiaries and delegation — parent and child organizations with
cross-org delegated permissions.
Teams, units, and subsidiaries each require the organizations feature.
Sync & Rollouts
SurrealKit separates schema authoring from how changes reach a database:
- `sync` — a fast desired-state reconciler for local, preview, and other
disposable databases. Edit database/schema/*.surql, run sync, and the database is made to match.
- `rollout` — a controlled, phased migration path for shared and production
databases: changes are planned into reviewed manifests, applied in stages, and can be rolled back.
Use sync when it is safe for the database to match local files immediately. Use rollout when changes need review, staged execution, rollback, or operator-controlled cutover.
Sync (development)
surrealkit sync # reconcile once
surrealkit sync --watch # re-sync on file changes, incl. deletions
surrealkit sync --dry-run # show what would change without applyingsync applies changed schema files and automatically removes SurrealKit-managed objects that were deleted from database/schema.
| Flag | Behaviour |
|---|---|
--watch | Watch schema files and re-sync on change |
--debounce-ms <n> | Debounce window for watch (default 1000) |
--dry-run | Report changes without applying them |
--fail-fast | Stop on first error (default true) |
--no-prune | Do not remove objects deleted from schema files |
--allow-shared-prune | Required override to allow destructive prune against a shared DB |
--allow-all-statements | Permit non-DEFINE statements (e.g. INSERT, UPDATE, CREATE); disables catalog entity tracking — only file-level hashes are tracked |
Hash-based re-sync gotcha
sync tracks schema files by content hash. Changing a template variable's value does not change the file's hash, so sync will not re-apply that file. To force re-application, touch the file or remove its tracking entry. In watch mode, variables are resolved once at startup; edits to surrealkit.toml require a restart.
Rollouts (shared / production)
The rollout lifecycle follows an expand → cutover → contract pattern:
# 1. Baseline an existing shared/prod DB before the first rollout
surrealkit rollout baseline
# 2. Generate a manifest from the current desired-state diff
surrealkit rollout plan --name add_customer_indexes
# → writes database/rollouts/<timestamp>__add_customer_indexes.toml
# 3. Apply the non-destructive expansion phase
surrealkit rollout start 20260302153045__add_customer_indexes
# 4. Let application cutover happen, then run the destructive contract phase
surrealkit rollout complete 20260302153045__add_customer_indexes| Subcommand | Purpose |
|---|---|
baseline | Establish initial state on an existing shared/prod DB |
plan --name <n> [--dry-run] | Turn the desired-state diff into a reviewed manifest |
start <target> | Apply the non-destructive expansion phase; records resumable state |
complete <target> | Perform the destructive contract phase (e.g. remove legacy objects) after cutover |
rollback <target> | Revert an in-flight rollout |
lint <target> | Validate a manifest without mutating the database |
status [target] | Inspect rollout state stored in the database |
repair <target> | Heal a rollout stuck in an intermediate state (metadata only, no SQL) |
Generated manifests live in database/rollouts/*.toml. Local snapshots are tracked in database/snapshots/schema_snapshot.json and catalog_snapshot.json.
Recovering a stuck rollout
If complete or rollback is killed mid-flight, the __rollout row can be left in an intermediate state (running_complete, running_rollback, or running_start) even though the schema is already materialised. Re-running complete/rollback will not always heal the metadata because the SQL steps are already applied.
surrealkit rollout repair 20260302153045__add_customer_indexesBehaviour by stuck state:
running_complete→ flips tocompleted, restorestarget_entities.running_rollback→ flips torolled_back, restoressource_entities.running_start→ flips tofailedwith a note; re-runstart(idempotent)
or rollback.
repair never re-executes per-step SQL — it only reconciles __rollout and __entity so subsequent sync / plan runs see a clean state.
Template variables in sync/rollout
--var KEY=VALUE (repeatable) works on sync, seed, apply, and rollout start/complete/rollback:
surrealkit sync --var schema_prefix=acme
surrealkit rollout start my_rollout --var schema_prefix=acmeSubstitution does not run on rollout plan, baseline, status, or lint, since they execute no user SQL. See the main SKILL.md for full variable resolution rules. Entity names containing ${VAR} tokens appear literally in catalog_snapshot.json and are not substituted, which affects drift detection — prefer fixed entity names in production schemas.
Seeding
surrealkit seedRuns the seeding files in database/seed/ on demand. Template variables apply.
Running sync from Vite
The `vite-plugin-surrealkit` package runs surrealkit sync from a Vite dev/build process so you do not need a separate surrealkit sync --watch terminal. See typegen.md for its use alongside TypeScript generation.
Testing Framework
surrealkit test runs declarative TOML suites from database/tests/suites/*.toml. By default each suite runs in an isolated ephemeral namespace/database and the command exits non-zero (failing CI) if any case fails.
surrealkit test
surrealkit test --json-out database/tests/report.json # machine-readable CI reportCLI flags
| Flag | Behaviour |
|---|---|
--suite <glob> | Only run matching suites |
--case <glob> | Only run matching cases |
--tag <tag> | Only run cases with this tag (repeatable) |
--fail-fast | Stop on first failure |
--parallel <N> | Run N suites concurrently (default 1) |
--json-out <path> | Write a machine-readable JSON report |
--no-setup | Skip running setup.surql |
--no-sync | Skip sync before tests |
--no-seed | Skip seeding before tests |
--base-url <url> | API base URL for api_request cases |
--timeout-ms <ms> | Per-case timeout |
--keep-db | Keep the ephemeral test database for inspection |
Global config
Global test settings live in database/tests/config.toml:
[defaults]
timeout_ms = 10000
base_url = "http://localhost:8000"
[actors.root]
kind = "root"Env fallbacks: SURREALKIT_TEST_BASE_URL, SURREALKIT_TEST_TIMEOUT_MS, and SURREALDB_HOST / DATABASE_HOST (used as the API base URL fallback).
Suite shape
name = "security_smoke"
tags = ["smoke", "security"]
[[cases]]
name = "guest_cannot_create_order"
kind = "sql_expect"
actor = "guest"
sql = "CREATE order CONTENT { total: 10 };"
allow = false
error_contains = "permission"
[[cases]]
name = "orders_api_returns_200"
kind = "api_request"
actor = "root"
method = "GET"
path = "/api/orders"
expected_status = 200
[[cases.body_assertions]]
path = "0.id"
exists = trueCase kinds
kind | Tests |
|---|---|
sql_expect | A SQL statement is allowed/denied and returns expected values |
permissions_matrix | A grid of record-level permission rules for an actor |
schema_metadata | Schema metadata (e.g. INFO FOR TABLE) contains expected content |
schema_behavior | Schema behavioural assertions |
api_request | An HTTP API endpoint returns expected status/body |
sql_expect
Use allow (true/false) and optionally error_contains for denied cases. Add [[cases.assertions]] to check returned values. To compare a field against the authenticated actor, use equals_auth with $auth or $auth.<property>:
[[cases]]
name = "user_can_create_calendar"
kind = "sql_expect"
actor = "user_alice"
sql = "CREATE calendar CONTENT { name: 'Alice Personal' };"
allow = true
[[cases.assertions]]
path = "0.owner"
equals_auth = "$auth.id"permissions_matrix
[[cases]]
name = "reader_permissions"
kind = "permissions_matrix"
actor = "reader"
table = "order"
record_id = "perm_test"
[[cases.rules]]
action = "select"
allow = true
[[cases.rules]]
action = "update"
allow = false
error_contains = "permission"api_request
Set method, path, expected_status, and optional [[cases.body_assertions]] (each with a path and a check such as exists or an expected value).
Actors
Actors define who runs each case. Configure them in config.toml ([actors.*]) or per suite. Secrets come from environment variables via *_env keys.
[actors.reader]
kind = "database"
namespace = "app"
database = "main"
username_env = "TEST_DB_READER_USER"
password_env = "TEST_DB_READER_PASS"
[actors.access_user]
kind = "record"
access = "app_access"
signup_params = { email = "viewer@example.com", password = "viewer-password" }
signin_params = { email = "viewer@example.com", password = "viewer-password" }
[actors.jwt_actor]
kind = "token"
token_env = "TEST_API_JWT"
[actors.custom_client]
kind = "headers"
headers = { "x-tenant-id" = "tenant_a" }kind | Authenticates as |
|---|---|
root | Root user |
database | A namespace/database user (username_env / password_env) |
record | A record-access user (access, signup_params, signin_params) |
token | A pre-issued JWT (token_env) |
headers | Custom HTTP headers (for api_request cases) |
For record actors, signup_params is optional and runs before authentication; signin_params is used for the signin step (legacy params still works as a signin alias).
CI
surrealkit test --json-out database/tests/report.jsonExits non-zero if any case fails, and the JSON report gives machine-readable results for CI pipelines. The Docker image (ghcr.io/surrealdb/surrealkit) exits on completion, which suits "apply schema then run tests" pipelines in Docker Compose alongside SurrealDB.
Type Generation
surrealkit typegen introspects a live database and emits a structured schema document describing tables, fields, functions, and params. JSON is the default output; TypeScript is an optional, config-driven emitter.
Because typegen reads the live database, run surrealkit sync (or apply your rollout) first so the database reflects your current schema.
JSON output
surrealkit typegen # writes {folder}/types/schema.json (pretty)
surrealkit typegen --out path.json # custom output path
surrealkit typegen --stdout # print JSON to stdout instead of a file
surrealkit typegen --compact # single-line JSON instead of pretty-printed| Flag | Behaviour |
|---|---|
--out <path> | Output path (default {folder}/types/schema.json) |
--stdout | Print to stdout instead of writing a file |
--compact | Emit compact (single-line) JSON |
The default folder follows --folder / SURREALDB_FOLDER (so database/types/schema.json by default).
TypeScript output
TypeScript generation is not a CLI flag — it is enabled via the [typegen] section of surrealkit.toml. When typescript is set, both surrealkit typegen and surrealkit sync --watch write an index.ts of SurrealDB JS SDK interfaces into that directory.
# surrealkit.toml
[typegen]
typescript = "../src/types" # directory for generated index.ts
format = "biome check --write" # optional formatter, e.g. prettier --write / eslint --fix- One
interfaceis generated per table, withid: RecordId<'table'>and
fields mapped to TypeScript types (record links become RecordId<'…'> unions, optional fields become | undefined).
- The
formatcommand is run on the written file (the file path is appended
as the final argument, e.g. biome check --write <path>). It inherits the working directory so it picks up your project's own config. A missing or failing formatter is a non-fatal warning — it never breaks typegen or sync.
Regenerating on schema change
Because sync --watch regenerates types when [typegen] typescript is set, the common dev loop is:
surrealkit sync --watchEdit database/schema/*.surql → sync applies the change → index.ts is rewritten automatically.
With Vite
Use `vite-plugin-surrealkit` to run surrealkit sync (and thus typegen, when configured) from your Vite dev server, avoiding a separate watch process:
// vite.config.ts
import { defineConfig } from 'vite';
import { surrealkitPlugin } from 'vite-plugin-surrealkit';
export default defineConfig({
plugins: [surrealkitPlugin()],
});Install it with npm i -D vite-plugin-surrealkit. It runs sync on dev-server startup, watches database/schema/**/*.surql, and re-runs on change. Options include syncArgs, schemaGlobs, runOnStartup, reloadOnSync, debounceMs, logLevel, and failBuildOnError.
Workflow notes
- Regenerate types after schema changes and in CI to catch drift.
- Decide deliberately whether to commit generated types (reproducible, no build
dependency on a live DB) or gitignore them (always fresh). Generated files carry a "Run surrealkit typegen to regenerate." header.