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.

Capy has two moving parts: the CLI on your machine, and the service that brokers the co-decrypt handshake. The CLI handles everything local - syncing, encrypting, invites, deploy setup - and spawns your app with decrypted env vars via capy run. The service holds only ciphertext and membership records.

The two components

Two-component architecture: the Capy CLI runs on developer machines and CI, syncs .env, invites, kicks, and wraps your app with capy run. The Capy service brokers the co-decrypt handshake and stores opaque ciphertext plus membership records. The service never sees plaintext.
The CLI is the only place plaintext values ever exist. The service only ever sees opaque ciphertext plus membership records.

On-disk state

Inside a Capy-managed project: Gitignored: .env, .env.pre-capy.old, .capy/. Committed: keep.lock.
my-project/
  .env              ← capy snippets
  .env.pre-capy.old ← plaintext backup
  keep.lock         ← versioning manifest
  .capy/
    config          ← last deploy platform
    decrypt         ← hex project key (CI)
And globally, in your home directory:
~/.capy/
  auth/
    session.json   ← refresh token
    sessions/      ← per-user cache
  orgs/
    {orgId}/
      users/
        {userId}/
          key.enc  ← wrapped master key
      projects/
        {projectId}/ ← cached
key.enc is the only long-lived secret on disk, and it lives only on the client. It’s AES-encrypted with an inner key derived from SHA256(userId || ":" || orgId) (never stored anywhere) and then outer-wrapped via the service’s /orgs/{orgId}/wrap endpoint. The service never stores a copy - to use key.enc the CLI presents it for co-decrypt, where the service strips only the outer layer.

keep.lock

keep.lock is a small JSON file that tells Capy which project this directory belongs to and what its current state is. It contains:
  • Org ID and project ID - which org and project this directory maps to.
  • Schema version - for format evolution.
  • Variable manifest - a sorted list of variable names with per-branch resource IDs and value hashes. Hashes, not plaintext, not ciphertext, not keys.
It doesn’t contain any keys, any plaintext, or anything sensitive. Committing keep.lock is what lets a teammate clone your repo, run capy, and sync the same secrets you’re working with. Active branch is tracked separately in .capy/branch (local state).

.env after Capy

Your .env after capy has run looks like:
DATABASE_URL=capy:aB3kF:iv+ct+tag-b64
STRIPE_KEY=capy:mP9zQ:iv+ct+tag-b64
REDIS_URL=capy:x7nYt:iv+ct+tag-b64
Each capy:… snippet is:
  • capy: literal prefix
  • {resourceId} - a 5-character hash stable for this {projectKey, varName} pair, used to diff without leaking plaintext
  • {blob} - base64 of iv || ciphertext || tag from AES-256-GCM under the project key
The snippets are inert without the project key. Even if someone sees them, they can’t tell if two projects share a value, because resourceIds are namespaced per project.

Git hooks

On first-run Capy installs two hooks:
  • post-checkout - runs capy status after you switch git branches, so you notice drift immediately.
  • post-merge - same, after git pull / git merge.
No pre-push hook. If an older Capy version installed one, capy cleans it out on the next run.

Sync engine

The sync engine is a three-way merge between:
  • Local - what’s currently in .env after any edits you’ve made.
  • Pinned - what was in .env the last time Capy saw it in sync with the remote.
  • Remote - what’s currently in the service’s blob for this branch.
For each variable, Capy picks automatically when only one side changed. When both sides changed (conflict), it prompts interactively. See Syncing secrets.

The wire

Every CLI request to the service is a plain HTTPS POST with a JSON body and an auth token. The bodies are small and the payloads are already-encrypted blobs - compromising the transport tells an attacker nothing they couldn’t get by compromising the service itself.

What’s next

Zero trust

Why two shares, and what each share holds.

Cryptography

Every client-side algorithm, key, and parameter.