| title | Command Line Interface |
|---|---|
| description | Complete guide for using the ObjectStack CLI to build metadata-driven applications |
Command Line Interface for building metadata-driven applications with the ObjectStack Protocol.
pnpm add -D @objectstack/cliThe CLI is available as objectstack or the shorter alias os. Installed as a dev dependency, the bins are project-local — invoke them as npx os … / pnpm exec os … or via your package scripts.
npm create objectstack@latest my-app
cd my-appThis scaffolds a working project with objectstack.config.ts, a sample object, and all dependencies installed — plus the AI skills bundle and an AGENTS.md for coding agents. (os init is the CLI's own scaffolder for plugin skeletons and bare configs — see below.)
os generate object customer # Add a Customer object
os generate action approve # Add an action
os generate flow onboarding # Add an automation flowos dev --uiOpen http://localhost:3000/_console/ — you'll see the Console UI with a data browser, metadata explorer, and API documentation.
os validate # Check schema + CEL predicates + widget bindings (no artifact)
os compile # Build production artifact → dist/objectstack.json| Command | Alias | Description |
|---|---|---|
os init [name] |
Initialize a new ObjectStack project in the current directory | |
os dev [package] |
Start development mode with hot reload | |
os serve [config] |
Start the ObjectStack server with plugin auto-detection | |
os db clean |
Reclaim SQLite free space with a one-time VACUUM (ADR-0057) |
Scaffolds a new ObjectStack project with configuration, TypeScript setup, and initial metadata files.
Which scaffolder? For a new app, prefer
npm create objectstack@latest— it also derives your namespace, pins the framework packages to the current release, and installs the AI skills bundle +AGENTS.md. Reach foros initwhen you want a plugin skeleton or a bare config in an existing directory.
os init my-app # Create with default "app" template
os init my-plugin -t plugin # Create a plugin project
os init blank -t empty # Minimal config only
os init my-app --no-install # Skip dependency installationOptions:
-t, --template <template>— Template:app(default),plugin,empty--no-install— Skip automatic dependency installation-p, --package-manager <npm|pnpm|yarn|bun>— Package manager to use (auto-detected from the environment)
Templates:
| Template | What it creates |
|---|---|
app |
Full application with objects, views, barrel imports |
plugin |
Reusable plugin package with objects |
empty |
Minimal project with just objectstack.config.ts |
Starts development mode. Three usage shapes:
- With local source (
objectstack.config.tsin cwd) — auto-compiles todist/objectstack.jsonif missing, then delegates toos serve --dev. - With a pre-built artifact (
--artifact <path|url>) — skips auto-compile and boots the artifact directly. Noobjectstack.config.tsneeded in cwd. Useful for trying out a published app in dev mode without cloning its source. - Monorepo root (cwd has
pnpm-workspace.yaml) — orchestratespnpm -r devacross packages.
os dev # Auto-compile cwd config, then start
os dev my-package # Workspace package (monorepo orchestration mode)
os dev --ui -v # Dev server with Console UI + verbose
# Boot a remote artifact in dev mode (no local config needed)
os dev --artifact https://raw.githubusercontent.com/<org>/<repo>/main/dist/objectstack.json
# Override storage / auth on the fly
os dev --database file:./data/test.db --auth-secret $(openssl rand -hex 32)Options (the runtime overrides mirror os start — each flag overrides the matching env var):
| Flag | Env equivalent | Purpose |
|---|---|---|
-a, --artifact <path|url> |
OS_ARTIFACT_PATH |
Boot a pre-built artifact directly; skips auto-compile |
-d, --database <url> |
OS_DATABASE_URL |
file:… / libsql:// / postgres:// / mongodb:// / memory:// |
--database-driver <kind> |
OS_DATABASE_DRIVER |
Force sqlite | turso | postgres | mongodb | memory |
--database-auth-token <t> |
OS_DATABASE_AUTH_TOKEN |
libsql/Turso token |
--auth-secret <s> |
AUTH_SECRET |
Override the dev-fallback secret |
--environment-id <id> |
OS_ENVIRONMENT_ID |
Environment identifier (default env_local) |
-p, --port <n> |
PORT / OS_PORT |
Listen port (default 3000). In dev a busy port auto-hops to the next free one; the banner shows the actual port. |
--ui |
— | Force Studio UI on (already on by default in dev) |
--no-compile |
— | Skip the auto-compile step (errors if no artifact present) |
-v, --verbose |
— | Verbose output |
With a file-backed SQLite database, dev also provisions a sibling
<db>.telemetry.<ext> file registered as the telemetry datasource —
lifecycle-classed system data (activity streams, job runs, notifications,
audit) lands there instead of the business DB (ADR-0057). Opt out with
OS_TELEMETRY_DB=0, or point it elsewhere (any mode, including serve)
with OS_TELEMETRY_DB=<path>.
Starts the ObjectStack server with automatic plugin discovery:
- Auto-loads ObjectQL Engine when objects are defined
- Auto-loads InMemory Driver in dev mode
- Auto-loads App Plugin for metadata
- Auto-loads Hono HTTP Server for REST APIs
- Auto-loads the
authtier plugins (@objectstack/plugin-auth,@objectstack/plugin-security,@objectstack/plugin-audit) when the preset includes theauthtier and the user did not pin them inobjectstack.config.ts
os serve # Default: port 3000
os serve -p 4000 # Custom port
os serve --dev # Development mode (pretty logs, devPlugins)
os serve --dev --ui # Dev mode with Console UI
os serve --no-server # Skip HTTP server (kernel only)
os serve --preset minimal # Skip auto-loaded auth/i18n/ui pluginsOptions:
-p, --port <port>— Server port (default: envPORTor3000)--dev— Development mode (loads devPlugins, pretty logging)--ui / --no-ui— Toggle Console UI at/_console/(defaulton)--server / --no-server— Toggle HTTP server plugin--prebuilt— Skip esbuild /bundle-requireand load the config as native ESM (use this in production builds where the config is already pre-compiled)--preset minimal | default | full— Override the auto-registration tier (see below)
Tier presets
os serve decides which optional plugins to auto-register from a tier
list. Any plugin already present in config.plugins always wins; tiers
only gate the automatic registration of optional plugins.
| Preset | Tiers | Auto-loaded optional plugins |
|---|---|---|
minimal |
core |
none |
default (default) |
core, i18n, ui, ai, auth |
i18n service, Studio UI, AI service, Auth + Security + Audit |
full |
core, i18n, ui, ai, auth |
currently an alias of default — same tiers, no additional plugins |
The auth tier requires AUTH_SECRET to be set; otherwise AuthPlugin
is skipped with a yellow warning and the /api/v1/auth/* endpoints will
return 404. To take full control, set tiers on the stack config:
import { defineStack } from '@objectstack/spec';
export default defineStack({
manifest: { /* ... */ },
tiers: ['core'], // disable all optional auto-registration
plugins: [
// ... only what you explicitly want
],
});Reclaims SQLite free space with a one-time VACUUM (ADR-0057 §3.4). The
platform reclaims space incrementally (auto_vacuum=INCREMENTAL), but that
setting only takes effect on a fresh database — files created before it
stay pinned at their high-water mark until one full VACUUM rebuilds them.
Non-destructive: every row survives; free pages return to the OS. Cleans
the telemetry sibling too when one exists.
os db clean # default: the per-project dev DB
os db clean --database file:./data/app.db # explicit targetOptions:
-d, --database <url>— SQLite database URL/path (defaults to$OS_DATABASE_URL, then the per-project dev DB)
Launch the development server with the Console UI:
os dev --ui # Default: port 3000
os serve --dev --ui -p 4000The Console UI is a metadata-driven admin interface that provides object exploration, package management, and runtime metadata diagnostics.
Architecture:
┌─────────────────────────────────────────┐
│ os dev --ui (:3000) │
├─────────────────────────────────────────┤
│ Hono Server │
│ ├─ /api/v1/* → ObjectStack API │
│ ├─ /_console/* → Console SPA │
│ └─ /* → custom routes │
└─────────────────────────────────────────┘
In this framework repo the prebuilt console bundle is served at /_console/.
For source-level Console UI work, run the sibling ../objectui Vite server and
point it at a backend on port 3000.
Boots a production server directly from a compiled objectstack.json artifact — no
objectstack.config.ts required. This is the canonical "deploy a built ObjectStack app"
command: hand a server one JSON file (or a URL pointing at one) and it runs.
# Quick start — load ./dist/objectstack.json with sqlite at file:<home>/data/objectstack.db
os start
# Pick everything via flags (no env vars needed)
os start \
--artifact ./build/myapp.json \
--database file:./data/prod.db \
--auth-secret $(openssl rand -hex 32) \
--port 8080
# Remote artifact + Turso/libSQL backing store
os start \
--artifact https://cdn.example.com/app.json \
--database libsql://my-db.turso.io \
--database-auth-token $TURSO_TOKEN
# Postgres
os start --database "postgres://user:pass@host:5432/mydb"
# Pure env-var style still works (Docker / Fly / k8s friendly)
OS_ARTIFACT_PATH=./build/myapp.json \
OS_DATABASE_URL=file:./data/prod.db \
AUTH_SECRET=… \
os startOptions (all override the matching env var):
| Flag | Env equivalent | Purpose |
|---|---|---|
-a, --artifact <path|url> |
OS_ARTIFACT_PATH |
File path or http(s):// URL to the compiled artifact |
-d, --database <url> |
OS_DATABASE_URL |
file:… / libsql:// / postgres:// / mongodb:// / memory:// |
--database-driver <kind> |
OS_DATABASE_DRIVER |
Force sqlite | turso | postgres | mongodb | memory when the URL is ambiguous |
--database-auth-token <token> |
OS_DATABASE_AUTH_TOKEN |
Auth token for libsql/Turso |
--auth-secret <secret> |
AUTH_SECRET |
Secret for @objectstack/plugin-auth; without it /api/v1/auth/* is skipped (server still runs) |
--environment-id <id> |
OS_ENVIRONMENT_ID |
Environment identifier (default env_local) |
-p, --port <port> |
PORT / OS_PORT |
Listen port (default 3000). Production fails loudly if the port is busy — see note below. |
--ui / --no-ui |
— | Mount the Console portal at /_console/. Enabled by default (so you can install marketplace apps); pass --no-ui to disable it. |
-v, --verbose |
— | Verbose output |
Port conflicts: production never auto-shifts. Unlike
os dev(which hops to the next free port for local convenience),os startexits with an error if its resolved port is in use. A silently drifted port would break your reverse-proxy upstream,OS_AUTH_URLcallbacks, andOS_TRUSTED_ORIGINS(CORS). Pin the port explicitly (PORT=8080 os start) and keepOS_AUTH_URL/OS_TRUSTED_ORIGINSin sync when you change it.
Resolution priority (artifact): --artifact > OS_ARTIFACT_PATH > <cwd>/dist/objectstack.json.
Resolution priority (database): --database > OS_DATABASE_URL > DATABASE_URL (legacy) > file:<home>/data/objectstack.db.
What it boots:
- Reads the artifact's
manifest,objects,views,flows, … - Auto-registers the platform services declared in
requires: [...](e.g.ai,automation,analytics,auth,ui). Declaring a service capability (automation,analytics,ai,audit, …) is a requirement: if its provider package isn't installed, boot fails fast with a clear error instead of silently starting without a capability you asked for. (authanduiare tier-gated with their own opt-in rules —auth's secret-gated skip is described below.) - Auto-detects the driver from the database URL scheme (
memory://→ in-memory,libsql:///https://→ Turso,postgres[ql]:///pg://→ pg,mongodb[+srv]://→ MongoDB, otherwise sqlite) - Runs standalone boot mode with one active environment.
Authentication:
The auth capability is auto-loaded only when both (1) the artifact declares requires: [..., 'auth'] and (2) a secret is provided via --auth-secret or AUTH_SECRET. Without a secret, AuthPlugin is silently skipped with a warning — the server still boots and serves data/REST routes, only /api/v1/auth/* (login/register) is omitted. This is intentional: os start is happy to run an unauthenticated, internal-network deployment.
| Command | Description |
|---|---|
os compile [config] |
Compile configuration to a JSON artifact (dist/objectstack.json) |
os validate [config] |
Validate schema, CEL predicates, and widget bindings — the same gates as os compile/os build, no artifact emitted |
os info [config] |
Display metadata summary (objects, fields, apps, agents, etc.) |
Bundles and validates your objectstack.config.ts against the ObjectStackDefinitionSchema, then outputs a deployable JSON artifact.
os compile # Default output: dist/objectstack.json
os compile -o build/stack.json # Custom output path
os compile --json # JSON output for CI pipelinesOptions:
-o, --output <path>— Output path (default:dist/objectstack.json)--json— Output compile result as JSON (for CI)
Output example:
◆ Compile
────────────────────────────────────────
→ Loading configuration...
Config: objectstack.config.ts
Load time: 104ms
→ Validating protocol compliance...
→ Writing artifact...
✓ Build complete (312ms)
Data: 10 Objects 217 Fields
UI: 1 Apps 3 Dashboards 8 Reports 10 Actions
Logic: 5 Flows 5 Agents 2 APIs
Artifact: dist/objectstack.json (48.2 KB)
The resulting dist/objectstack.json is a portable, self-describing deployment unit —
you can hand it to os start (locally or on a server), publish it to a CDN, or fetch it
over HTTP from another runtime. See os start and
Source vs Artifact for details.
The fast, artifact-free verification gate. It runs the same structural and
semantic checks as os compile/os build but writes no dist/, so it is the
command to run after every metadata edit. Use it before reporting a change done.
os validate # Validate current directory
os validate --strict # Warnings as errors
os validate --json # JSON output for CI
os validate path/to/config # Validate specific fileGates run (each exits non-zero with a located, corrective message):
- Protocol schema — the stack conforms to
ObjectStackDefinitionSchema(@objectstack/spec). - CEL / predicate validation (ADR-0032) — every
visible/disabled/requiredWhen/ validation rule / flow condition / sharing rule is parsed for CEL syntax and checked that eachrecord.<field>reference exists on the target object. This catches a bare field ref (doneinstead ofrecord.done) that would otherwise evaluate tonulland silently hide an action on every record (#2183/#2185). - Widget-binding integrity (ADR-0021) — every dashboard widget's
dataset/dimensions/valuesresolves to a declared dataset/field, so a dangling binding fails here instead of rendering an empty chart.
Options:
--strict— Treat warnings as errors (exit code 1)--json— Output results as JSON
Warnings checked (advisory, non-blocking unless --strict):
- Missing
manifest.id(required for deployment) - Missing
manifest.namespace(required for multi-app hosting) - No objects defined
- No apps or plugins defined
Displays a summary of your metadata without compilation or validation:
os info # Show metadata summary
os info --json # JSON output for toolingOutput example:
◆ Info
────────────────────────────────────────
Enterprise CRM v3.0.0
com.example.crm
Namespace: crm
Type: app
Data: 10 Objects 217 Fields
UI: 1 Apps 3 Dashboards 8 Reports 10 Actions
Logic: 5 Flows 5 Agents 2 APIs
Objects:
account (16 fields, own) — Account
contact (24 fields, own) — Contact
...
Loaded in 90ms
The metadata→database sync is additive-only: on boot it creates missing
tables and adds new columns, but never alters or drops existing ones. So a
non-additive change to an object already backed by a database — relaxing
required (drop NOT NULL), changing a field's type/length, or removing a
field — silently diverges from the live schema, and the physical column wins at
write time. os migrate reconciles the database to the metadata (the source of
truth).
| Command | Description |
|---|---|
os migrate plan |
Dry-run: show how the database has drifted from metadata, categorised safe / needs-confirm / destructive (no changes applied) |
os migrate apply |
Reconcile the database to metadata. Applies loosening changes; destructive ones require --allow-destructive |
os migrate plan # Preview drift (no changes)
os migrate apply # Apply safe (loosening) changes, with a confirm prompt
os migrate apply --yes # Skip the prompt (CI / scripts)
os migrate apply --allow-destructive --yes # Also drop orphaned columns, tighten NOT NULL, narrow types
os migrate plan --json # Machine-readable output| Category | Examples | Applied by |
|---|---|---|
safe |
relax NOT NULL → nullable, widen a varchar |
os migrate apply (and dev auto-reconcile) |
needs_confirm |
non-narrowing type change | os migrate apply |
destructive |
drop an orphaned column, tighten NOT NULL, narrow a type |
os migrate apply --allow-destructive |
| Command | Alias | Description |
|---|---|---|
os generate <type> <name> |
os g |
Generate metadata files |
os create <type> [name] |
Create a new package from template |
Generates properly typed metadata files with barrel index management.
os g object customer # Generate a Customer object
os g view customer # Generate a Customer list view
os g action approve # Generate an action
os g flow customer # Generate an automation flow
os g agent support # Generate an AI agent
os g dashboard sales # Generate a dashboard
os g app crm # Generate an app definition
os g object task -d lib/ # Override target directory
os g object task --dry-run # Preview without writingAvailable types:
| Type | Default Directory | Description |
|---|---|---|
object |
src/objects/ |
Business data object with fields |
view |
src/views/ |
List or form view definition |
action |
src/actions/ |
Button or batch action |
flow |
src/flows/ |
Automation flow |
agent |
src/agents/ |
AI agent |
dashboard |
src/dashboards/ |
Analytics dashboard |
app |
src/apps/ |
Application navigation |
Options:
-d, --dir <directory>— Override target directory--dry-run— Preview without writing files
What it does:
- Creates a typed TypeScript file using
Data.Object,UI.View,Automation.Flow, etc. - Creates or updates the barrel
index.tsin the target directory - Shows a hint to run
objectstack validate
Creates new packages from built-in templates (for monorepo-level scaffolding):
os create plugin analytics # Create packages/plugins/plugin-analytics
os create example my-app # Create examples/my-app| Command | Description |
|---|---|
os test [files] |
Run Quality Protocol test scenarios against a running server |
os doctor |
Check development environment health |
Runs Quality Protocol test scenarios (JSON-based BDD) against a running ObjectStack server.
os test # Default: qa/*.test.json
os test qa/my-test.json # Specific test file
os test --url http://localhost:4000 # Custom server URL
os test --token my-api-key # With authenticationChecks your development environment and reports issues:
os doctor # Check health
os doctor -v # Show fix suggestions for warningsChecks performed:
- Node.js version (≥18 required)
- pnpm installation
- TypeScript availability
- Dependencies installed
@objectstack/specbuild status- Git availability
| Command | Description |
|---|---|
os register |
Create an account and store local credentials |
os login |
Sign in and store credentials in ~/.objectstack/credentials.json |
os whoami |
Show the current authenticated user |
os logout |
Revoke the server session and clear local credentials |
Creates a user account and stores the returned token locally.
os register
os register --email user@example.com --name "Jane Doe" --password secret
os register --url https://api.example.comIn an interactive terminal, login uses a browser-based device flow by default: the CLI prints a one-time verification URL, opens the browser, and polls until you approve access in Studio.
os login
os login --url https://api.example.com
os login --no-browserIf a valid token already exists, os login exits successfully with
"Already logged in as <email>". Use os logout to switch users, or pass
--force to re-authenticate.
For CI and other non-interactive contexts, pass email/password directly:
os login --email user@example.com --password secretLogout calls POST /api/v1/auth/sign-out before deleting local credentials, so
the server-side session is revoked as well.
os logout| Command | Description |
|---|---|
os environments list |
List environments visible to the current session |
os environments show <id> |
Show one environment |
os environments create |
Provision a new environment |
os environments switch <id> |
Set the active environment for later CLI calls |
os environments bind <id> |
Bind a compiled local artifact to an existing environment |
Compile first, then create an environment and bind the generated
dist/objectstack.json in one call:
os compile
os environments create --org <org-id> --name CRM --artifact ./dist/objectstack.jsonThe server stores the absolute artifact path in environment metadata.
On environment-kernel boot, ObjectStack loads the JSON bundle, registers schemas, and
seeds records from the bundle's data arrays.
os environments bind <environment-id> --artifact ./dist/objectstack.json
os environments bind <environment-id> --artifact ./dist/objectstack.json --build--build runs objectstack compile before updating the project. --reseed
is reserved for the server-side reseed endpoint; use it only when that endpoint
is available in your deployment.
The CLI looks for objectstack.config.ts (or .js, .mjs) in the current directory:
import { defineStack } from '@objectstack/spec';
import * as objects from './src/objects';
import * as actions from './src/actions';
export default defineStack({
manifest: {
id: 'com.example.my-app',
namespace: 'my_app',
version: '1.0.0',
type: 'app',
name: 'My App',
description: 'My ObjectStack application',
},
objects: Object.values(objects),
actions: Object.values(actions),
});The CLI searches for configuration files in this order:
objectstack.config.tsobjectstack.config.jsobjectstack.config.mjs
You can also specify a path explicitly:
os compile path/to/my-config.ts# 1. Create project
os init my-crm && cd my-crm
# 2. Define your data model
os g object account
os g object contact
os g object opportunity
# 3. Add business logic
os g flow lead-qualification
os g agent sales-assistant
# 4. Validate everything
os validate
# 5. Start development with Console UI
os dev --ui
# 6. Build for production
os compile
# 7. Deploy: ship just the artifact
os start # locally
OS_ARTIFACT_PATH=https://cdn.you.com/app.json os start # remote artifactObjectStack treats objectstack.config.ts and objectstack.json as two forms of the same
schema — authoring source vs compiled artifact:
| Aspect | objectstack.config.ts |
objectstack.json |
|---|---|---|
| Role | Authoring source | Deployable artifact |
| Format | TypeScript (defineStack({...})) |
Pure JSON |
| May contain code | Yes (handler: async (ctx) => {...}) |
No — handlers are lowered to a sibling objectstack-runtime.<hash>.mjs |
| Loaded by | os serve (via bundle-require) |
os start (via loadArtifactBundle — file or http(s)://) |
| Schema | ObjectStackDefinitionSchema |
Same schema, plus runtimeModule reference |
| Produced by | You (or os generate) |
os compile / os build |
The artifact is fully self-describing: its requires: [...] field declares which platform
services (ai, automation, analytics, …) the runtime should auto-register, so os start
needs nothing other than the JSON itself to bring up a working server.
This is why an artifact is the canonical "portable deployment unit" — you can host it on S3 / GitHub raw / a CDN, and any ObjectStack runtime can fetch and execute it with no additional source code on the server.
- Examples Guide — Run the built-in example apps (Todo, CRM, BI)
- Developer Guide — Learn data modeling, security, and automation
- Protocol Reference — Complete schema documentation
All commands that produce output support --json for machine-readable output:
# In CI pipeline
os validate --json --strict
os compile --json -o dist/objectstack.json
os info --jsonExample GitHub Actions step:
- name: Validate ObjectStack Config
run: npx objectstack validate --strict --json
- name: Build ObjectStack Artifact
run: npx objectstack compile --json