ObjectStackObjectStack

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/cli

The 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-app

This 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 flow

Launch the dev server

os dev --ui

Open 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.json

These 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

CommandAliasDescription
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 cleanReclaim 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 for os init when 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 installation

Options:

  • -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:

TemplateWhat it creates
appFull application with objects, views, barrel imports
pluginReusable plugin package with objects
emptyMinimal project with just objectstack.config.ts

os dev

Starts development mode. Three usage shapes:

  1. With local source (objectstack.config.ts in cwd) — auto-compiles to dist/objectstack.json if missing, then delegates to os serve --dev.
  2. With a pre-built artifact (--artifact <path|url>) — skips auto-compile and boots the artifact directly. No objectstack.config.ts needed in cwd. Useful for trying out a published app in dev mode without cloning its source.
  3. Monorepo root (cwd has pnpm-workspace.yaml) — orchestrates pnpm -r dev across 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):

FlagEnv equivalentPurpose
-a, --artifact <path|url>OS_ARTIFACT_PATHBoot a pre-built artifact directly; skips auto-compile
-d, --database <url>OS_DATABASE_URLfile:… / libsql:// / postgres:// / mongodb:// / memory://
--database-driver <kind>OS_DATABASE_DRIVERForce sqlite | turso | postgres | mongodb | memory
--database-auth-token <t>OS_DATABASE_AUTH_TOKENlibsql/Turso token
--auth-secret <s>AUTH_SECRETOverride the dev-fallback secret
--environment-id <id>OS_ENVIRONMENT_IDEnvironment identifier (default env_local)
-p, --port <n>PORT / OS_PORTListen port (default 3000). In dev a busy port auto-hops to the next free one; the banner shows the actual port.
--uiForce Studio UI on (already on by default in dev)
--no-compileSkip the auto-compile step (errors if no artifact present)
-v, --verboseVerbose 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 auth tier plugins (@objectstack/plugin-auth, @objectstack/plugin-security, @objectstack/plugin-audit) when the preset includes the auth tier and the user did not pin them in objectstack.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 plugins

Options:

  • -p, --port <port> — Server port (default: env PORT or 3000)
  • --dev — Development mode (loads devPlugins, pretty logging)
  • --ui / --no-ui — Toggle Console UI at /_console/ (default on)
  • --server / --no-server — Toggle HTTP server plugin
  • --prebuilt — Skip esbuild / bundle-require and 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.

PresetTiersAuto-loaded optional plugins
minimalcorenone
default (default)core, i18n, ui, ai, authi18n service, Studio UI, AI service, Auth + Security + Audit
fullcore, i18n, ui, ai, authcurrently 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 target

Options:

  • -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 4000

The 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 start

Options (all override the matching env var):

FlagEnv equivalentPurpose
-a, --artifact <path|url>OS_ARTIFACT_PATHFile path or http(s):// URL to the compiled artifact
-d, --database <url>OS_DATABASE_URLfile:… / libsql:// / postgres:// / mongodb:// / memory://
--database-driver <kind>OS_DATABASE_DRIVERForce sqlite | turso | postgres | mongodb | memory when the URL is ambiguous
--database-auth-token <token>OS_DATABASE_AUTH_TOKENAuth token for libsql/Turso
--auth-secret <secret>AUTH_SECRETSecret for @objectstack/plugin-auth; without it /api/v1/auth/* is skipped (server still runs)
--environment-id <id>OS_ENVIRONMENT_IDEnvironment identifier (default env_local)
-p, --port <port>PORT / OS_PORTListen port (default 3000). Production fails loudly if the port is busy — see note below.
--ui / --no-uiMount the Console portal at /_console/. Enabled by default (so you can install marketplace apps); pass --no-ui to disable it.
-v, --verboseVerbose output

Port conflicts: production never auto-shifts. Unlike os dev (which hops to the next free port for local convenience), os start exits with an error if its resolved port is in use. A silently drifted port would break your reverse-proxy upstream, OS_AUTH_URL callbacks, and OS_TRUSTED_ORIGINS (CORS). Pin the port explicitly (PORT=8080 os start) and keep OS_AUTH_URL / OS_TRUSTED_ORIGINS in 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

CommandDescription
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 pipelines

Options:

  • -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 file

Gates run (each exits non-zero with a located, corrective message):

  1. Protocol schema — the stack conforms to ObjectStackDefinitionSchema (@objectstack/spec).
  2. CEL / predicate validation (ADR-0032) — every visible / disabled / requiredWhen / validation rule / flow condition / sharing rule is parsed for CEL syntax and checked that each record.<field> reference exists on the target object. This catches a bare field ref (done instead of record.done) that would otherwise evaluate to null and silently hide an action on every record (#2183/#2185).
  3. Widget-binding integrity (ADR-0021) — every dashboard widget's dataset / dimensions / values resolves 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 tooling

Output 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

Schema 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).

CommandDescription
os migrate planDry-run: show how the database has drifted from metadata, categorised safe / needs-confirm / destructive (no changes applied)
os migrate applyReconcile 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
CategoryExamplesApplied by
saferelax NOT NULL → nullable, widen a varcharos migrate apply (and dev auto-reconcile)
needs_confirmnon-narrowing type changeos migrate apply
destructivedrop an orphaned column, tighten NOT NULL, narrow a typeos 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

CommandAliasDescription
os generate <type> <name>os gGenerate 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 writing

Available types:

TypeDefault DirectoryDescription
objectsrc/objects/Business data object with fields
viewsrc/views/List or form view definition
actionsrc/actions/Button or batch action
flowsrc/flows/Automation flow
agentsrc/agents/AI agent
dashboardsrc/dashboards/Analytics dashboard
appsrc/apps/Application navigation

Options:

  • -d, --dir <directory> — Override target directory
  • --dry-run — Preview without writing files

What it does:

  1. Creates a typed TypeScript file using Data.Object, UI.View, Automation.Flow, etc.
  2. Creates or updates the barrel index.ts in the target directory
  3. 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-app

Quality

CommandDescription
os test [files]Run Quality Protocol test scenarios against a running server
os doctorCheck 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 authentication

os doctor

Checks your development environment and reports issues:

os doctor           # Check health
os doctor -v        # Show fix suggestions for warnings

Checks performed:

  • Node.js version (≥18 required)
  • pnpm installation
  • TypeScript availability
  • Dependencies installed
  • @objectstack/spec build status
  • Git availability

Authentication

CommandDescription
os registerCreate an account and store local credentials
os loginSign in and store credentials in ~/.objectstack/credentials.json
os whoamiShow the current authenticated user
os logoutRevoke 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.com

os 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-browser

If 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 secret

os logout

Logout calls POST /api/v1/auth/sign-out before deleting local credentials, so the server-side session is revoked as well.

os logout

Cloud Environments

CommandDescription
os environments listList environments visible to the current session
os environments show <id>Show one environment
os environments createProvision 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.json

The 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:

  1. objectstack.config.ts
  2. objectstack.config.js
  3. objectstack.config.mjs

You can also specify a path explicitly:

os compile path/to/my-config.ts

Typical 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 artifact

Source vs Artifact

ObjectStack treats objectstack.config.ts and objectstack.json as two forms of the same schema — authoring source vs compiled artifact:

Aspectobjectstack.config.tsobjectstack.json
RoleAuthoring sourceDeployable artifact
FormatTypeScript (defineStack({...}))Pure JSON
May contain codeYes (handler: async (ctx) => {...})No — handlers are lowered to a sibling objectstack-runtime.<hash>.mjs
Loaded byos serve (via bundle-require)os start (via loadArtifactBundle — file or http(s)://)
SchemaObjectStackDefinitionSchemaSame schema, plus runtimeModule reference
Produced byYou (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

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 --json

Example GitHub Actions step:

- name: Validate ObjectStack Config
  run: npx objectstack validate --strict --json

- name: Build ObjectStack Artifact
  run: npx objectstack compile --json

On this page