ClosedLoopOS

status: authoritative

last_verified: 2026-03-25

owner: founding-engineer

applies_to: [founding-engineer]

Quickstart

Get ClosedLoopOS running locally and execute your first SOP in under 15 minutes.

Looking for the full walkthrough? The Getting Started guide covers everything here plus backend setup, troubleshooting, and a clos demo preview.

Prerequisites: Node.js ≥ 22 (node --version), a terminal, and access to the internal Nextcloud sync of the ClosedLoopOS repo.

Important — CLI binaries are NOT synced to Nextcloud. The dist/ folder (compiled CLI binaries) is in .gitignore and must be built locally after cloning/syncing. Run pnpm --filter @closedloopos/cli build before using clos for the first time, or you will see module/shebang errors from stale .js files. This takes ~30 seconds after pnpm install.

Path note: The repo path ~/ClosedLoopOS appears throughout this guide. Replace it with wherever you synced the repo (e.g. /home/you/Nextcloud/notes/03 Projects/ClosedLoopOS). Or create a symlink: ln -s /path/to/your/sync/ClosedLoopOS ~/ClosedLoopOS.

No pnpm installed? Enable it via corepack (Node.js 16+): corepack enable && corepack prepare pnpm@latest --activate. Or use the Nix devshell: nix develop /path/to/ClosedLoopOS --command bash -c "pnpm <cmd>".


Step 1 — Install the CLI

Build from source. Pick the path that matches your environment:

Option A — With Nix (recommended)

Internal access required. The ClosedLoopOS repo is private and maintained in Nextcloud. Sync it locally first:

nextcloudcmd ~/ClosedLoopOS https://your-nextcloud-instance/remote.php/dav/files/<username>/ClosedLoopOS

Or open the Nextcloud web UI, navigate to the ClosedLoopOS folder, and click "Sync."

cd ~/ClosedLoopOS
nix develop . --command bash -c "
  pnpm install &&
  pnpm --filter @closedloopos/cli build &&
  mkdir -p ~/.local/bin &&
  ln -sf ../node_modules/@closedloopos/cli/dist/cli.js ~/.local/bin/clos &&
  export PATH=\"$HOME/.local/bin:$PATH\"
"
# Add to ~/.bashrc or ~/.zshrc to persist PATH:
#   echo 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc

Option B — With pnpm installed globally

Internal access required. The ClosedLoopOS repo is private and maintained in Nextcloud. Sync it locally first:

nextcloudcmd ~/ClosedLoopOS https://your-nextcloud-instance/remote.php/dav/files/<username>/ClosedLoopOS
cd ~/ClosedLoopOS
pnpm install
pnpm --filter @closedloopos/cli build
mkdir -p ~/.local/bin
ln -sf ../node_modules/@closedloopos/cli/dist/cli.js ~/.local/bin/clos
export PATH="$HOME/.local/bin:$PATH"   # add to ~/.bashrc or ~/.zshrc

Verify:

clos --version
# 0.14.0

Expected time: ~2–4 minutes (first build); ~30 seconds for incremental rebuilds

PATH not set? If clos --version says command not found even after the steps above, your shell doesn't have ~/.local/bin in its PATH. Add this line to your shell config and restart your shell:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
# Or for zsh: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc

clos --version fails with module errors or command not found? The CLI dist/ is stale (not synced to Nextcloud). Rebuild it:

pnpm --filter @closedloopos/cli build

Then re-run clos --version. This is required after every fresh clone/sync.

clos: command not found (PATH issue)? Make sure ~/.local/bin is in your PATH:

export PATH="$HOME/.local/bin:$PATH"
# Add that line to ~/.bashrc or ~/.zshrc to make it permanent

Pre-commit error when using nix develop? If you see FatalError: git failed when running clos init from a directory that is not a Git repository — this is harmless. The command succeeds anyway. The error comes from the nix devshell's pre-commit hook trying to install into a non-git directory.


Step 2 — Scaffold a project

mkdir my-closedloopOS-project && cd my-closedloopOS-project
clos init

Note: clos init --dir <path> supports both relative and absolute paths. For absolute paths, the project is created directly at the specified location regardless of cwd. See CLO-1369 for the fix details.

Expected output:

Initialized ClosedLoopOS project: my-closedloopOS-project
  docs/sops/example-sop.yaml  — starter SOP
  .clos.config.json           — project config
  README.md

Next steps:
  clos sop list               — list your SOPs
  clos sop validate --all     — validate SOPs
  clos sop run example-sop --dry-run  — preview a run (no backend needed)

Note: The project name matches your directory name (e.g. my-closedloopOS-project).

Your project contains:

