# Introduction Sovrium is a source-available, self-hosted platform that turns a single configuration file into a complete web application. :::callout **Sovrium v0.10.0 — Active Development.** This project is under active development. APIs, configuration format, and features may change between versions. ::: ## What is Sovrium? Sovrium is a configuration-driven application platform. You describe your application in a YAML or JSON file (data models, authentication, pages, themes, analytics) and Sovrium turns it into a running, full-stack web application. No boilerplate code, no framework setup, no build pipeline. Just one file that declares what your app should be. ```yaml name: my-app tables: - id: 1 name: tasks fields: - id: 1 name: title type: single-line-text required: true auth: strategies: - type: emailAndPassword theme: colors: primary: '#3b82f6' ``` ## Why Sovrium? Most business applications share the same building blocks: data tables, user authentication, server-rendered pages, and a design system. Sovrium provides all of these out of the box, configured through a single schema. ### No vendor lock-in Self-hosted on your infrastructure. Your data stays yours. ### Configuration over code Declare what you need instead of writing boilerplate. 49 field types, ~80 component types, built-in auth. ### Progressive complexity Start with just a name. Add tables, theme, pages, auth, and analytics as your needs grow. ### Source-available Business Source License 1.1. Free for internal use. Becomes Apache 2.0 in 2030. ## How it works Write a configuration file, run one command, and get a working application: 1. Define your schema in YAML or JSON 2. Run `sovrium start app.yaml` 3. Get a full-stack app with data tables, auth, pages, and more ## Next steps Ready to try it? Install Sovrium and build your first app in under 5 minutes. :::callout **Installation** — Install Sovrium globally or as a project dependency using Bun. ::: ## Getting help Found a bug, have a question, or want to request a feature? Sovrium is open source. If you run into any issues or have ideas for improvement, the best way to reach us is through GitHub Issues. [Open an issue on GitHub →](https://github.com/sovrium/sovrium/issues) # Installation Install Sovrium globally or as a project dependency using Bun. ## Prerequisites Tables and auth work out of the box on an embedded SQLite database — no setup required. PostgreSQL 15+ is optional and unlocks advanced features (raw SQL, vector search). Running from source or as a library requires [Bun 1.3+](https://bun.sh). ## Standalone binary (recommended) Sovrium ships as a self-contained binary — the recommended way to install. No runtime to install separately. ```bash # Install script curl -fsSL https://sovrium.com/install | sh # Homebrew brew install sovrium/tap/sovrium # Docker docker pull ghcr.io/sovrium/sovrium:latest ``` After installing, the `sovrium` command is available from anywhere. ## As a library To use Sovrium programmatically from a Bun project, add it as a dependency: ```bash bun add sovrium ``` :::callout **Authoring config in TypeScript?** Install the zero-dependency `@sovrium/types` package for IDE autocompletion (`bun add -d @sovrium/types`) and author with `defineConfig`. See [Configuration Files](/en/configuration-files). ::: ## Verify installation Run the help command to check that Sovrium is installed correctly: ```bash sovrium --help ``` ## Create a config file Sovrium reads a YAML or JSON configuration file. Create an `app.yaml` with the simplest valid config: ```yaml name: my-app ``` :::callout **YAML or JSON.** Sovrium supports both `.yaml`/`.yml` and `.json` files. YAML is recommended for readability. ::: ## Database setup Sovrium uses an embedded SQLite database by default — tables and auth work with zero configuration. Set `DATABASE_URL` only to choose the SQLite file location or to switch to PostgreSQL: ```bash # Default: embedded SQLite — no DATABASE_URL needed # Optional — choose where the SQLite file lives: export DATABASE_URL="file:./data/app.db" # Optional — use PostgreSQL for advanced features: export DATABASE_URL="postgresql://user:password@localhost:5432/myapp" ``` :::callout **SQLite by default.** Leave `DATABASE_URL` unset and Sovrium stores data in a local SQLite file. Use a `file:` URL to pick its location, or a `postgresql://` URL to switch engines. Pure static sites (pages and theme only) need no database at all. ::: ## Next steps - [Quick Start](/en/quick-start) — build and run your first app. - [Core Concepts](/en/concepts) — the anatomy of a Sovrium app. - [Configuration Files](/en/configuration-files) — YAML, JSON, TypeScript, and `$ref`. # Quick Start Build your first Sovrium app in minutes. From zero to a running application. Choose the approach that fits your workflow. ## Choose your approach Sovrium supports two configuration formats. YAML is great for simplicity; TypeScript gives you full type safety and autocompletion. ### Option A: YAML + CLI The simplest path. Install the Sovrium CLI, write a YAML config, and start the server: 1. **Install the CLI** — Install Sovrium globally with Bun to get the `sovrium` command. ```bash bun add -g sovrium ``` 2. **Create a config file** — Create an `app.yaml` with the simplest valid configuration: just a name. ```yaml name: my-app ``` 3. **Add data tables** — Define your data models with typed fields, options, and validation. ```yaml name: my-app tables: - id: 1 name: tasks fields: - id: 1 name: title type: single-line-text required: true - id: 2 name: status type: single-select options: [To Do, In Progress, Done] ``` 4. **Start the server** — Run the dev server and visit `http://localhost:3000` to see your app. ```bash sovrium start app.yaml ``` :::callout **Add more as you go.** Start small with just tables. Then progressively add theme, auth, pages, and analytics as your needs grow. ::: ### Option B: TypeScript + Bun The power-user path. Create a Bun project, add Sovrium as a dependency, and write type-safe code: 1. **Initialize a project** — Scaffold a new Bun project with `bun init` and move into the directory. ```bash bun init my-app && cd my-app ``` 2. **Add Sovrium** — Install Sovrium as a project dependency. ```bash bun add sovrium ``` 3. **Write your app** — Open `index.ts` and import the `start` function with a minimal configuration. ```typescript import { start } from 'sovrium' await start({ name: 'my-app', }) ``` 4. **Add data tables** — Extend the configuration with typed fields, options, and validation, with full autocompletion. ```typescript import { start } from 'sovrium' await start({ name: 'my-app', tables: [ { id: 1, name: 'tasks', fields: [ { id: 1, name: 'title', type: 'single-line-text', required: true, }, { id: 2, name: 'status', type: 'single-select', options: ['To Do', 'In Progress', 'Done'], }, ], }, ], }) ``` 5. **Run your app** — Execute `index.ts` with Bun. Visit `http://localhost:3000` to see your app. ```bash bun run index.ts ``` :::callout **Why TypeScript?** TypeScript gives you autocompletion for every property, compile-time validation of field types, and the full power of Bun as your runtime. Ideal for developers who prefer code over config files. ::: ## What's next? Now that your app is running, explore the schema reference to add more capabilities: - [Core Concepts](/en/concepts): The anatomy of a Sovrium app - [Schema Overview](/en/overview): All 18 root properties explained - [Tables Overview](/en/tables-overview): 49 field types, permissions, indexes - [Theme](/en/theme): Colors, fonts, spacing, and design tokens - [Pages Overview](/en/pages-overview): ~80 component types for server-rendered pages # Core Concepts A Sovrium app is a single declarative **configuration object**. You describe _what_ your application is — its data, pages, forms, authentication, automations — and Sovrium turns that description into a running, full-stack web application. There is no boilerplate, no framework scaffolding, and no build pipeline to wire up. This page explains the anatomy of that configuration object so the rest of the [App Schema](/en/overview) reference makes sense. ## The configuration object Everything starts from one root object. Only `name` is required; every other section is optional and layered on as your app grows. ```yaml name: my-app # App identifier (required) version: 1.0.0 # SemVer version description: My application # One-line description tables: [...] # Data models (49 field types) pages: [...] # Server-rendered pages (~80 component types) forms: [...] # Standalone forms that capture submissions auth: { ... } # Authentication, roles, RBAC theme: { ... } # Design tokens (colors, fonts, spacing, …) languages: { ... } # Multi-language support ($t: syntax) automations: [...] # Event-driven workflows (triggers + actions) actions: [...] # Reusable action templates ($ref) connections: [...] # External-service credentials agents: [...] # AI agents acting under an auth role buckets: [...] # Named file-storage containers components: [...] # Reusable UI templates ($ref, $variable) analytics: { ... } # Cookie-free, first-party analytics env: { ... } # Declared automation env vars ($env.NAME) scripts: { ... } # Global page scripts ``` :::callout **Progressive complexity.** A valid app can be as small as `name: my-app`. Add sections only when you need them — there is no "minimum viable scaffold" to fight through first. ::: ## The root sections The configuration object groups its capabilities into the sections below. Each links to its dedicated reference. | Section | Purpose | Reference | | ------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------ | | `name` | App identifier (npm naming rules). The only required property. | [App Metadata](/en/app-metadata) | | `version` | Semantic Versioning string. Feeds the immutable version ledger. | [App Metadata](/en/app-metadata) | | `description` | Single-line app description (max 2000 chars). | [App Metadata](/en/app-metadata) | | `tables` | Data models — the columns records can store across 49 field types. | [Tables Overview](/en/tables-overview) | | `pages` | Server-rendered pages built from a tree of component types, with SEO and i18n. | [Pages Overview](/en/pages-overview) | | `forms` | Standalone forms that capture submissions into a table or trigger an automation. | [Forms Overview](/en/forms-overview) | | `auth` | Authentication strategies, roles, RBAC, sessions, and two-factor. | [Auth Overview](/en/auth-overview) | | `theme` | Design tokens: colors, fonts, spacing, shadows, animations, breakpoints, radii. | [Theme](/en/theme) | | `languages` | Multi-language support with `$t:` translation syntax and URL routing. | [Languages](/en/languages) | | `automations` | Event-driven workflows: a trigger fires, a sequence of actions runs. | [Automations Overview](/en/automations-overview) | | `actions` | Reusable action templates referenced from automations via `$ref`. | [Automations Overview](/en/automations-overview) | | `connections` | Reusable credentials for external services (OAuth2, API key, basic, bearer). | [Automations Overview](/en/automations-overview) | | `agents` | AI agents that act on behalf of an auth role within a constrained tool set. | [AI Overview](/en/ai-overview) | | `buckets` | Named storage containers with per-bucket file limits and permissions. | [Buckets Overview](/en/buckets-overview) | | `components` | Reusable UI templates inserted into pages via `$ref` with `$variable` substitution. | [Pages Overview](/en/pages-overview) | | `analytics` | First-party, cookie-free analytics. `true` for defaults or an object for options. | [Schema Overview](/en/overview) | | `env` | Declared environment variables resolved at runtime, referenced as `$env.NAME`. | [Environment Variables](/en/env-vars) | | `scripts` | Global scripts loaded on every page before page-level scripts. | [Pages Overview](/en/pages-overview) | :::callout **Records are not a root section.** Records are the rows _inside_ your tables. They are created and queried at runtime through the REST/MCP API rather than declared in the config — see [Records Overview](/en/records-overview). ::: ## Configuration-driven philosophy Sovrium inverts the usual relationship between code and configuration. Instead of writing application code that occasionally reads a config file, you write configuration that Sovrium executes directly. - **Declare, don't implement.** You declare a `relationship` field; Sovrium creates the foreign key, the join queries, and the linked-record UI. You declare an `automation`; Sovrium wires the trigger, runs the actions, and records the run history. - **One source of truth.** The same object drives the database schema, the REST API, the MCP server, the rendered pages, and the admin dashboard. There is no second place where the shape of your data is restated. - **Cross-section validation.** Sections are validated together, not in isolation. A record automation that references a missing table, an attachment field pointing at an undeclared bucket, or a permission naming an unknown role are all rejected before the server starts — see [`sovrium validate`](/en/cli). - **Self-hostable, no lock-in.** The config runs on your infrastructure against commodity storage (SQLite by default, PostgreSQL optional). Your data and your schema stay yours. ## The layer model (at a glance) Internally, Sovrium follows a four-layer architecture. You never write code in these layers for a config-only app, but the model explains _why_ the schema is shaped the way it is. | Layer | Responsibility | What your config touches | | ------------------ | -------------------------------------------------------- | -------------------------------------------------- | | **Presentation** | Renders pages and serves the REST/MCP API. | `pages`, `forms`, `components`, `theme`, `scripts` | | **Application** | Orchestrates workflows and use-cases. | `automations`, `actions`, `agents` | | **Domain** | Pure business rules and the validated app schema itself. | `tables`, `auth`, `languages`, validation | | **Infrastructure** | Talks to the database, file storage, and external APIs. | `connections`, `buckets`, `env` | The dependency direction always flows inward (Presentation → Application → Domain ← Infrastructure), which is why declaring data and rules (`tables`, `auth`) is independent of how they are rendered (`pages`) or persisted (`buckets`). ## Next steps - [Configuration Files](/en/configuration-files) — YAML vs TypeScript, `defineConfig`, and multi-file configs with `$ref`. - [Schema Overview](/en/overview) — the full root-property reference. - [Quick Start](/en/quick-start) — build and run your first app. # CLI Reference The Sovrium CLI runs your application and manages its lifecycle: start and stop the server, hot-reload config without downtime, build a static site, scaffold a project, print the JSON Schema, and validate a config file. Configuration is loaded from a file or environment variable. ## Usage The CLI accepts a command followed by an optional config file path and flags. ```bash sovrium [command] [config] [flags] sovrium start app.yaml # Start the server (default command) sovrium stop # Stop the running server sovrium restart app.yaml # Restart the running server sovrium reload # Hot-reload config without downtime sovrium build app.yaml # Build static site sovrium init ./my-app # Scaffold a new project sovrium schema # Print JSON Schema sovrium validate app.yaml # Validate config sovrium --help # Show help ``` ## Commands The most common commands are below. If no command is specified, `start` is used by default. The CLI also exposes commands to extend a project (`agents`), operate it (`admin`, `secret`), and keep the binary current (`update`) — all documented below. Run `sovrium --help` for the full list. ### `sovrium start` Start a development server that serves your application. This is the default command — running `sovrium app.yaml` is equivalent to `sovrium start app.yaml`. ```bash sovrium start app.yaml ``` ### `sovrium stop` Stop the running Sovrium server. ```bash sovrium stop ``` ### `sovrium restart` Restart the running server, optionally with a new config file. ```bash sovrium restart app.yaml ``` ### `sovrium reload` Hot-reload the configuration of a running server without downtime. The reload first probes the live app's drift posture; if the live schema has drifted from the file (edited at runtime via the admin API or MCP), the reload is refused unless `--force` is passed. See [App Metadata](/en/app-metadata#schema-drift-detection). ```bash sovrium reload sovrium reload --force # overwrite a drifted live schema with the file ``` ### `sovrium init` Scaffold a new project in the given directory (or the current directory). Use `--template ` to start from a template and `--name ` to set the app name. ```bash sovrium init ./my-app --template blog ``` ### `sovrium build` Generate a static site from your configuration. Produces HTML, CSS, and assets ready for deployment to any static hosting provider. ```bash sovrium build app.yaml ``` ### `sovrium schema` Print the JSON Schema (Draft-07) for the Sovrium app configuration to stdout. Use `--output ` to write to a file instead. ```bash sovrium schema sovrium schema --output app.schema.json ``` ### `sovrium validate ` Validate a configuration file against the Sovrium AppSchema. Returns exit code 0 if valid, 1 if invalid with error details. ```bash sovrium validate app.yaml ``` ### `sovrium agents list` List the agent templates embedded in the binary. These are Claude Code subagent definitions you can install into a project's `.claude/agents/` directory. ```bash sovrium agents list ``` ### `sovrium agents install ` Install an embedded agent template into `.claude/agents/.md`. `add` is an alias of `install`. By default the agent is written under the current directory; pass `--target ` to choose another project root. The command refuses to overwrite an existing agent file unless `--force` is supplied. ```bash sovrium agents install crud-editor sovrium agents add crud-editor --target ./my-project --force ``` ### `sovrium admin create ` Provision an admin user against the database without standing up a full schema. The password is taken from `--password ` when supplied (useful for CI/scripting); otherwise you are prompted interactively with echo disabled. Running without `--password` in a non-interactive shell (no TTY) is an error. The database schema is resolved in this order: an explicit `--config `, then `APP_SCHEMA`, then a minimal built-in default — so the bare `sovrium admin create ` flow works with no config file. If the admin already exists, no changes are made. ```bash # Prompt for the password interactively sovrium admin create me@example.com # Supply the password (CI / scripting) sovrium admin create me@example.com --password 's3cret-pass' # Resolve the schema from a specific config sovrium admin create me@example.com --config app.yaml ``` ### `sovrium secret generate` Print freshly-generated, cryptographically random secrets as `.env`-ready lines. Each secret is a 256-bit (64 hex character) value. With no scope, both `AUTH_SECRET` and `SOVRIUM_ENCRYPTION_KEY` are printed. Pass a scope to print a subset. | Scope | Output | | ------------ | ---------------------------------------- | | _(none)_ | `AUTH_SECRET` + `SOVRIUM_ENCRYPTION_KEY` | | `all` | Both (explicit) | | `auth` | `AUTH_SECRET` only | | `encryption` | `SOVRIUM_ENCRYPTION_KEY` only | The generated `.env` lines are written to stdout (so redirects work cleanly); informational notes go to stderr. ```bash # Both secrets sovrium secret generate # Append a fresh AUTH_SECRET to your .env sovrium secret generate auth >> .env # Encryption key only sovrium secret generate encryption ``` ### `sovrium update` Update Sovrium to the latest version. The behavior depends on how Sovrium was installed: | Install method | Behavior | | -------------- | -------------------------------------------------------- | | `binary` | Self-replaces from GitHub Releases (Unix) | | `homebrew` | Delegates to `brew upgrade sovrium/tap/sovrium` | | `scoop` | Delegates to `scoop update sovrium` | | `docker` | Prints the `docker pull` instruction | | `npm` | Prints a deprecation notice (the npm package is retired) | ```bash sovrium update sovrium update --help ``` ### `sovrium --help` Display the help message with a summary of commands, options, and examples. ```bash sovrium --help ``` ## Flags Flags can be placed anywhere in the command. | Flag | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------- | | `--watch, -w` | Watch the config file for changes and hot-reload the server automatically. Only available with the `start` command. | | `--version, -V` | Show the Sovrium version number and exit. | | `--output ` | Write output to a file instead of stdout (`schema` command only). | | `--template ` | Project template to scaffold from (`init` command). | | `--name ` | App name to set when scaffolding (`init` command). | | `--target ` | Install destination, defaults to the current directory (`agents install` command). | | `--config ` | Config file used to resolve the database schema (`admin create` command). | | `--password ` | Admin password; if omitted you are prompted interactively (`admin create` command). | | `--force` | Overwrite existing files or a drifted live schema (`init`, `reload`, `agents install`). | | `--help, -h` | Show the help message and exit. | ## Configuration Sources Sovrium can load configuration from a file path or from the `APP_SCHEMA` environment variable. A file path takes precedence when both are provided. ### File path Pass a path to a JSON or YAML configuration file as the second argument. ```bash sovrium start app.yaml sovrium build config.json ``` ### Environment variable Set the `APP_SCHEMA` variable to provide configuration without a file. Supports inline JSON, inline YAML, or a remote URL. ```bash # Inline JSON APP_SCHEMA='{"name":"my-app"}' sovrium start # Inline YAML APP_SCHEMA='name: my-app' sovrium start # Remote URL APP_SCHEMA='https://example.com/app.yaml' sovrium start ``` :::callout **YAML or JSON.** Sovrium supports both `.yaml`/`.yml` and `.json` files. YAML is recommended for readability. ::: ## Watch Mode Watch mode monitors your config file and automatically reloads the server when changes are detected. ```bash # Start with file watching sovrium start app.yaml --watch # Edit app.yaml in another terminal... # Server reloads automatically ``` :::callout **Error recovery.** If the updated config file is invalid, the reload fails gracefully and the previous server continues running. Fix the config and save to retry. ::: ## Examples Common CLI usage patterns. ### `sovrium start` ```bash # Start from a JSON file sovrium start app.json # Start from a YAML file sovrium start app.yaml # Implicit start (default command) sovrium app.yaml # Start with watch mode (hot reload) sovrium start app.yaml --watch sovrium start app.yaml -w # Start on a custom port PORT=8080 sovrium start app.yaml ``` ### `sovrium build` ```bash # Basic static build sovrium build app.yaml # Build for GitHub Pages SOVRIUM_DEPLOYMENT=github-pages sovrium build app.yaml # Build with sitemap and custom output SOVRIUM_OUTPUT_DIR=./public \ SOVRIUM_BASE_URL=https://example.com \ SOVRIUM_GENERATE_SITEMAP=true \ SOVRIUM_GENERATE_ROBOTS=true \ sovrium build app.yaml ``` ### `sovrium schema` ```bash # Print JSON Schema to stdout sovrium schema # Write JSON Schema to a file sovrium schema --output app.schema.json ``` ### `sovrium validate ` ```bash # Validate a YAML config sovrium validate app.yaml # Validate a JSON config sovrium validate config.json ``` ## Exit Codes The CLI uses standard exit codes. | Code | Meaning | | ---- | ----------------------------------------------------- | | `0` | Success | | `1` | Error (invalid config, missing file, unknown command) | ## Related Pages - [Configuration Files](/en/configuration-files) — YAML, JSON, TypeScript, and `$ref` multi-file configs. - [Environment Variables](/en/env-vars) — `APP_SCHEMA`, `PORT`, `DATABASE_URL`, and build vars. - [App Metadata](/en/app-metadata) — the version ledger and drift detection that `reload` respects. # TypeScript API Use Sovrium as a library in your TypeScript project. Import `start`, `build`, and the `AppConfig` type for full programmatic control with typed configuration. ```typescript import { start, build, generateAppJsonSchema, validateConfig } from 'sovrium' import type { AppConfig, SimpleServer, ValidateConfigResult } from 'sovrium' // All config types are available: // PageConfig, TableConfig, ThemeConfig, AuthConfig, // ComponentConfig, LanguageConfig, AnalyticsConfig, ... ``` ## Why TypeScript? Using Sovrium programmatically gives you advantages beyond the CLI. ### Type safety The `AppConfig` type provides autocomplete for every property and field type. Catch errors at compile time, not runtime. ### Programmatic control Generate configuration dynamically, compose schemas, and integrate Sovrium into existing applications. ### IDE integration Full IntelliSense in VS Code and JetBrains — hover docs, go-to-definition, and type errors. ## `start(app, options?)` Start a development server from a typed configuration object. Returns a server instance with a `stop()` method. ```typescript import { start } from 'sovrium' const server = await start({ name: 'my-app', }) console.log(`Server running at ${server.url}`) ``` ### `StartOptions` Optional second argument to configure the server. | Property | Description | | ----------- | ------------------------------------------------------------------ | | `port` | Port number (0–65535). 0 selects an available port. Default: 3000. | | `hostname` | Server hostname. Default: `localhost`. | | `publicDir` | Static file directory. Files are served at their relative path. | ```typescript import { start } from 'sovrium' const server = await start( { name: 'my-app', tables: [ { id: 1, name: 'tasks', fields: [ { id: 1, name: 'title', type: 'single-line-text', required: true }, { id: 2, name: 'done', type: 'checkbox' }, ], }, ], }, { port: 8080, hostname: '0.0.0.0', publicDir: './public', } ) ``` ## `build(app, options?)` Generate a static site from a typed configuration object. ### `GenerateStaticOptions` Optional second argument to control static output. | Property | Description | | -------------------- | --------------------------------------------------- | | `outputDir` | Output directory. Default: `./static`. | | `baseUrl` | Base URL for sitemap and canonical links. | | `basePath` | Path prefix for subdirectory deployments. | | `deployment` | Target platform: `github-pages` or `generic`. | | `languages` | Array of language codes for generation. | | `defaultLanguage` | Default language code. | | `generateSitemap` | Generate `sitemap.xml`. Default: `false`. | | `generateRobotsTxt` | Generate `robots.txt`. Default: `false`. | | `hydration` | Enable client-side hydration. Default: `false`. | | `generateManifest` | Generate `manifest.json` for PWA. Default: `false`. | | `bundleOptimization` | Splitting strategy: `split` or `none`. | | `publicDir` | Static asset directory to copy into output. | ```typescript import { build } from 'sovrium' await build( { name: 'my-site', pages: [ { name: 'home', path: '/', sections: [ { type: 'heading', content: 'Welcome' }, { type: 'paragraph', content: 'Built with Sovrium.' }, ], }, ], }, { outputDir: './dist', baseUrl: 'https://example.com', deployment: 'github-pages', generateSitemap: true, generateRobotsTxt: true, } ) ``` ## `generateAppJsonSchema()` Generate the JSON Schema (Draft-07) for the Sovrium app configuration. Returns a plain object suitable for serialization or programmatic use. ```typescript import { generateAppJsonSchema } from 'sovrium' const schema = generateAppJsonSchema() Bun.write('app.schema.json', JSON.stringify(schema, null, 2)) ``` ## `validateConfig(config)` Validate an unknown value against the Sovrium AppSchema. Returns a result object indicating whether the config is valid, with parsed config or error details. ```typescript import { validateConfig } from 'sovrium' const result = validateConfig({ name: 'My App' }) if (result.valid) { console.log(result.config.name) } else { console.error(result.errors) } ``` ## `AppConfig` The primary type for typing your JSON or YAML configuration objects. Import it from `sovrium` to get full autocomplete and type checking. | Property | Description | | -------------- | -------------------------------------------------------------------------------------- | | `name` | Application name. Lowercase, npm-compatible (e.g. `my-app`, `@org/app`). | | `version?` | SemVer version string (e.g. `1.0.0`). | | `description?` | Single-line application description. | | `tables?` | Array of data table definitions with fields, indexes, and constraints. | | `theme?` | Design tokens: colors, fonts, spacing, animations, breakpoints, shadows, borderRadius. | | `pages?` | Array of page configurations with metadata, sections, and scripts. | | `forms?` | Standalone form definitions that capture submissions or trigger automations. | | `auth?` | Authentication config: strategies, roles, plugins (admin, organization). | | `languages?` | Multi-language config: supported languages, default language, translations. | | `components?` | Array of reusable component templates with variable substitution. | | `automations?` | Event-driven workflows: triggers plus sequential actions. | | `actions?` | Reusable action templates referenced from automations via `$ref`. | | `connections?` | External-service credentials (OAuth2, API key, basic, bearer). | | `agents?` | AI agents acting under an auth role with constrained tools. | | `buckets?` | Named file-storage containers with per-bucket limits and permissions. | | `env?` | Declared automation environment variables, referenced as `$env.NAME`. | | `analytics?` | Built-in privacy-friendly analytics. Set to `true` for defaults or pass an object. | ```typescript import type { AppConfig } from 'sovrium' const config: AppConfig = { name: 'my-app', version: '1.0.0', description: 'My application', tables: [ /* ... */ ], theme: { /* ... */ }, pages: [ /* ... */ ], auth: { /* ... */ }, languages: { /* ... */ }, components: [ /* ... */ ], analytics: true, } ``` :::callout **Incremental complexity.** Only `name` is required. Add `tables`, `theme`, `pages`, `auth`, `languages`, `components`, and `analytics` as you need them. ::: ## Type Reference Additional TypeScript types exported from the `sovrium` package. Import them with `import type { ... } from "sovrium"`. ### `SimpleServer` Returned by `start()`. A lightweight interface to the running server. | Property | Description | | -------- | -------------------------------------------------------------------------------------- | | `url` | Server URL including protocol and port (e.g. `"http://localhost:3000"`). | | `stop()` | Gracefully stop the server. Returns a Promise that resolves when shutdown is complete. | ```typescript import { start } from 'sovrium' import type { SimpleServer } from 'sovrium' const server: SimpleServer = await start({ name: 'my-app' }) console.log(server.url) // "http://localhost:3000" await server.stop() // graceful shutdown ``` ### `PageConfig` Configuration for a single page. Element of `AppConfig['pages']`. ```typescript const page: PageConfig = { name: 'home', path: '/', sections: [{ type: 'heading', content: 'Welcome' }], } ``` ### `TableConfig` Configuration for a single data table. Element of `AppConfig['tables']`. ```typescript const table: TableConfig = { id: 1, name: 'tasks', fields: [{ id: 1, name: 'title', type: 'single-line-text' }], } ``` ### `ComponentConfig` Reusable component template with variable placeholders. Element of `AppConfig['components']`. ```typescript const hero: ComponentConfig = { name: 'hero', type: 'section', children: [{ type: 'heading', content: '$title' }], } ``` ### `ThemeConfig` Design tokens for colors, typography, spacing, shadows, and more. ```typescript const theme: ThemeConfig = { colors: { primary: '#3b82f6' }, fonts: { sans: 'Inter, sans-serif' }, } ``` ### `AuthConfig` Authentication strategies, roles, and plugin configuration. ```typescript const auth: AuthConfig = { strategies: [{ type: 'emailAndPassword' }], roles: [{ name: 'admin' }, { name: 'member' }], } ``` ### `LanguageConfig` Multi-language support with translations and language detection. ```typescript const languages: LanguageConfig = { supported: ['en', 'fr'], default: 'en', } ``` ### `AnalyticsConfig` Built-in privacy-friendly analytics. Use `true` for defaults or an object for custom settings. ```typescript // Simple: just enable defaults const analytics: AnalyticsConfig = true // Custom settings const custom: AnalyticsConfig = { retentionDays: 90, excludedPaths: ['/admin'] } ``` ### `GenerateStaticResult` Returned by `build()`. Contains the output directory path and the list of generated files. | Property | Description | | ----------- | ------------------------------------------------------------------- | | `outputDir` | Absolute path to the output directory (e.g. `"./static"`). | | `files` | Array of file paths generated during the build (HTML, CSS, assets). | ```typescript import { build } from 'sovrium' import type { GenerateStaticResult } from 'sovrium' const result: GenerateStaticResult = await build({ name: 'my-site', pages: [ /* ... */ ], }) console.log(result.outputDir) // "./static" console.log(result.files.length) // number of generated files ``` ### `ValidateConfigResult` Returned by `validateConfig()`. Indicates whether the config is valid and provides parsed config or error details. | Property | Description | | -------- | -------------------------------------------------------------------- | | `valid` | Boolean. `true` if the configuration is valid against the AppSchema. | | `errors` | Array of validation error objects. Empty when `valid` is `true`. | | `config` | The parsed `AppConfig` object. Only present when `valid` is `true`. | ```typescript import { validateConfig } from 'sovrium' import type { ValidateConfigResult } from 'sovrium' const result: ValidateConfigResult = validateConfig({ name: 'my-app' }) console.log(result.valid) // true or false console.log(result.errors) // validation error array (empty if valid) console.log(result.config) // parsed AppConfig (if valid) ``` ## Watch Mode In development, use Bun's built-in watch mode to automatically restart your script when files change. ```bash bun --watch index.ts ``` :::callout **Reload vs watch.** `bun --watch` restarts the entire process when any imported file changes. For config-only changes, the CLI's `--watch` flag is more efficient. ::: ## Examples Common usage patterns with Sovrium in TypeScript. ### Minimal server ```typescript import { start } from 'sovrium' const server = await start({ name: 'my-app', }) console.log(`Server running at ${server.url}`) ``` ### With data tables ```typescript import { start } from 'sovrium' const server = await start( { name: 'my-app', tables: [ { id: 1, name: 'tasks', fields: [ { id: 1, name: 'title', type: 'single-line-text', required: true }, { id: 2, name: 'done', type: 'checkbox' }, ], }, ], }, { port: 8080, hostname: '0.0.0.0', publicDir: './public', } ) ``` ### Static site generation ```typescript import { build } from 'sovrium' await build( { name: 'my-site', pages: [ { name: 'home', path: '/', sections: [ { type: 'heading', content: 'Welcome' }, { type: 'paragraph', content: 'Built with Sovrium.' }, ], }, ], }, { outputDir: './dist', baseUrl: 'https://example.com', deployment: 'github-pages', generateSitemap: true, generateRobotsTxt: true, } ) ``` ### Dynamic configuration ```typescript import { start } from 'sovrium' import type { AppConfig, PageConfig, TableConfig, ThemeConfig } from 'sovrium' // Compose config from typed sub-configs const theme: ThemeConfig = { colors: { primary: process.env.BRAND_COLOR ?? '#3b82f6' }, } const tables: TableConfig[] = ['users', 'posts'].map((name, i) => ({ id: i + 1, name, fields: [ { id: 1, name: 'title', type: 'single-line-text' as const, required: true }, { id: 2, name: 'created_at', type: 'date' as const, includeTime: true }, ], })) const pages: PageConfig[] = [{ name: 'home', path: '/', sections: [] }] const app: AppConfig = { name: 'dynamic-app', tables, pages, theme, } await start(app) ``` # Environment Variables Sovrium reads configuration from environment variables at startup. Set them in a `.env` file or your server environment. All variables are optional unless noted otherwise. ```bash # .env # Application APP_SCHEMA='app.yaml' # Server PORT=3000 BASE_URL=http://localhost:3000 # Database (optional — defaults to embedded SQLite) DATABASE_URL=file:./data/app.db # Authentication AUTH_SECRET=your-secret-key-here # Default Admin AUTH_ADMIN_EMAIL=admin@example.com AUTH_ADMIN_PASSWORD=secure-admin-password # OAuth (Google example) GOOGLE_CLIENT_ID=your-client-id GOOGLE_CLIENT_SECRET=your-client-secret # Email (SMTP) SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password ``` ## Application Core application configuration. `APP_SCHEMA` is the primary way to provide a schema without a file argument. | Variable | Description | | ------------ | ---------------------------------------------------------------------------------------------------------------- | | `APP_SCHEMA` | App schema as inline JSON, inline YAML, or a remote URL. Alternative to passing a file path on the command line. | ## Server Server networking options for the `start` command. | Variable | Description | | ---------- | ------------------------------------------------------------------------------------------------------------------------------- | | `PORT` | Server port number (0–65535). 0 selects an available port. Default: 3000. | | `HOSTNAME` | Server hostname. Default: `localhost`. | | `BASE_URL` | Base URL of the application (e.g., `https://myapp.com`). Used for authentication callback URLs and email links. | | `NODE_ENV` | Runtime environment. Set to `"production"` for secure cookies, CSRF checks, and strict SMTP validation. Default: `development`. | ## Database Database connection settings. Optional — Sovrium defaults to embedded SQLite when unset. | Variable | Description | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DATABASE_URL` | Database connection string. Optional: unset uses embedded SQLite; a `file:` URL (e.g., `file:./data/app.db`) picks the SQLite file; a `postgresql://` URL (e.g., `postgresql://user:password@localhost:5432/dbname`) switches to PostgreSQL. | ## Authentication Variables for Better Auth integration. Required when the schema defines an `auth` section. | Variable | Description | | ------------- | ------------------------------------------------------------------------------------------------------------------- | | `AUTH_SECRET` | Secret key for signing tokens and encrypting sessions. Must be a strong random string (32+ characters recommended). | ### Default Admin User Optional variables to create a default admin user on first startup. All three must be set for the admin user to be created. | Variable | Description | | --------------------- | ------------------------------------------------------------------------- | | `AUTH_ADMIN_EMAIL` | Email address for the default admin user. | | `AUTH_ADMIN_PASSWORD` | Password for the default admin user. Must be at least 8 characters. | | `AUTH_ADMIN_NAME` | Display name for the default admin user. Optional, defaults to `"Admin"`. | ### OAuth Providers Credentials for each OAuth provider configured in the auth schema. Replace `{PROVIDER}` with the uppercase provider name. | Variable | Description | | -------------------------- | ---------------------------------------------------------------------------------- | | `{PROVIDER}_CLIENT_ID` | OAuth client ID from the provider's developer console. | | `{PROVIDER}_CLIENT_SECRET` | OAuth client secret from the provider's developer console. Keep this confidential. | :::callout **Supported providers.** Sovrium supports Google, GitHub, Microsoft, Slack, GitLab, and Facebook. For example, set `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` for Google OAuth. ::: ## Email (SMTP) SMTP configuration for sending authentication emails (verification, password reset, magic link). In development, falls back to Mailpit on `localhost:1025` if `SMTP_HOST` is not set. | Variable | Description | | ---------------- | ---------------------------------------------------------------------------------------------- | | `SMTP_HOST` | SMTP server hostname (e.g., `smtp.gmail.com`). Required in production. | | `SMTP_PORT` | SMTP server port. Default: 587. | | `SMTP_SECURE` | Use SSL/TLS connection. Default: `false` for port 587, `true` for port 465. | | `SMTP_USER` | SMTP authentication username. | | `SMTP_PASS` | SMTP authentication password or app-specific password. | | `SMTP_FROM` | Default sender email address (e.g., `noreply@yourdomain.com`). Default: `noreply@sovrium.com`. | | `SMTP_FROM_NAME` | Default sender display name. Default: `Sovrium`. | :::callout **Local development.** When `SMTP_HOST` is not configured in development mode, Sovrium automatically falls back to Mailpit (`localhost:1025`). Install Mailpit to view sent emails in a local web UI. ::: ## Build Options for the `build` command that generates a static site. | Variable | Description | | ----------------------------- | ---------------------------------------------------------------------------------- | | `SOVRIUM_OUTPUT_DIR` | Output directory. Default: `./dist`. | | `SOVRIUM_BASE_URL` | Base URL for sitemap generation and canonical links (e.g., `https://example.com`). | | `SOVRIUM_BASE_PATH` | Path prefix for subdirectory deployments (e.g., `/my-app`). | | `SOVRIUM_DEPLOYMENT` | Deployment target: `github-pages` or `generic`. | | `SOVRIUM_LANGUAGES` | Comma-separated language codes to build (e.g., `en,fr,de`). | | `SOVRIUM_DEFAULT_LANGUAGE` | Default language code (e.g., `en`). | | `SOVRIUM_GENERATE_SITEMAP` | Generate `sitemap.xml`. Default: `false`. | | `SOVRIUM_GENERATE_ROBOTS` | Generate `robots.txt`. Default: `false`. | | `SOVRIUM_HYDRATION` | Enable client-side hydration. Default: `false`. | | `SOVRIUM_GENERATE_MANIFEST` | Generate `manifest.json` for PWA. Default: `false`. | | `SOVRIUM_BUNDLE_OPTIMIZATION` | Optimization strategy: `split` (code splitting) or `none`. | | `SOVRIUM_PUBLIC_DIR` | Directory containing static assets to copy into the output. | ## Debug Diagnostic and testing variables. Not needed in normal operation. | Variable | Description | | --------------------------- | -------------------------------------------------------------------------------------------------------------- | | `LOG_LEVEL` | Set to `"debug"` for verbose logging (request logs, CSS compilation diagnostics). Default: follows `NODE_ENV`. | | `EFFECT_DEVTOOLS` | Set to `"1"` to enable Effect DevTools integration for runtime inspection. | | `RATE_LIMIT_WINDOW_SECONDS` | Rate limit window in seconds. Used in tests for faster execution. Default: 60. | # Configuration Files Sovrium loads your application from a configuration file. The same configuration object can be written as YAML, JSON, or TypeScript, split across many files with `$ref`, and checked ahead of time with `sovrium validate`. ## YAML and JSON The simplest configuration is a YAML or JSON file. YAML is recommended for hand-authoring — it supports comments, requires less punctuation, and is easier to read. JSON is convenient when generating configs programmatically. ```yaml # app.yaml name: my-app version: 1.0.0 description: A simple todo list tables: - id: 1 name: tasks fields: - { id: 1, name: title, type: single-line-text, required: true } - { id: 2, name: done, type: checkbox, default: false } ``` ```json { "name": "my-app", "version": "1.0.0", "description": "A simple todo list", "tables": [ { "id": 1, "name": "tasks", "fields": [ { "id": 1, "name": "title", "type": "single-line-text", "required": true }, { "id": 2, "name": "done", "type": "checkbox", "default": false } ] } ] } ``` Run either with the CLI: ```bash sovrium start app.yaml sovrium start app.json ``` :::callout **Format detection is by extension.** Sovrium routes `.yaml`/`.yml` files through its YAML parser and `.json` files through its JSON parser. The shape of the resulting object is identical — every example in this documentation can be transcribed between the two formats. ::: ## TypeScript with `defineConfig` For full type safety and IDE autocompletion, author your config in TypeScript and validate it against the `@sovrium/types` package. `@sovrium/types` is a **zero-dependency** package of TypeScript types extracted directly from the Sovrium schema. ```bash bun add -d @sovrium/types ``` ```typescript // app.ts import { defineConfig } from '@sovrium/types' export default defineConfig({ name: 'my-app', version: '1.0.0', description: 'A simple todo list', tables: [ { id: 1, name: 'tasks', fields: [ { id: 1, name: 'title', type: 'single-line-text', required: true }, { id: 2, name: 'done', type: 'checkbox', default: false }, ], }, ], }) ``` ```bash sovrium start app.ts sovrium validate app.ts ``` `defineConfig` is an identity helper: it returns its argument unchanged but annotates it with the `AppConfig` type, so your editor reports invalid field types, unknown component types, and missing required properties as you type — no separate type import or `satisfies` needed. :::callout **`@sovrium/types` vs the `sovrium` package.** `@sovrium/types` ships only types (MIT-licensed, zero runtime). Use it to author config files. To run Sovrium programmatically (`import { start } from 'sovrium'`), see the [TypeScript API](/en/typescript). ::: ## Multi-file configs with `$ref` As an app grows, a single file becomes unwieldy. Sovrium supports `$ref` to split configuration across multiple files. Any object containing exactly one key, `$ref`, whose value is a relative path, is replaced with the parsed contents of that file before validation. ```yaml # app.yaml name: crm-workspace version: 2.0.0 auth: $ref: ./config/auth.yaml theme: $ref: ./config/theme.yaml tables: - $ref: ./config/tables/companies.yaml - $ref: ./config/tables/contacts.yaml - $ref: ./config/tables/deals.yaml pages: - $ref: ./config/pages/sign-in.yaml - $ref: ./config/pages/companies.yaml agents: - $ref: ./config/agents/records-assistant.yaml ``` ```yaml # config/tables/companies.yaml id: 1 name: Companies fields: - { id: 1, name: name, type: single-line-text, required: true } - { id: 2, name: website, type: url } ``` | Rule | Behavior | | ----------------- | -------------------------------------------------------------------------------------------------------------- | | Path resolution | `$ref` paths resolve relative to the file that contains them, not the working directory. | | Mixed formats | A YAML root file may `$ref` a JSON partial and vice versa — each file is parsed by its extension. | | Array elements | A `$ref` can stand in for a whole array element (`- $ref: ...`) or a whole object value. | | Resolution timing | All `$ref`s are resolved into one object _before_ schema validation, so cross-section checks see the full app. | :::callout **TypeScript configs compose differently.** `$ref` is a YAML/JSON mechanism. In TypeScript you compose with ordinary `import`s — define sub-configs in separate modules and assemble them inside `defineConfig({ ... })`. ::: ## Loading without a file Configuration can also come from the `APP_SCHEMA` environment variable instead of a file path — inline JSON, inline YAML, or a remote URL. See the [CLI Reference](/en/cli#configuration-sources) for details. ## Validating a config `sovrium validate` checks a config file against the full app schema — including cross-section rules such as "a record automation must reference an existing table" — and returns a non-zero exit code with error details when something is wrong. ```bash sovrium validate app.yaml sovrium validate app.ts sovrium validate config.json ``` Run it in CI to catch configuration errors before deployment. Because validation runs the same schema the server uses at boot, a config that passes `validate` is guaranteed to start. ## Next steps - [Core Concepts](/en/concepts) — the anatomy of the configuration object. - [CLI Reference](/en/cli) — every command, flag, and configuration source. - [TypeScript API](/en/typescript) — running Sovrium as a library. - [Schema Overview](/en/overview) — the full root-property reference. # Templates & Examples The fastest way to learn Sovrium is to start from a working app. Sovrium ships a set of example configurations — each a complete, runnable project that composes real features (tables, auth, pages, theme, i18n, automations) into a single config tree. The same examples double as `sovrium init` templates, so you can scaffold any of them into a new directory and start iterating immediately. Every example is a **directory** with an `app.yaml` entry point. Anything beyond the smallest starter splits its configuration across a `config/` subtree using [`$ref`](/en/configuration-files#multi-file-configs-with-ref) — one file per collection entity, one file per singleton, scalars stay inline. This mirrors the structure `sovrium init` scaffolds for you. ## Available templates | Template | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **hello-world** | Minimal starter — one page, no collections. The default for `sovrium init`. Stays a single `app.yaml` to demonstrate when _not_ to pre-split. | | **landing-page** | Bilingual marketing site with i18n, a theme, reusable components, and the home page split out for size. | | **crud-app** | CRUD app with tables (contacts, companies), email/password auth, a theme, and dashboard + sign-in pages. | | **api-only** | Headless API mode with tables (projects, tasks) and auth — no pages. Demonstrates Sovrium as a backend. | | **member-portal** | Public marketing pages plus an auth-gated portal area with role-gated sections. Magic-link + password auth. | | **mcp-server** | Headless MCP server exposing tables to an LLM client via per-entity `aiAccess` — no pages. See [MCP Integration](/en/mcp-integration). | | **blog** | Blog with posts (rich-text), tags, authors, and an index plus a dynamic `/blog/:slug` detail route. | | **docs-site** | Documentation website showcasing the markdown-pages feature: real `.md` files under `content/docs/`, a `contentDir` collection, a frontmatter sidebar, prev/next chrome, a TOC, and Shiki-highlighted code. No tables, no auth. | Several templates ship with a paired editor agent (a Claude Code subagent definition) that `sovrium init` installs into `.claude/agents/`. See [`sovrium agents`](/en/cli#sovrium-agents-list) for managing agent templates directly. ## Scaffolding a project `sovrium init` copies a template into a new (or current) directory. With no `--template`, the minimal `hello-world` starter is used. ```bash # Default (hello-world, no paired agent) sovrium init my-app # Choose a template — also installs the paired editor agent sovrium init my-app --template crud-app sovrium init my-app --template landing-page sovrium init my-app --template blog sovrium init my-app --template member-portal # Skip the paired agent install sovrium init my-app --template crud-app --no-agent ``` Then run the scaffolded app: ```bash sovrium start app.yaml # Start the dev server sovrium validate app.yaml # Validate without starting ``` See the [CLI Reference](/en/cli#sovrium-init) for all `init` flags. ## Anatomy of an example A non-trivial example looks like this (abbreviated `crud-app` layout): ``` crud-app/ ├── app.yaml # Entry point — references everything via $ref ├── config/ │ ├── auth.yaml # Singleton: auth strategies, roles │ ├── theme.yaml # Singleton: design tokens │ └── tables/ │ ├── companies.yaml # One file per table │ └── contacts.yaml └── public/ # Static assets (favicon, images) ``` The `app.yaml` entry point pulls each part together with `$ref`, keeping the top-level file small and each entity in its own file: ```yaml name: crud-app auth: $ref: ./config/auth.yaml theme: $ref: ./config/theme.yaml tables: - $ref: ./config/tables/companies.yaml - $ref: ./config/tables/contacts.yaml pages: - $ref: ./config/pages/dashboard.yaml - $ref: ./config/pages/sign-in.yaml ``` `$ref` resolution happens **before** validation: any object containing exactly one key — `$ref` whose value is a relative path — is replaced with the parsed contents of that file. A `$ref` may itself contain further `$ref`s, so configs nest to any depth. The single-file and multi-file forms are interchangeable; pick whichever keeps the project readable. :::callout **When to split.** Keep a config in one file while it is small (like `hello-world`). Split with `$ref` once a section grows large enough to deserve its own file — typically once you have more than one table, page, or a substantial theme. The full mechanics live in [Configuration Files → Multi-file configs](/en/configuration-files#multi-file-configs-with-ref). ::: ## Learning path A practical order for working through the examples: 1. **hello-world** — understand the minimal shape of a config and the `pages` array. 2. **landing-page** — add a theme, reusable components, and i18n with `$t:` translation keys. 3. **crud-app** — introduce tables, auth, and data-bound pages (forms + data tables). 4. **api-only** / **mcp-server** — run Sovrium headless: as a REST backend or an LLM-facing MCP server. 5. **blog** / **member-portal** / **docs-site** — combine dynamic routes, role-gated areas, and markdown content. ## Related Pages - [Quick Start](/en/quick-start) — zero to running app via YAML + CLI or TypeScript + Bun. - [Configuration Files](/en/configuration-files) — YAML, JSON, TypeScript, and `$ref` multi-file composition. - [CLI Reference](/en/cli) — `sovrium init`, `sovrium agents`, and the full command surface. - [MCP Integration](/en/mcp-integration) — the headless MCP-server template explained. # Schema Overview The complete reference for the Sovrium app schema. A declarative configuration object with 18 root properties — only `name` is required. Everything else is optional, enabling progressive complexity from a minimal app identifier to a full-stack application. ## Schema Structure ```yaml name: my-app # App identifier (required) version: 1.0.0 # SemVer version description: My application # One-line description tables: [...] # Data models with 49 field types pages: [...] # Server-rendered pages (~80 component types) forms: [...] # Standalone forms capturing submissions auth: { ... } # Authentication, roles, RBAC, 2FA theme: { ... } # Design tokens (colors, fonts, etc.) languages: { ... } # Multi-language support ($t: syntax) automations: [...] # Event-driven workflows (triggers + actions) actions: [...] # Reusable action templates ($ref) connections: [...] # External-service credentials agents: [...] # AI agents acting under an auth role buckets: [...] # Named file-storage containers components: [...] # Reusable UI templates ($ref, $variable) analytics: { ... } # Cookie-free, first-party analytics env: { ... } # Declared automation env vars ($env.NAME) scripts: { ... } # Global page scripts ``` :::callout **Progressive complexity.** Only `name` is required. Add `tables`, `theme`, `pages`, `auth`, and other sections as your app grows. ::: ## Root Properties The app schema has 18 root properties. Only `name` is required. | Property | Type | Description | | ------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------ | | `name` | string (required) | App identifier following npm naming conventions. Lowercase, max 214 chars, supports scoped format (`@scope/name`). | | `version` | string | Semantic Versioning 2.0.0 string (e.g. `1.0.0`, `2.0.0-beta.1`). Feeds the immutable version ledger. | | `description` | string | Single-line app description (max 2000 chars). No line breaks. Unicode and emojis supported. | | `tables` | array | Data models with 49 field types, relationships, indexes, permissions, and views. | | `theme` | object | Design tokens: colors, fonts, spacing, shadows, animations, breakpoints, and border radius. | | `languages` | object | Multi-language support with `$t:` translation syntax, browser detection, and URL routing. | | `auth` | object | Authentication strategies (email/password, magic link, OAuth), roles, RBAC, sessions, and two-factor. | | `analytics` | object \| boolean | Privacy-friendly, cookie-free, first-party analytics. Set to `true` for defaults or configure with options. | | `components` | array | Reusable UI templates with `$ref` referencing and `$variable` substitution. | | `pages` | array | Server-rendered pages with ~80 component types, SEO metadata, and i18n support. | | `forms` | array | Standalone forms that capture submissions into a table or trigger an automation. | | `connections` | array | Reusable credentials for external services: OAuth2, API key, basic auth, and bearer token. | | `env` | object | Declared environment variables resolved at runtime, referenced in actions as `$env.NAME` (never logged). | | `actions` | array | Reusable action templates referenced from automations via `$ref` with `$vars`. | | `automations` | array | Event-driven workflows: a trigger fires, a sequence of actions runs. | | `agents` | array | AI agents acting on behalf of an auth role within a constrained tool set, with approval gates and rate limits. | | `buckets` | array | Named storage containers with per-bucket file limits and access permissions. | | `scripts` | object | Global scripts loaded on every page before page-level scripts. | ## Property Details Detailed rules and constraints for the three scalar root properties — `name`, `version`, and `description` — are covered on the [App Metadata](/en/app-metadata) page, which also documents the version ledger, drift detection, and draft staleness. ### `name` The app name follows npm naming conventions. Lowercase, URL-safe, and unique within your deployment. | Constraint | Description | | ---------- | ------------------------------------------------------------------------------------------------------------ | | Pattern | `^(?:@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$`. Lowercase letters, digits, hyphens, dots, tildes. | | Max length | 214 characters maximum (including `@scope/` prefix if scoped). | | Scoped | Supports npm-style scoped packages: `@scope/name` (e.g. `@acme/dashboard`). | ```yaml # Valid names name: my-app name: task-tracker-v2 name: '@acme/dashboard' ``` ### `version` Follows Semantic Versioning 2.0.0 ([semver.org](https://semver.org)). Format: `MAJOR.MINOR.PATCH` with optional pre-release and build metadata. ```yaml version: 1.0.0 # Stable release version: 2.0.0-beta.1 # Pre-release version: 1.0.0+build.42 # Build metadata ``` ### `description` A single-line text describing the application. Displayed in the admin UI and metadata. | Constraint | Description | | ---------- | ------------------------------------------------------------- | | Format | Single line only. No line breaks (`\n`) allowed. | | Max length | 2000 characters maximum. | | Unicode | Full Unicode support including emojis and special characters. | ## Configuration Formats Sovrium accepts YAML, JSON, and TypeScript configuration. YAML is recommended for readability; JSON suits programmatic generation; TypeScript adds type safety via `@sovrium/types`. Large configs can be split across files with `$ref`. See [Configuration Files](/en/configuration-files) for the full guide. ```yaml # YAML format (recommended) name: my-app version: 1.0.0 tables: - id: 1 name: tasks fields: - { id: 1, name: title, type: single-line-text } ``` ```json { "name": "my-app", "version": "1.0.0", "tables": [ { "id": 1, "name": "tasks", "fields": [{ "id": 1, "name": "title", "type": "single-line-text" }] } ] } ``` ## Cross-section validation Root sections are validated _together_, not in isolation. Sovrium rejects a config before the server starts when, for example: - a `user`/`created-by`/`updated-by` field exists but `auth` is not configured; - a table or bucket permission names a role that is not declared; - an attachment field references a `bucket` not present in `buckets`; - a record automation trigger or action references a table that does not exist; - an automation `$ref` action, AI agent reference, or form trigger names something undeclared. Run [`sovrium validate`](/en/cli) to check these rules ahead of deployment. ## Related Pages - [App Metadata](/en/app-metadata) — `name`, `version`, `description`, version ledger, drift detection. - [Tables Overview](/en/tables-overview) — data models and 49 field types. - [Pages Overview](/en/pages-overview) — ~80 component types for server-rendered pages. - [Forms Overview](/en/forms-overview) — standalone forms and submissions. - [Auth Overview](/en/auth-overview) — strategies, roles, and RBAC. - [Automations Overview](/en/automations-overview) — triggers, actions, and connections. - [AI Overview](/en/ai-overview) — agents and AI-native fields. - [Buckets Overview](/en/buckets-overview) — file-storage containers. - [Records Overview](/en/records-overview) — querying and mutating rows at runtime. - [Theme](/en/theme) · [Responsive Design](/en/responsive-design) · [Animations](/en/animations) · [Languages](/en/languages). # App Metadata Three scalar root properties identify your application: `name`, `version`, and `description`. Only `name` is required. Together they anchor Sovrium's immutable version ledger and its schema-drift detection. ```yaml name: '@acme/crm' version: 2.1.0 description: 'A CRM workspace for managing contacts, deals, and tasks.' ``` ## `name` The app name follows npm package naming conventions. It is lowercase, URL-safe, and the only required property in the entire schema. | Constraint | Description | | ---------- | ------------------------------------------------------------------------------------------------------------- | | Pattern | `^(?:@[a-z0-9-~][a-z0-9-._~]*/)?[a-z0-9-~][a-z0-9-._~]*$` — lowercase letters, digits, hyphens, dots, tildes. | | Length | 1–214 characters (including the `@scope/` prefix if scoped). | | Leading | Cannot start with a dot or underscore; no leading/trailing spaces. | | Scoped | npm-style scoped names are allowed: `@scope/name` (e.g. `@acme/dashboard`). | ```yaml # Valid names name: my-app name: task-tracker-v2 name: '@acme/dashboard' ``` ## `version` A Semantic Versioning 2.0.0 string ([semver.org](https://semver.org)). Optional, but recommended — when present it feeds the version ledger described below. | Rule | Description | | -------------- | ----------------------------------------------------------------------------- | | Format | `MAJOR.MINOR.PATCH` (e.g. `1.0.0`). Each component is a non-negative integer. | | No leading 0 | `01.0.0` is rejected — version components must not have leading zeros. | | Pre-release | Optional hyphen suffix: `1.0.0-alpha`, `1.0.0-beta.1`, `2.0.0-rc.1`. | | Build metadata | Optional plus suffix: `1.0.0+build.123`, `1.0.0-alpha+001`. | ```yaml version: 1.0.0 # Stable release version: 2.0.0-beta.1 # Pre-release version: 1.0.0+build.42 # Build metadata ``` ## `description` A single-line description shown in the admin UI and metadata. | Constraint | Description | | ---------- | --------------------------------------------------------- | | Format | Single line only — line breaks (`\n`, `\r`) are rejected. | | Max length | 2000 characters. | | Unicode | Full Unicode support including emojis. | ```yaml description: 'Full-featured e-commerce platform with cart, checkout & payment processing' ``` ## Version history Every Sovrium app keeps an **immutable version ledger** — an append-only audit log of schema snapshots. Each successful publish appends one row containing the encoded schema snapshot, a SHA-256 checksum of that snapshot, and the `source` that produced it. Restoring an older version copies its snapshot into a _new_ row; history is never mutated. ### The four transports A schema change can arrive through one of four transports. Each transition appends exactly one ledger row, and the `source` column records which transport produced it. | Transport | `source` value | How a change arrives | | --------------- | -------------- | ---------------------------------------------------------------------------------- | | **Config file** | `config-file` | `sovrium start app.yaml` at boot, or `sovrium reload` for a running server. | | **Env var** | `env` | The `APP_SCHEMA` environment variable (inline JSON/YAML or a remote URL snapshot). | | **Admin API** | `api` | The admin HTTP API (`POST /api/admin/schema/draft/publish`). | | **MCP** | `mcp` | An MCP schema-edit tool (`{app}_schema_draft_publish`). | A fifth `source`, `restore`, marks rows created by restoring an earlier version. Checksums are self-contained (each row hashes its own snapshot, not a parent), which is what makes pruning interior rows safe. ## Schema drift detection Because the live schema can be edited at runtime (via the admin API or MCP) while a `config-file` still sits on disk, the live app can **drift** away from its source file. Sovrium computes a drift posture from the ledger and exposes it so tooling can warn before overwriting work. | Drift status | Meaning | | ------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `in-sync` | The active version came from a `config-file`/`env` transport — the running app matches its source. | | `drifted-from-file` | The active version came from `api`/`mcp`/`restore` — the live app was edited at runtime and no longer matches the file. | | `file-ahead` | The on-disk file's normalized checksum changed — the source file is now ahead of the live app. | :::callout **`sovrium reload` respects drift.** Reloading a `config-file`-launched server first probes the live drift status. If the live app has `drifted-from-file`, the reload is refused unless you pass `--force` — preventing an accidental overwrite of runtime edits with stale file contents. ::: ## Draft staleness Runtime editing happens against a **mutable draft** — a single working copy branched from the active version. The draft records the `baseVersion` it branched from, which doubles as both an optimistic-concurrency token on publish and a **staleness anchor**. A draft becomes **stale** whenever `draft.baseVersion` no longer equals the active version number. This occurs when another admin publishes, or when a `config-file`/`env` reload moves the base underneath an open draft. | Operation on a stale draft | Allowed? | | -------------------------- | --------------------------------------- | | Preview / validate / edit | Yes | | Publish | No — blocked until the draft is rebased | To clear staleness, **rebase** the draft: re-point its `baseVersion` to the active version, leaving the draft contents untouched. There is no field-level merge — the next publish is a whole-snapshot, last-writer-wins operation, consistent with Sovrium's snapshot-atomic schema system. ## Next steps - [Schema Overview](/en/overview) — all root properties at a glance. - [Configuration Files](/en/configuration-files) — how `config-file` and `env` transports load your app. - [CLI Reference](/en/cli) — `sovrium reload` and drift handling. # Theme The `theme` property defines your design system through optional token categories. All tokens generate CSS custom properties and Tailwind CSS utility classes. Every category is optional — a minimal theme can be colors-only. ## Theme Properties | Property | Description | | -------------- | ------------------------------------------------------------------------------------------------------------ | | `baseline` | `'extend'` (default) keeps Sovrium's default design-system look; `'replace'` swaps it for a neutral floor. | | `colors` | Named color tokens. Each generates `--color-{name}` and `bg-{name}`/`text-{name}`/`border-{name}` utilities. | | `darkColors` | Dark-mode color overrides, mirroring the `colors` structure. | | `fonts` | Typography for heading, body, and mono fonts. | | `spacing` | Named spacing tokens as Tailwind class strings. | | `animations` | Keyframe and motion design tokens. See [Animations](/en/animations). | | `breakpoints` | Responsive breakpoints as pixel values. See [Responsive Design](/en/responsive-design). | | `shadows` | Named box-shadow tokens, each becoming a `shadow-{name}` utility. | | `borderRadius` | Named radius tokens, each becoming a `rounded-{name}` utility. | | `codeBlock` | Syntax-highlighting theme for markdown fenced code blocks. | ## `colors` Named color tokens as key-value pairs. Each becomes a CSS variable (`--color-{name}`) and Tailwind class (`bg-{name}`, `text-{name}`). | Property | Description | | ---------- | -------------------------------------------------------------------------------------------------------------- | | `{name}` | Token name (e.g., `primary`, `accent`). Used to generate CSS variable `--color-{name}` and Tailwind utilities. | | CSS output | Generates `--color-{name}` CSS variable plus `bg-{name}`, `text-{name}`, `border-{name}` utility classes. | ```yaml theme: colors: primary: '#3b82f6' secondary: '#8b5cf6' accent: '#f59e0b' background: '#0a0e1a' text: '#e8ecf4' muted: '#64748b' ``` ## `fonts` Typography configuration for heading, body, and mono fonts. Supports family, fallback, weights, style, size, line height, letter spacing, transform, and url. | Property | Description | | --------------- | ------------------------------------------------------------------------------------------------------ | | `family` | Font family name (e.g., `"Inter"`, `"Roboto"`). Maps to CSS `font-family`. | | `fallback` | Fallback font stack used when primary font is unavailable (e.g., `"system-ui, sans-serif"`). | | `weights` | Array of numeric font weights to load (e.g., `[400, 600, 700]`). Optimizes download size. | | `style` | Font style: `normal`, `italic`, or `oblique` (default: `normal`). | | `size` | Base font size as CSS value (e.g., `"16px"`, `"1rem"`). Applied to body text. | | `lineHeight` | Line height multiplier or CSS value (e.g., `"1.5"`, `"1.2"`). Controls vertical spacing between lines. | | `letterSpacing` | Letter spacing as CSS value (e.g., `"0.05em"`, `"-0.01em"`). | | `transform` | Text transformation: `none`, `uppercase`, `lowercase`, or `capitalize`. | | `url` | Font file URL or Google Fonts URL. Injected as `` in ``. | ```yaml theme: fonts: heading: family: Inter weights: [600, 700] lineHeight: '1.2' body: family: Inter size: '16px' url: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700' ``` :::callout **Google Fonts.** Add a `url` to automatically load custom fonts. The URL is injected as a `` tag in the page head. ::: ## `spacing` Named spacing tokens as Tailwind class strings. Define container widths, section padding, gaps, and component spacing. ```yaml theme: spacing: container: 'max-w-7xl mx-auto px-4' section: 'py-16 sm:py-20' card: 'p-6' gap: 'gap-8' ``` ## `baseline` & dark mode `baseline` controls whether Sovrium's prebuilt components extend the default look or start from a neutral floor. | Value | Behavior | | ----------- | -------------------------------------------------------------------------- | | `'extend'` | _(default)_ Components inherit Sovrium's v1 default design-system tokens. | | `'replace'` | Components drop the default look and start from a neutral, unstyled floor. | `darkColors` mirrors the `colors` structure and supplies overrides applied in dark mode, so a single theme can ship both palettes. ```yaml theme: baseline: extend colors: background: '#ffffff' text: '#0f172a' darkColors: background: '#0f172a' text: '#f8fafc' ``` ## Shadows, Border Radius & Code Blocks Additional design tokens for elevation, corner styling, and syntax highlighting. ### `shadows` Named shadow tokens as CSS box-shadow values. Each becomes a `shadow-{name}` utility. ### `borderRadius` Named border radius tokens as CSS values. Each becomes a `rounded-{name}` utility class. ### `codeBlock` Selects the syntax-highlighting theme used for markdown fenced code blocks. ```yaml theme: shadows: card: '0 4px 6px rgba(0, 0, 0, 0.1)' elevated: '0 10px 30px rgba(0, 0, 0, 0.2)' borderRadius: card: '0.75rem' button: '0.5rem' ``` ## Responsive Breakpoints & Animations `breakpoints` and `animations` each have a dedicated reference page: - [Responsive Design](/en/responsive-design) — breakpoint pixel values and per-breakpoint overrides. - [Animations](/en/animations) — keyframe and motion tokens. ```yaml theme: breakpoints: sm: '640px' md: '768px' lg: '1024px' ``` :::callout **Breakpoint values are pixel strings.** Each `breakpoints` value must match the `px` format (e.g. `'768px'`) — bare numbers and non-pixel units are rejected by the schema. ::: ## Full Example A complete theme configuration combining colors, fonts, spacing, and shadows. ```yaml theme: colors: primary: '#6366f1' secondary: '#8b5cf6' accent: '#f59e0b' background: '#0f172a' surface: '#1e293b' text: '#f8fafc' muted: '#94a3b8' fonts: heading: family: Inter weights: [600, 700] url: 'https://fonts.googleapis.com/css2?family=Inter:wght@600;700&display=swap' body: family: Inter weights: [400, 500] spacing: container: 'max-w-6xl mx-auto px-4' section: 'py-16 sm:py-20' ``` ![A CRM application rendered with custom theme colors, fonts, and spacing.](/docs/screenshots/app-page-en.png) # Responsive Design Sovrium's responsive system is built on **breakpoints** defined in `theme.breakpoints`. Each breakpoint becomes a min-width media query, and component props can be overridden per breakpoint for mobile-first layouts. ## `theme.breakpoints` A map of breakpoint names to pixel values. Names are lowercase alphanumeric; values are pixel strings. | Property | Description | | -------- | ----------------------------------------------------------------------------- | | key | Breakpoint name (lowercase alphanumeric, e.g. `sm`, `md`, `lg`, `xl`, `2xl`). | | value | Min-width threshold as a pixel string (e.g. `'640px'`, `'768px'`). | ```yaml theme: breakpoints: sm: '640px' md: '768px' lg: '1024px' xl: '1280px' '2xl': '1536px' ``` These map to the standard responsive tiers: | Breakpoint | Min width | Typical target | | ---------- | --------- | -------------- | | `sm` | `640px` | Large mobile | | `md` | `768px` | Tablet | | `lg` | `1024px` | Laptop | | `xl` | `1280px` | Desktop | | `2xl` | `1536px` | Large desktop | :::callout **Values must be pixel strings.** Each breakpoint value must match the `px` format (e.g. `'768px'`). Non-pixel units (`rem`, `em`, `%`) are rejected by the schema. ::: ## Per-breakpoint overrides Breakpoints power Tailwind's mobile-first utility variants. A base utility applies at all sizes; a prefixed variant (`md:`, `lg:`, …) applies from that breakpoint up. This is how a single component adapts across screen sizes. ```yaml pages: - name: home path: / components: - type: container element: section props: # 1 column on mobile, 2 from md, 3 from lg className: 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4' children: - { type: card, props: { className: 'p-4 md:p-6' } } - { type: card, props: { className: 'p-4 md:p-6' } } - { type: card, props: { className: 'p-4 md:p-6' } } ``` In the example above: - `grid-cols-1` applies on all screens. - `md:grid-cols-2` overrides it from `768px` up. - `lg:grid-cols-3` overrides it again from `1024px` up. - `p-4` is the base padding; `md:p-6` increases it from the `md` breakpoint. :::callout **Mobile-first.** Always declare the smallest layout as the base (unprefixed) utility, then layer larger-screen overrides with breakpoint prefixes. This guarantees a sensible default on the smallest viewport before any media query matches. ::: ## Custom breakpoints Define your own names alongside or instead of the standard tiers. A custom breakpoint generates a matching utility prefix. ```yaml theme: breakpoints: phone: '480px' tablet: '834px' wide: '1440px' ``` ```yaml props: className: 'flex-col tablet:flex-row wide:gap-12' ``` ## Next steps - [Theme](/en/theme) — the full theme token reference. - [Animations](/en/animations) — keyframe and motion tokens. - [Pages Overview](/en/pages-overview) — component props and the `className` prop. # Animations The `theme.animations` block defines motion design tokens — named animations, reusable durations and easing curves, and `@keyframes` definitions. Tokens generate CSS that any component can reference through its `className`. ## `theme.animations` `animations` is a map keyed by token name (alphanumeric, must start with a letter). Each value is one of four shapes, giving you a spectrum from a one-line toggle to a full keyframe definition. | Value shape | Use it for | | ------------- | --------------------------------------------------------------------------------- | | Boolean | Enable or disable a named animation: `fadeIn: true`. | | String | A raw CSS animation value or a utility class name: `slideUp: 'animate-slide-up'`. | | Config object | Detailed control: `enabled`, `duration`, `easing`, `delay`, `keyframes`. | | Token map | A nested map of named `duration`, `easing`, or `keyframes` design tokens. | ### Boolean and string forms The quickest way to switch a built-in animation on, or to alias a class name. ```yaml theme: animations: fadeIn: true slideUp: 'animate-slide-up' ``` ### Config-object form A detailed animation with timing control. | Property | Description | | ----------- | ----------------------------------------------------------------------- | | `enabled` | Boolean. Whether the animation is active. | | `duration` | CSS duration (e.g. `'300ms'`, `'0.5s'`). | | `easing` | CSS easing/timing function (e.g. `'ease-in-out'`, `cubic-bezier(...)`). | | `delay` | CSS duration to wait before starting (e.g. `'0ms'`). | | `keyframes` | Inline keyframe definition map. | ```yaml theme: animations: modalOpen: enabled: true duration: '300ms' easing: 'ease-in-out' delay: '0ms' ``` ### Nested token maps For a reusable motion system, declare `duration`, `easing`, and `keyframes` as named token maps. Other animations and component classes can then reference these tokens instead of repeating raw values. ```yaml theme: animations: duration: fast: '200ms' normal: '300ms' slow: '500ms' easing: smooth: 'cubic-bezier(0.4, 0, 0.2, 1)' bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)' keyframes: fadeIn: from: { opacity: '0' } to: { opacity: '1' } colorPulse: '0%': { backgroundColor: '$colors.primary' } '50%': { backgroundColor: '$colors.accent' } '100%': { backgroundColor: '$colors.primary' } ``` | Token map | Key rule | Value | | ----------- | ----------------------------------- | ------------------------------------------------- | | `duration` | Alphanumeric, starts with a letter. | CSS duration string. | | `easing` | Alphanumeric, starts with a letter. | CSS easing/timing function. | | `keyframes` | Alphanumeric, starts with a letter. | A keyframe map (`from`/`to` or percentage stops). | :::callout **Reference theme colors in keyframes.** Keyframe values may reference other theme tokens with the `$colors.` syntax (e.g. `backgroundColor: '$colors.primary'`), keeping motion in sync with your palette. ::: ## Ecoconception note Motion is a footprint concern. Prefer short durations and avoid long-running infinite animations on first paint. Sovrium's eco posture is operator-controlled via environment variables, not theme schema — see the [Environment Variables](/en/env-vars) reference for the `ECO_*` controls that govern client behavior. ## Next steps - [Theme](/en/theme) — the full theme token reference. - [Responsive Design](/en/responsive-design) — breakpoints and per-breakpoint overrides. - [Pages Overview](/en/pages-overview) — applying animation classes to components. # Languages Multi-language support with translation keys, browser language detection, and automatic URL-based language routing (`/en/...`, `/fr/...`). Reference translations in pages using the `$t:` prefix. ## Defining Languages Set a default language and list supported languages with code, locale, label, and text direction. | Property | Description | | ------------------ | -------------------------------------------------------------------------------------------------- | | `default` | ISO 639-1 code for the default language (e.g., `"en"`). Used when no language is detected. | | `supported` | Array of language entry objects. Each defines a supported language. | | `fallback` | Fallback language code (2 letters). Used when a translation key is missing in the active language. | | `detectBrowser` | Boolean. When `true`, auto-detects the user's browser language on first visit. | | `persistSelection` | Boolean. When `true`, remembers the user's language choice across sessions. | ```yaml languages: default: en supported: - code: en locale: en-US label: English direction: ltr - code: fr locale: fr-FR label: 'Français' direction: ltr - code: ar locale: ar-SA label: 'العربية' direction: rtl ``` ## Language Entry Properties Each entry in the `supported` array describes a language with these properties. | Property | Description | | ----------- | ------------------------------------------------------------------------------------------------------------ | | `code` | ISO 639-1 language code (e.g., `"en"`, `"fr"`, `"ar"`). Used in URL routing (`/en/`, `/fr/`). | | `locale` | Full locale identifier (e.g., `"en-US"`, `"fr-FR"`, `"ar-SA"`). Used for number/date formatting. | | `label` | Human-readable language name shown in language switchers (e.g., `"English"`, `"Français"`). | | `direction` | Text direction: `"ltr"` (left-to-right) for most languages, `"rtl"` (right-to-left) for Arabic, Hebrew, etc. | | `flag` | Flag emoji or icon path displayed in language switchers. | :::callout **RTL Support.** Set `direction: rtl` for right-to-left languages like Arabic or Hebrew. Sovrium automatically mirrors the page layout, aligns text to the right, and applies the `dir="rtl"` attribute to the HTML root. ::: ## Translation Keys Define key-value pairs for each language. Keys use dot notation for organization. ```yaml languages: translations: en: hero.title: 'Welcome to My App' hero.description: 'Build faster with Sovrium' nav.home: 'Home' nav.about: 'About' fr: hero.title: 'Bienvenue sur Mon App' hero.description: 'Construisez plus vite avec Sovrium' nav.home: 'Accueil' nav.about: 'À propos' ``` ## Using Translations Reference translations in any content or prop value with the `$t:` prefix. ```yaml # Reference translations with $t: prefix pages: - name: home path: / sections: - type: section children: - type: h1 content: '$t:hero.title' - type: paragraph content: '$t:hero.description' ``` :::callout **`$t:` Translation Syntax.** Use `$t:key.path` in any page content or prop value to reference a translation. Example: `$t:hero.title` resolves to `"Welcome"` in English and `"Bienvenue"` in French. ::: ![English version of the app](/docs/screenshots/app-translation-en.png) ![French version of the app](/docs/screenshots/app-translation-fr.png) ## Adding a New Language Follow these steps to add a new language to your application. 1. **Add language entry** — Add a new item to the `supported` array with `code`, `locale`, `label`, and `direction`. 2. **Add translations** — Create a new `translations` section for the language code with all required keys. 3. **Test the language** — Visit `/[lang-code]/` in your browser to verify the new language renders correctly. # Tables Overview Tables define your data models. Each table is a distinct entity (users, products, orders) whose `fields` declare the columns that records can store. Tables are the foundation of a Sovrium app: pages render their records, forms write to them, automations react to their changes, and the REST/MCP APIs expose them. A table has at minimum an `id`, a `name`, and a `fields` array. Everything else — primary keys, indexes, constraints, views, permissions, webhooks, and AI exposure — is optional and layered on top. ```yaml tables: - id: 1 name: Contacts fields: - { id: 1, name: email, type: email, required: true, unique: true } - { id: 2, name: full_name, type: single-line-text, required: true } - { id: 3, name: created_at, type: created-at, indexed: true } ``` ## Table Properties Each entry in the `tables` array accepts the following properties. | Property | Description | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Unique integer identifier for the table. Auto-generated when omitted. | | `name` | User-friendly table name. Can contain spaces and mixed case; sanitized for database use (lowercase with underscores). Maximum 63 characters. | | `fields` | Array of field definitions. At least one field is required. Field names and IDs must be unique within the table. | | `primaryKey` | Primary key configuration. Defaults to an auto-generated `id` column. See [Indexes & Constraints](/en/table-indexes-constraints). | | `unique` | Array of top-level unique-constraint declarations. Each entry covers one or more fields (e.g. `[{ fields: [slug] }]`). | | `indexes` | Array of index definitions for query performance and uniqueness enforcement. | | `uniqueConstraints` | _(via `unique`)_ Multi-field uniqueness. Single-field entries fold into `field.unique`; composite entries become unique btree indexes. | | `foreignKeys` | Composite (multi-column) foreign key definitions. Single-column foreign keys are created automatically from `relationship` fields. | | `constraints` | Array of CHECK constraints with SQL expressions for cross-field data validation. | | `views` | Saved views with pre-configured filters, sorting, grouping, and visible fields. See [Views](/en/table-views). | | `permissions` | RBAC permissions controlling create, read, update, delete, restore, comment, and per-field access. See [Permissions](/en/table-permissions). | | `rowLevelPermissions` | Server-side `when` predicates per CRUD operation for defense-in-depth row scoping. | | `webhooks` | Outgoing webhooks fired on record create/update/delete events. See [Webhooks](/en/table-webhooks). | | `comments` | Comment-system configuration (guest commenting, moderation, threading). | | `aiAccess` | Declares the table eligible for exposure via Sovrium's MCP server so AI assistants can list/read/write records (subject to RBAC). | | `allowDestructive` | Boolean. When `true`, allows destructive schema migrations (column drops, type changes). Defaults to `false`. | | `allowForceDelete` | Boolean. Per-table opt-in permitting hard-delete via the admin dashboard (GDPR erasure). Defaults to `false`. See note below. | :::callout **Soft-delete by default.** Deleting a record sets `deleted_at`/`deleted_by` and leaves the row recoverable via `/restore`. Set `allowForceDelete: true` only when a table needs irreversible hard-delete (e.g. GDPR right-to-erasure on an HR table). Financial ledgers and audit-adjacent tables should leave it at the `false` default — the admin force-delete endpoint returns `404` when force-delete is not allowed. ::: ## Base Field Properties Every field — regardless of type — extends a common base. These properties are available on all 49 field types. | Property | Description | | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Unique integer identifier for the field within the table. Auto-generated sequentially when omitted. | | `name` | Column identifier. Must start with a letter and contain only lowercase letters, digits, and underscores (`^[a-z][a-z0-9_]*`). Maximum 63 chars. | | `type` | One of the 49 available field types (e.g. `single-line-text`, `integer`, `relationship`). See [Field Types](/en/field-types-overview). | | `required` | Boolean. When `true`, the field must have a value for every record. | | `unique` | Boolean. When `true`, no two records can share the same value. | | `indexed` | Boolean. When `true`, creates a database index on this field for faster queries. | | `searchWeight` | PostgreSQL full-text-search relevance weight: `A` (highest) to `D` (lowest). Only effective when `indexed: true` and the data source uses FTS. | | `storage` | Storage configuration object. `storage.compression` (boolean) enables compression for large text or attachment values. | :::callout **`default` is type-specific, not a base property.** Most value-carrying fields (text, numeric, selection, date, color, …) expose a `default` whose value type matches the field. System fields (`created-at`, relational, computed, AI) do not. Each field-type page lists whether `default` is available. ::: ### Example: a richer table ```yaml tables: - id: 2 name: Products fields: - { id: 1, name: sku, type: single-line-text, required: true, unique: true } - { id: 2, name: title, type: single-line-text, required: true, searchWeight: A, indexed: true, } - { id: 3, name: description, type: long-text, searchWeight: B } - { id: 4, name: price, type: currency, required: true, currency: USD } - { id: 5, name: in_stock, type: checkbox, default: true } primaryKey: { type: auto-increment, field: id } indexes: - { name: idx_products_sku, fields: [sku], unique: true } permissions: read: all create: [admin, editor] update: [admin, editor] delete: [admin] ``` ## Related Pages - [Field Types Overview](/en/field-types-overview) — all 49 field types by category. - [Permissions](/en/table-permissions) — RBAC and per-field access control. - [Indexes & Constraints](/en/table-indexes-constraints) — primary keys, indexes, CHECK constraints. - [Relationships](/en/table-relationships) — linking tables together. - [Views](/en/table-views) — saved filters, sorts, and grouping. - [Validation](/en/table-validation) — field- and table-level validation rules. - [Webhooks](/en/table-webhooks) — outgoing HTTP on record events. # Field Types Sovrium provides **49 field types**, organized into 9 categories. Every field shares the [base field properties](/en/tables-overview#base-field-properties) (`id`, `name`, `type`, `required`, `unique`, `indexed`, `searchWeight`, `storage`) and adds type-specific properties on top. :::callout **Counting note.** This documentation describes **49** field types, derived directly from the schema's field union (`FieldUnionSchema`, excluding the internal `unknown` catch-all). The product feature overview (`FEATURES.md`) currently states **52** — that figure is drift and should not be propagated. The authoritative breakdown is the per-category table below, which sums to exactly 49. ::: ## The 9 Categories | Category | Count | Field types | | ------------------------------------------- | :----: | ---------------------------------------------------------------------------------------------------- | | [Text](/en/text-fields) | 6 | `single-line-text`, `long-text`, `rich-text`, `email`, `url`, `phone-number` | | [Numeric](/en/number-fields) | 6 | `integer`, `decimal`, `currency`, `percentage`, `rating`, `progress` | | [Date & Time](/en/datetime-fields) | 7 | `date`, `datetime`, `time`, `duration`, `created-at`, `updated-at`, `deleted-at` | | [Selection](/en/selection-fields) | 4 | `checkbox`, `single-select`, `multi-select`, `status` | | [Relational](/en/relational-fields) | 3 | `relationship`, `lookup`, `rollup` | | [User & Audit](/en/user-audit-fields) | 4 | `user`, `created-by`, `updated-by`, `deleted-by` | | [Attachment / Media](/en/attachment-fields) | 3 | `single-attachment`, `multiple-attachments`, `barcode` | | [Advanced](/en/advanced-fields) | 9 | `formula`, `count`, `autonumber`, `button`, `json`, `array`, `color`, `code`, `geolocation` | | [AI](/en/ai-fields) | 7 | `ai-generate`, `ai-summary`, `ai-categorize`, `ai-extract`, `ai-sentiment`, `ai-tag`, `ai-translate` | | **Total** | **49** | | ## Quick Summary | Type | Category | What it stores | | ---------------------- | ----------- | ---------------------------------------------------- | | `single-line-text` | Text | Short single-line text (names, titles, labels). | | `long-text` | Text | Multi-line plain text (notes, descriptions). | | `rich-text` | Text | Formatted HTML via a WYSIWYG editor. | | `email` | Text | Email address with RFC 5322 validation. | | `url` | Text | Web address with URL validation. | | `phone-number` | Text | Phone number stored as text (international formats). | | `integer` | Numeric | Whole numbers with optional min/max. | | `decimal` | Numeric | Fixed-precision decimals (1–10 places). | | `currency` | Numeric | Monetary values with ISO 4217 code and formatting. | | `percentage` | Numeric | Percentage values, displayed with `%`. | | `rating` | Numeric | Star/icon rating (`max` 1–10). | | `progress` | Numeric | Progress bar 0–100 with optional color. | | `date` | Date & Time | Calendar date, optional time. | | `datetime` | Date & Time | Date plus time, timezone-aware. | | `time` | Date & Time | Time-of-day without a date. | | `duration` | Date & Time | Elapsed time / work hours. | | `created-at` | Date & Time | System-set creation timestamp. | | `updated-at` | Date & Time | System-updated modification timestamp. | | `deleted-at` | Date & Time | Soft-delete timestamp (NULL = active). | | `checkbox` | Selection | Boolean true/false. | | `single-select` | Selection | One option from a list. | | `multi-select` | Selection | Multiple options from a list. | | `status` | Selection | Colored workflow states. | | `relationship` | Relational | Foreign-key link to another table. | | `lookup` | Relational | A field read from a related record. | | `rollup` | Relational | Aggregation over related records. | | `user` | User/Audit | Reference to a user (single or multiple). | | `created-by` | User/Audit | System-set creating user. | | `updated-by` | User/Audit | System-set last-modifying user. | | `deleted-by` | User/Audit | System-set deleting user. | | `single-attachment` | Attachment | One uploaded file. | | `multiple-attachments` | Attachment | Many uploaded files. | | `barcode` | Attachment | Barcode value (product identification). | | `formula` | Advanced | Computed value from a formula expression. | | `count` | Advanced | Count of related records. | | `autonumber` | Advanced | Auto-incrementing number with prefix/padding. | | `button` | Advanced | Interactive button triggering a URL or automation. | | `json` | Advanced | Structured JSON data. | | `array` | Advanced | Array of typed values. | | `color` | Advanced | Hex color value. | | `code` | Advanced | Source code with syntax highlighting. | | `geolocation` | Advanced | Latitude/longitude coordinates. | | `ai-generate` | AI | AI-generated text from a prompt. | | `ai-summary` | AI | AI summary of source fields. | | `ai-categorize` | AI | AI single-category classification. | | `ai-extract` | AI | AI structured extraction (JSON Schema). | | `ai-sentiment` | AI | AI sentiment analysis. | | `ai-tag` | AI | AI multi-tag assignment from an allow-list. | | `ai-translate` | AI | AI translation to a target language. | ## Category Pages - [Text Fields](/en/text-fields) - [Number Fields](/en/number-fields) - [Date & Time Fields](/en/datetime-fields) - [Selection Fields](/en/selection-fields) - [Relational Fields](/en/relational-fields) - [User & Audit Fields](/en/user-audit-fields) - [Attachment Fields](/en/attachment-fields) - [Advanced Fields](/en/advanced-fields) - [AI Fields](/en/ai-fields) # Text Fields Six field types store textual content, from short labels to formatted rich text and validated strings. All share the [base field properties](/en/tables-overview#base-field-properties). | Type | Stores | | ------------------ | ---------------------------------------------------------------- | | `single-line-text` | Short, single-line text (names, titles, labels). | | `long-text` | Multi-line plain text (descriptions, notes, comments). | | `rich-text` | Formatted HTML rendered with a WYSIWYG editor. | | `email` | Email address validated against RFC 5322. | | `url` | Web address validated as an absolute URL. | | `phone-number` | Phone number stored as text; preserves international formatting. | ## `single-line-text` Short text limited to a single line. Stored as-is without formatting. | Property | Description | | --------- | --------------------------------------- | | `default` | Default string assigned to new records. | ```yaml - { id: 1, name: title, type: single-line-text, required: true, indexed: true, default: Untitled } ``` ## `long-text` Multi-line plain text. Supports line breaks; no rich formatting (no bold/italics). | Property | Description | | --------- | --------------------------------------- | | `default` | Default string assigned to new records. | ```yaml - { id: 2, name: description, type: long-text, default: 'Enter description here...' } ``` ## `rich-text` Formatted text with bold, italic, links, lists, headings, and more. Rendered with a Tiptap WYSIWYG editor and stored as HTML. | Property | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `maxLength` | Maximum length in characters (integer ≥ 1). Validates on input. | | `fullTextSearch` | Boolean. Enables full-text-search indexing for this field. | | `toolbar` | Array of allowed toolbar actions (e.g. `bold`, `italic`, `link`, `heading`, `list`, `image`, `code-block`, `table`). When omitted, all actions are available. | | `placeholder` | Placeholder text shown when the editor is empty. | | `collaborative` | Boolean. Enables real-time collaborative editing (Yjs). | ```yaml - id: 3 name: article_content type: rich-text required: true maxLength: 10000 toolbar: [bold, italic, link, heading, list] placeholder: 'Write your article...' ``` ## `email` Text field that validates email addresses (RFC 5322). Commonly `unique: true` and `indexed: true` for authentication and lookups. | Property | Description | | --------- | ---------------------------------------------- | | `default` | Default email address assigned to new records. | ```yaml - { id: 4, name: email, type: email, required: true, unique: true, indexed: true } ``` ## `url` Text field that validates web addresses. Supports multiple protocols (`http://`, `https://`, `ftp://`, …). | Property | Description | | --------- | ------------------------------------ | | `default` | Default URL assigned to new records. | ```yaml - { id: 5, name: website, type: url, indexed: true, default: 'https://example.com' } ``` ## `phone-number` Text field for phone numbers. Numbers are stored exactly as entered (spaces, dashes, parentheses preserved); no automatic formatting is applied, allowing any international format. | Property | Description | | --------- | --------------------------------------------- | | `default` | Default phone number assigned to new records. | ```yaml - { id: 6, name: mobile_phone, type: phone-number, unique: true, default: '+1 (555) 000-0000' } ``` :::callout **Full-text search & relevance.** Set `indexed: true` plus `searchWeight` (`A`–`D`) on text fields whose data source uses full-text search, to control relevance ranking. `rich-text` also exposes `fullTextSearch` to opt that column into the search index. ::: # Number Fields Six field types store numbers, currencies, percentages, ratings, and progress indicators. All share the [base field properties](/en/tables-overview#base-field-properties). | Type | Stores | | ------------ | -------------------------------------------------- | | `integer` | Whole numbers with optional min/max range. | | `decimal` | Fixed-precision decimals (1–10 decimal places). | | `currency` | Monetary values with ISO 4217 code and formatting. | | `percentage` | Percentage values, displayed with `%`. | | `rating` | Star/icon rating with a configurable maximum. | | `progress` | A 0–100 progress bar with an optional color. | ## `integer` Whole numbers without decimal places. Optimized for performance. | Property | Description | | --------- | ---------------------------------- | | `min` | Minimum allowed value (inclusive). | | `max` | Maximum allowed value (inclusive). | | `default` | Default integer for new records. | ```yaml - { id: 1, name: quantity, type: integer, required: true, min: 0, max: 1000, default: 1 } ``` ## `decimal` Numbers with decimal places, stored with exact `DECIMAL` precision. | Property | Description | | ----------- | --------------------------------------------------- | | `precision` | Number of decimal places. Integer from **1 to 10**. | | `min` | Minimum allowed value (inclusive). | | `max` | Maximum allowed value (inclusive). | | `default` | Default decimal for new records. | ```yaml - { id: 2, name: weight, type: decimal, precision: 2, min: 0.01, max: 999.99, default: 1.0 } ``` ## `currency` Monetary values with locale-aware display formatting. | Property | Description | | -------------------- | ----------------------------------------------------------------------------------- | | `currency` | **Required.** ISO 4217 three-letter code (e.g. `USD`, `EUR`, `GBP`, `JPY`). | | `precision` | Number of decimal places. Integer from **0 to 10** (default 2 for most currencies). | | `min` | Minimum allowed value (inclusive). | | `max` | Maximum allowed value (inclusive). | | `symbolPosition` | Currency-symbol placement: `before` ($100) or `after` (100€). | | `negativeFormat` | Negative-value display: `minus` (-100) or `parentheses` ((100)). | | `thousandsSeparator` | Thousands grouping character: `comma`, `period`, `space`, or `none`. | ```yaml - id: 3 name: price type: currency required: true currency: USD precision: 2 symbolPosition: before negativeFormat: parentheses thousandsSeparator: comma ``` :::callout **Corrected from earlier docs.** `currency` uses `thousandsSeparator` — an enum (`comma | period | space | none`), **not** a boolean. `negativeFormat` accepts `minus` or `parentheses` (there is no `red` option), and `precision` ranges **0–10**. ::: ## `percentage` Percentage values (typically 0–100), displayed with a `%` symbol. | Property | Description | | ----------- | --------------------------------------------------------------- | | `precision` | Number of decimal places. Integer from **0 to 10** (default 0). | | `min` | Minimum allowed value (inclusive, typically 0). | | `max` | Maximum allowed value (inclusive, typically 100). | | `default` | Default percentage for new records. | ```yaml - { id: 4, name: discount_rate, type: percentage, precision: 1, min: 0, max: 100, default: 10.0 } ``` ## `rating` A rating value rendered as stars or other indicators. | Property | Description | | --------- | ----------------------------------------------------------- | | `max` | Maximum rating value. Integer from **1 to 10** (default 5). | | `style` | Visual style for the rating (e.g. `stars`). | | `default` | Default rating value for new records. | ```yaml - { id: 5, name: product_rating, type: rating, max: 5, style: stars } ``` :::callout **Corrected from earlier docs.** The rating field uses `max` and `style` — **not** `ratingMax` / `ratingStyle`. ::: ## `progress` Displays a 0–100 value as a progress bar. Used to track completion toward a goal. | Property | Description | | --------- | ----------------------------------------------------------- | | `color` | Progress-bar color as a 6-digit hex value (e.g. `#10B981`). | | `default` | Default progress value (0–100) for new records. | ```yaml - { id: 6, name: task_completion, type: progress, color: '#10B981', default: 0 } ``` :::callout **Corrected from earlier docs.** The progress field uses `color` (a hex string) — **not** `progressColor`. ::: # Date & Time Fields Seven field types store dates, times, durations, and system-managed audit timestamps. All share the [base field properties](/en/tables-overview#base-field-properties). | Type | Stores | | ------------ | --------------------------------------------------------- | | `date` | A calendar date, with optional time component. | | `datetime` | A date plus time, timezone-aware. | | `time` | A time of day without a date. | | `duration` | An elapsed-time / work-hours value (stored in seconds). | | `created-at` | System-set timestamp captured when a record is created. | | `updated-at` | System-updated timestamp refreshed on every modification. | | `deleted-at` | Soft-delete timestamp; NULL means the record is active. | ## `date` Calendar dates, optionally including a time component. | Property | Description | | ------------- | -------------------------------------------------------------------------------- | | `format` | Free-form display format string (e.g. `YYYY-MM-DD`, `MM/DD/YYYY`, `DD-MM-YYYY`). | | `dateFormat` | Display preset: `US`, `European`, or `ISO`. | | `timeFormat` | Time display: `12-hour` or `24-hour`. | | `includeTime` | Boolean. When `true`, the date field also captures a time. | | `timezone` | IANA timezone identifier (e.g. `UTC`, `America/New_York`, `Europe/London`). | | `timeZone` | Timezone setting accepting a specific zone or `local` (browser timezone). | | `default` | Default date value for new records. | ```yaml - { id: 1, name: due_date, type: date, dateFormat: ISO, includeTime: false } ``` ## `datetime` Date plus time. Timezone-aware for accurate cross-region scheduling. | Property | Description | | ------------ | -------------------------------------------------------------------------------- | | `format` | Free-form display format string (e.g. `YYYY-MM-DD HH:mm`, `MM/DD/YYYY hh:mm A`). | | `dateFormat` | Display preset: `US`, `European`, or `ISO`. | | `timeFormat` | Time display: `12-hour` or `24-hour`. | | `timezone` | IANA timezone identifier. | | `timeZone` | Timezone setting accepting a specific zone or `local`. | | `default` | Default datetime value for new records. | ```yaml - { id: 2, name: published_at, type: datetime, timeFormat: 24-hour, timezone: UTC } ``` ## `time` Time-only values without a date. Used for schedules, opening hours, and time slots. | Property | Description | | ------------ | ---------------------------------------------------- | | `timeFormat` | Time display: `12-hour` or `24-hour`. | | `default` | Default time in `HH:MM:SS` format (e.g. `09:00:00`). | ```yaml - { id: 3, name: start_time, type: time, required: true, timeFormat: 24-hour } ``` ## `duration` Elapsed time or work hours, stored as a number of seconds. | Property | Description | | --------------- | -------------------------------------------------------- | | `format` | Free-form display format string for the duration. | | `displayFormat` | Display preset: `h:mm`, `h:mm:ss`, or `decimal`. | | `default` | Default duration value, in **seconds**, for new records. | ```yaml - { id: 4, name: work_hours, type: duration, displayFormat: 'h:mm' } ``` ## System Audit Timestamps `created-at`, `updated-at`, and `deleted-at` are **system-managed** — their values are set automatically and cannot be edited manually. They take no type-specific properties beyond the base field properties (commonly `indexed: true`). | Type | Behavior | | ------------ | ---------------------------------------------------------------------------------------------- | | `created-at` | Captured once when the record is created. Read-only. | | `updated-at` | Refreshed automatically whenever the record is modified. Read-only. | | `deleted-at` | Set when the record is soft-deleted. `NULL` indicates an active record; a timestamp = deleted. | ```yaml fields: - { id: 5, name: created_at, type: created-at, indexed: true } - { id: 6, name: updated_at, type: updated-at, indexed: true } - { id: 7, name: deleted_at, type: deleted-at, indexed: true } ``` :::callout **Soft-delete lifecycle.** Adding a `deleted-at` field enables Sovrium's soft-delete-by-default behavior: deleting a record sets the timestamp instead of removing the row, and `/restore` clears it. Pair with [`deleted-by`](/en/user-audit-fields) to record who performed the deletion. ::: # Selection Fields Four field types let records choose from predefined options. All share the [base field properties](/en/tables-overview#base-field-properties). | Type | Stores | | --------------- | ------------------------------------------------------------------- | | `checkbox` | A boolean true/false value. | | `single-select` | One option chosen from a predefined list. | | `multi-select` | Multiple options chosen from a predefined list. | | `status` | A colored workflow state from a list of `{ value, color }` options. | ## `checkbox` Boolean field, typically rendered as a checkbox. | Property | Description | | --------- | ----------------------------------------- | | `default` | Default boolean value (`true` / `false`). | ```yaml - { id: 1, name: is_active, type: checkbox, required: true, default: false } ``` ## `single-select` A single choice from a list of string options. | Property | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `options` | Array of strings defining the available choices. | | `default` | Default selected option (a string). | | `conditions` | Optional behavioral conditions: `[{ when: