Postil

Security

A reviewer should not be able to compromise the repo it reviews.

Postil's design assumes the review pipeline is a high-value target. The principles below are structural, not policy: the App cannot push code because it never holds write-to-code credentials, and the gate cannot silently pass because failure is the default.

01 — Least privilege

Read your code. Write your checks. Nothing else.

PermissionLevelWhy
contentsreadFetch the PR diff at review time.
pull_requestswritePost inline review comments in one batched review.
checkswriteCreate and complete postil/gate and postil/review.
metadatareadResolve repository identity.
contents: writenever requestedA reviewer does not push code.

In a publicly reported August 2025 disclosure, security researchers described a remote-code-execution chain in a leading reviewer's pipeline that exposed installation credentials carrying write access across a large share of customer repositories.

The mitigation is not a patch, it is an architecture: hold the smallest credential set that does the job. Even a full compromise of a Postil installation token cannot push a commit, open a PR, or alter a workflow, because the App never holds that authority.

02 — Fail closed

An unreviewed head is not a passing head.

When the model returns invalid or ungrounded output, Postil retries one JSON repair, then emits a synthetic error finding and fails the gate. When the worker crashes or a review exceeds its ten-minute deadline, a watchdog completes postil/gate as failure — never neutral.

The worker owns the check-run ids from the moment the job starts, so there is no window in which a crashed review leaves a check hanging in_progress and merge-eligible.

Repositories can opt into gate.onError: advisory, which fails open on provider outages only; the default remains fail-closed.

Failure semantics

  • postil/gate on operational error: failure
  • postil/review on operational error: neutral, with the error summary
  • Clean PR: both green, zero comments

03 — Secrets

Keys are sealed at rest and write-only in the UI.

Bring-your-own inference keys are sealed with AES-256-GCM before they touch the database and are decrypted only inside the worker, at the moment a review starts. The settings form is write-only: a stored key can be replaced or removed, never read back out. Keys are never logged and never leave the worker except as the Authorization header to the endpoint you configured.

GitHub installation tokens are minted on demand from the App key, held in memory only, and expire within an hour. The App private key is provided via environment configuration and is never written to the database or to logs.

Credential lifetimes

BYO inference key
AES-256-GCM at rest, write-only
GitHub installation token
in-memory, ≤ 1 hour
GitHub App private key
env only, never persisted
Session cookie
set only after sign-in
Source code
never persisted

04 — Data handling

We store the review, not the code.

The control plane persists one artifact per review: the envelope — a JSON document with the summary, findings, token usage, and gate verdict. The diff is fetched by the CLI at review time, sent to the model endpoint you configured, and discarded with the process. There is no code cache, embedding index, or repository clone on our infrastructure. Self-hosted deployments send nothing to us: no telemetry, no license pings, no update checks.

Full detail in the privacy policy and the envelope schema.

05 — Disclosure

Report a vulnerability.

We operate coordinated disclosure. Report suspected vulnerabilities privately through GitHub Security Advisories; do not open a public issue or PR for a security report. We aim to acknowledge a report within 5 business days, keep you updated through remediation, and credit reporters who want it.

Researchers who report valid issues are listed, with their permission, in our security acknowledgments. Machine-readable contact is published at /.well-known/security.txt.

Where to report

Target: report acknowledged within 5 business days.

06 — Distribution integrity

What the install checksum does and does not guarantee.

The one-line installer downloads the prebuilt binary over HTTPS and verifies it against a SHA-256 checksum fetched over HTTPS from the same GitHub release. The checksum alone protects against a corrupted or in-transit-tampered download, not against a compromised release — so release artifacts are additionally signed with Sigstore keyless signing (cosign) via GitHub OIDC in release CI.

Keyless signing means there is no long-lived published key to manage or leak: the signature is bound to the certificate identity of the release workflow. When cosign is installed, the installer verifies the signature automatically; without it, verification falls back to the checksum. You can also build from source with cargo install --git https://github.com/postil-dev/postil-cli --locked, or cross-check the published SHA-256 on the releases page.