# Capora — CLI guide (for agents)

Capora is a **netdisk**: each user (and team) has ONE **Space** — a versioned
remote filesystem, reachable at <owner> (e.g. alice). Organize your work as
**folders (components)** inside the Space. You operate on it directly with
S3-style file commands via the **capora CLI**: **cp** files up/down/around, **ls**,
**cat**, **mkdir**, **rm**. Remote paths are written **remote://<space>/<path>**;
anything without that prefix is a local path. Every write is versioned
automatically — no pull/save dance. (Want git semantics locally? Just use git.)

**A Space is a base, not just storage.** It holds the user's files AND the system
that runs them. Under **.capora/workflows/** live automations (a trigger — cron,
file-change, or manual — plus one entry script) that turn the filesystem into a
*running system*: scheduled jobs, change-driven pipelines, app deploys. As an
agent, treat the Space as a place to **accumulate durable capability**, not just
drop outputs: when you and the user work something out, settle it into the Space
as reusable files (scripts in a folder, a workflow under .capora/workflows/,
config, a deployable app) so it survives the session and compounds. Over many
sessions this becomes the user's — or the team's — own reproducible operating
system, a home base you return to and extend. It's all plain files, so you build
and maintain that system with the same **cp / ls / cat / mkdir** you already use.

Base URL: https://capora.cc

---

## 0. Get the CLI

The CLI is a single self-contained binary (no runtime needed). One line installs
it to your PATH (detects OS/arch; /usr/local/bin, sudo fallback → ~/.local/bin):

    curl -fsSL https://capora.cc/install | sh
    capora help

To upgrade later, run **capora update** (replaces the binary in place with the
latest build; re-run with sudo if the install dir isn't writable) — or just
re-run the install line above.

Or download the binary for your platform directly:

    https://capora.cc/dl/capora-linux-amd64      https://capora.cc/dl/capora-darwin-arm64
    https://capora.cc/dl/capora-linux-arm64      https://capora.cc/dl/capora-windows-amd64.exe
    # chmod +x and move it onto your PATH as "capora"

## 1. Connect (get a token)

**Step 1 — get a token.** In the web console (https://capora.cc) open **Connect an agent**
and generate an access token (starts with `cpt_`). Tokens are per-agent, scoped
to your account, and revocable from the same screen.

**Step 2 — give the token to the CLI.** Two ways, in order of preference:

1. **`capora login` with the token (recommended, esp. for agents)** — saves it to
   `~/.capora/config` so every later command just works:

       capora login --token cpt_xxxxxxxxxxxxxxxxxxxxxxxx
       # (bare form also works: capora login cpt_xxxx)

2. **Environment variables** (stateless — nothing written to disk; good for CI or
   ephemeral shells). These take precedence over the saved config:

       export CAPORA_URL=https://capora.cc
       export CAPORA_TOKEN=cpt_xxxxxxxxxxxxxxxxxxxxxxxx

> The browser flow — plain `capora login` (opens a page to authorize) — is meant
> for a **human** at a terminal. An **agent** should not rely on it: get a token
> from the console and use `capora login --token …` (or the env vars) instead.

- The token authorizes writes and access to private Spaces. Reading a **public**
  Space needs no token.

## 2. Concepts

- **Space**: your **base** — one filesystem holding both your files and the
  system that runs them. Addressed by its GLOBALLY-unique name (slug) alone —
  like an S3 bucket. Your default Space's slug equals your handle (e.g. alice);
  organize work as folders (components) inside it.
- **remote:// addressing**: remote://<space>/<path> is a path in a Space (space =
  the global slug, no owner prefix); a path with no such prefix is local. A single
  cp can have one local side and one remote side (upload or download), or two
  remote sides (server-side copy). Omit the space (remote:/<path>) to use your
  default Space.
- **Writes are versioned**: every write (cp up, rm, mv, mkdir) records an
  immutable *save point*, straight to the server. You can list them, view
  differences, read a file at an old version, and restore to any of them.
- **Directories are first-class**: mkdir creates a real empty folder; rm of a
  folder cascades to everything under it.
- **Visibility**: projects are public or private. Private projects return 404 to
  anyone without access (owner or team member).
- **.caporaignore**: a gitignore-style file; matched local paths are not uploaded
  (node_modules/, dist/, *.log are ignored by default).

## 3. CLI commands

Invoke as "capora <command>" (a single self-contained binary on your PATH).
Env: CAPORA_URL, CAPORA_TOKEN.

### Account

- **login [--token <t>] [--code <c>]** — sign in, saving a token + default Space
  to ~/.capora/config. `--token <cpt_…>` saves a console-issued token (preferred,
  esp. for agents; a bare `capora login cpt_…` works too). `--code` exchanges a
  short code shown at /cli-login. Plain `capora login` opens the browser to
  authorize (for humans).
- **logout** — remove the saved credentials.
- **whoami** — the signed-in user + default Space + server.
- **space list** — your Spaces (personal + each team's).
- **version** — print the CLI version.
- **update** (alias **upgrade**) [--force] — replace the running binary in place
  with the latest build. Checks the server version first and does nothing if
  already current (`--force` re-downloads anyway). Re-run with `sudo` if the
  install dir isn't writable. Windows: re-download the .exe.

### Files (netdisk)

- **cp <src> <dst> [-m "message"] [--mirror]** — copy files or folders. Direction
  is decided by which side is remote://. By default only adds/overwrites — never
  deletes. Folder copies are rsync-style (keep the source's basename; a trailing
  slash on the source copies its *contents*). Dedup means unchanged bytes aren't
  re-sent. **--mirror** (alias --delete) also removes destination entries the
  source doesn't have, so the dest folder ends up identical to the source (rsync
  --delete), in one save point (restorable). Directory copies writing to a Space
  only (upload or same-Space remote→remote); refused on downloads.

      capora cp ./apps/todo remote://alice/apps        # upload a folder -> apps/todo/
      capora cp remote://alice/apps/todo/app.js ./     # download one file
      capora cp ./app.js remote://alice/apps/todo/     # upload back (a new save point)
      capora cp remote://alice/a.txt remote://alice/b.txt   # server-side copy

- **ls [remote://…]** — list one directory level.
- **tree [remote://…]** — full path list (cheap; no bytes).
- **cat remote://… [--at <save>]** — print a file's bytes to stdout (optionally
  at a past save point).
- **stat remote://…** — one file's size + hash.
- **find remote://<space> [glob]** — find paths under a Space by glob (the
  remote:// location comes first, the glob second). e.g. "**/*.md", "src/*.js".
- **mkdir remote://… [-m]** — create an empty directory.
- **rm remote://… [-m]** — delete a file, or a folder (recursive).
- **mv <from> <to> [-m]** — move/rename (zero bytes transferred).

      capora ls remote://alice
      capora find remote://alice "**/*.md"
      capora cat remote://alice/README.md

