| 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. Sign in with the seeded dev admin (admin@objectos.ai / admin123) — os dev provisions it automatically on an empty database. The boot banner also prints the app's MCP endpoint (/api/v1/mcp) so a coding agent can connect to the running app.
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> |
OS_AUTH_SECRET |
Override the dev-fallback secret |
--environment-id <id> |
OS_ENVIRONMENT_ID |
Environment identifier (default env_local) |
-p, --port <n> |
OS_PORT / 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 Console UI on (already on by default in dev) |
--compile |
— | Force compiling objectstack.config.ts → dist/objectstack.json before starting (auto when the artifact is missing; ignored with --artifact) |
--fresh |
— | Ephemeral OS_HOME in the OS tempdir (clean DB, uploads, storage), auto-deleted on exit; implies --seed-admin |
--seed-admin / --no-seed-admin |
— | Seed a dev admin (admin@objectos.ai / admin123) on an empty DB — default on; override with --admin-email / --admin-password |
-v, --verbose |
— | Verbose output |
By default os dev keeps your data between restarts in a project-local SQLite
file at .objectstack/data/dev.db (created on first run). Pass --database,
set OS_DATABASE_URL, or use --fresh for a throwaway run.
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: envOS_PORTor3000)--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, Console 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 OS_AUTH_SECRET to be set; otherwise AuthPlugin
is skipped with a yellow warning and the /api/v1/auth/* endpoints will
return 404. (In --dev mode the CLI falls back to an insecure local secret so
login works out of the box.) 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 │
└─────────────────────────────────────────┘
The prebuilt Console bundle ships with the framework packages and is served at
/_console/ — no separate frontend install or build step is needed.
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. When the cwd
does contain an objectstack.config.ts and no artifact exists yet, os start
auto-compiles it first; with no config and no artifact at all it boots an empty kernel
with the Console + marketplace, so you can install apps interactively.
# 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 \
OS_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> |
OS_AUTH_SECRET / AUTH_SECRET |
Secret for @objectstack/plugin-auth. If neither the flag nor the env var is set, os start auto-generates one and persists it at <home>/auth-secret |
--home <dir> |
OS_HOME |
Home directory for persistent state (default <cwd>/.objectstack when an objectstack.config.ts is present, otherwise ~/.objectstack) |
--environment-id <id> |
OS_ENVIRONMENT_ID |
Environment identifier (default env_local) |
-p, --port <port> |
OS_PORT / 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 (OS_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 > <home>/dist/objectstack.json > auto-compile from objectstack.config.ts (when present) > empty kernel.
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:
os start always resolves an auth secret — --auth-secret > OS_AUTH_SECRET /
AUTH_SECRET env > a secret auto-generated and persisted at <home>/auth-secret
on first run — so /api/v1/auth/* (login/register) and the Console's login flow work
out of the box, without any manual secret provisioning. Set the env var (or flag)
explicitly when you deploy across multiple nodes or want to rotate the secret.
| Command | Description |
|---|---|
os compile [config] |
Compile configuration to a JSON artifact (dist/objectstack.json) |
os build [config] |
Alias for os compile (scaffolded projects wire it as npm run build) |
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: 57ms
→ Normalizing stack definition...
→ Lowering inline handlers...
→ Validating protocol compliance...
→ Validating expressions (ADR-0032)...
→ Checking dashboard widget bindings (ADR-0021)...
→ Checking SDUI styling (ADR-0065)...
→ Checking security posture (ADR-0090 D7)...
→ Collecting package docs (ADR-0046)...
→ Writing artifact...
✓ Build complete (74ms)
Data: 2 Objects 6 Fields
UI: 1 Views 1 Actions
Runtime: 3 plugins
Artifact: dist/objectstack.json (7.6 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
────────────────────────────────────────
My App v0.1.0
my-app
Minimal ObjectStack environment — a clean slate for building.
Namespace: my_app
Type: app
Data: 2 Objects 6 Fields
UI: 1 Views 1 Actions
Runtime: 3 plugins
Objects:
my_app_note (2 fields, user) — Note
my_app_ticket (4 fields, user) — Ticket
Loaded in 59ms
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 lint [config] |
Check metadata for style and convention issues (beyond validate's hard gates) |
os test [files] |
Run Quality Protocol test scenarios against a running server |
os doctor |
Check development environment health |
Style and convention checks on top of os validate — naming, labels, translation coverage — with a 0-100 quality score:
os lint # Style / convention checks
os lint --score # Append a 0-100 metadata quality score (letter-graded)
os lint --fix # Show what would be fixed (dry-run)
os lint --json # JSON output for CIRuns 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 your browser.
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