ackt

Docs

What ackt is

ackt is a read receipt for your own pull request. An agent wrote the code; you opened the PR. ackt is the one place you say, with a passkey, that you personally read the diff. That claim is recorded against the exact commit.

It is explicitly not a quality claim. Attested doesn’t mean tested, reviewed by a second person, or free of bugs. It means one specific human looked at one specific commit and put a name behind having done so.

Install

Adding ackt to a repo is one workflow file: no app to install, no org admin to petition, no elevated access to grant beyond what this file itself requests.

name: ackt
on:
  pull_request:
    types: [opened, reopened, synchronize]
  issue_comment:
    types: [created, edited]

permissions:
  contents: read
  id-token: write
  statuses: write
  pull-requests: write
  issues: write

jobs:
  ackt:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # required — the Action needs real history to check ancestry, even though it fetches its own head SHA
      - uses: jrschumacher/ackt-action@v1
        with:
          service: https://ackt.dev
          fail-on-unattested: "false"

Two inputs: service (which ackt deployment to use) and fail-on-unattested (whether a missing attestation fails the check rather than just reporting it). The permissions: block is required. Without statuses: write, pull-requests: write, and issues: write, the job can’t post the status or comment, and a first install fails silently.

id-token: write is required too, and this one fails loudly: the Action identifies itself to ackt with a GitHub Actions OIDC token, minted per run by the runner, whose signed claims say which repository the run belongs to. That is what lets ackt answer for a private repository at all, and what records the check on your dashboard. It grants nothing about your code — only the ability for this workflow to mint identity tokens.

fetch-depth: 0 on the checkout matters for the same reason a shallow clone is usually fine and here is not: deciding whether a commit you attested earlier is still in this pull request’s history takes real history. A rebase or force-push removes it, and only git merge-base can tell that apart from a fast-forward.

There is no explicit ref: to get right, either. The Action resolves the pull request’s real head from the GitHub API itself — never the event payload, never whatever actions/checkout’s default happened to leave checked out (the ephemeral merge commit on a pull_request event, the default branch on an issue_comment event, neither of which is the pull request’s actual head) — and fetches that exact commit before comparing anything against it. That is the one line worth keeping, though: dropping fetch-depth: 0 still degrades ancestry results to unknown, since fetching the two endpoints by SHA doesn’t substitute for the history between them.

The Action itself lives at github.com/jrschumacher/ackt-action.

How it works

  • Enroll once. Sign in with GitHub (identity only; the OAuth scope is empty) and register a passkey: Touch ID, Face ID, Windows Hello, or a security key. That binds the passkey to your GitHub login.
  • Attest per commit. ackt comments on each pull request with a link. Opening it and completing the passkey ceremony, a biometric or PIN gesture rather than just a click, records an attestation against that exact commit.
  • The check. The Action queries ackt and posts a status named ackt / human-review. A new commit invalidates the old attestation; re-run the check with a /ackt comment or the comment’s checkbox.

What it doesn’t claim

ackt is not a security boundary. The status check is postable by anyone who can add a workflow to the repo. A self-attesting workflow could post ackt / human-review = success without ever touching ackt: no OAuth, no passkey, no record. That is an accepted trade, not an oversight. Anyone who can push a self-attesting workflow can also exfiltrate secrets or merge at will, so hardening this one gate specifically would solve the least interesting problem available. See the design note this trade is recorded in.

What isn’t weakened by that trade is the attestation record itself. It still requires OAuth identity plus a WebAuthn signature carrying the user-verification flag. An agent holding your GitHub token can push, comment, and call the API. It cannot produce your biometric.

The protocol

Briefly: the statement a passkey signs binds the repository, pull request, head commit, and your GitHub login into one canonical text block. Its SHA-256 becomes the WebAuthn challenge, so the signature is over this exact diff’s coordinates, not an arbitrary nonce. The passkey’s user-verification flag is the entire human-presence claim: proof the authenticator required a biometric or PIN, not just a tap.

Full detail, including the exact statement format and verification steps: github.com/jrschumacher/ackt/blob/main/SPEC.md.