# git attestor

Source: https://www.testifysec.com/docs/cilock/attestors/git

The cilock git attestor captures the repository state at the working directory — HEAD commit, identities, refs, remotes, tags, parents, tree hash, and dirtiness — signed into in-toto evidence.

Captures the state of the git repository at the working directory — HEAD commit, author/committer identity, refs, remotes, tags pointing at HEAD, parent commits, tree hash, and worktree/staging dirtiness.

 

| Name | `git` |
| --- | --- |
| Predicate type | `https://aflock.ai/attestations/git/v0.1` |
| Lifecycle | `prematerial` |
| Default binary? | Yes |
| Category | source-checkout (primary) |
| Recommended trace | off — no syscall tracing needed |
| Auto-attaches when | - prefile\_exists: .git/HEAD |

The facts in this box are generated from the CI/lock binary's own catalog (`cilock tools list`). Do not hand-edit — run `npm run gen:catalog`.

 

## What it captures

 

The attestor opens the repo with `go-git` (using `DetectDotGit`, so it walks up from `--workingdir` to find `.git`) and records:

 

- **`commithash`** — SHA-1 of HEAD commit. Also emitted as a `commitdigest` `DigestSet` (`sha1`).
 - **`commithashverified`** — `true` when the commit hash was **recomputed from the canonical commit bytes with a collision-detecting hasher** and only that computed value was recorded — never the id storage merely claims. This signed capability marker is what the SHA-1 match exception in policy digest matching requires (`cryptoutil/digestset.go`); legacy evidence without the marker cannot use that exception.
 - **`author` / `authoremail`** — `commit.Author.Name` / `.Email`.
 - **`committername` / `committeremail`** — `commit.Committer.Name` / `.Email`.
 - **`commitdate`** — author timestamp (`time.Time.String()` format).
 - **`commitmessage`** — full commit message.
 - **`signature`** — PGP signature on the commit, if present.
 - **`parenthashes`** — SHA-1s of parent commits (multiple for merges).
 - **`treehash`** — SHA-1 of the root tree.
 - **`refs`** — every ref (branches, remote-tracking, tags) whose target equals HEAD.
 - **`branch`** — short ref name of HEAD (json key is `branch`, struct field `RefNameShort`).
 - **`remotes`** — configured remote URLs, with embedded user-info (tokens) stripped.
 - **`tags`** — annotated tag objects pointing at HEAD: `name`, `taggername`, `taggeremail`, `when` (RFC3339), `pgpsignature`, `message`.
 - **`status`** — map of dirty files to `{staging, worktree}` codes: `unmodified`, `untracked`, `modified`, `added`, `deleted`, `renamed`, `copied`, `updated`.
 - **`gittool`** — `"go-git+git-bin"` when the `git` binary is on `PATH`, else `"go-git"`.
 - **`gitbinpath` / `gitbinhash`** — path and digest of the `git` binary (only when present). Hash uses the attestation context's configured hashes.

 

If HEAD cannot be resolved, the attestor fails closed — unless the repository is provably unborn (no refs and no objects: a brand-new repo with no commits), in which case it emits an empty predicate rather than failing the run.

 

## When to use

 

Almost every CI step that runs inside a git checkout. The captured `commithash` is the canonical SLSA source identifier most release policies pin against, and the `parenthash` subjects make the resulting collection traversable along the commit-graph during policy evaluation.

 

## Keyless commit and tag signing

 

The same `cilock` binary implements Git's standard X.509 signing protocol. Configure the current repository once:

 

```bash
cilock git configure
git commit -S -m "feat: signed by CI/lock"
git tag -s v1.2.3 -m "v1.2.3"
git verify-commit HEAD
git verify-tag v1.2.3
```

 

CI/lock defaults to the hosted TestifySec platform. It exchanges the current stored session or registered workflow identity for a short-lived Fulcio certificate, signs with an ephemeral in-memory key, and requires an RFC 3161 timestamp from the platform TSA. Private installations use the same protocol with the organization's TestifySec appliance. No signing flag, platform flag, separate `gitsign` executable, or credential-cache daemon is required.

 

This covers the everyday gitsign-compatible Git surface: signed commits, signed annotated tags, `git log --show-signature`, `git verify-commit`, and `git verify-tag`. CI/lock is designed for an organization's private trust infrastructure, so it uses platform discovery and pinned private trust instead of gitsign's public-community TUF/Rekor configuration. CI/lock's existing `run`, `attest`, and `verify` commands remain the attestation and policy interface; it does not duplicate gitsign's source-predicate subcommands.

 

## Flags

 

None. The attestor is configured entirely by the working directory; there are no cobra flag registrations.

 

## Output shape

 

