# Building on substrio Build and ship an app on substrio as an AI agent, over MCP or plain HTTP with the same results. ## First: greet your owner Call `substrio_start`, tell your owner what it says, and wait for their answer before building. Before each call, say what you are doing and why. After the call, relay its `Tell your owner:` line. ## The normal flow The live app changes only when you make a version live, after it ran in a sandbox. 1. **Orient.** `substrio_brief`: the goals (website, api, database, auth, events), the tools and the skill of each. 2. **Read the data.** `substrio_collection_list`, `substrio_sql_schema`. 3. **Commit.** `substrio_source_tree` lists files, `substrio_version_unit` reads one. `substrio_version_commit` writes files, `substrio_source_edit` replaces text. Each creates a commit and returns its `number`. 4. **Sandbox.** Call `substrio_sandbox_create` `{"name": "dev"}` once to copy the data into a sandbox that captures its mail. `substrio_live_set` `{"version": , "sandbox": "dev"}` runs the commit there. 5. **Check it.** `substrio_route_call` `{"method": "GET", "path": "/", "sandbox": "dev"}`; `substrio_function_errors` and `substrio_function_logs` show what failed. 6. **Live.** `substrio_live_set` `{"version": }` (data backed up first). 7. **Roll back.** `substrio_live_set` `{"version": "previous"}`. Guidance opens with `Tell your owner:`, followed by `Next: : ` lines. It also appears in `_meta` under `substrio/next`. Its `pass` field is the value to send as the argument named by `as`. An error adds `Fix: : `. ## Start here with no key: sign the person up or in All you need is the base URL `https://mcp.substrio.com` and the person's email. These two HTTPS calls require no key, password, SDK, or plugin. **1. Mail the person a code.** ```bash curl -X POST https://mcp.substrio.com/v1/_agent/signup/start \ -H 'Content-Type: application/json' -d '{"email":"ada@example.com"}' ``` ```json {"accepted":true,"expires_at":1792658700} ``` The response (202) is the same whether or not the address has an account, so it does not reveal who is signed up. A six-digit code is sent to that address, live for 30 minutes. Ask the person for it. **2. Prove the address with the code and get your session.** ```bash curl -X POST https://mcp.substrio.com/v1/_agent/signup/finish \ -H 'Content-Type: application/json' \ -d '{"email":"ada@example.com","code":"483920","name":"my agent"}' ``` ```json {"account_id":7,"account_created":true,"person_created":true,"user_id":42, "account":{"id":7,"name":"ada","created_at":1792656900,"services":0,"suspended":false, "require_mfa":false,"personal":true, "space_bytes":524288000,"used_bytes":0,"role":"owner"}, "app_defaults":{"storage_bytes_limit":0,"documents_limit":0,"requests_per_minute":0}, "session":{"id":"q3RkV0xYbW5PcGQ4c1p0Ww","kind":"ratcheted","expires_at":1793261700, "account_id":7,"user_id":42,"role":"owner","apps":[],"capabilities":["system","auth","..."], "requests_per_minute":0,"request_budget":0,"keys":["substrio_ak_..."]}, "next_steps":[{"operation":"account.overview","route":"GET /v1/_accounts/{id}/overview"}, {"operation":"account.usage","route":"GET /v1/_accounts/{id}/usage"}, {"operation":"account.app.create","route":"POST /v1/_accounts/{id}/apps"}, {"operation":"account.app.list","route":"GET /v1/_accounts/{id}/apps"}, {"operation":"agent.session.delegate","route":"POST /v1/_accounts/{id}/agent/delegations"}, {"operation":"documentation.brief","route":"GET /v1/_brief"}]} ``` A new address creates a person and a free account. The same two calls sign in a person whose address is already registered. The `session` is shown once and never again; keep it out of logs and output. **The account starts at 500 MB.** A new account has 500 MB (`space_bytes` 524288000) across all its apps. Its limit increases to the plan's space (Free: 2 GB) once your owner signs in to the console with Google, GitHub or another provider. The email code they gave you, a password, and the console link you mint for them (`account.console.link`) do not open it. When `space_bytes` is 524288000 and they need more, tell your owner: "Your account opens to 2 GB when you sign in with Google, GitHub or another provider once." See [authentication.md](authentication.md#your-account-opens-to-2-gb-when-you-sign-in-with-a-provider). **Use the key.** Send `Authorization: Bearer ` with `session.keys[0]`. Every reply carries the next key in its `Substrio-Next-Keys` header: use that one on your next call (each key works once). To sign requests with your own key instead, send `"public_key"` (base64url of the raw key) and `"algorithm"` (`ed25519` or `p256`) to finish: `session.key` is then a handle, and each request carries a `Substrio-Proof` header signed with your private key. ```bash curl https://mcp.substrio.com/v1/_accounts/7/overview -H 'Authorization: Bearer substrio_ak_...' ``` **What the session may do.** The session acts for the person across the apps in their account, with every capability allowed by their account role. Calls that cannot be undone, such as deleting an app, require approval. The response identifies the approval. The person signs in and approves it, then you repeat the call with that approval. Tell the person about the free account using `account.overview` and `account.usage`. `space_bytes` is the shared storage limit for its apps, and `app_defaults` describes the defaults for each new app. Then create their app: `account.app.create` `{"id": 7, "slug": ""}` creates the app. An agent session uses its existing credential; the response does not include the app key. A slug that is taken or reserved is `TENANT_ALREADY_EXISTS` (409), and one that is no slug `TENANT_INVALID` (400), each with `field` `slug`. To give a developer agent that one app only, make it a narrower session: `agent.session.delegate` with `"apps": [""]` and the capabilities it needs. **Which account.** Without `account_id` the session acts in the person's personal account. A person who belongs to accounts but has no personal one is asked which: the answer is `ACCOUNT_REQUIRED` with `account_ids`; ask the person, start again (the code is spent) and finish with `"account_id"`. **Refusals** name their `reason`. A code that proves nothing (400): `MISSING` (no code was asked for this address), `WRONG` (with `attempts_remaining`), `TOO_MANY_ATTEMPTS`, `EXPIRED`, `USED`; start again for a new code. Then `MFA_REQUIRED` (403: the person signs in with 2FA, which a mailed code does not replace; signed in to the console with it, they enroll you themselves with `agent.enroll.create` and give you its one-time code for `agent.enroll.exchange`), `ACCOUNT_REQUIRED` (409), `NOT_MEMBER` (404: the person does not belong to that `account_id`), `PERSON_DISABLED`, `ACCOUNT_SUSPENDED` (403), `ACCOUNT_LIMIT` (409). A start sent too often for one address, or too many from one source, returns 429. Holding a key already? Skip this and start with `substrio_brief` below. ## Moving an existing project If the app already exists as a backend somewhere else, follow [bring-your-project.md](bring-your-project.md): inventory, collections, data import, ids, sign-in, routes, jobs, secrets, deploy. Every step is a CLI command you can run with `--json`. Project files (`substrio.toml`, `collections/.json`) are described in [project-files.md](project-files.md). To try each change before it goes live, `substrio push` runs it in the app's dev sandbox ([getting-started.md](getting-started.md)). ## Start here with a key: `substrio_brief` For your first call on the app's `/mcp`, use `substrio_brief` (`GET /v1/_brief`, operation `documentation.brief`). It takes no input and returns at most 3 KB. The response identifies the app and your key scope (`tenant` for a key that administers the app, `user` for a person's session), the `live_version`, the `goals` an app is built for and the `next_steps` of the build loop by tool name: | Goal | What it builds | Skill | |---|---|---| | `website` | pages, styles, scripts and images under `site/` | `files-and-site` | | `api` | HTTP routes run by `functions/.js` | `functions` | | `database` | collections and SQL tables, their schema, indexes and queries | `data-and-queries` | | `auth` | sign-up and sign-in for the app's users, and their permissions | `users-auth-permissions` | | `events` | triggers on data changes and scheduled jobs | `queues-cron-triggers` | Each goal lists the 5 to 8 tools it needs beyond those in the loop. Call `substrio_tools` with `{"area": ""}` for their full definitions, and `substrio_skill` with `{"name": ""}` for guidance on the goal. `tools/list` lists the loop's tools. Use `{"area": ""}` or `{"area": "all"}` to list more, and call a tool by name. `substrio_start` (`GET /v1/_start`) returns the longer list of areas, tools, and summaries. The lists use the same registry as `tools/list` and are filtered for your key's scope. The rest of this page describes the flow in more detail. ## Connect - Your app is reached at `https://.` (or any Substrio host with the header `X-Substrio-Tenant: `). - Credential: the app's key, or a key of the account that owns the app. Keep it out of logs and output. - MCP: `POST https://./mcp` with `Authorization: Bearer `. `tools/list` shows every built-in tool with its input schema. - HTTP: send `Authorization: Bearer `. The full API is at `GET /v1/_openapi`. ### Connect an MCP client with the key in an environment variable `substrio mcp config [--client claude|cursor|generic]` prints the client config for your app with the key read from an environment variable, so the key never lands in a config file or shell history ([mcp.md](mcp.md)). ### Start from an account key With only an account key, make the app first on `mcp.substrio.com`'s own MCP: `POST https://mcp.substrio.com/mcp` (a host no app serves) with `Authorization: Bearer `. It offers the account's tools only. `substrio_account_app_create` `{"id": , "slug": ""}` makes the app and returns its key once; `substrio_account_app_list`, `substrio_account_key_list` and `substrio_account_member_list` show the rest. The same account key then runs the loop below on the app's own `/mcp`. Over HTTP: `POST /v1/_accounts//apps` with `Authorization: Bearer `. ### Every tool over plain HTTPS Each tool is also available over HTTPS. `GET /v1/_agent/tools` lists the tools available to your key, and `POST /v1/_agent/tools/` runs one with its arguments as the body. The response includes the operation's status and body and `next` ([agent-http.md](agent-http.md)). ### A connect code from your owner (`substrio_ae_...`) A `substrio_ae_` code is a one-time connect code your owner made with `POST /v1/_accounts/{id}/agent/enrollments`. Redeem it once with plain HTTPS: `POST https://mcp.substrio.com/v1/_agent/exchange` `{"code": ""}`. The answer's `key` is shown once: send `Authorization: Bearer ` on every request, and its `next` line says how long it lasts. MCP works the same way: connect `https://mcp.substrio.com/mcp` with that header; with no key yet, the tool `substrio_connect` `{"code": ""}` redeems the code there. The CLI does both steps: `npx @substrio/cli connect `. A chat app already connected through the Substrio connector does not need the code: `substrio_connect` refuses it there (`AGENT_ALREADY_CONNECTED`) and leaves it unused. ## The loop: commit, check the sandbox, promote ### 1. Commit to the app's history The app's history tree is your project: list it, read it, edit it and commit to it with plain MCP calls. No call takes a hash ([versioning.md](versioning.md), "Developing over MCP"). - Write units with `substrio_version_commit`: `{"branch": "main", "message": "v1", "units": [{"path": "functions/hello.js", "text": "export default () => new Response('hello');\n"}, {"path": "functions/hello.json", "text": "{\"routes\":[\"GET /hello\"]}"}, {"path": "site/index.html", "text": "

