Command Line Interface
Complete guide for using the ObjectStack CLI to build metadata-driven applications
@objectstack/cli
Command Line Interface for building metadata-driven applications with the ObjectStack Protocol.
Installation
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.
Your First App in 2 Minutes
Create a project
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.)
Add more metadata
os generate object customer # Add a Customer object
os generate action approve # Add an action
os generate flow onboarding # Add an automation flowLaunch the dev server
os dev --uiOpen http://localhost:3000/_console/ — you'll see the Console UI with a data browser, metadata explorer, and API documentation.
Validate & Build
os validate # Check schema + CEL predicates + widget bindings (no artifact)
os compile # Build production artifact → dist/objectstack.jsonThese commands are the AI build loop. In day-to-day work, Claude Code writes the
metadata and runs two of them for you: os validate is the gate (it rejects
predicate/schema/binding mistakes that fail silently at runtime), and os dev --ui is
the human verify surface (the Console, where you confirm the app matches intent). See
Build with Claude Code for the full loop.
os dev --ui starts a dev server with the bundled Console UI, auto-loads ObjectQL, an in-memory driver when appropriate, and the Hono HTTP server.
Commands
Development
| 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) |
os init
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 |
os dev
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>.
os serve
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
],
});os db clean
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)
Console UI
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.
Production
os start
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) - 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.
os start vs os serve: os serve boots from objectstack.config.ts (TypeScript source).
os start boots from objectstack.json (compiled artifact) and falls back to the same
default-host path if you happen to run it without a config but with an artifact present.
The two commands ultimately go through the same kernel — they just differ in which input
shape they accept. See Source vs Artifact below.
Build & Validate
| 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.) |
os compile
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.
os validate
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
os validate and os build share one validator, so a config that passes
os validate will not fail the build on schema/predicate/binding grounds. In a
scaffolded project these are wired as npm run validate and npm run build;
your AGENTS.md tells coding agents to run npm run validate after editing
metadata. See Validating metadata.
os info
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 90msSchema migrations
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 |
Dev self-heal. os dev runs the SQL driver with autoMigrate: 'safe', so
loosening changes (e.g. you just made a field optional) are applied to your
existing dev database automatically on restart — no os migrate needed, no data
loss. Auto-reconcile is dev-only and never destructive; it is force-disabled
under NODE_ENV=production, where you run os migrate deliberately.
os migrate only sees objects in your compiled artifact — run os build
first. It never drops a table that is absent from your metadata, and on SQLite
it reconciles via a table rebuild (copy → swap) that preserves your data.
Scaffolding
| Command | Alias | Description |
|---|---|---|
os generate <type> <name> | os g | Generate metadata files |
os create <type> [name] | Create a new package from template |
os generate (alias: os g)
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
os create
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-appQuality
| Command | Description |
|---|---|
os test [files] | Run Quality Protocol test scenarios against a running server |
os doctor | Check development environment health |
os test
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 authenticationos doctor
Checks 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
Authentication
| 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 |
os register
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.comos login
In 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 secretos logout
Logout calls POST /api/v1/auth/sign-out before deleting local credentials, so
the server-side session is revoked as well.
os logoutCloud Environments
| 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 |
Create an environment from a local artifact
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.
Bind an existing environment
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.
Configuration
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),
});Config File Auto-Detection
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.tsTypical Workflow
# 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 artifactSource vs Artifact
ObjectStack 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.
Next Steps
- Examples Guide — Run the built-in example apps (Todo, CRM, BI)
- Developer Guide — Learn data modeling, security, and automation
- Protocol Reference — Complete schema documentation
CI/CD Integration
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