Contributing to Caracal

June 1, 2026 · View on GitHub

Prerequisites
ToolVersion
Node.js24+
pnpm11.1.1
Docker + Compose v224+
Go1.26+
Python3.14+
Bunlatest
  • <os>linux · darwin · windows
  • <arch>x64 · arm64
Modes
DevRCStable
PurposeDevelopment buildsrc buildsReleased production versions
Version2026.05.27-dev.sha<sha>2026.05.27-rc.1 / 0.1.3-rc.12026.05.27 / 0.1.3
Container imageslocalhost/caracal-{svc}:2026.05.27-dev.sha<sha>ghcr.io/garudex-labs/caracal-{svc}:v2026.05.27-rc.1ghcr.io/garudex-labs/caracal-{svc}:v2026.05.27

Setup

git clone https://github.com/Garudex-Labs/caracal.git && cd caracal
pnpm install
pnpm caracal up                     # Build and start the full stack

# Essential Commands
pnpm caracal --help                 # Show runtime shell help and available lifecycle commands
pnpm caracal status                 # Check health status of all services
pnpm caracal down [--help]          # Stop the stack
pnpm caracal purge                  # Remove stack, volumes, logs, cache, and installed data
Drop the `pnpm` prefix
pnpm link --global            # Install global symlink
pnpm unlink --global caracal  # Remove global symlink

Console

pnpm caracal console          # Human-facing product management

Standalone execution

pnpm caracal run -- <command> reads validated runtime config from platform environment variables or an explicit runtime profile, exchanges the configured application credentials with STS, injects only the configured scoped resource-token environment variables into the child process, and executes without a shell. The stack does not create zones, applications, client secrets, or runtime profiles. Create a managed application with credential type token, copy the one-time client secret from the create result, and store the local secret and credential manifest under the OS Caracal config directory.

Run example workloads from their example directory:

cd examples/ResearchAgent
cp env.example .env
$EDITOR .env
. .env
pnpm caracal run -- node agent.mjs

The .env file contains only the zone and application identifiers. Local authentication still comes from the auto-detected app client secret at ${XDG_CONFIG_HOME:-$HOME/.config}/caracal/runtime/<zone-id>/<application-id>/client-secret, and resource injection comes from the sibling credentials.json manifest.

Control API (optional)

The Console is the primary human product-management surface. The control service is an optional OAuth-protected HTTP API for approved external clients that need to drive Caracal programmatically.

Clients authenticate by exchanging the Control key credentials for a token whose audience matches CONTROL_AUDIENCE, which defaults to caracal-control. Create the key from the Console Control menu; Caracal generates client_secret and shows it once in the create result. Store it, then drive the enabled Control API from the workflow or client that will use it in production.

Tests

pnpm test                                    # full suite (ts + go + py)
pnpm run test:typescript | test:go | test:python

scripts/testCi.sh mirrors .github/workflows/test.yml locally:

scripts/testCi.sh                # full suite (ts + go + py + docs)
scripts/testCi.sh --smoke | --go | --py | --ts

Submitting Changes

  1. Create a branch from main and keep changes focused.
  2. Keep the scope minimal (few files/components, small commits).
  3. Run a quick local sanity check:
  • pnpm caracal up
  • pnpm caracal status
  • pnpm caracal console
  • pnpm caracal down
  1. Ensure tests pass:
  • pnpm test
  • scripts/testCi.sh --smoke (post-commit parity)
  • scripts/testCi.sh (daily-check parity)
  1. Commit with a clear message and open a PR.

Releases

Release artifacts share one CalVer: vYYYY.MM.DD (.N for same-day re-cuts). Only .github/MAINTAINERS can run release workflows. Stable releases require release-approval from a different maintainer.

Create dev builds

Use dev builds only for development:

pnpm --dir apps/runtime build:release                          # stamp dev + build local images + bun compile (all targets)
pnpm --dir apps/console build:release                          # stamp dev + bun compile (all targets)
BIN="$(pwd)/apps/runtime/dist/caracal-<os>-<arch>"                 # absolute path; survives cd
CONSOLE="$(pwd)/apps/console/dist/caracal-console-<os>-<arch>"         # Console binary; same OS/arch matrix
pnpm caracal down                                          # Stop dev before testing
    "$BIN" --version                                           # → caracal 2026.05.27-dev.sha<sha> [dev (sha <sha>)]
    "$CONSOLE" --version                                           # → caracal-console 2026.05.27-dev.sha<sha> [dev (sha <sha>)]