hi

\n"}]}`. Bytes go as `{"path": ..., "base64": ...}`; `removals` deletes paths. The response contains the commit's `number`, which every step below takes as `version`. - `substrio_source_tree` `{"version": "main"}` lists the paths; `substrio_version_unit` `{"version": "main", "path": "functions/hello.js", "start_line": 1, "end_line": 20}` reads lines; `substrio_source_search` `{"version": "main", "pattern": "hello"}` finds them. - `substrio_source_edit` `{"branch": "main", "path": "functions/hello.js", "edits": [{"old": "hello", "new": "hello v2"}]}` replaces exact text and commits it (`EDIT_NO_MATCH` names the closest line, `EDIT_AMBIGUOUS` the lines it occurs on); `substrio_source_move` renames a unit. - A file larger than one call: `substrio_site_blob_upload` with no hash, in parts (`offset`, `total`, then the `upload` id returned by the first part). The last part returns `hash`, which `substrio_version_commit` takes as the unit's `id`. - `version` takes an id, a number, a branch, `live` or a sandbox's name. ### 1b. Or push a built version From a checkout, `substrio push` sends a whole built version by hash. - MCP: `substrio_version_create` with `{"message": "...", "functions": [{"name": "hello", "hash": "", "size": 123, "routes": ["GET /hello"]}]}`. The result contains `version.number` and `missing` (hashes to upload). For each: `substrio_site_blob_upload` with `{"hash": "", "content_base64": ""}`. Upload a file larger than one call in parts, in order: `{"hash": "", "offset": , "total": , "content_base64": ""}`, each part at most `mcp_blob_max_bytes` (640 KiB by default). Each part returns `received`. The last returns `stored`. - HTTP: curl -X POST https://./v1/_versions -H "Authorization: Bearer $SUBSTRIO_KEY" \ -H "Content-Type: application/json" -d @version.json curl -X PUT https://./v1/_blobs/ -H "Authorization: Bearer $SUBSTRIO_KEY" \ -H "Content-Type: application/octet-stream" --data-binary @hello.js A version may also carry `rules`, `mcp`, `site`, `cron`, `triggers`, `plans`, `auth` and `login`; see the `substrio_version_create` input schema. Check before you ship, at three levels, all with the same check (parse as script, CommonJS or ES module without running; deploy's `require()` refusal): - `substrio_function_check` takes one function's source (the `substrio_function_deploy` body) and returns `ok` and `diagnostics` (severity, message, file, line, column). Nothing is written. - `substrio_version_check` takes the `substrio_version_create` body and runs that check over every function whose source is uploaded; a source not uploaded yet is an error diagnostic, since the version cannot go live without it. It returns `ok`, `diagnostics` (function, severity, message, line, column) and `missing`. Nothing is stored. - `substrio_version_create` and `substrio_live_set` run it themselves unless sent `check: false`. A failing check refuses with `400`, the usual error body and a `diagnostics` list of the errors. `substrio_version_create` checks the sources already uploaded (so upload first, or push again); `substrio_live_set` checks all of them before anything changes, and never checks a rollback. ### 2. Check it in the sandbox A sandbox is a copy of the app's data, at `https://--.`, whose outbound requests are fenced. Use `dev`. - Create it once, or reset its data from the app when it exists: - MCP: `substrio_sandbox_create` `{"name": "dev"}`; a `409` means it exists, so `substrio_sandbox_reset` `{"name": "dev"}`. - HTTP: `POST /v1/_sandboxes` `{"name": "dev"}`, else `POST /v1/_sandboxes/dev/reset`. - Make the version live there: - MCP: `substrio_live_set` `{"version": , "sandbox": "dev"}`. - HTTP: `POST /v1/_live` `{"version": , "sandbox": "dev"}`. - Try it on the sandbox host, `https://--dev.`, with the same key: - HTTP: `curl https://--dev./hello`. - MCP: `substrio_route_call` `{"method": "GET", "path": "/hello", "sandbox": "dev"}` sends one request to any route of the sandbox (leave out `sandbox` for the app), with optional `query`, `headers` and `body` (or `body_base64`). It runs in process as a real request: the function routes, then the site, with the sandbox's outbound fence. It returns `status`, `headers` and `body` (`body_base64` when the body is not UTF-8). A version's `mcp` manifest (`{"tools": [{"name": "hello", "description": "...", "route": "GET /hello"}]}`) also makes each route a tool on the sandbox's own `/mcp`. `substrio_function_invoke` `{"name": "...", "event": {...}}` runs a function that takes a JSON event. - `substrio_function_logs` shows the function logs; `substrio_function_errors` shows what the functions threw, with stack frames, newest first, and takes `revision` to show only the errors of one deploy (every error carries the deploy counter, `substrio_version_log` with the function path reports it per commit); a log line carries no revision, so find a call's lines by its `request_id`. `substrio_sandbox_outbox` shows the outbound requests the fence held back. - A private `host:port` is out of reach of your app's outbound requests; no app or account key can open one. What an agent can do with its own key is `substrio_sandbox_allow_list`, `substrio_sandbox_allow_add` and `substrio_sandbox_allow_remove` (the host patterns one sandbox may reach) and read what the fence held back in `substrio_sandbox_outbox`. ### 3. Promote Make the same version live on the app: - MCP: `substrio_live_set` `{"version": }`. - HTTP: `POST /v1/_live` `{"version": }`. Promote only a version you ran and checked in the sandbox. Promotion changes the live app without downtime. Each request runs entirely on the old or the new version, even under continuous traffic. Requests sent after the promotion response use the new version, and routes added by that version are available immediately. The app's data is backed up first. `substrio restore` restores it. ### Roll back Put the version that was live before back, or name any earlier one: - MCP: `substrio_live_set` `{"version": "previous"}` (or `{"version": }`). - HTTP: `POST /v1/_live` `{"version": "previous"}`. `substrio_version_list` (`GET /v1/_versions`) shows every version and which one is live; `substrio_version_diff` (`GET /v1/_versions//diff/`) shows what changes between two. ## Give your owner an admin dashboard The person you build for runs their app from the Substrio console. Give them an admin screen by writing `dashboard.json`. The console renders its pages, metrics, tables with filters and paging, records, forms, and row buttons such as refund, resend, and delete. No console code is needed. Use a dashboard for data the owner needs to view or act on, such as orders, sign-ups, sends, jobs, and errors. See [dashboard.md](dashboard.md) for the full reference. The `dashboard` skill (`substrio://skills/dashboard`) walks through an example. 1. Give the app the routes the screens read and the actions call, each with a gate that restricts access (`GET /stats auth`, `POST /orders/:id/refund auth:ADMIN`). The console calls them as the owner. Access is enforced by the route gates, not by the dashboard. 2. Write `dashboard.json` and name it in `substrio.toml` (`dashboard_file = "dashboard.json"`). A minimal one: ```json {"nav": [{"id": "home", "title": "Home", "icon": "overview", "panels": [ {"kind": "metric", "title": "Sign-ups today", "source": {"route": "GET /stats"}, "value": "/today", "format": "number"}, {"kind": "keys"}]}]} ``` 3. Check it before you ship: `substrio_version_check` (`POST /v1/_versions/_check`) with the version body carrying `"dashboard"`. An invalid dashboard returns `400 DASHBOARD_INVALID`, with the path in `field` and the problem in `detail`: ```json {"code": "DASHBOARD_INVALID", "field": "nav[0].panels[0].source.route", "detail": "nav[0].panels[0].source.route names no route this version declares: GET /stat"} ``` Fix the file at that path and check again. After the push, `substrio_dashboard_get` with the pushed `version` returns the validated nav items and panels as the console will render them. 4. Ship as usual (commit or push, check the sandbox, promote). The dashboard goes live with its version. 5. Verify: `substrio_dashboard_get` (`GET /v1/_accounts//apps//dashboard`) returns the stored dashboard. Call one panel's route with `substrio_route_call`, as the console will, to see the response its pointers read. Then tell the owner where to find it: their app in the console. ## Writing a function: the `db` API A function file evaluates to a function of the event: `module.exports = async function (event) { ... }`. A route function's `event.body` is the request body as text. The app's data is the `db` global; every call below is synchronous. A wrong argument throws (a `TypeError` for a filter, field name or value the call cannot take) with the call's name first in the message, and a failed SQL statement throws the database's error. Everything a function can call, with its limits and where it differs from the web platform, is typed in `GET /runtime.d.ts` (over MCP: the resource `substrio://runtime/types`; the prompts `write-function`, `debug-function` and `design-schema` say what to read and call). `db.transaction(tx => ...)` runs synchronous SQL and collection writes as one transaction: `tx.sql`, `tx.prepare` and `tx.` belong to that callback's `tx` argument in the function runtime (JavaScript you deploy), not operations or tools; from outside a function the same atomic SQL is `substrio_sql_batch`. The runtime guide for agents, one page per area: [index](runtime/index.md) (handler shapes, limits, differences from Workers), [kv](runtime/kv.md), [sql](runtime/sql.md), [collections and chains](runtime/collections-and-chains.md), [records](runtime/records.md), [services](runtime/services.md) and [errors and logs](runtime/errors-and-logs.md). ### Documents (`db.collection(name)`) ```js const notes = db.collection("agentnotes"); const { _id } = notes.insert({ title: "hi", n: 1 }); notes.get(_id).title; // "hi" notes.update(_id, { $inc: { n: 1 }, $set: { done: true }, $unset: { title: 1 } }).n; // 2 notes.delete(_id); // true const Note = notes; const draft = new Note({ title: "draft" }); draft.save(); // true draft._version; // 1 draft.title = "final"; draft.save(); // true notes.find({ title: "final" }, { sort: { n: -1 }, limit: 10 }).length; // 1 ``` A collection is also a class: `new notes({ ... }).save()` inserts a row and fills `_id` and `_version`; a stored row's `save()` writes what changed and throws `ConflictError` if the document changed since it was read. Reads are chains (`eq`, `gt`, `in`, `sort`, `limit`, `page`, `with`, `whereHas`) or filter objects (`find`, `findOne`, `count`, `exists`, `updateWhere`, `deleteWhere`; `null` means every document, anything else that is not an object throws). Writes: `insert`, `insertMany`, `upsert`, `upsertMany`, `update` (`$set`, `$inc`, `$push`, ... and `{ if }` guards), `updateMany`, `findOneAndUpdate`, `replace`, `delete`, `deleteMany`, `deleteWhere`. Relations: `hasMany`, `hasOne`, `belongsTo`. Full reference: [collections and chains](runtime/collections-and-chains.md), the [chain reference](runtime/chain-reference.md) and [records](runtime/records.md). ### Chained queries ```js const posts = db.collection("agentposts"); const users = db.collection("agentusers"); posts.insertMany([{ n: 1, done: false }, { n: 3, done: false }, { n: 4, done: false }, { n: 2, done: true }]); const ann = users.insert({ name: "ann" })._id; posts.insert({ n: 9, authorId: ann }); posts.eq("done", false).gt("n", 2).sort("n", -1).limit(5).pluck("n"); // [4, 3] posts.in("n", [1, 2]).count(); // 2 posts.or(q => q.eq("n", 1), q => q.eq("n", 2)).count(); // 2 posts.eq("authorId", ann).join("agentusers", "authorId", "_id", "author").first().author.name; // "ann" posts.eq("n", db.arg(0)).count(3); // 1 ``` `db.arg(i)` stands for the run's i-th argument. Terminals: `all`, `first`, `count`, `ids`, `pluck(field)`, `sum`, `avg`, `min`, `max`, `facets`. ### SQL (`db.sql`, `db.prepare`) ```js db.sql("CREATE TABLE t (id INT PRIMARY KEY, price DECIMAL(10,2), at DATETIME)"); db.sql("INSERT INTO t VALUES (?, ?, ?)", [1, "9.99", "2026-10-01 12:00:00"]); db.sql("SELECT * FROM t WHERE id = ?", [1]); // [{ id: 1, price: "9.99", at: "2026-10-01 12:00:00" }] const byId = db.prepare("SELECT * FROM t WHERE id = ?"); byId.all(1); byId.first(1); // rows; first row or null ``` - A SELECT returns an array of row objects. Other statements return `{ changes, lastInsertId }`. Syntax errors, missing tables, duplicate keys, and incorrect parameter counts throw an error. - Parameters: null/undefined, booleans, numbers, strings, BigInts (int64 and uint64) and binary data (`Uint8Array`, `ArrayBuffer`). Pass a `Date` as a string (`d.toISOString().slice(0, 19).replace("T", " ")`). - Results: integers past 2^53 come back as BigInt; DECIMAL, DATE, TIME and DATETIME as their exact SQL text; BLOB/BINARY columns as `Uint8Array`. - A TEXT or BLOB column holds 65535 bytes. A longer value returns error 1406 (Data too long) under the default strict `sql_mode`. A session that sets a non-strict `sql_mode` gets MariaDB's truncation with a warning. Declare MEDIUMTEXT/LONGTEXT (or MEDIUMBLOB/LONGBLOB) for more. - `db.collection(name).query(sql, ...args)` runs SQL over a collection's documents. ### Key-value (`db.kv(name)`) ```js const cache = db.kv("cache"); cache.put("k", "v"); // true cache.get("k"); // "v"; null when absent cache.delete("k"); ``` KV stores bytes, one per character. A value can be a string of characters up to U+00FF, a number, a boolean, a BigInt, or binary data. `get` returns a string of bytes. For example, `put("n", 5)` followed by `get("n")` returns `"5"`. Text with other characters throws an error. Store it with `new TextEncoder().encode(text)`, then decode the returned string with `new TextDecoder().decode(Uint8Array.from(s, c => c.charCodeAt(0)))`. ## Limits - One MCP call carries at most `mcp_blob_max_bytes` of a file, decoded (640 KiB by default); a larger file goes up in parts, up to `mcp_blob_upload_max_bytes` (256 MiB by default). Over HTTP, `PUT` the whole file. - The `substrio` CLI runs the same loop: `substrio push`, `substrio promote`, `substrio rollback`. ## Going further - `substrio_start` (above) lists what exists for your key; `substrio://operations` (MCP) lists every tool with its input and output schema. - [agent-workflow.md](agent-workflow.md): the whole loop with exact operations, one worked example. - [app-agents.md](app-agents.md): let app users connect their agents to `/mcp` with their credentials. - [versioning.md](versioning.md): the app's one history (`branch.*`, `tag.*`, `version.commit`, `version.log`, `merge.*`, `ref.log`, `ref.restore`, `version.gc`, and the git export). The version numbers above are a view over it, so a push you made with `substrio_version_create` is a commit `substrio_version_log` reports. - [sql-over-api.md](sql-over-api.md): `query.sql`, `sql.batch`, `sql.schema`, `index.*`. - [chains-over-api.md](chains-over-api.md): `query.chain`, the JSON plan of the JS chain. - [function-errors.md](function-errors.md): thrown errors with frames, and how to page them. - [schedules.md](schedules.md): create and run schedules, and view their next fire times and run history. ## Operations for the loop tools/list answers the everyday tools only. substrio_tools {area} answers the full definitions of one area (the areas substrio_start lists), tools/list with {"area": "all"} (or an area's name) lists them, and any platform tool runs by its own name or through substrio_call {tool, arguments}, for a client that can call only the tools it was listed. - `documentation.start` / `substrio_start` (`GET /v1/_start`): Start here - `version.create` / `substrio_version_create` (`POST /v1/_versions`): Push an app version - `site.blob.upload` / `substrio_site_blob_upload` (`PUT /v1/_site/blobs/{hash}`): Upload one site or function file a version or release names - `version.get` / `substrio_version_get` (`GET /v1/_versions/{number}`): Read one app version by number (the route's {number}) - `sandbox.create` / `substrio_sandbox_create` (`POST /v1/_sandboxes`): Create a sandbox from the app's data and live version - `sandbox.reset` / `substrio_sandbox_reset` (`POST /v1/_sandboxes/{name}/reset`): Copy the app's data into a sandbox again, keeping its live version - `sandbox.list` / `substrio_sandbox_list` (`GET /v1/_sandboxes`): List the app's sandboxes, their hosts and live versions - `version.live.set` / `substrio_live_set` (`POST /v1/_live`): Make an app version live, or roll back with version - `route.call` / `substrio_route_call` (`POST /v1/_route`): Send one HTTP request to a route of the app or of one of its sandboxes and return the response - `function.invoke` / `substrio_function_invoke` (`POST /v1/functions/{name}/_invoke`): Invoke one deployed function with a JSON event - `function.logs` / `substrio_function_logs` (`GET /v1/_logs`): Read the app's function logs - `function.errors` / `substrio_function_errors` (`GET /v1/_function_errors`): Read the errors the app's functions threw, newest first - `function.check` / `substrio_function_check` (`POST /v1/_functions/_check`): Check a function before deploying it - `version.check` / `substrio_version_check` (`POST /v1/_versions/_check`): Check an app version before it is pushed or made live - `version.list` / `substrio_version_list` (`GET /v1/_versions`): List the app's versions, newest first, with the live version number (live), the version a rollback would restore (rollback_to), the live history with the backup each promote took, and each sandbox's live version - `version.diff` / `substrio_version_diff` (`GET /v1/_versions/{from}/diff/{to}`): Show what changes between two app versions (the route's {from} and {to}) - `sandbox.outbox` / `substrio_sandbox_outbox` (`GET /v1/_sandboxes/{name}/outbox`): Read the outbound requests a sandbox's fence captured instead of sending - `sql.schema` / `substrio_sql_schema` (`GET /v1/_sql/schema`): Describe the SQL catalog from the engine's typed descriptors - `query.sql` / `substrio_sql` (`POST /v1/_sql`): Run one SQL statement with bound parameters - `sql.batch` / `substrio_sql_batch` (`POST /v1/_sql/batch`): Run SQL statements with bound parameters in order in one transaction - `query.chain` / `substrio_query_chain` (`POST /v1/_chain`): Run a typed chain query over a collection - `index.list` / `substrio_index_list` (`GET /v1/_indexes`): List the indexes of the SQL tables with each one's value-key state - `index.create` / `substrio_index_create` (`POST /v1/_indexes`): Create an index - `index.drop` / `substrio_index_drop` (`DELETE /v1/_indexes/{name}`): Drop one index of a SQL table - `function.logs.stream` (`GET /v1/_logs/ws`, WebSocket only): Tail function logs live over WebSocket - `route.list` / `substrio_route_list` (`GET /v1/_routes`): List the HTTP routes the deployed functions answer - `schedule.create` / `substrio_schedule_create` (`POST /v1/_cron`): Create a cron schedule - `schedule.run` / `substrio_schedule_run` (`POST /v1/_cron/{id}/run`): Run a schedule's function now with its cron event, and record the run in the schedule's history - `schedule.next` / `substrio_schedule_next` (`GET /v1/_cron/{id}/next`): List a schedule's next fire times (in its time zone's wall clock) as Unix seconds and ISO 8601 UTC - `schedule.history` / `substrio_schedule_history` (`GET /v1/_cron/{id}/history`): Page a schedule's stored runs, newest first - `tenant.usage.self` / `substrio_tenant_usage_self` (`GET /v1/_usage`): Read this tenant's own exact usage and limits - `source.shell` / `substrio_shell` (`POST /v1/_source/shell`): Run one shell command over the app, the way you work on a checkout ## Skills Step-by-step guides an agent follows (GET the path; MCP: the resource substrio://skills/ or the prompt skill-). - [Accounts and apps](/v1/_docs/skills/admin): Manage an account's apps, members, invitations, and keys through Substrio customer operations. - [Collections and schema](/v1/_docs/skills/collections-and-schema): Creates collections, declares their schema and indexes (unique, compound, text, expiry, aggregate); use this before storing any document, or when a write is refused for its shape. - [Dashboard](/v1/_docs/skills/dashboard): Gives the app's owners an admin dashboard in the Substrio console from one dashboard.json (metrics, charts, tables with filters, paging, drill-down and row actions, records, forms); use this whenever an app holds something its owner will want to see or act on. - [Data and queries](/v1/_docs/skills/data-and-queries): Reads and writes documents in a substrio collection - insert, get, update, delete, query, aggregate, import and export; use this for any data operation once a collection exists. - [Files and site](/v1/_docs/skills/files-and-site): Uploads, lists and serves stored files, and publishes a static website as a versioned release; use this for file storage or a site deploy, separate from app versions. - [Functions](/v1/_docs/skills/functions): Deploys, invokes, inspects and rolls back cloud functions on substrio, written in JavaScript or Rust; use this when writing, shipping or debugging a function's code. - [Ledger](/v1/_docs/skills/ledger): Declares a ledger catalog and posts, clears, voids or reverses double-entry ledger entries, and reads balances and totals; use this for money or any append-only accounted quantity. - [MCP](/v1/_docs/skills/mcp): Connects any MCP client (Claude Code, Claude Desktop, Cursor, ChatGPT) to a substrio app's /mcp endpoint and explains what each kind of key can call; use this to set up or debug an MCP connection. - [Versions and stored copies](/v1/_docs/skills/mesh-and-replicas): Review an app's stored versions and backups, or move app data through the supported customer interfaces. - [Observability](/v1/_docs/skills/observability): Reads usage, the audit trail, function logs, log-based alerts, log drains and process metrics for an app; use this to answer "what happened" or "what is this app costing/doing right now". - [Queues, cron and triggers](/v1/_docs/skills/queues-cron-triggers): Declares durable queues, cron schedules and collection triggers, and inspects or recovers failed queue messages; use this for background work and scheduled or event-driven functions. - [Realtime](/v1/_docs/skills/realtime): Publishes and subscribes to typed realtime events on a collection; use this for live updates (chat, presence, live queries) instead of polling. - [Secrets and settings](/v1/_docs/skills/secrets-and-settings): Sets and reads an app's secrets (environment-style values, never returned once set) and its per-tenant settings; use this for API keys, passwords and tenant-level configuration. - [Sell plans and credits in your app](/v1/_docs/skills/sell-plans-and-credits): Sell recurring plans and prepaid credit packs in a Substrio app, connect the seller through Stripe, and manage subscribers and credit balances. - [Start here](/v1/_docs/skills/start-here): Orients any AI or agent new to substrio - what it is, how to get a key, and the first calls to make over HTTP, MCP or the CLI; use this before any other substrio skill. - [Users, auth and permissions](/v1/_docs/skills/users-auth-permissions): Signs users in, manages user records, and grants or revokes declared permissions and memberships; use this for anything about who can do what on an app. - [Versions and releases](/v1/_docs/skills/versions-and-releases): Commits to the app's history over MCP (tree, read, edit, search, commit, no hash anywhere) or pushes a built version, checks it in a sandbox, and promotes or rolls it back live; use this to ship or revert functions, rules, site, cron, triggers and declared collections together. ## Docs Every page (GET the path; MCP: the resource substrio://docs/). - [System overview](/v1/_docs/pages/01-system-overview): Substrio is a persistent, multi-tenant data and execution system embedded in this repository. TurboCore is the storage engine. A shared typed operation boundary serves HTTP and protocol adapters, JavaScript callers, and compatibility frontends. Optional Fractal execution coordinates bounded subtask - [Module and build map](/v1/_docs/pages/02-module-map): The Substrio implementation is under `src/substrio`. Top-level CMake and Bazel manifests select optional protocol and runtime components. The list below is organized by responsibility so an agent can find the smallest source family needed for a change or answer. - [Substrio system documentation](/v1/_docs/pages/AI_INDEX): This is the AI-first documentation surface for Substrio. It is an indexed projection of the running system, its typed contracts, and the verification evidence that supports each claim. An agent should begin here, identify the user intent, then open only the smallest linked document that answers it. - [Substrio Documentation](/v1/_docs/pages/README): Substrio is a backend platform with compute and storage in the same process. This page indexes documentation and agent skills. For an intent-based index and evidence rules, see [AI_INDEX.md](AI_INDEX.md). - [Accounts and apps](/v1/_docs/pages/accounts-and-apps): Everything you deploy belongs to an app, and every app belongs to an account. - [Managing accounts and apps](/v1/_docs/pages/agent-admin-tooling): Agents manage an account's apps, members, and keys through the same customer operations available over HTTP and the CLI. - [Agent function tooling](/v1/_docs/pages/agent-function-tooling): Use the typed HTTP endpoints to read, search, check, and edit a deployed function's source and environment in small, bounded calls. - [Agents over plain HTTPS](/v1/_docs/pages/agent-http): Every MCP tool is also available through HTTPS under the same name, with the same credential and response. - [Agent sessions](/v1/_docs/pages/agent-sessions): An agent session lets an AI agent act for a person in one account. - [Agent shell](/v1/_docs/pages/agent-shell): Run shell commands over your app's files, with pipes and heredocs. - [Agent workflow](/v1/_docs/pages/agent-workflow): An AI agent with an app key can discover the platform, write and test a function in dev, promote it, and roll it back. - [Building on substrio](/v1/_docs/pages/agents): Build and ship an app on substrio as an AI agent, over MCP or plain HTTP with the same results. - [API documentation index](/v1/_docs/pages/api-README): Substrio exposes one typed operation model through several protocol adapters. Start with the operation registry to identify an operation, then use the transport page for encoding. The shared contract is more authoritative than an adapter-specific example. - [Agent and function tooling](/v1/_docs/pages/api-agent-tools): The agent API can configure a tenant, inspect data and functions, propose changes, obtain approval, apply a change, and invoke the result. The same operations are available through HTTP or JSC without an agent. - [Compatibility status and gaps](/v1/_docs/pages/api-compatibility): The RESP frontend, Lua scripting, and LuaJIT compile only with `SUBSTRIO_BUILD_REDIS`, which is off by default. - [API contracts](/v1/_docs/pages/api-contracts): Call an operation over HTTP, MCP, or the CLI with the same arguments and receive the same result and errors. - [Errors](/v1/_docs/pages/api-errors): A failed call returns a JSON envelope with a stable code, an HTTP status, and a hint for the next step. - [HTTP API](/v1/_docs/pages/api-http): Call operations over HTTP. Select an app by host or header, send a bearer key, and receive typed JSON. - [Operation registry](/v1/_docs/pages/api-operation-registry): One operation registry defines the HTTP routes, MCP tools, CLI commands, and SDK calls available to your app. - [HTTP API reference](/v1/_docs/pages/api-reference): Use HTTP to manage your account, deploy apps, store records, and call functions. - [HTTP routes](/v1/_docs/pages/api-route-inventory): The operation reference lists each customer route alongside its credential, request body, and response. - [Transport adapters](/v1/_docs/pages/api-transports): The operation registry (`src/substrio/core/substrio_operation_registry.h`, - [JavaScript API Updates](/v1/_docs/pages/api-updates): When adding new JavaScript APIs to workerd, please remember that all compatibility flags MUST be - [Let your users' agents use your app](/v1/_docs/pages/app-agents): Your users can connect AI agents to your app's MCP endpoint using their credentials. Each agent calls your tools with the permissions of the user who connected it. - [App archives](/v1/_docs/pages/app-archive): Download your app as an archive. You can restore the archive as a backup or read it as an export. - [Calls and reads between apps](/v1/_docs/pages/app-calls): Call a function or read a query that another app offers, with no HTTP and no shared code. - [The app loop: build, test and ship in a few calls](/v1/_docs/pages/app-loop): An agent with a key can build an app over MCP. It commits files to the app's history, runs the commit in - [Architecture](/v1/_docs/pages/architecture): Substrio is a zero-copy Backend-as-a-Service (BaaS) platform. Unlike traditional BaaS platforms where compute and storage are separate services connected over the network, Substrio co-locates them in a single process. Cloud Functions read directly from the same mmap'd pages as the storage engine: ze - [Users and auth](/v1/_docs/pages/authentication): Sign users up, sign them in with a token, and gate routes and records by role, permission, or ownership. - [Building benchmarks](/v1/_docs/pages/benchmarking): Benchmarks should be built using `--config=benchmark` configuration, which builds a release - [TurboCore Hydration Baseline (Low-Level)](/v1/_docs/pages/benchmarks-baseline_hydration): 1. **Hydration scales linearly with field count:** - [Data modeling](/v1/_docs/pages/best-practices-data-modeling): Pick collections and ids, declare schemas and unique fields, index for the queries you run, and choose KV, SQL, the ledger or files when a collection is the wrong shape. - [Errors and retries](/v1/_docs/pages/best-practices-errors-and-retries): Branch on the typed error code, retry only what the envelope says is safe, and keep a retried write from running twice. - [Keys](/v1/_docs/pages/best-practices-keys): Pick the right kind of key for the caller, rotate it without downtime, and never let one reach function source. - [Plan limits](/v1/_docs/pages/best-practices-plan-limits): Read your limits and usage before you reach a limit, and learn what each error means. - [Security and permissions](/v1/_docs/pages/best-practices-security-and-permissions): Gate collections with security rules, gate function routes by sign-in, require a verified email or 2FA where it matters, and keep secrets and outbound calls scoped. - [Versions and test hosts](/v1/_docs/pages/best-practices-versions-and-test-hosts): Push a version, test it in a sandbox or on a test host, and promote it. If a change goes wrong, you can roll it back. - [Plans and billing](/v1/_docs/pages/billing): An account's plan sets its usage and storage limits. The platform's plans, - [Bring your project](/v1/_docs/pages/bring-your-project): Move an existing backend to substrio in order: data, sign-in, routes, jobs, mail, files, secrets, tests, deploy. - [Chain queries over the API](/v1/_docs/pages/chains-over-api): Run the filter, sort, join, and aggregate plan of a JavaScript chain as one typed JSON request. - [CLI](/v1/_docs/pages/cli): Install the command-line client, sign in, and call any operation from your terminal or a script. - [Cloud functions](/v1/_docs/pages/cloud-functions): Deploy server-side JavaScript functions that run next to your data, with direct access to collections, SQL, and key-value stores. - [App dashboards](/v1/_docs/pages/dashboard): An app can declare an admin dashboard for members of its account and customers of a featured app. - [Reliable delivery](/v1/_docs/pages/delivery): Webhooks, document triggers, and cron runs are stored before the first delivery attempt. - [Development](/v1/_docs/pages/development): Run your app locally with hot reload, and test it against a throwaway server, both from one CLI. - [Email](/v1/_docs/pages/email): Send email from a function over SMTP, and configure the function that sends your app's sign-in, verification, and invite emails. - [TurboAlloc](/v1/_docs/pages/engines-turboalloc): TurboAlloc is the allocator under every StaxDB engine. It hands out memory inside the database file, and what it hands out is a word offset into that file, not a pointer. An offset stored in a record means the same block after a restart and after the mapping grows, so the data structures above it ar - [TurboCore](/v1/_docs/pages/engines-turbocore): TurboCore is StaxDB's storage engine: durable, named trees in one database file. Each tree is a [TurboMap](/docs/turbomap/) whose nodes and records are stored in the file itself, allocated by [TurboAlloc](/docs/turboalloc/). There is no log, no memtable and no compaction: a record is written once, w - [TurboHeap](/v1/_docs/pages/engines-turboheap): TurboHeap is how StaxDB stores a document. An object is kept in its final form, in the database file: a small header, the id of its shape, and its values. There is no text to parse on a read and nothing to serialize on a write; the stored bytes are the object. - [TurboJS](/v1/_docs/pages/engines-turbojs): TurboJS runs your JavaScript inside the database, on JavaScriptCore with its optimizing JIT. A function reaches its data with no network, no driver and no serialization between them: the record it reads is built as a JavaScript object straight from the stored bytes. - [TurboMap](/v1/_docs/pages/engines-turbomap): TurboMap is the ordered key-value core every StaxDB engine stores its data in. It is a concurrent radix tree: keys are byte strings kept in byte order, one node is visited per key byte on a lookup, and reads are lock-free. - [TurboSQL](/v1/_docs/pages/engines-turbosql): TurboSQL answers MariaDB SQL over the same data the rest of StaxDB stores. SQL rows and documents are both [TurboHeap](/docs/turboheap/) records, so every document collection is also a table, and SQL reads it where it lies. - [Featured apps](/v1/_docs/pages/featured-apps): A featured app, or jet, is a Substrio app available to apps in every account. - [Files](/v1/_docs/pages/files): Store, list, download, change, move, and delete files by path over HTTP or from a function. Public files can be served without a key. - [Fractal Execution](/v1/_docs/pages/fractal-execution): Fractal Execution is the enterprise hierarchical execution subproject of Substrio. Its core has no model, provider, network, or conversation-history dependency. AI is an optional executor selected by policy. - [Function errors](/v1/_docs/pages/function-errors): When a function throws, Substrio records a typed error with parsed stack frames. You can read it by request, function, or revision. - [Getting started](/v1/_docs/pages/getting-started): Sign in, create an app, and push a function. You can test it in a sandbox before making it live. - [Glossary](/v1/_docs/pages/glossary): This glossary defines terms used throughout the documentation and links to further details. - [Install](/v1/_docs/pages/install): Install the CLI, SDK, and client packages from npm. - [JSC Upstream Delta Inventory](/v1/_docs/pages/jsc_upstream_delta_inventory): This document records the intentional Substrio delta around JavaScriptCore so the - [JSC Upstream Reimplementation Instructions](/v1/_docs/pages/jsc_upstream_reimplementation_instructions): Use this document when rebuilding the vendored JSC integration from a clean - [MCP](/v1/_docs/pages/mcp): Connect Claude Code, Claude Desktop, Cursor, or another MCP client to an app's tools without writing an MCP server. - [Operations](/v1/_docs/pages/operations): The public API has 500 operations. Each operation is available through HTTP, MCP, and the CLI. - [Static sites](/v1/_docs/pages/operations-static-sites): Deploy a directory as a release and serve it with clean URLs, a real 404 page, and cache headers. - [Custom domains](/v1/_docs/pages/operations-tenant-domains): Attach, list, and detach custom domains for your app. The app remains available on its default subdomain as well. - [Permissions](/v1/_docs/pages/permissions): Substrio uses three permission layers for requests from your app's users and public callers. - [Plans and limits](/v1/_docs/pages/plans-and-limits): Limits apply to function calls, apps, and keys. An account's plan sets app limits, and you can add rate-limit rules. - [Project files](/v1/_docs/pages/project-files): Declare functions, collections, roles, and auth providers as files so the sandbox and production build from the same source. - [Query language](/v1/_docs/pages/query-language): Filter, sort, page, and aggregate a collection's documents with a JSON query. - [Query and language gaps](/v1/_docs/pages/query-language-gaps): The repository does not by itself prove universal SQL grammar coverage, every Mongo aggregation operator, every Redis module command, every Cloudflare compatibility endpoint, or performance superiority across all value sizes and durability modes. These require named, reproducible gates. - [Query and pipeline support](/v1/_docs/pages/query-support): The native query engine in [`substrio_query_native.h`](../../src/substrio/core/substrio_query_native.h) and `.cpp` parses JSON with simdjson, compiles iterators and bytecode, executes scans, and can stream results. It supports explain, query caching, and timeout accounting. `QueryResult` can return - [Quickstart](/v1/_docs/pages/quickstart): Use HTTP, the CLI, or MCP to create and query data, then deploy and call a function. - [Chain reference](/v1/_docs/pages/runtime-chain-reference): Look up chain method signatures, arguments, return values, errors, and runnable examples on one page. - [Collections and chains](/v1/_docs/pages/runtime-collections-and-chains): Read, filter, sort, join, and write JSON documents with synchronous calls on a collection. - [Concurrency and async calls](/v1/_docs/pages/runtime-concurrency): Run asynchronous work inside a function call and understand which state concurrent calls share. - [Durable objects](/v1/_docs/pages/runtime-durable-objects): Export a class as a durable object. Each id has one live instance, calls to that instance are serialized, and its state is stored in the app's database. - [Errors and logs](/v1/_docs/pages/runtime-errors-and-logs): Read recorded errors and function console output, and set up log drains and alerts. - [Fractal execution and optional AI](/v1/_docs/pages/runtime-fractal): The Fractal module coordinates bounded parent/child work. Its purpose is context-efficient orchestration: a child receives only its charter, direct-parent handoff and required input artifacts, then returns a bounded summary/evidence/unresolved/next-action handoff. - [Isolate eviction diagnostic](/v1/_docs/pages/runtime-hammer-isolate-probe): This diagnostic compares the eviction policies on the real JSC engine and real - [Function runtime](/v1/_docs/pages/runtime-index): JavaScript functions have database, platform service, environment, and fetch globals in scope. - [JavaScript, Lua and cloud functions](/v1/_docs/pages/runtime-javascript-functions): Deploy JavaScript functions that run next to your app's data, and run Lua scripts over its Redis-compatible keys. - [Key-value stores](/v1/_docs/pages/runtime-kv): Store and read bytes by key from a function. The API also supports typed numeric counters, prefix scans, and a byte cursor. - [Ledger](/v1/_docs/pages/runtime-ledger): Post signed entries in minor units against a kind, party, and currency. Read balances maintained as entries change. - [Embedded ORM coverage](/v1/_docs/pages/runtime-orm-coverage): Source inspection dated 2026-10-05. This matrix covers the synchronous embedded - [Queues and background jobs](/v1/_docs/pages/runtime-queues): Send a message from a function to a named queue and have another function consume it in the background. - [Realtime](/v1/_docs/pages/runtime-realtime-sync): Open a WebSocket to a collection or one of its fields and receive an event the instant a write commits. - [Records and query results](/v1/_docs/pages/runtime-records): Read a row from a collection as a live record, assign its fields, and write them back with one call. - [Rust functions](/v1/_docs/pages/runtime-rust): A function can be written in Rust. This guide covers when to use Rust, the available calls, and how to deploy and test a function. - [Services](/v1/_docs/pages/runtime-services): Call your app's API and other functions directly, and access files, KV, graph data, and authentication. - [SQL](/v1/_docs/pages/runtime-sql): Run synchronous SQL with typed parameters, prepared statements, and transactions. - [Wasm functions (Rust)](/v1/_docs/pages/runtime-wasm-functions): A function can be written in Rust and deployed from source. - [The ledger and files in Rust](/v1/_docs/pages/runtime-wasm-ledger): A Rust function can use the same ledger and file operations as a JavaScript function. - [Rust: objects, fetch, timers, crypto and the clock](/v1/_docs/pages/runtime-wasm-platform): A Rust function can call a durable object, fetch a URL, set timers, read random bytes, and read the clock. - [Writing records from Rust](/v1/_docs/pages/runtime-wasm-records): A Rust function writes documents through the same calls a JavaScript function uses. - [Services and gates in Rust](/v1/_docs/pages/runtime-wasm-services): A Rust function can use the same platform services, database guards, and gates as a JavaScript function. - [Schedules](/v1/_docs/pages/schedules): Run a deployed function on a cron schedule, with retries, catch-up and a run history. - [Secrets](/v1/_docs/pages/secrets): Write, list and delete the named values your app's functions read as environment variables. - [Authentication, authorization and tenancy](/v1/_docs/pages/security-auth-tenancy): A tenant's key (its owner's credential, sent as `X-Substrio-Master-Key` or a bearer) is 32 random characters (192 bits). It is shown exactly once, in the answer of `account.app.create` and of `tenant.key.rotate`; no other call, log or error returns it, and the server keeps only its SHA-256 digest. A - [Source of truth and evidence](/v1/_docs/pages/source-of-truth): When statements disagree, use this order: - [SQL dialect](/v1/_docs/pages/sql-dialect): Substrio supports the SQLite and MySQL SQL dialects. - [SQL: where MariaDB and SQLite differ, and which answer substrio gives](/v1/_docs/pages/sql-mariadb-and-sqlite): substrio answers SQL as MariaDB 12.3 does. Where MariaDB and SQLite give different answers to the - [SQL over the API](/v1/_docs/pages/sql-over-api): Run parameterized SQL over HTTP or MCP against an app's data, with batched transactions and schema introspection. - [Durability, backups, jobs and synchronization storage](/v1/_docs/pages/storage-durability-backups): There is one server-wide durability knob, `--sync-interval-ms` (`ServerConfig::sync_interval_ms`, default 1000): each database fsyncs its writes this often, and a reply never waits for the fsync. `0` means no periodic fsync at all; a database syncs only on close and on the exit signal (shown as `ker - [Graph storage and analytics](/v1/_docs/pages/storage-graph): Store directed edges between integer node ids under a label. You can read neighbors and degrees and run graph analytics over them. - [Schemas, namespaces and views](/v1/_docs/pages/storage-schema-views): Declare a collection, set its schema, inspect its namespaces, and build materialized or live views over its data. - [TurboCore and TurboHeap](/v1/_docs/pages/storage-turbocore): TurboCore is Substrio’s persistent database engine. It is mmap-backed and maintains records, B-tree/trie structures, indexes, epochs, write batches, transactions, cursors, Redis lookup views and vector/geo structures. There is no supported RAM-only product mode; durability behavior is selected thr - [TurboVector9](/v1/_docs/pages/storage-turbovector9): TurboVector9 (TV9) is the dense vector index behind a vector field's `$knn`. It lives in the database file (mapped storage, persistent like every other tree), is trained and maintained in the background while inserts go on, and answers a top k by scanning compact codes of the vectors in the clusters - [Vectors, geo and RAG search](/v1/_docs/pages/storage-vectors-geo-rag): Index a collection's vector, geo, or sparse fields, then query them with k-nearest, bounding-box, and RAG search operators. - [Subscriptions](/v1/_docs/pages/subscriptions): An app can sell plans through Substrio. This applies to Substrio, featured apps such as SendJet and MapJet, - [Test hosts and root domains](/v1/_docs/pages/test-hosts): Bind a sandbox to a host, test a version there, and promote it from that host to live. - [Multi-document transactions](/v1/_docs/pages/transactions): Run insert, update and delete operations across collections in one call, all committed or all rolled back. - [TurboNode Spec Sheet](/v1/_docs/pages/turbonode_spec_sheet): Prototype a single active routing node for TurboMap that replaces the current active ladder: - [Types](/v1/_docs/pages/types): To find the exact type of an operation's input or response, use the server's - [Unified API Completion Checklist](/v1/_docs/pages/unified-api-completion-checklist): This is the execution checklist for completing the parser-native internal API and - [Substrio User Guide](/v1/_docs/pages/user_guide): Substrio is a high-performance BaaS (Backend-as-a-Service) database with: - [Versioning](/v1/_docs/pages/versioning): An app's versions share one history. You can push a commit, move a branch or tag, merge, compare changes, and collect unused objects with Git-like commands.