Guides

carrick.json

Per-repo config that tells the scanner which env vars and domains name internal services versus third-party APIs.

carrick.json is a small JSON file you check in at the root of each indexed repo. It tells the Rust scanner how to classify outbound HTTP calls that were built from environment variables or URL prefixes. Without it, calls like fetch(${process.env.USER_SERVICE_URL}/users) cannot be resolved at scan time, and contract checking has nothing to compare them against.

This is different from your agent instruction file (AGENTS.md or CLAUDE.md). That file tells your coding agent when to call Carrick; carrick.json tells the scanner how to classify outbound calls. See Connecting your agent for the agent side.

Why it exists

When the scanner sees a call expression like fetch(process.env.ORDER_SERVICE_URL + '/orders'), it has the HTTP method, but not the destination. The env var might point at another service in your org (in which case Carrick should validate the call against the index) or at a third-party API like Stripe or GitHub (in which case Carrick should ignore the call entirely).

Carrick can’t guess which is which, so you declare it once per env var.

When you have not classified an env var, the action surfaces it as a warning in the PR comment:

Unclassified env var: POST /orders using [ORDER_SERVICE_URL] (from src/orders.ts) — add to internalEnvVars or externalEnvVars in carrick.json

Resolve the warning by adding the env var to the appropriate list and committing the file.

Schema

{
  "serviceName": "order-service",
  "internalEnvVars": ["USER_SERVICE_URL", "INVENTORY_API"],
  "externalEnvVars": ["STRIPE_API", "GITHUB_API"],
  "internalDomains": ["https://api.yourcompany.com"],
  "externalDomains": ["https://api.stripe.com", "https://api.github.com"]
}
FieldTypePurpose
serviceNamestringFriendly name for this service, used in MCP responses and the dashboard. Defaults to the repo name if omitted.
internalEnvVarsstring arrayEnv var names whose values point at other services in your org. Calls built from these are matched against the index.
externalEnvVarsstring arrayEnv var names whose values point at third-party APIs. Calls built from these are ignored by contract checking.
internalDomainsstring arrayFull URL prefixes for internal services. Calls whose target URL starts with one of these are matched against the index.
externalDomainsstring arrayFull URL prefixes for third-party APIs. Calls whose target URL starts with one of these are ignored.

All fields are optional. An empty carrick.json is valid but does nothing; the unclassified-env-var report will fire on every run until you populate at least one list.

Env vars vs. domains

Both env-var matching and domain matching work. Pick whichever fits how your code constructs URLs:

  • Env vars catch indirect patterns: fetch(${process.env.USER_SERVICE_URL}/users/${id}). You declare the env-var name, and the scanner classifies every call that uses it regardless of how the path is composed.
  • Domains catch URL literals: fetch('https://api.stripe.com/v1/charges'). You declare a prefix, and the scanner classifies any call whose target URL starts with that prefix.

Most production codebases lean on env vars for internal services and inline literals or SDKs for third-party APIs, so the typical mix is internalEnvVars for your own services and externalDomains for the third parties.

A monorepo with multiple services

If your repo holds more than one service (each with its own package.json), you can place a carrick.json next to each service’s package.json. Carrick finds every carrick.json in the repo and merges them: all the internalEnvVars together, all the domains together, and so on. The first non-empty serviceName wins.

For a polyrepo where each service is its own GitHub repo, the simpler pattern is one carrick.json at the repo root.

Example: a service with mixed dependencies

{
  "serviceName": "checkout",
  "internalEnvVars": [
    "ORDER_SERVICE_URL",
    "INVENTORY_SERVICE_URL",
    "USER_SERVICE_URL"
  ],
  "externalEnvVars": [
    "STRIPE_API_BASE"
  ],
  "externalDomains": [
    "https://api.sendgrid.com",
    "https://hooks.slack.com"
  ]
}

The checkout service in this example talks to three internal services (declared by env var), to Stripe (declared by env var because the base URL is configured per environment), and to SendGrid and Slack (declared by domain because the URLs are literal in source).

  • Quickstart. Where carrick.json fits into the initial setup.
  • PR comments. Where the unclassified-env-var warning appears.