Repository scanning works on public repositories with no configuration. A private one needs a token, and TRUSTIVAN stores it per git host — not per repository, and not per scan.

Store the token

Settings → Registries and repos → Repository credentials → Add credential. Over the API:
credentials:manage is required, and it is not grantable to an API key — this call needs a human session. A credential that could store or rotate a repository token could decide what your scans clone.

A repository token is not a registry token

github.com serves repositories; ghcr.io serves container images. They are different hosts, the tokens are different, and they are rotated on different schedules. Storing a ghcr.io token under github.com produces a scan that fails to clone, and the reason is not obvious from the error. That is why the two stores are separate: /registry-credentials and /git-credentials. A credential of the wrong kind is refused, not converted — the scanner will not present a git token to a registry, or a registry token to a git host.

Check it before a scan depends on it

Press Check access on the credential and name a repository, or:
You must name a repository. There is no host-level check, because there is no host-level question worth asking: a fine-grained GitHub token or a GitLab deploy token scoped to one repository grants nothing at the host level, so a host-level probe would report a working token as broken. Nothing is cloned. The check sends one smart-HTTP advertisement request — what git ls-remote sends, and the request that authenticates — so your source code never reaches the API host. The answer is in the body, never in the status code. A refused token is a successful request. Branch on access.ok. access.rejected separates “the host said no” from “we could not ask”. Only a rejection changes the credential’s stored status; an unreachable host leaves it alone, because marking a working credential invalid on a network blip is how scanning stops for a reason nobody can find.

Scan

Nothing special. The credential is selected by the repository’s host:
ref is a branch, tag or commit. It is a property of the scan, not of the asset: a repository scanned on main and on release/1.0 is one protected thing with two scans.

What happens to the token

It is encrypted at rest with the deployment’s keyring, decrypted for the duration of one clone, and:
  • never placed in the clone URL. A URL with a userinfo section is copied into the checkout’s own .git/config and echoed in every git error message.
  • never put in the process environment. The environment is inherited by every subprocess of the worker, including credential helpers that a repository being scanned can configure.
  • never passed as a command-line argument. argv is world-readable on a shared worker.
  • never sent to a redirect target on another host. Go’s own redirect rule keeps an Authorization header across a redirect to a subdomain, so a 302 from example.com to attacker.example.com would carry the token. TRUSTIVAN drops it on any host change, exact match only.
  • never logged. Errors name the credential’s id — which identifies a row, not a value — so a failed clone still tells you which token to rotate.
Where a clone shells out to git, the token is written to a 0600 credential-store file whose path is on the command line and whose contents are not, and the inherited helper list is cleared first so no helper on the worker can answer before it.

When a credential goes missing

A scan that was told to use a credential and cannot resolve it — deleted, disabled, or the wrong kind — fails. It does not fall back to an anonymous clone. An anonymous clone of a name that also exists publicly scans somebody else’s code and reports its findings as yours, which is a worse outcome than a failed scan. A repository with no stored credential for its host is cloned anonymously, which is correct: most repositories scanned are public.

Rotating

Rotate in place, from the panel or PUT /git-credentials/{id}/secret. The host cannot change during a rotation — that would be a new credential wearing an old one’s history. The status returns to unverified until the next check or scan proves the new token. There is no endpoint that reads a stored token back, at any permission. A lost token is rotated, not recovered.