Capora Overview Use cases Install CLI Deploy SDK Agents Paths & API Database Agent docs Sign in
Documentation

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 — schedules, change-driven pipelines, and app deploys, all defined and maintained as files. Session after session with your agents, it accumulates into your — or your team's — own operating system: a home base you keep building on, not a pile of outputs. (Every write is versioned; a single change can build and deploy an app.)

A Space is a base, not a bucket. Files are just the substrate. What you actually build — driven entirely by the filesystem — is a living system: .capora/workflows/ holds the automations that run it, folders hold the components/apps, save points hold its whole history. Everything you and your agents work out over many sessions settles here as a durable, reproducible operating system you (or your team) return to.

The model in one minute

Space
Your base — one content-addressed filesystem that stores your files and the system that runs them. Globally-unique name (like an S3 bucket), so remote://<space> addresses it with no owner prefix.
Save point
Every save writes an immutable version. Diff, restore, or pin a deploy to any of them — history is free.
Workflow
A trigger (file change / manual / cron) + one entry script. Runs in a container with your secrets + a scoped token.
App
A folder with a capora.app.json. Deploys to /x/<you>/<app> as a Worker + static assets.
Template
Start from a real, hosted template (Vite+React+Tailwind+shadcn, static, API, full-stack) with one command.
Agent-native
The same CLI + a scoped token let an AI agent copy files in and out, edit, and deploy on your behalf — safely.

What you can build

Everything below is just the same primitives composed — state = files, logic = a triggered script. If you can express it as "some files + a script run on a schedule / on change / on demand", you can build it.

