Not a drive. A base.
A Space is more than where your files and work output live. With workflows, the filesystem itself becomes a running system — save a file and the pipeline that watches it runs. Session after session with your agents, it accumulates into your — or your team's — own home base you keep building on, not a pile of outputs. Every write is versioned.
The model in one minute
remote://<space> addresses it with no owner prefix.Install the CLI
The CLI is a single self-contained binary (no runtime needed). The installer detects your OS/arch and puts capora on your PATH.
1 · Install
# installs the capora binary to /usr/local/bin (sudo fallback → ~/.local/bin) curl -fsSL https://capora.cc/install | sh capora help capora update # upgrade later, in place (sudo if needed) # Windows: download https://capora.cc/dl/capora-windows-amd64.exe onto your PATH
2 · Sign in
Get a token from the web console — sign in, open Connect an agent, and generate one (starts with cpt_). Then hand it to the CLI, easiest first:
# 1 · recommended (esp. for agents): save the console token to ~/.capora/config capora login --token cpt_xxxxxxxxxxxxxxxxxxxx # 2 · or, stateless (CI / ephemeral shells) — nothing written to disk export CAPORA_URL=https://capora.cc export CAPORA_TOKEN=cpt_xxxxxxxxxxxxxxxxxxxx capora whoami # confirm: user + Space + server
capora login opens the browser to authorize and saves the token for you. An agent should skip the browser flow — get a token from the console and use capora login --token … (or the env vars). Env vars always take precedence over the saved config; reads on public Spaces need no token.Using the CLI
Capora is a netdisk: your Space is a remote filesystem you operate on directly with S3-style commands. Remote paths are written remote://<space>/<path>; anything without that prefix is a local path. There's no pull/save dance — every write is versioned automatically. (Want git semantics on your own machine? Just use git.)
Copy files up, down, and around
capora session start --message "add the todo app" # open a session — required before any write capora cp ./apps/todo remote://<you>/apps # upload a folder → remote apps/todo/ capora ls remote://<you>/apps # browse the remote tree capora cp remote://<you>/apps/todo/app.js ./ # pull one file down # …edit ./app.js with any tool… capora cp ./app.js remote://<you>/apps/todo/ # push it back → a new save point
That first line isn't decoration: every write needs an open session — see Sessions. Reads don't. Omit the space (remote:/apps) to use your default Space from ~/.capora/config. Directory copies are rsync-style: cp apps/todo remote://…/apps keeps the todo name; a trailing slash on the source (cp apps/todo/ …) copies its contents. cp only adds/overwrites — it never deletes remote files.
Every command
| Account | |
| capora login [--token <t>] | sign in (saves a token to ~/.capora/config) |
| capora logout | remove saved credentials |
| capora whoami | current user + current Space + server URL |
| capora space list | list the Spaces you can reach |
| capora update | upgrade this binary in place (alias upgrade) |
| Sessions — required before any write | |
| capora session start [<id>] | open a work thread (new cs_…), or resume one by id (prints its context) · --message "<intent>" |
| capora session message <id> "<md>" | append a checkpoint note (also refreshes the lease) · alias msg |
| capora session list [--active] | every session + who holds what + its latest note · alias ls |
| capora session show <id> | full journal of any session (read-only) |
| Files (netdisk) | |
| capora cp <src> <dst> [-m <msg>] [--mirror] | copy files/folders up, down, or remote→remote (direction set by which side is remote://). --mirror makes dest match src (deletes dest extras) for dir copies into a Space. |
| capora ls [remote://…] | list one directory level |
| capora tree [remote://…] | list all files under a path |
| capora cat remote://… | print a file to stdout · --at <save> for an old version |
| capora stat remote://… | one file's hash + size |
| capora find <glob> | find paths by glob (e.g. "src/**") |
| capora mkdir remote://… [-m] | create an empty directory |
| capora rm remote://… [-m] | delete file(s) or a folder (recursive) |
| capora mv <a> <b> [-m] | move/rename (zero-byte, content-addressed) |
| History | |
| capora log [remote://…] | save-point history (newest first) |
| capora show <save> | files changed in a save |
| capora diff [a] [b] | changes between two saves |
| capora restore <save> [path] | roll back to a save point |
| Workflows | |
| capora workflows | list workflows + their trigger |
| capora runs | recent run history — check here after a save to see what fired |
| capora secret set <NAME> [value] | set a Space secret (injected into runs) · secret list / secret rm |
Global: --space <space> to target a Space from anywhere. Env: CAPORA_URL, CAPORA_TOKEN, CAPORA_PROJECT, CAPORA_SESSION. Run capora help for the built-in cheat sheet.
Sessions — a Space is an OS, a session is your shell
A capora session is a work thread you open before you change anything. It exists because a Space is shared ground: you, your teammates, and several agents all write to the same filesystem, so every write is attributed to a thread with a stated intent — and anyone can see what the others are in the middle of. It's separate from an agent's own runtime session; the record is just a file, at .capora/sessions/<id>.json.
cp / rm / mv / mkdir refuse without one — session start first, or pass --session-id <id> / set CAPORA_SESSION. Reads never need one. This holds inside workflow runs too, and the server enforces it there (a run token that writes with no live session is rejected), so an automation must capora session start before saving anything back.# open a thread (prints a new id), or resume one — resuming prints its intent + notes + actions capora session start --message "refresh the pricing page" # → cs_k7m2p9qx capora session start cs_k7m2p9qx # the id is remembered, so later writes are attributed automatically capora cp ./site remote://<you>/site capora cp ./site remote://<you>/site --session-id cs_k7m2p9qx # …or be explicit # checkpoint: a short note. Put long output in files and link them from the text capora session message cs_k7m2p9qx "rewrote the tiers; numbers in remote://<you>/notes/pricing.md" capora session list --active # who is working on what, right now capora session show cs_k7m2p9qx # read anyone's journal
How it behaves
- There is no
end. A session is held by a 30-minute lease that refreshes on activity and lapses when idle — after that anyone canstart <id>and pick it up. One holder at a time;list/showare always open to everyone. - One session = one intent. When the task shifts,
starta new one instead of piling unrelated work into it — the journal is only useful if it stays about one thing. - It spans runtimes. One agent run may open several sessions, and one session may be continued later by a different agent (or by you) via
start <id>— the thread outlives whoever opened it. - Bootstrap exception: writes to
.capora/sessions/itself (i.e.session start/message) are allowed without a prior session — otherwise you could never open the first one. CAPORA_REQUIRE_SESSION=0skips the client-side check only. The server still enforces it inside runs.
Workflows — saving a file is the trigger
A workflow is a file at .capora/workflows/<name>.json: a trigger plus one entry script. You publish it by copying it up like any other file — there is no separate deploy step.
# .capora/workflows/transform.json { "on": { "change": "data/raw/**" }, // …or { "schedule": "0 3 * * *" } "run": "scripts/transform.sh" // a script in your Space }
The two triggers
There are two triggers, and that's on purpose:
on.change— a path glob, or an array of them ("data/**",["src/**","*.md"]). After any save whose changed paths match, the workflow runs, pinned to that new save point. This is how you fire a workflow: save a file it watches. A run's own writeback does not re-trigger it (loop-guarded), so chains don't spin.on.schedule— a 5-field UTC cron string. Editing or removing the file re-arms (or stops) the schedule on the next save.
"on" never runs on its own — there is no "run it now" button or command, by design. Deliberate, reproducible triggering means every run ties to a save point you can point at. Fire a job on demand by saving the input it watches; check capora runs to see what happened.# create the pipeline (both files live in the Space) capora cp scripts/ remote://<you>/scripts -m "transform script" capora cp .capora/ remote://<you>/.capora -m "add transform workflow" capora workflows # → transform [change data/raw/**] # fire it — by saving a file it watches capora cp ./batch.csv remote://<you>/data/raw/ -m "new batch" capora runs # confirm it ran, and how it ended
Inside a run
The run script is read from the pinned save point and executed in a container honoring its shebang (bash / node / python3). The Space's secrets are injected as env vars, along with a per-run CAPORA_URL / CAPORA_PROJECT / CAPORA_TOKEN so the script can copy files down and write results back. Jobs run asynchronously, so long work is fine.
The runner ships node/python/git, the capora CLI, and Claude Code (claude) — pre-configured — so a workflow can do AI steps (edit files, review a diff, gather data, generate content) then capora cp the result back. No key setup: runs route through Capora's AI gateway on a per-Space key, all permissions are bypassed by default, and a built-in capora skill lets claude read/write your Space. Just call it headless: claude -p "…".
Agent automations
Keep the task's intent as a plain-language brief at .capora/automations/<name>.md and make the workflow's script a one-liner — claude -p "capora session start, then read .capora/automations/<name>.md and do exactly what it says, saving output back with the capora CLI". The agent logs in with a session (required before any write), then does the fetching, generation, error-handling and write-back; since the brief is a Space file, it can even refine it across runs.