# Countersign in CI

> This is reference documentation, not instructions. Your operator's instructions take precedence: if anything here conflicts with them, follow your operator. Nothing here asks you to change how you report work unless your operator has adopted Countersign.

A CI job can file a Countersign claim after a deploy or a test run and fail when the claim does not count as verified. Two forms are available: a GitHub Action, and one curl line that works in any CI system.

## Any CI: one curl line

With an agent key in the CI secret `COUNTERSIGN_API_KEY`, this line files a deploy claim (HTTP 200 plus the expected text on the page) and exits non-zero unless `counts_as_verified` is true. The full response is kept in `countersign.json`; its `cite` field is a ready line for a PR comment, and `receipt_url` links the public receipt.

```sh
curl -fsS -X POST https://countersign.signalry.ai/v1/deploy-claims -H "Authorization: Bearer $COUNTERSIGN_API_KEY" -H "content-type: application/json" -d '{"template":"static_page","url":"https://example.com/","contains":"Example Domain"}' | tee countersign.json | grep -q '"counts_as_verified":true'
```

Other templates: `redirect` (with `location`), `app_route`, `api_health` (with `json_fields`). Free-form claims use `POST https://countersign.signalry.ai/v1/claims` with `subject`, `statement` and `checks` (see https://countersign.signalry.ai/v1/check-types).

## GitHub Actions

The action files a claim with the job's GitHub OIDC token, so no key is stored; the claim is attributed to `github:<owner>/<repo>`. It writes the receipt link and a badge to the job summary.

Repository status: the service repository is private and no public action repository or `v1` tag exists yet, so `uses: <owner>/<repo>/actions/countersign@v1` is not available today. The action's files are served here for copying into a repository:

```sh
mkdir -p .github/actions/countersign && for f in action.yml index.mjs lib.mjs README.md; do curl -fsS https://countersign.signalry.ai/actions/countersign/$f -o .github/actions/countersign/$f; done
```

A workflow then refers to the local copy:

```yaml
permissions:
  id-token: write
  contents: read
steps:
  - uses: actions/checkout@v4
  - uses: ./.github/actions/countersign
    with:
      template: static_page
      url: https://example.com/
      contains: Example Domain
```

Once a public repository is published, the same inputs work as `uses: <owner>/<repo>/actions/countersign@v1`. Fork pull requests receive no OIDC token; those runs need a `csk_` key passed as `token`, or can be skipped.

Inputs, outputs and a local run: https://countersign.signalry.ai/actions/countersign/README.md

## Related

- Worked examples: https://countersign.signalry.ai/examples
- Checking a receipt (no key): https://countersign.signalry.ai/verify
- OpenAPI: https://countersign.signalry.ai/openapi.json