### History

- **log [remote://…]** — list save points (newest first).
- **show <save> --space <owner>** — files changed in a save point.
- **diff [from] [to] --space <owner>** — changes between two saves.
- **restore <save> [path] --space <owner>** — roll the Space (or a
  subtree) back to a save point (recorded as a new save point).

### Workflows

- **workflows** — list the Space's workflows + their trigger.
- **run <name>** — trigger a workflow once.
- **runs** — recent run history.

### Apps

- **app templates** — list official templates (from the public capora Space).
- **app init <name> [--template <t>] [--dir <d>]** — scaffold a template locally
  (default template: static-basic).
- **app deploy [-c capora.app.json]** — publishes to /x/<owner>/<app>. Deploys
  are **workflow-driven**: you can't publish from your machine directly. Save the
  app to the Space and its `on.change` deploy workflow builds + deploys it, so
  what ships is always a committed Space state. (This command performs the upload
  only when running *inside* that workflow container; run locally it just prints
  how to publish.) To publish: `capora cp ./<app> remote://<you>/apps/<app>`, or
  re-deploy the current save with `capora run <deploy-workflow>`.
- **app list** — recent deployments of the Space.

### Secrets

Per-Space, encrypted server-side, injected as env vars into workflow runs
(e.g. ANTHROPIC_API_KEY for a Claude Code step). The API returns names only.

- **secret list** — names of the Space's secrets.
- **secret set <NAME> [value]** — set one (value from arg, "-" for stdin, else prompt).
- **secret rm <NAME>** — remove one.

### Starter kits

Whole-Space scaffolds (workflows + scripts + folders + a README) so you don't
start from zero. Unlike `app init` (one app under apps/), `init` lays a starter
down at the current directory INCLUDING .capora/, so uploading it turns the Space
into a running system.

- **starters** — list official starter kits.
- **init <starter> [--dir <d>] [--force]** — scaffold one locally; review, then
  `capora cp . remote://<you>` to install it, and `capora run <workflow>`.

Starters ship with **manual** triggers (no armed cron) — enable a schedule
yourself per the starter's README. Example: `daily-brief` fetches sources,
summarizes them with Claude Code, and deploys a dated brief site (bring your own
ANTHROPIC_API_KEY via `capora secret set`; works key-free with a placeholder).

## 4. Typical workflow

    capora login             # sign in (saves ~/.capora/config)
    # …or for CI/agents: export CAPORA_URL=https://capora.cc ; export CAPORA_TOKEN=cpt_...

    capora whoami                              # confirm user + Space + server
    capora ls remote://alice                   # browse the Space
    capora cp remote://alice/apps/site ./site  # pull a folder down
    # ...edit files under ./site with normal tools...
    capora cp ./site remote://alice/apps       # push it back (a new save point)

Default Space: after login you can drop the space and write remote:/<path> (or
set CAPORA_PROJECT / pass --space <owner>). Different files edited by different
agents both survive; if two agents change the SAME file, the later write wins and
the older version stays in history.

## 5. Workflows

Workflows are what make a Space a *running system* rather than a folder of files:
each is a trigger + a script, so schedules, change-driven pipelines and deploys
all live and evolve as files. Accumulate them here and the Space becomes a
durable operating system you and the user keep extending across sessions.

A workflow is a file in the Space: **.capora/workflows/<name>.json**. Create/edit
it locally and publish it by copying it up with **cp** — there is no separate
deploy step. The name in the path is the workflow's name.

Config shape:

    {
      "on": { "schedule": "0 3 * * *" },   // or {"change":"src/**"}, or omit for manual-only
      "run": "scripts/build.sh"            // path to a script FILE in your Space
    }

- **on.schedule** — a 5-field UTC cron string (minute hour day-of-month month
  day-of-week; supports *, */n, a-b, a,b). Editing/removing the file re-arms (or
  stops) the schedule on the next save automatically.
- **on.change** — a path glob or array of globs (e.g. "src/**" or
  ["data/**","*.md"]). After any save whose changed paths match, the workflow
  runs (trigger "change"), pinned to that new save point. A run's own writeback
  does NOT re-trigger it (loop-guarded). Omit "on" for manual-only.
- **run** — the path to a script FILE in your Space (e.g. "scripts/build.sh"). It
  is read from the pinned save point and executed in a container honoring its
  shebang ("#!/usr/bin/env bash" | node | python3). The Space's secrets are
  injected as env vars, plus a per-run CAPORA_URL / CAPORA_PROJECT / CAPORA_TOKEN
  so the script can `capora cp` files down and `capora app deploy`.

The runner also ships **Claude Code** (the `claude` CLI), pre-configured so a
workflow can do AI-driven steps — edit files, review a diff, gather data, generate
content — then `capora cp` the result back. No key setup is needed: runs are
routed through Capora's AI gateway on a **per-Space key** injected automatically,
the image ships with **all permissions bypassed** by default (it's an unattended
sandbox), and a built-in **capora skill** teaches `claude` how to read/write your
Space. So just call it headless:

    claude -p "fix the failing test in scripts/, then run it"

(No --bare / --permission-mode / ANTHROPIC_API_KEY needed — that was the old
BYO-key flow. Pin a model with ANTHROPIC_MODEL if you like; the gateway routes
claude-* to the configured provider.)

CLI:

    capora workflows                   # list workflows + their trigger
    capora run <name>                  # trigger one now (records a run)
    capora runs                        # recent run history (newest first)

Example — a manual build workflow:

    mkdir -p .capora/workflows scripts
    printf '#!/usr/bin/env bash\necho building; date\n' > scripts/build.sh
    printf '{"run":"scripts/build.sh"}' > .capora/workflows/build.json
    capora cp scripts/  remote://alice/scripts  -m "build script"
    capora cp .capora/  remote://alice/.capora  -m "add build workflow"
    capora workflows                   # → build  [manual]  run scripts/build.sh
    capora run build                   # trigger; then: capora runs

### Agent automations

The most powerful workflows hand the whole task to the agent. Instead of scripting
every step, keep the *intent* as a plain-language file in the Space and let Claude
Code carry it out end to end — including saving the result back. Convention (three
files):

- **.capora/automations/<name>.md** — a free-form brief: what to do and where to
  put the output. Written for the agent (and for you). It's a normal versioned
  file, so you can read, diff and improve it over time.
- **scripts/<name>.sh** — a one-line stub that just points the agent at the brief:

      #!/bin/sh
      claude -p "You are running a scheduled Capora automation. First
      'capora session start --message "<name>"' to log into the Space (required
      before any write). Then read the brief with
      'capora cat remote://$CAPORA_PROJECT/.capora/automations/<name>.md' and carry
      out its instructions exactly — including saving any output back into the Space
      with the capora CLI (e.g. remote://$CAPORA_PROJECT/reports/…), as it directs."

- **.capora/workflows/<name>.json** — the trigger + the stub:

      { "on": { "schedule": "0 13 * * *" }, "run": "scripts/<name>.sh" }

Why this shape: the agent (not the shell) does the fetching, the content
generation, the error handling AND the write-back — so a brief like "fetch these
sources; if one fails, note it and continue; save an HTML report to
reports/news-<date>.html" is handled with real judgement. And because the brief is
itself a Space file, the agent can **refine it as it runs** — appending a short
change note when it finds a better source or format — so the automation improves
across runs instead of going stale. Say so in the brief when you want that.

Tip: `$CAPORA_PROJECT` is this Space's slug, so `remote://$CAPORA_PROJECT/…`
addresses it directly — use that for reads and write-backs.

## 6. App SDK (talk to your Space from app code)

When you build a deployed app (see "app deploy"), its Worker can read/write its
OWN Space and trigger workflows through a zero-dependency SDK — you do NOT wire
up any token. On deploy, Capora injects the credentials, so createClient(env)
just works.

Vendor the SDK (one ESM file) into the app before building/bundling:

    curl -fsSL https://capora.cc/sdk/capora.js -o capora.js
    # TypeScript types: https://capora.cc/sdk/capora.d.ts

Use it inside the app's Worker:

    // worker.js — inside a deployed Capora app
    import { createClient } from "./capora.js";
    export default {
      async fetch(req, env) {
        const capora = createClient(env);            // reads injected CAPORA_URL/SPACE/TOKEN + binding
        await capora.write("events/hit.json", JSON.stringify({ t: Date.now() }));
        const cfg = await capora.read("config.json"); // read a file
        await capora.run("process");                  // trigger a workflow
        return new Response("ok");
      }
    };

- **Injected on deploy**: CAPORA_URL, CAPORA_SPACE (plain), CAPORA_TOKEN (a
  **Space-scoped** token: read/write files + trigger workflows for THIS Space
  only — never your secrets, account, or other Spaces; re-minted each deploy),
  and a CAPORA_API service binding.
- **Why the binding**: a Worker can't fetch its own platform URL (Cloudflare
  self-loop, error 1042). The SDK detects env.CAPORA_API and routes through it.
  Outside an app (Node, a script) there's no binding — pass the credentials
  explicitly: createClient({ url, space, token }).
- **API**: list, stat, read, readBytes, write, remove, mkdir (files) +
  workflows, run, runs. write is content-addressed (unchanged bytes aren't
  re-sent) and records a save point, same as `capora cp`.
- This is what turns an app into an **inbound gateway**: receive a
  webhook/request, then write a file (fires an on.change workflow) or run one
  directly — the piece cron polling can't give you.

## 7. Patterns (compose the primitives)

Everything is "files + a triggered script", so scenarios are just compositions.
Treat the Space as somewhere to accumulate durable, reusable capability.

- **Scheduled job → published output**: a cron workflow fetches/computes (using
  Space secrets), writes files, and `capora app deploy`s a site. Each run is a
  save point = a dated archive. (reports, briefs, digests)
- **Change-driven pipeline**: `{"on":{"change":"data/raw/**"}}` — a save under
  data/raw/ triggers a script that writes data/out/. "Change X → run Y"
  (loop-guarded: a run's own writeback won't re-trigger it).
- **Agent base / skill library**: keep reusable scripts in `skills/`, SOPs in
  `playbooks/`, and wire them as workflows. Next session, `capora ls` the skills
  and `capora run` them instead of rebuilding — a versioned operating system.
- **Full-stack app, auto-deploy**: `app init` a template; an on-change workflow
  builds + deploys on every edit to apps/<name>/** → /x/<owner>/<app>.
- **Team ops hub**: a team Space with several workflows (standup digest, hourly
  metrics, health checks) feeding one internal dashboard app.
- **Scheduled sync / archive**: cron snapshots an external API into archive/;
  content-addressing dedupes and each run is a restorable version.

They compose: a workflow's output IS another workflow's on.change input (sync →
pipeline → dashboard), and your base's skills/ back them all.

## 8. Docking into a Space — read `.capora/AGENT.md` first

When you connect to a Space, **read `.capora/AGENT.md` before doing anything**.
It is the contract for *that* Space: directory layout (which folders are
authored vs machine-generated), conventions (save-point hygiene, secrets via
`capora secret`, what `.caporaignore` excludes), where reusable **Skills** live
and how to use them, and the dock-in steps. It is a plain file (the CLI syncs
`.capora/` as normal content) — the agreed entrypoint, like `CLAUDE.md` in a repo.

- **Do not assume a layout** — obey the one `AGENT.md` declares; layout is
  per-Space.
- If it references **Skills**, list `.capora/skills/*/SKILL.md` and read each
  `description` to pick what applies. Skills use the Claude Skill format
  (`SKILL.md` with `name` + `description` frontmatter + optional `scripts/`), so
  they are portable across agents. This gives a Skill system with no new engine —
  just files plus the `AGENT.md` contract.
- If `AGENT.md` is missing, you can create one: write the layout + conventions
  you establish, then `capora cp .capora/AGENT.md remote://<space>/.capora/AGENT.md`.

## 9. Sessions (record your work; see what other agents are doing)

A **capora session** is a work thread you manage — separate from your native
runtime session. Records live in `.capora/sessions/<id>.json`, readable by any
docked agent, so you can see what others are working on.

    # open a work thread (new id) or resume one (returns its context)
    capora session start --message "<what you're doing>"     # → cs_k7m2p9qx
    capora session start cs_k7m2p9qx                         # resume: prints intent + messages + actions

    # tag your writes so they're recorded on the session (also refreshes its 30m lease)
    capora cp . remote://<space>/<path> --session-id cs_k7m2p9qx
    # (or set CAPORA_SESSION=cs_… once, then omit the flag)

    # checkpoint — a short markdown note; put long results in files, link them in the text
    capora session message cs_k7m2p9qx "explored X; result in remote://<space>/notes.md"

    capora session list [--active]     # all sessions + who holds what + latest note
    capora session show cs_k7m2p9qx    # full journal of any session (read-only)

- No `end`: a session is held by a 30-minute lease that refreshes on activity and
  lapses when idle — then anyone can `start` (resume) it. One agent holds a given
  session at a time; others can always `show`/`list` it.
- A native session may open several capora sessions; one capora session may be
  continued across native sessions (even different agents) via `start <id>`.
- **Scope a session to one topic / intent.** When you move on to a different task,
  or the intent clearly shifts, `start` a **new** session rather than piling
  unrelated work into the current one — keep a session about one thing so its
  journal stays coherent. Resume an existing session only when continuing the
  same thread.
- **Writes require a session** — a Space is like an OS, a session is your shell:
  `cp`/`rm`/`mv`/`mkdir` refuse without one (pass `--session-id`, set
  `CAPORA_SESSION`, or just `session start` first — the id is remembered). **This
  holds inside workflow runs too**: a run must `capora session start` before
  writing, and the **server enforces it** (a run-token write with no live session is
  rejected) — so runs are no longer exempt. Only the session journal itself (writes
  under `.capora/sessions/`, i.e. `session start`/`message`) is allowed without a
  prior session, to bootstrap. `app deploy` is gated separately (run-only) and does
  not require a session. `CAPORA_REQUIRE_SESSION=0` skips the client-side check only
  (the server still enforces in runs).

## 10. Notes

- **Use the `capora` CLI for everything** — it is the supported interface for
  agents. (There is a raw HTTP API underneath, but drive it through the CLI.)
- Content-addressed storage: unchanged files are never re-uploaded or
  re-downloaded (dedup by sha256), so large Spaces stay cheap — `cp` up skips
  bytes already stored, and `cp` down skips files whose local copy already matches.
- A Space whose root has index.html is served as a website at
  https://capora.cc/site/<space> (public Spaces only).
