Skip to content

C:\> Documentation

02

Ingesting your codebase

Contents

Codepanion indexes your source by receiving a gzipped tarball of your repo. You send it with a single curl call: no custom GitHub Actions, no CLI to install. You'll need an API key (or the keyless OIDC setup below); see getting your API key.

Creating the tarball

Pipe git ls-files into tar. This includes exactly the files tracked in your repo. Your .gitignore is the source of truth, so build artifacts and dependencies are excluded automatically.

bash
# Tar everything git tracks. Respects .gitignore, no extra config needed
git ls-files -z --recurse-submodules | tar -czf repo.tar.gz --null -T -

Tip: uncommitted changes won't be included, so commit (or stash to a branch) first. --recurse-submodules picks up submodule files; drop it if you only want the outer repo.

Adding context with .codepanion files

You can hand the agent extra context by committing .codepanion files anywhere in your repo: repo-wide (.codepanion), per-directory (src/payments/.codepanion), or per-file (src/payments/refund.ts.codepanion). They're indexed alongside your source, and the agent fetches the relevant ones on demand, so this is the place to record domain knowledge that isn't obvious from the code itself ("amounts are in cents," "Stripe for cards, Adyen for direct debit").

Sending the tarball

Post the gzipped archive to the ingest endpoint. Replace {tenantId} with your tenant ID: the identifier of your Codepanion account, and the only part of the URL that differs between customers.

bash
curl -X POST https://api.codepanion.app/api/ingest/{tenantId} \
  -H "Authorization: Bearer $CODEPANION_TOKEN" \
  -H "Content-Type: application/gzip" \
  -H "X-Commit-Sha: $(git rev-parse HEAD)" \
  --data-binary @repo.tar.gz --fail-with-body

X-Commit-Sha is the full 40-character commit the tarball was built from; run the command from the repository root so git rev-parse HEAD names it. --fail-with-body makes curl exit non-zero on an error and still print the response, which says what was wrong.

Where to find your tenant ID

Your tenant ID is a GUID that identifies your Codepanion account. Owners find it in Settings → API Keys, with a copy button and this command with your URL already filled in. The tenant ID alone grants nothing: an ingest call still has to present a valid API key (or a keyless OIDC token from an allow-listed repository), and a key only ever works for the account it was created in.

The API returns the snapshot ID and status; indexing then runs asynchronously (chunk → embed → store):

json
{
  "snapshotId": 42,
  "status": "pending",
  "codeBase": "default",
  "environment": null,
  "name": null
}

The snapshot moves pending → indexing → ready as the worker picks it up, and the agent only ever searches ready snapshots, so an upload in flight never leaves it answering from half a repository. If indexing hits an error it is retried automatically. A snapshot is marked failed only when the retries run out, or straight away when the archive holds no files, and the previous ready snapshot keeps serving either way.

Settings → Codebases shows the latest push to each codebase: its status, commit, how many files were indexed and how many were skipped as vendored, minified or binary, and the reason if it failed. History lists earlier pushes. When a push fails, your team's owners also get an email saying why; each owner can turn it off under Settings → Email Notifications.

Each account has three upload caps. By default they are 2 MB compressed, 10 MB uncompressed and 10,000 files. A push over any of them is refused with a 413 whose message names the cap it hit, and nothing is stored. The caps are raised together, per account, on request: tell us the size of your repository when you onboard and we set your account's limit to fit it. Each step up (4, 8, 16 or 32 MB compressed) allows five times its size uncompressed and 5,000 files per MB.

Tag an upload with an environment label and a snapshot name to keep QA or staging separate from prod; see environments.

Multiple repositories (codebases)

One Codepanion tenant can index more than one repository. Each upload lands in a codebase; tell us which one with the X-CodeBase header (or a codebase query parameter):

bash
curl -X POST "https://api.codepanion.app/api/ingest/$TENANT_ID" \
  -H "Authorization: Bearer $CODEPANION_TOKEN" \
  -H "Content-Type: application/gzip" \
  -H "X-Commit-Sha: $(git rev-parse HEAD)" \
  -H "X-CodeBase: billing-service" \
  --data-binary @repo.tar.gz --fail-with-body

