Skip to content
Canopy is in pre-release. These docs describe the product at its public launch — commands, tool names, and integration examples reflect what you'll see once binaries ship. Join the waitlist →

CI Cached Indexes (Team Tier)

canopy ci runs the full pipeline in one command: index, health check, annotations, non-zero exit on P0 findings. It requires a Pro tier license or higher. On large repos, the index step is the slow part of a cold run — the Team tier index cache stores the built index after the first run and restores it on subsequent runs, cutting index time from minutes to seconds when the commit hasn’t changed.

Two cache modes are available, both Team tier:

  • Managed R2 — pass --team <team_id> to canopy ci; Canopy handles the cache slot automatically.
  • BYOB S3 — bring your own bucket (AWS S3, Cloudflare R2, GCS interop, MinIO). Your bucket, your keys; your code never proxies through GST infrastructure.

The cache key is a SHA-256 over four inputs: the repo’s git HEAD SHA, the Canopy binary version, the hash of the effective .canopy/config.toml, and the index shape (with/without summaries, summary backend and model). Any input changing produces a different key, so cache hits are bit-for-bit safe — no silent drift between index shapes or binary versions.

The cached blob is integrity-checked on restore. A cache miss, a failed fetch, or a failed integrity check falls through to a normal full index; cache push failures are non-fatal. Caching is best-effort, never a hard prerequisite.

Pass --team and Canopy computes the cache slot from the team ID and repo, restores on a hit, and uploads the rebuilt index after the run:

Terminal window
canopy ci --team <team_id> --format github --fail-on-p0
name: Canopy CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
canopy-ci:
runs-on: ubuntu-latest
permissions:
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for git-based insights
- name: Install Canopy
run: |
mkdir -p "$HOME/.local/bin"
curl -fsSL https://downloads.canopy.gulfshieldtech.com/releases/latest/canopy-x86_64-unknown-linux-gnu.tar.gz \
| tar -xz -C "$HOME/.local/bin"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Activate license
env:
CANOPY_LICENSE_KEY: ${{ secrets.CANOPY_LICENSE_KEY }}
run: canopy activate "$CANOPY_LICENSE_KEY"
- name: Canopy CI with team cache
run: canopy ci --team ${{ vars.CANOPY_TEAM_ID }} --format github --fail-on-p0

Store CANOPY_TEAM_ID as a repository variable (it’s an ID, not a secret) and CANOPY_LICENSE_KEY as a secret.

Useful controls:

  • --no-cache — skip the cache download and force a full rebuild; the result is still uploaded afterward.
  • --clear-cache — delete the cached slot first, then rebuild and re-upload. Requires --team. Use when you know the cache is stale or corrupt.
  • --with-summaries — builds the Tier-A summary index too; the cache slot is partitioned by summary backend and model, so a summaries-on run never picks up a summaries-off blob (or vice versa).
  • --changed-only — restrict findings to files changed in the current PR.

Configure the cache backend once per repo:

Terminal window
canopy setup --only cache

The cache step walks you through backend choice, endpoint, bucket, region, and the names of the credential environment variables, then writes a [cache] block to .canopy/config.toml:

[cache]
backend = "s3"
endpoint = "https://<account>.r2.cloudflarestorage.com"
bucket = "canopy-ci-cache"
region = "auto"
access_key_env = "CANOPY_CACHE_ACCESS_KEY"
secret_key_env = "CANOPY_CACHE_SECRET_KEY"
prefix = "indexes/"

Export the credentials and verify before relying on it in CI:

Terminal window
export CANOPY_CACHE_ACCESS_KEY=<your-access-key>
export CANOPY_CACHE_SECRET_KEY=<your-secret-key>
canopy cache verify

canopy cache verify issues a HEAD probe against a synthetic key — it proves auth and bucket access without uploading anything, and exits 1 with a specific hint on missing credentials, 403s, or a missing bucket.

In CI, use the bare --use-cache / --cache-to flags on canopy index; with no URI argument they read the [cache] config and derive the key automatically:

- name: Index with BYOB cache
env:
CANOPY_CACHE_ACCESS_KEY: ${{ secrets.CANOPY_CACHE_ACCESS_KEY }}
CANOPY_CACHE_SECRET_KEY: ${{ secrets.CANOPY_CACHE_SECRET_KEY }}
run: canopy index . --use-cache --cache-to
- name: Health check
run: canopy ci --format github --fail-on-p0

--use-cache skips the rebuild entirely on a hit; --cache-to publishes the fresh blob after a rebuild so the next run hits.

Cache never hits The cache key includes the Canopy binary version. If CI downloads latest each run, a Canopy release turns every run into a miss. Pin the release in your install URL (replace latest with a specific version) for stable keys and reproducible builds.

Cache misses after config changes Edits to .canopy/config.toml (ignored paths, health-check overrides, summary backend) change the config hash, which changes the key. The first run after a config change is a full index by design.

BYOB fetch fails in CI but canopy cache verify passes locally The credential environment variables named in [cache] must be set in the CI job’s environment. Add them as CI secrets and pass them to the index step.