(cd /tmp && "$BIN" up && "$BIN" status && "$CONSOLE" && "$BIN" down)

build:release stamps dev binaries and local localhost/caracal-{svc}:<base>-dev.sha<sha> images. Do not use dev builds downstream.

Release flow

Use the same flow for rc and stable: plan, dry-run, publish, validate. rc proves the exact artifacts downstream; stable promotes the approved release.

Steprcstable
Preparescripts/release.sh rc prepare --base-version 2026.05.27 --suffix rc.1scripts/release.sh stable --dry-run
ReviewCommit the generated manifest and metadata.Review the stable diff and generated version.
Dry-runscripts/release.sh rc dry-run --base-version 2026.05.27 --suffix rc.1 --local for local simulation; remote dry-run requires the rc commit on the selected ref.scripts/release.sh stable --dry-run
PublishTag and push v2026.05.27-rc.1.scripts/release.sh stable
ValidatepostReleaseValidation.yml runs after release.postReleaseValidation.yml gates stable promotion.
# rc
scripts/release.sh rc prepare --base-version 2026.05.27 --suffix rc.1
git add -A && git commit -m "rc: v2026.05.27-rc.1"
scripts/release.sh rc dry-run --base-version 2026.05.27 --suffix rc.1 --local
git tag -a v2026.05.27-rc.1 -m v2026.05.27-rc.1
git push origin HEAD && git push origin v2026.05.27-rc.1

# stable
scripts/release.sh stable --dry-run
scripts/release.sh stable

Remote rc dry-runs dispatch release.yml without publishing. They only read the default branch or the exact release tag ref, and the working tree must be clean unless --allow-dirty is used deliberately.

Post-release validation

postReleaseValidation.yml checks registries, archives, installers, containers, and provenance. npm, PyPI, binary, and installer checks cover Ubuntu, macOS, and Windows.

Reproduce one area locally:

CARACAL_RELEASE=v2026.05.27-rc.1 FINDINGS_DIR=/tmp/findings \
  bash scripts/postRelease/validateRegistryMetadata.sh

Package publishing

pnpm release:plan --since v2026.05.14
pnpm release:stamp:check
gh workflow run publishNpm.yml -f package=changed -f dryRun=true -f runner=ubuntu-24.04
gh workflow run publishNpm.yml -f package=changed -f runner=ubuntu-24.04
gh workflow run publishPypi.yml -f package=changed -f dryRun=true -f runner=ubuntu-24.04
gh workflow run publishPypi.yml -f package=changed -f runner=ubuntu-24.04

Protected workflows are the normal path for npm and PyPI. They read release.config.json, ignore examples/**, publish changed packages, include exact-pin dependents, preflight Ubuntu/macOS/Windows, and publish once from the selected runner. Use baseRef to override the diff base and package=all only for deliberate full publishes. Local stable publishing requires approval and CARACAL_ALLOW_LOCAL_STABLE_PUBLISH=1.

Published artifacts

npm:    @caracalai/{core,oauth,admin,identity,revocation,sdk,
                    transport-mcp,transport-a2a,
                    mcp-express,mcp-fastmcp,tokenstate-postgres,revocation-redis}
pypi:   caracalai-{core,oauth,identity,revocation,sdk,transport-mcp,mcp-fastmcp,revocation-redis}
ghcr:   ghcr.io/garudex-labs/caracal-{api,sts,gateway,audit,coordinator,redis}

Browse: npm · PyPI.

Rollback

Never delete a published tag. Roll forward with a new CalVer tag. The floating vYYYY.MM image tag moves with the new cut; pinned v<calver> tags are immutable.

Security

Do not file public issues for vulnerabilities. See SECURITY.md.

License

Apache-2.0. By contributing you agree your contribution is licensed under the same terms (LICENSE).