my-closedloopOS-project/
├── docs/
│   └── sops/
│       └── example-sop.yaml    # starter SOP — customise this
├── .clos.config.json            # project config
└── README.md

Step 3 — List your SOPs

clos sop list

Expected output:

info: Running from local YAML. Set CLOSEDLOOP_API_URL to use the SOP Registry.

SOPs  (1 found in docs/sops)

  ID            Name         Version  Tags
  ────────────────────────────────────────
  example-sop   Example SOP  0.1.0    example

Note: The info: Running from local YAML... line is normal, not an error — it means the CLI is reading SOPs from your local docs/sops/ directory. To list SOPs from a live server registry instead, set CLOSEDLOOP_COMPANY_ID (without it, the CLI stays in local mode regardless of CLOSEDLOOP_API_URL). When CLOSEDLOOP_COMPANY_ID is set but the server does not implement the SOP Registry API (e.g., Thin Server), clos sop list automatically falls back to local YAML mode with a 404 warning — this is expected and harmless.


Step 4 — Preview the SOP

Use --dry-run to validate your SOP and see what it would do — no API connection required:

clos sop run example-sop --dry-run

Expected output:

Dry run: Example SOP (example-sop v0.1.0)

Steps:
  1. [ceo] Log message

What just happened? The SOP runner parsed example-sop.yaml, resolved all inputs (using defaults for anything not supplied), and printed the execution plan. In a live run, each step is dispatched to a real AI agent — the runner orchestrates their work automatically.

Note: The starter template is intentionally minimal (1 step). The repo source at docs/sops/example-sop.yaml contains a richer 3-step example demonstrating inputs, DAG dependencies, and outputs. Open that file directly to see the full version.


Step 5 — Open the example SOP

Open docs/sops/example-sop.yaml in your editor. It contains a minimal starter SOP:

schema_version: "1"

sop:
  id: example-sop
  version: 0.1.0
  name: Example SOP
  description: A starter SOP — replace with your own workflow.
  tags: [example]

  triggers:
    - type: manual

  inputs:
    - name: message
      type: string
      description: A message to include in the run
      required: false
      default: Hello, ClosedLoopOS!

  steps:
    - id: log-message
      name: Log message
      agent:
        role: ceo
      action: |
        Log the following message and mark this step done: {{ inputs.message }}

The starter SOP in your new project is intentionally minimal (1 step). The Nextcloud sync of the ClosedLoopOS repo at ~/ClosedLoopOS/docs/sops/example-sop.yaml contains a richer 3-step example demonstrating inputs, DAG dependencies, step outputs, and on_complete hooks. Open that file directly as a reference when writing your own SOPs.

What is role: ceo? Agent roles are how a SOP addresses the right AI worker. ceo is the built-in default. When you connect your ClosedLoopOS project to a running ClosedLoopOS instance, the step will be dispatched to whichever agent holds that role.


Step 6 — (Optional) Create your own SOP from a template

The SOP library ships with four ready-to-use templates. Scaffold one into your project's docs/sops/ directory:

clos sop init --template customer-onboarding docs/sops/my-sop.yaml

Important: always pass the output path as docs/sops/<your-sop-id>.yaml. The CLI writes to whatever path you supply; omitting the directory prefix creates the file in your current working directory, where clos sop validate and clos sop run will not find it.

Available templates: api-agent, customer-onboarding, incident-response, weekly-team-review.

Validate and preview the new SOP:

clos sop validate my-sop
clos sop run my-sop --dry-run

Go live: connect to ClosedLoopOS

Dry runs validate your SOPs without executing them. To execute real agent steps you need a running ClosedLoopOS instance.

Choose your backend

ClosedLoopOS supports two backends:

| Backend | Port | Notes | |---------|------|-------| | Thin Server (recommended for local dev) | 3100 | Zero-config, SQLite, no external deps. Runs as Next.js API routes. | | Full Paperclip API | 3100 | Requires the full Paperclip platform deployment. |

Thin Server setup (skip if using the Paperclip API at port 3100):

# In one terminal — start the Thin Server (Next.js dev server)
cd ~/ClosedLoopOS
pnpm dev

# In another terminal — bootstrap the database (one-time)
curl -X POST http://localhost:3100/api/setup
# Save the companyId and agentId from the response
# Expected response: {"companyId":"...","agentId":"..."}
# Verify the server is running:
curl http://localhost:3100/health
# Expected: {"status":"ok","version":"0.1.0"}

# Point the CLI at the Thin Server
export CLOSEDLOOP_API_URL=http://localhost:3100
export CLOSEDLOOP_COMPANY_ID=<companyId-from-setup>
export CLOSEDLOOP_API_KEY=<agentId-from-setup>

