Skip to main content
The generated workflow already references every secret this action can use. Each one is skipped silently when unset, so the file runs exactly as init wrote it and nothing here is required. Adding a secret turns a feature on without editing any YAML.
The audit itself needs no credential at all. The deterministic layer costs nothing, calls nothing, and finds most drift. Everything on this page is optional.

Setting a secret

1

Open the repository's secrets

Settings → Secrets and variables → Actions → New repository secret. The name must match exactly, including case.
2

Paste the value, not a file path

GitHub stores the value encrypted and masks it in logs. It cannot be read back afterwards, only overwritten.
3

Re-run the workflow

Secrets are read at job start. A run already in progress will not pick up a secret added while it is running.
Or from the CLI, which is quicker when you are setting several:
An organisation secret (Settings → Secrets and variables → Actions at the org level) covers every repository you grant it to, which is the right choice for a model key shared across services. An environment secret is scoped to a deployment environment and needs environment: on the job; the generated workflow does not use one.

What each secret turns on

None of them is needed for the audit to run and comment on a pull request.

OPENROUTER_API_KEY

Turns on the stage that reads code and decides whether it matches a sentence in your documentation. Without it the run is free and still catches most drift.
1

Create the key

Sign in at openrouter.ai/keys and create a key. It begins sk-or-v1-.
2

Add credit

OpenRouter is prepaid. The evaluation in this repository — 16 cases, 159 model calls — cost $0.076, so a few dollars lasts a long time.
3

Set it

gh secret set OPENROUTER_API_KEY

Using a different provider

The client sends whatever model id it is given to whatever endpoint it is pointed at, so any OpenAI-compatible API works. Set base-url alongside the key:
The default is https://openrouter.ai/api/v1 with z-ai/glm-5.3-flash. Azure OpenAI, Together, Groq, Fireworks and a self-hosted vLLM all speak the same protocol. The secret’s name is yours to choose — only the input matters.

Keeping the bill small

workers controls parallelism, not spend. What controls spend is how many endpoints the model is asked about, and the deterministic layer already settles most of them before the model is called. Two further levers:
  • contract-middleware removes routes outside the published contract from the audit entirely, so no model call is ever made for them.
  • exclude-paths does the same for paths you name.

Slack

Sends a short message with counts and severities. Never endpoint paths — a channel holds people who cannot read the code.
1

Create an app

api.slack.com/appsCreate New App → From scratch. Name it, pick the workspace.
2

Enable incoming webhooks

Incoming Webhooks → toggle on → Add New Webhook to Workspace → choose the channel.
3

Copy the URL

It looks like https://hooks.slack.com/services/T…/B…/…. This URL is the credential; anyone holding it can post to that channel.
4

Set it

gh secret set CONTRACT_AUDIT_SLACK_WEBHOOK_URL

Telegram

1

Create the bot

Message @BotFather, send /newbot, follow the prompts. He replies with a token like 123456789:AA….
2

Find the chat id

Add the bot to the group (or message it directly), send it any message, then:
A group id is negative, e.g. -1001234567890. A direct-message id is positive.
3

Set both

Neither works without the other.
Both channels are gated on verification and on notify-min-severity, which defaults to high. A clean run stays silent rather than posting “nothing to report” every morning.

Your own sink

CONTRACT_AUDIT_WEBHOOK_URL receives the full JSON report as a POST — the whole findings array, not a formatted message. Intended for a database or a dashboard rather than a chat channel. CONTRACT_AUDIT_WEBHOOK_SECRET is sent with it as the X-Auditor-Token header so your receiver can tell a real delivery from anyone who guessed the URL:
Set the secret even if the URL is unguessable. An endpoint that accepts unauthenticated POSTs is one someone will eventually find.

Memory, for self-improvement

The ledger of which claims survived their test. It lives wherever your credentials point and nowhere else: nothing is stored in your repository, nothing ships inside the image, and no history is shared between projects. Five backends, chosen by the URL scheme: For a non-AWS S3 store, give the endpoint in the URL:

Cloudflare R2, end to end

1

Create the bucket

Cloudflare dashboard → R2 → Create bucket. Note the account id from the URL.
2

Create an API token

R2 → Manage R2 API Tokens → Create API token, permission Object Read & Write, scoped to that bucket. Copy the Access Key ID and Secret Access Key — the secret is shown once.
3

Set the secrets

4

Prove it round-trips

make memory-check writes a row and reads it back, so a misconfigured store fails here rather than silently keeping no memory.
Turning memory on also turns on the verification gate for your repository, because a claim with no verdict teaches nothing. Expect runs to take longer: the gate writes a test beside your code, runs it, and deletes it.

GITHUB_TOKEN

You do not create this one. Actions mints it per run, and the generated workflow declares what it needs:
If the comment step fails with Resource not accessible by integration, the repository default is capping the token: Settings → Actions → General → Workflow permissions → Read and write. A workflow cannot grant itself more than the repository allows.

Forks

This catches people out, and it is worth knowing before you test on one. A fork does not inherit the upstream repository’s secrets. If you forked a project to try this out, every secret above has to be set again on your fork. gh secret list on the fork shows what it actually has, which is nothing until you add it. A pull_request run from a fork gets no secrets at all, even secrets the base repository holds. This is a GitHub security boundary, not a setting: it stops a stranger’s pull request from exfiltrating your keys. So on a pull request from a fork the audit runs its deterministic layer, posts its comment, and makes no model calls — which is the correct behaviour, and worth expecting rather than debugging. Pull requests within one repository, and push runs, have the secrets.
Workflows are disabled on a new fork. Open the Actions tab and choose “I understand my workflows, go ahead and enable them” before anything runs.

Checking what is set

The run log states plainly which optional features were skipped, so a secret that was set under the wrong name shows up as the feature quietly not running:

Rotating and revoking

Secrets cannot be read back, so rotation is always “set the new value over the old one”. Overwrite with gh secret set <NAME>, then revoke the old credential at the provider — in that order, so no run lands in the gap. If a key leaks, revoke it at the provider first. Removing the GitHub secret stops this workflow using it but does nothing about the key itself.