Codebase names are lowercase letters, digits and hyphens (anything else is normalised, so acme/Widget API becomes acme-widget-api), and a codebase is created automatically the first time you ingest into it. Leave the header off and the upload goes to your default codebase, so single-repo setups need no configuration. With keyless OIDC ingest (below), each repository lands in a codebase named after it automatically. The agent searches across all your codebases by default and can scope an investigation to one.

One codebase per repository
Push as many repositories as you like. CI targets a codebase by its slug, and the agent searches across all of them unless you name one.
One codebase per repository
Screenshot of One codebase per repository. Push as many repositories as you like. CI targets a codebase by its slug, and the agent searches across all of them unless you name one.
Push as many repositories as you like. CI targets a codebase by its slug, and the agent searches across all of them unless you name one.

Settings → Codebases lists what you've pushed to, with the number of snapshots that indexed and the latest push for each, and lets you create one up front rather than waiting for the first ingest. A slug is permanent, because it's the address CI and the agent use, so only the display name can be renamed. Codebases can't be deleted, because their snapshots belong to them. Environment labels are independent of codebases: a qa label applies within whichever codebase you upload to.

CI example: GitHub Actions

A complete workflow that ingests on every push to main, using plain curl:

.github/workflows/codepanion-ingest.yml
name: Ingest codebase

on:
  push:
    branches: [main]

jobs:
  ingest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Create tarball
        run: git ls-files -z --recurse-submodules | tar -czf repo.tar.gz --null -T -

      - name: Send to Codepanion
        env:
          CODEPANION_TOKEN: ${{ secrets.CODEPANION_TOKEN }}
          TENANT_ID: ${{ vars.CODEPANION_TENANT_ID }}
        run: |
          curl -X POST "https://api.codepanion.app/api/ingest/$TENANT_ID" \
            -H "Authorization: Bearer $CODEPANION_TOKEN" \
            -H "Content-Type: application/gzip" \
            -H "X-Commit-Sha: $GITHUB_SHA" \
            --data-binary @repo.tar.gz \
            --fail-with-body --silent --show-error

Store CODEPANION_TOKEN as a repository secret and CODEPANION_TENANT_ID as a repository variable. --fail-with-body fails the step on an error and keeps the response in the log, so a refused push says which cap it hit or which header was wrong instead of just curl: (22).

Keyless ingest (recommended): GitHub Actions OIDC

GitHub's OpenID Connect removes the stored key. Your workflow proves its identity per run; Codepanion validates that short-lived token and checks the repository against an allowlist you manage in Settings → Keyless CI. This also removes API key rotation from the workflow.

.github/workflows/codepanion-ingest.yml
name: Ingest codebase

on:
  push:
    branches: [main]

permissions:
  id-token: write   # mint the short-lived OIDC token
  contents: read

jobs:
  ingest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: git ls-files -z --recurse-submodules | tar -czf repo.tar.gz --null -T -
      - name: Send to Codepanion (keyless)
        env:
          TENANT_ID: ${{ vars.CODEPANION_TENANT_ID }}
        run: |
          TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
            "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=codepanion-ingest" | jq -r .value)
          curl -X POST "https://api.codepanion.app/api/ingest/$TENANT_ID" \
            -H "Authorization: Bearer $TOKEN" \
            -H "Content-Type: application/gzip" \
            -H "X-Commit-Sha: $GITHUB_SHA" \
            --data-binary @repo.tar.gz --fail-with-body --silent --show-error

Add your-org/your-repo to the allowlist in Settings → Keyless CI. Keyless ingest stays off until at least one repository is listed. No repository secret needed, only the tenant ID variable.

Switching from an API key

Without an X-CodeBase header, a key-based push lands in your default codebase, but a keyless push lands in a codebase named after the repository. If you move a repository from a key to keyless as it stands, you end up with two copies of it: the new codebase keeps updating, and default stays frozen at the last key-based push. The agent searches every codebase, so it keeps finding that outdated code, and codebases can't be deleted. Add -H "X-CodeBase: default" (or whichever slug the key-based pushes used) to the keyless curl so it keeps updating the same codebase.

The first accepted push binds that allowlist entry to the repository's GitHub ID, and later pushes have to come from the same repository. An owner/repo name can be renamed, transferred, or released and claimed by someone else, so the name on its own is treated as a label. If you rename or transfer a repository, remove the old entry and add the new name.

Need help getting set up?

We do the first setup on a call with you: the CI step, the connection string, and a first investigation against your real code.

Or email us at hello@codepanion.app