Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.capy.sc/llms.txt

Use this file to discover all available pages before exploring further.

The bare capy command is the main loop. Run it any time and Capy:
  1. Authenticates you (if needed) via a browser-based login.
  2. Unlocks your master key by co-decrypting it with the Capy service.
  3. Pulls the latest encrypted keep.lock from the service.
  4. Diffs remote values against your local .env.
  5. Prompts you to resolve any conflicts.
  6. Rewrites .env in place with capy:… snippets that encode the resolved values.
The plaintext only exists in memory during the diff. It’s never written to disk.

First run vs. subsequent runs

On the first run in a project, Capy treats .env as authoritative:
  • Asks which organization and project to use (or creates them).
  • Generates a project encryption key via the zero-trust co-decrypt flow.
  • Writes keep.lock to the project root and commits it to git.
  • Creates a development branch.
  • Encrypts every value in .env and uploads the ciphertext.
  • Adds .env and .capy/ to .gitignore.
  • Installs git hooks (post-checkout and post-merge) that run capy status after branch switches and merges.
  • Writes a commented-out backup of your original .env to .env.pre-capy.old (gitignored) and rewrites .env with capy:… snippets.
On subsequent runs, Capy reconciles three sources: what’s on the server, what you last pinned, and what’s in your local .env. Conflicts open an interactive prompt where you pick a value per variable.

Conflicts and resolution

Every capy run is a three-way compare between:
  • Pinned - hashes stored in keep.lock from the last sync.
  • Local - what’s in your .env right now (decrypted in memory).
  • Remote - what’s on the service for this branch.
For each variable, Capy classifies the state from the triple (pinned, local, remote) and either applies it automatically or prompts you. Here’s every case, using A for one value, B for another, and for “absent”:

One side changed - auto-applied

PinnedLocalRemoteAction
AAAunchanged (not shown)
Apush A to remote (new locally)
Apull A to local (new on remote)
ABApush B to remote (changed locally)
AABpull B to local (changed on remote)
AApush deletion to remote (deleted locally)
AAprompt; typically pull the delete
When only one side moved, there’s nothing to reconcile and Capy just applies the change.

Both sides changed - conflict

PinnedLocalRemoteAction
ABCCONFLICT — pick local or remote
BCCONFLICT — same name, different origins
ABBboth agree on B — apply, no prompt
ABCONFLICT — you edited, remote deleted
ABCONFLICT — you deleted, remote edited
Adelete propagated — no prompt
On a conflict Capy prints a three-way diff table and then a bulk-action menu:
  Variable       Pinned              Local               Remote
  ────────────────────────────────────────────────────────────────────
  DATABASE_URL   postg...5432/old    postg...5432/dev    postg...5432/prod
  API_KEY        sk_li...abcd        sk_li...wxyz        sk_li...abcd

? What would you like to do?
❯ Commit all local values
  Retrieve all pinned values
  Retrieve all remote values
  Individually resolve
  Continue working
Pick Individually resolve to open an interactive arrow-key table where each variable has its own row and its own Choice:
  ← → select value   ↑ ↓ move between rows   Enter confirm   q cancel
  Resolved: 0/2

  Variable       Pinned              Local               Remote              Choice
  ──────────────────────────────────────────────────────────────────────────────────
  DATABASE_URL   postg...5432/old    postg...5432/dev    postg...5432/prod   local   
  API_KEY        sk_li...abcd        sk_li...wxyz        sk_li...abcd
  ← → select value   ↑ ↓ move between rows   Enter confirm   q cancel
  Resolved: 0/2

  Variable       Pinned              Local               Remote              Choice
  ──────────────────────────────────────────────────────────────────────────────────
  DATABASE_URL   postg...5432/old    postg...5432/dev    postg...5432/prod   local   
  API_KEY        sk_li...abcd        sk_li...wxyz        sk_li...abcd
← → moves the selection across pinned, local, remote, and delete; ↑ ↓ moves between variables; Enter confirms the row and jumps to the next unresolved one. Once every row is confirmed, Capy applies the merged set and rewrites .env in place. q cancels without writing anything.

”New on both sides” vs. “edit vs. edit”

A subtle distinction: if both sides independently added a variable with the same name but different values (pinned is ), Capy marks the conflict as isNew and the prompt frames it as two new candidates. If both sides edited a previously-synced variable (pinned is A), it’s framed as an edit conflict. The two look the same at the value level but have different provenance. Capy determines the difference by comparing resource IDs (the capy:{resourceId}:… prefix): different IDs mean the variables were created independently; the same ID means they share a common ancestor.

Remote unreachable

If the service is down or you lack access to the branch, Capy falls back to a two-way compare of pinned vs. local. It won’t destructively overwrite anything it can’t see, and it reports every affected variable with a ? marker (see capy status).

Status and push

capy status       # show drift between local, pinned, and remote
capy push         # push local changes without pulling
status is read-only and does not modify anything.

What ends up in git

  • keep.lock - a versioning manifest with no keys and no plaintext. Committed.
  • .env - ciphertext snippets at rest, but gitignored. Capy syncs the content over the wire instead of through git.
  • .env.pre-capy.old - commented-out backup of your original .env from before first-run. Gitignored.
  • .capy/ - local state (session tokens, cache). Gitignored.

Branches

Capy has its own concept of secret branches - independent sets of values for dev, staging, prod, or ephemeral environments. Secret branches are tracked separately from git branches, but post-checkout hooks can auto-switch them when your git branch changes. See Branches.

What’s next

capy (CLI reference)

Every flag on the sync command.

Branches

Isolate secrets per environment.