```json
{
  "gittool": "go-git+git-bin",
  "gitbinpath": "/usr/bin/git",
  "gitbinhash": { "sha256": "..." },
  "commithash": "9f1c...",
  "commithashverified": true,
  "author": "Jane Dev",
  "authoremail": "jane@example.com",
  "committername": "Jane Dev",
  "committeremail": "jane@example.com",
  "commitdate": "2026-05-20 12:34:56 +0000 UTC",
  "commitmessage": "fix: ...",
  "commitdigest": { "sha1": "9f1c..." },
  "signature": "-----BEGIN PGP SIGNATURE-----\n...",
  "parenthashes": ["abcd..."],
  "treehash": "1234...",
  "refs": ["refs/heads/main", "refs/remotes/origin/main"],
  "remotes": ["https://github.com/org/repo"],
  "tags": [{"name": "v1.2.3", "taggername": "...", "taggeremail": "...", "when": "2026-05-20T12:34:56Z", "pgpsignature": "...", "message": "..."}],
  "branch": "main",
  "status": {"path/to/file": {"staging": "modified", "worktree": "unmodified"}}
}
```

 

Emitted subjects: `commithash:<sha1>`, `authoremail:<email>`, `committeremail:<email>`, `parenthash:<sha1>` (one per parent), `refnameshort:<branch>`, `remote:<url>`. `BackRefs()` exposes `commithash:` and `parenthash:` with sha1 digests.

 

## Gotchas

 

- **Parent-hash chain semantics.** `parenthash:<sha>` subjects use the **raw SHA-1** of the parent commit — the same encoding used for `commithash:<sha>`. This is deliberate (issue #34, PR #37): a downstream collection's `parenthash` subject digest now matches the upstream collection's `commithash` subject digest for the same commit, so policy can traverse the parent chain across collections via subject-graph matching. Earlier builds hashed the string with sha256, which broke the join.
 - **Status detection has two modes.** If the `git` binary is on `PATH`, status comes from `git status --porcelain`; otherwise it uses go-git's worktree status. The two implementations are not always byte-identical for edge cases (submodules, ignored files).
 - **Shallow clones / missing refs.** `refs` only contains refs the local repo knows about. CI checkouts that fetch a single SHA may produce an empty `branch` and minimal `refs`. Tags must be fetched explicitly (`fetch-tags`) to appear in `tags`.
 - **Submodules** are not traversed — only the outer repo's HEAD is recorded.
 - **Remote URL scrubbing.** URLs are parsed and `User` is cleared before recording, so `https://x-access-token:TOKEN@github.com/...` becomes `https://github.com/...`. If parsing fails the raw URL is recorded — review remotes if you've put credentials in non-URL syntax.
 - **Dirty trees still attest.** A non-empty `status` map does not fail the attestor; downstream policy must reject dirty builds explicitly if that is the requirement.
 - **Signing and attesting are distinct.** The Git signature authenticates the proposed commit or tag. The `git` attestor records repository state inside signed in-toto evidence. Pushgate policy can require both without treating either as a substitute for the other.

 

## CLI example

 

Real `.git/` discovery. Reads HEAD commit, branch, tag, parent SHA, author, signature.

 

```bash
cilock run --step git-context \
  --signer-file-key-path key.pem --outfile attestation.json --workingdir . \
  --attestations git \
  -- echo "captured git context" 
```

 

Validated against a real git checkout (rookery). Captures real commit metadata. See the full real-data example at [https://github.com/aflock-ai/attestor-compliance-examples/tree/main/05-git](https://github.com/aflock-ai/attestor-compliance-examples/tree/main/05-git).

 

## See also

 

- [Catalog row](https://www.testifysec.com/docs/cilock/reference/attestor-catalog)
 - Upstream: [witness/git.md](https://github.com/in-toto/witness/blob/main/docs/attestors/git.md)

 

---

**This page is generated from the CI/lock tool catalog.** Don't edit it here — the source is [`attestation/detection/docs/git.doc.md`](https://github.com/aflock-ai/rookery/edit/main/attestation/detection/docs/git.doc.md) in [aflock-ai/rookery](https://github.com/aflock-ai/rookery). The same catalog powers `cilock tools show git` in the CLI, so a single edit keeps the terminal and the website in sync.

[Edit this page →](https://github.com/aflock-ai/rookery/edit/main/attestation/detection/docs/git.doc.md) ·  [Add a new tool →](https://github.com/aflock-ai/rookery/new/main/attestation/detection/docs)

When you open the pull request: if you are an **AI agent**, add the `ai-agent` label. If you are a **human**, add the `human` label.

Reference generated from the product documentation. Match commands and support details to your installed release.