Daily brief → live site
A cron workflow fetches sources (using your secrets), writes a report, and app deploys a site. Each run is a save point = a dated archive.
Change-driven pipeline
Drop data into data/raw/; an on-change workflow transforms it and writes data/out/. "Change X → run Y", loop-guarded.
Your agent's base
Accumulate skills/*.sh, playbooks, and workflows across sessions — a reusable, versioned operating system your agent extends and reruns.
Full-stack app, auto-deployed
app init from a template; an on-change workflow builds & deploys on every edit — "change code = ship" with no CI to wire.
Team ops hub
A team Space with several workflows (standup digest, hourly metrics, health checks) feeding one internal dashboard app.
Scheduled sync / archive
A cron workflow snapshots an external API into archive/. Content-addressing dedupes; every run is an immutable version you can restore.
They compose. Because it's all files in one Space, a workflow's output is another workflow's on.change input — the sync feeds the pipeline, which feeds the dashboard; your base's skills/ back them all. Not isolated features — a composable, file-driven control plane.

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
Human at a terminal? plain 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 cp ./apps/todo remote:///apps        # upload a folder → remote apps/todo/
capora ls remote:///apps                    # browse the remote tree
capora cp remote:///apps/todo/app.js ./     # pull one file down
# …edit ./app.js with any tool…
capora cp ./app.js remote:///apps/todo/     # push it back → a new save point

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 [--code <code>]sign in (saves a token to ~/.capora/config)
capora logoutremove saved credentials
capora whoamicurrent user + current Space + server URL
capora space listlist the Spaces you can reach
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 workflowslist workflows + their trigger
capora run <name>trigger a workflow once
capora runsrecent run history
Apps
capora app templateslist official templates
capora app init <name>scaffold an app + deploy workflow · --template <t> --dir <d>
capora app deploydeploy the app here · -c capora.app.json
capora app listrecent deployments
Secrets · starter kits
capora secret set <NAME> [value]set a Space secret (injected into runs) · secret list / secret rm
capora starterslist official starter kits
capora init <starter>scaffold a whole-Space starter locally (incl. .capora/) · --dir <d> --force

Global: --space <space> to target a Space from anywhere. Env: CAPORA_URL, CAPORA_TOKEN, CAPORA_PROJECT. Run capora help for the built-in cheat sheet.

Starter kits — pull & go

Don't start from zero. A starter is a whole-Space scaffold (workflows + scripts + folders + a README) you pull, review, and upload. init lays it at your current directory including .capora/, so uploading it turns your Space into a running system.

capora starters                             # list official starter kits
capora init daily-brief --dir ./brief
cd brief && capora cp . remote:// -m "init daily-brief"
capora run daily-brief                       # fetch → summarize (Claude Code) → deploy a site

daily-brief fetches your sources, summarizes them with Claude Code, and deploys a dated brief to /x/<you>/brief (bring your own key: capora secret set ANTHROPIC_API_KEY …; works key-free with a placeholder). Starters ship with manual triggers — arm a cron yourself per the README (no surprise container spend).

Workflows

A workflow is a file at .capora/workflows/<name>.json: a trigger plus one entry script.

# .capora/workflows/nightly.json
{
  "on": { "schedule": "0 3 * * *" },   // or {"change":"data/**"}, or omit for manual
  "run": "scripts/nightly.sh"          // a script in your Space
}

Triggers: manual (capora run), change (on.change path globs, fired on matching saves), and cron (on.schedule, 5-field UTC). Jobs run asynchronously, so long builds are fine.

The runner container 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.

Deploy an app

Deploying is just a workflow that builds your app and calls capora app deploy. Scaffold one from a template locally, then copy it up:

capora app templates                       # list official templates
capora app init myapp --template vite-react-tailwindcss-shadcn
capora cp ./myapp remote:///apps       # upload → the change workflow builds + deploys
# → live at https://capora.cc/x//myapp

app init writes an app folder with a capora.app.json (declares an assets directory and/or a worker entry), a deploy.sh, and a change-triggered workflow. On save the workflow builds in a container and hands the artifact to the platform, which uploads a Worker + real static assets — your build never holds any cloud keys.

Deploys are workflow-driven. You can't publish from your machine directly — the platform only accepts a deploy from inside a workflow run, so what ships is always a committed Space state (every deployment ties to a save point). Publish by saving (capora cp ./myapp remote://<you>/apps) → the on.change workflow deploys; re-deploy the current save with capora run <deploy-workflow>.

Templates: vite-react-tailwindcss-shadcn, static-basic, worker-api, fullstack. One app can be a static site (assets only), a backend API (worker only), or full-stack (both — a worker for /api/* plus static assets).

App SDK — talk to your Space from app code

A deployed app can read/write its own Space and trigger workflows from inside its Worker — via a zero-dependency SDK. This is what turns an app into an inbound gateway: receive a webhook/request, drop a file (fires an on.change workflow) or call one directly.

# vendor the SDK (single ESM file) into your app, then build/bundle it
curl -fsSL https://capora.cc/sdk/capora.js -o capora.js
// 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 + service binding
    await capora.write("events/hit.json", JSON.stringify({ t: Date.now() }));
    await capora.run("process");                    // trigger a workflow
    const cfg = await capora.read("config.json");    // read a file
    return new Response("ok");
  }
};

On deploy, Capora injects a Space-scoped token (read/write files + trigger workflows — never your secrets or other Spaces) plus a service binding, so createClient(env) just works. API: list · stat · read · readBytes · write · remove · mkdir · workflows · run · runs. TypeScript types at https://capora.cc/sdk/capora.d.ts. Outside an app (Node, etc.) pass createClient({ url, space, token }).

Agents — .capora/AGENT.md

A Space is a home base for you and your agents. .capora/AGENT.md is the file every agent reads first when it docks in — the equivalent of CLAUDE.md / AGENTS.md in a repo, but living inside the Space so it travels with the data and is shared across every framework (Claude Code, Codex, pi, …). It is a plain file — nothing special server-side; create it and cp it up.

Put in it whatever an agent needs to work here safely:

Layout
What each folder is, and which are authored (edit freely) vs generated (machine output — don't hand-edit).
Conventions
Save-point hygiene, secrets via capora secret, what .caporaignore excludes, how to restore.
Capabilities by convention
e.g. "Skills live in .capora/skills/<name>/ as SKILL.md + optional scripts/." Agents obey AGENT.md, so you get a Skill system with no new engine — just files plus the agreed contract.
# create the contract, then cp it up (the CLI syncs .capora/ as normal content)
capora cp .capora/AGENT.md remote://<space>/.capora/AGENT.md

Layout is per-Space: different Spaces can define entirely different structures — the only rule is that AGENT.md describes the layout that Space actually uses, and agents honor it. When docking, an agent should read .capora/AGENT.md before acting; if it references Skills, list .capora/skills/*/SKILL.md and read each description to decide what applies.