# Important: The Thin Server does not implement the SOP Registry API.
# With all three env vars set, `clos sop list` calls the registry and gets a 404.
# For listing local SOPs (recommended), unset the two registry vars first:
#   unset CLOSEDLOOP_COMPANY_ID CLOSEDLOOP_API_KEY
#   clos sop list      # lists docs/sops/*.yaml
# Then restore them before running against the server:
#   export CLOSEDLOOP_COMPANY_ID=<companyId-from-setup>
#   export CLOSEDLOOP_API_KEY=<agentId-from-setup>
#   clos sop run docs/sops/example-sop.yaml  # works with all three vars set

# ⚠️ Thin Server role note: The bootstrapped agent has role=engineer, NOT ceo.
# The `example-sop.yaml` scaffold uses role=ceo in every step and on_complete.
# Thin Server has no ceo agent by default — you must map the ceo role to your
# bootstrapped agent ID (see "Map agent roles" step below). Without this mapping,
# live SOP runs will fail to dispatch steps.

Prerequisites (Paperclip API at port 3100)

If using the full Paperclip API instead of the Thin Server, set:

export CLOSEDLOOP_API_URL=http://localhost:3100
export CLOSEDLOOP_COMPANY_ID=<your-company-id>
export CLOSEDLOOP_API_KEY=<your-api-key>

Where to find your credentials: Log into the dashboard at http://localhost:3100, go to Settings, and copy the Company ID and API Key.

Important: Use a user API key (from Settings → API Keys), not an agent JWT. Agent tokens lack the tasks:assign permission needed to dispatch SOP steps to agents.

Thin Server users: clos agent-roles init will fail with a 404 (the Thin Server doesn't implement that endpoint). Use clos agent-roles set ceo <agent-id-from-setup> instead. See the manual assignment below.

Map agent roles

Before running a SOP against a live backend, the CLI needs to know which agent ID corresponds to each role name in your SOPs.

For the full Paperclip API (port 3100):

clos agent-roles init

For the Thin Server (port 3101): clos agent-roles init requires GET /api/companies/:id/agents which the Thin Server does not implement — it will fail with a 404. Use manual assignment instead:

clos agent-roles set ceo <agent-id-from-setup>
# example: clos agent-roles set ceo 00000000-0000-0000-0000-000000000002

Note: If you don't know your agent ID, run curl -s -H "Authorization: Bearer <your-api-key>" http://localhost:3101/api/agents/me — the id field in the response is your agent ID.

This writes a .clos/agent-roles.json mapping file in the project root. Re-run when your team roster changes.

Pre-flight check

Run clos doctor to verify your setup is correct before executing any SOPs:

clos doctor

This validates:

Thin Server: clos doctor connectivity check uses GET /api/agents/me which works on both backends. If the API connectivity check fails but env vars and .clos/agent-roles.json are valid, double-check your CLOSEDLOOP_API_KEY matches the agentId from the /api/setup response. Do NOT run clos agent-roles init on Thin Server — that endpoint does not exist. Use clos agent-roles set ceo <agent-id> to create the roles file manually.

Fix any issues reported by clos doctor before running SOPs.

Trigger a live run

clos sop run dispatches each SOP step as a ClosedLoopOS issue, assigned to the matching agent role, and waits for them to complete:

clos sop run docs/sops/example-sop.yaml

Check the status of the run from the CLI:

clos sop status <instance-id>

Where <instance-id> is the run ID printed after "Run:" in the output of clos sop run (e.g. 14137AD7).

Thin Server: clos run status is not supported on the Thin Server (port 3101). Always use clos sop status <instance-id> — it reads the local run record from .clos/sop-runs/<instance-id>.json and works on both backends. Add --watch to stream step completions:

clos sop status 14137AD7 --watch

SOP Registry (clos sop push, clos run list, clos sop run <id> by registry ID) requires server-side SOP Registry endpoints. Use clos sop run <path> with a local file path if your backend does not yet support the registry.


Next steps

| What you want to do | Where to go | |---|---| | Run the Thin Server locally | Thin Server | | Browse ready-to-use SOP templates | SOP Library | | Write a multi-step SOP | SOP Authoring Guide | | Full SOP YAML reference | SOP Schema | | Full CLI reference | CLI Reference | | TypeScript SDK | SDK Reference | | Self-host ClosedLoopOS | Self-hosting Guide | | Stuck? Common errors & fixes | Troubleshooting · Error Catalog | | Common questions | FAQ | | Multiple projects | Multi-Project Guide | | Configure agent hats | Hat Management |