garnet-org/action v2.3.0, the pinned release in the quickstart.
Workflow permissions
The action needs no
pull-requests: write, contents: write, checks: write, statuses: write, actions: write, or secrets: inherit. It posts no comment, no Check, sets no commit status, and writes nothing to the repository; that is a statement about this action, not about the App or any other consumer. The pull request comment is the GitHub App’s, posted under its own installation (changed in v2.3.0: earlier releases posted a fallback comment themselves and asked for pull-requests: write). A top-level permissions: {} with per-job grants keeps each grant scoped to the job that needs it, rather than handing the whole workflow the default token scope. Fork pull requests are a separate mechanism: on the standard pull_request trigger, they receive no repository secrets and no id-token: write.
These are the workflow’s grants to the action step. The GitHub App below is a separate install with its own permission set; installing it changes nothing in your workflow file. The table below is what the App asks for; what an installation actually granted is visible only in the installing organization’s settings. Recording does not depend on the App: the workflow grants above cover the action, the job summary and the profile in the dashboard on their own. The comment does.
GitHub App permissions
Installing Garnet Runtime Review shows GitHub’s permission screen. This is what each grant is used for, read from the public App registration (last changed 14 September 2026) and the control plane. The App asks for four grants and nothing under Checks or Workflows.Webhook events
The App subscribes toworkflow_run and repository. GitHub sends every App installation and installation_repositories as well. Deliveries go to api.garnet.ai, are checked against the webhook secret (X-Hub-Signature-256), queued, and deduplicated by delivery id.
The App does not subscribe to
pull_request or workflow_job.
A webhook payload names the repository and its visibility, the run, its conclusion and its head commit. It carries no code, no diff and no log.
One change is planned: when the App starts posting a Check Run for each recorded job (informational only, it never fails a check), it will ask for Checks, write, and GitHub will ask you to approve that one new grant.
Installing the App on a repository that runs the action
The App owns the Runtime Review comment; the action records. The App does not change what runs on the runner, what the sensor sends, or which jobs are recorded. Fork pull requests stay unrecorded with or without the App: the workflow there has no credentials, so nothing is recorded and the comment carries no fold for that job.Secrets
The action declares two token inputs,api_token and github_token, and reads no other entry in secrets.
- Where
api_tokengoes. The action writes it to/etc/default/jibril, owned by root, mode600, and the sensor reads it from there. It is not passed on a command line and the run log prints the file redacted. - What it is used for. Minting an Execution Profile into your Garnet organization is the use this path exercises; the token’s full authorized scope is defined by Garnet, not by this workflow. Rotate it under Settings → API Tokens in app.garnet.ai.
github_tokendefaults to${{ github.token }}. It is used bygh attestation verifyon the sensor release and to read the job’s status when no Execution Profile was produced. It is not used to post anything.- Fork pull requests receive no repository secrets and no
id-token: write, so the action has no credential. It skips recording with a warning that names the missing credential, writes the same explanation to the job summary, and the job runs unrecorded. Nothing to configure.
GitHub OIDC removes the secret. Leave
api_token empty and grant id-token: write: the action requests a short-lived GitHub OIDC token in the job and exchanges it with Garnet. A non-empty api_token always wins and no OIDC token is requested. Both paths are in the quickstart.What runs on the runner
The step downloads the Jibril sensor from garnet-org/jibril-releases over HTTPS, installs it withsudo, and starts it as a systemd service. The sensor runs as root because attaching eBPF programs requires it. Every eBPF program passes the kernel verifier before it loads.
- Pin the sensor.
jibril_versiondefaults tov2.17.0; set it explicitly so the sensor version is visible in the workflow file. An empty value onlatestlets the binary change under an unchanged action pin. - Integrity. The download is the release bundle at the tag you pin, fetched over TLS from GitHub. Before the sensor runs, the action verifies the bundle’s checksums and checks its signature with
gh attestation verifyagainstgarnet-org/jibril-releases. Ifgithub_tokenis unset or theghCLI is unavailable, attestation verification is skipped with a warning in the log. - Observe-only. The sensor reads kernel events. It runs no sidecar, no proxy, and reorders none of your steps. Errors the action catches are logged as warnings and the job continues; that is error handling in the action, not a guarantee about every possible sensor or runtime failure.
- Ephemeral. The post step stops the sensor and removes its configuration and credentials from the runner before the job ends. The stop waits for the sensor to flush its Execution Profile, bounded by
stop_timeout_seconds(default1800); on busy jobs this has been observed at 1–3 minutes. - Where it runs. Linux x86_64 runners with systemd and
sudo. On any other platform or architecture the step logs a warning and skips. Runner by runner: Coverage.
What leaves the runner
The sensor sends one Execution Profile per job toapi.garnet.ai over HTTPS. It carries:
- execution chains: process names, the recorded workflow step, and the working directory a process ran from;
- destinations: domains, IP addresses, ports, and protocols each outbound connection reached;
- runner facts: OS, architecture, kernel version, the workflow and job names, the run ID, and the commit.
What becomes public
The dashboard exposes recorded jobs to your Garnet organization. A Runtime Review comment is visible to whoever can read its pull request; on a public repository, that audience is public. A public run report is a separately published view. Confirm publication controls for your repository in the Garnet dashboard. A published public run report resolves anonymously at its exact?profile= permalink. A bare run URL or a wrong selector fails closed with a 404. Anyone with a published report’s URL can read it without signing in; the selector identifies the profile and is not authentication. Details: The public run report.