Skill format. Use the Claude Skill layout — SKILL.md with name + description frontmatter — so accumulated skills are portable: Claude Code can consume them directly, other agents parse the same frontmatter.

Paths & API

Pages

GET /sign in / register (landing)
GET /appyour Spaces (home)
GET /app/<owner>[/<space>][/…]Space browser — Files, Workflows, Apps
GET /x/<owner>/<app>[/…]a deployed app (Worker + static assets)
GET /site/<owner>/<space>[/path]serve a Space's files directly (raw)
GET /docsthis page
GET /docs/cli(.md)CLI reference as markdown (for agents)
GET /dl/capora-<os>-<arch>prebuilt CLI binary (what /install downloads)
GET /installinstall script (pipe to sh)
GET /healthhealth check

HTTP API — account

Auth is a bearer token: Authorization: Bearer <CAPORA_TOKEN> (or the cap_sess cookie in the browser).

POST /api/register{email, password, handle, name} → creates a user + default Space
POST /api/login{email, password} → { token }
POST /api/logoutclear session
GET /api/mecurrent user
GET /api/me/overviewyour Spaces + summary
POST /api/me/tokensmint a CLI/agent access token (PAT)
GET /api/me/tokens · POST …/<id>/revokelist / revoke tokens
POST /api/teams · …/<slug>/memberscreate a team / add members
GET /api/owners/<owner>/spaceslist an owner's Spaces

HTTP API — a Space

All under /api/p/<owner>/<space>/. Reads on a public Space need no token.

Read
GET …/manifest[?path=&at=]full path → [hash, size]
GET …/list?dir=[&at=]one directory level
GET …/stat?path= · …/file?path=one file's metadata / bytes (Range ok)
GET …/find?glob= · …/commits · …/commit/<id> · …/diff?from=&to=search + history
Write (token)
POST …/check-blobs · PUT …/blob/<sha256>upload content-addressed bytes
POST …/save · …/restorewrite a save point / roll back
Workflows · apps · secrets
GET …/workflows · POST …/workflows/<name>/runlist / trigger
GET …/runs · POST …/runs/<id>/finishrun history / container completion callback
POST …/deploy · GET …/deploymentsdeploy an app / deployment history
GET/POST/DELETE …/secretsmanage secrets (values encrypted; only names returned)

Database

Capora runs on TiDB (MySQL-compatible). Every table is cap_-prefixed; IDs are short prefixed strings. File bytes live in R2 (deduped by sha256) — the DB keeps only metadata.

Accounts & ownership
cap_usersaccounts — handle (unique namespace), name, email, password_hash
cap_tokensCLI/agent access tokens (PAT) — stores only the token's sha256 + prefix/label; revoked_at
cap_teamsshared namespaces (like an org) — slug, owner_user_id
cap_team_membersteam membership — team_id, user_id, role (owner|member)
cap_spacesa Space — owner_type(user|team)/owner_id, slug, visibility (public|private), head_commit_id. Namespace = <owner>/<slug>
Content-addressed storage
cap_blobsbyte-store metadata — hash (sha256, PK), r2_key, size, content_type. Bytes live in R2, deduped by hash
cap_commitssave points — parent_id (linear chain), tree_hash (→ a blob holding the {path:[hash,size]} tree manifest), message, author_id
cap_filesworking tree (current state) — one row per live file: path/dir/name, blob_hash, size. Updated in place by save
Workflows · apps · secrets
cap_runsone row per workflow run — workflow name, trigger_src (manual|schedule|change), status, note, commit_id (pinned save point)
cap_deploymentsone row per deploy — app, script_name, status (deployed|failed), url, commit_id
cap_secretsper-Space secrets — name + value_enc (AES-GCM encrypted). The API returns names only; values injected into runs
Full CLI reference (markdown, for agents) Sign in / create a Space Home