Kai Ole Hartwig
pinup · documentation

Security

A dependency bot holds a token that can write to every repository it scans. And it reads configuration from all of them. These are the lines pinup holds, and what a repository can and cannot make the bot do.

  • Reports: security reports go to the address in the repository's SECURITY.md, not as an issue

The invariants

Every run produces a plan before any write

What changes is known in bytes before the first byte changes.

Apply consumes byte-range edits, never dependencies

Nothing downstream of the plan can invent a change; YAML and JSON are never re-serialised.

The core decides, plugins apply

A task runs in a scope it may not leave; a path outside discards its whole result.

The stricter of two labels wins

An analyzer loosens an automerge only with trustEffective: true.

Plugins get no credentials

A task sees PATH, LANG, TZ and whatever PINUP_PLUGIN_ENV names; never the platform token, never the signing key.

A plan explains why nothing happens

A held update carries its reason; a refused command is named, not skipped.

The platform token

The token is bound to the instance's host and, on GitLab, to the API paths the bot itself calls: releases, tags, raw files, packages, the group Composer registry, its own user. A URL a repository configuration names on the instance (a customDatasources template, a registryUrls entry) is requested without it. Otherwise any developer in any scanned repository could point the bot's api token at /api/v4/groups/<id>/variables and read the result as a “release list”.

Registry access exists only for the estate's own registry (PINUP_REGISTRY_HOST), exchanged at the instance's token realm over TLS, scoped to the one repository a lookup needs. Every other registry is asked anonymously.

The GitHub token is bound to api.github.com (or the Enterprise host). A redirect away from a platform's host is refused, not followed: net/http drops Authorization across hosts, a PRIVATE-TOKEN header it does not.

No token ever lands in a file or a URL. git asks for credentials through GIT_ASKPASS, which is the pinup binary itself, and it answers only a prompt for the platform's host.

What a repository can decide

A repository's own configuration file can disable itself, change versionings and registries, add rules, request tasks. It cannot:

Commits

Every commit is signed as the runner is configured (openpgp or ssh with PINUP_SIGNING_KEY), by an identity the key vouches for (CheckIdentity rejects a key that does not name the author). The run reads the platform's verdict on its own commits. Unsigned works only with the explicit word none.

A branch somebody else committed to is theirs: the run neither rebuilds it nor closes its request, and it says whose it is.

Reports

Security reports go to the address in the repository's SECURITY.md, not as an issue.

Next

Back to pinup

The overview: the problem, the fit check, the quickstart, every chapter.

pinup →
Back to pinup

Getting started

The binary, a token, the first plan, the first run, a CI job.

Read →
Getting started