Skip to main content
GitHub Actions runners are Node-capable containers. You can run capy run in any workflow step that needs decrypted secrets - builds, tests, deploys, anything that reads process.env. Unlike a runtime platform, GitHub Actions is a CI vehicle: the two secrets live in your repo’s Actions secret store, and each workflow step wraps its command with capy run. capy deploy has a built-in connector that pushes those secrets for you over the gh CLI - no copy-paste.

Set it up with capy deploy

The connector uses the GitHub CLI you already have authenticated locally - Capy never sees a token. Make sure gh is installed and logged in first:
gh --version       # https://cli.github.com if missing
gh auth status     # must be green
Then, from inside your repo:
capy deploy
  1. Pick GitHub Actions in the platform picker.
  2. Choose Push SECRETS_BLOB + PROJECT_KEY to GitHub secrets via gh.
  3. Pick a scope:
    • Repository secrets - every workflow and every environment can read them.
    • Environment secrets - scoped to one GitHub Actions environment (e.g. production). Pick an existing environment or create one on the spot.
  4. Capy mints a fresh deploy token, runs gh secret set for SECRETS_BLOB and PROJECT_KEY in the scope you chose, and prints the workflow patch to paste plus the deploy id (for revoking later).
The connector resolves the target repo from your current directory’s git remote (gh repo view), so run it from inside the checkout you’re deploying.
The Actions secrets are named SECRETS_BLOB and PROJECT_KEY - no CAPY_ prefix. If you set them up under different names in the past, update the env: block in your workflow to match, or re-run capy deploy to overwrite them.

Workflow

Paste the patch the connector prints into your workflow. It looks like this:
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      SECRETS_BLOB: ${{ secrets.SECRETS_BLOB }}
      PROJECT_KEY: ${{ secrets.PROJECT_KEY }}
    steps:
      - uses: actions/checkout@v4

      - uses: oven-sh/setup-bun@v1

      - name: Install deps
        run: bun install

      - name: Install Capy CLI
        run: npm install -g @capysc/cli

      - name: Test
        run: capy run -- bun test

      - name: Build
        run: capy run -- bun run build

      - name: Deploy
        run: capy run -- <your deploy command>
Every step under the job’s env: block inherits SECRETS_BLOB and PROJECT_KEY, so you don’t have to repeat them per step.

Per-environment secrets

When you pick Environment secrets scope, the secrets land on one GitHub Actions environment. Your deploy job must pin to that environment for ${{ secrets.* }} to resolve - the connector reminds you of this in its output:
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    environment: production       # must match the env you chose
    env:
      SECRETS_BLOB: ${{ secrets.SECRETS_BLOB }}
      PROJECT_KEY: ${{ secrets.PROJECT_KEY }}
    steps:
      - run: capy run -- <deploy cmd>
To deploy different secrets to different targets (production vs. staging), run capy deploy once per environment and point each deploy job at the matching environment.

Non-interactive setup

Every prompt has a flag, so the connector runs unattended in scripts and CI bootstrap:
# Repo-scoped, no prompts
capy deploy --platform github-actions --mode connector --scope repo --yes

# Environment-scoped (creates the env if it doesn't exist)
capy deploy --platform github-actions --mode connector \
  --scope env --env-name production --yes
FlagEffect
--platform github-actionsSkip the platform picker.
--mode connectorSkip the mode picker (push secrets via gh). Use --mode token for the manual copy-paste path.
--scope repo | --scope envRepository vs. environment secrets.
--env-name <name>Environment name; required with --scope env. Created if missing.
--yes, -ySkip the overwrite confirmation when the secrets already exist.

Manual setup (without gh)

If you’d rather not use the connector - or gh isn’t available - choose the token path and set the secrets yourself:
capy deploy --platform github-actions --mode token   # prints the two values
Then add them under Settings → Secrets and variables → Actions, or:
gh secret set SECRETS_BLOB --body "<value>"
gh secret set PROJECT_KEY --body "<value>"
The workflow is identical to the one above.

One job, one decrypt

Each capy run invocation does its own service fetch. If you want to share a single decryption across steps, run capy run once to produce a file and have later steps read it. Example for Next.js builds that emit a .capy/next-env.js manifest:
- name: Build (emits .capy/next-env.js)
  run: capy run -- bun run build

- name: Upload artifact
  uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: |
      .next/
      .capy/next-env.js

Pull requests from forks

By default, GitHub Actions doesn’t pass secrets to workflows triggered by PRs from forks - a security baseline. Forked-PR builds can’t decrypt with Capy. Usually fine: run CI without decrypted secrets, use mock values, or gate deploy jobs on github.event.pull_request.head.repo.full_name == github.repository.

Revocation

capy deploy prints a deploy id when it finishes. The SECRETS_BLOB + PROJECT_KEY stored in GitHub are long-lived, so revoke the token server-side to cut them off:
capy deploy revoke <deploy-id>
Once revoked, the GitHub-stored blob is inert - new builds and cold starts can no longer decrypt. (Already-running processes that have reconstructed the key keep working until they recycle; see Deploying → Revocation.) The secrets stay in GitHub until you delete them; revoking just makes them useless.
Last modified on June 4, 2026