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 enforced in code: 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
Minimal permissions for reviews, checks, and approvals.
| Permission | Level | Why |
|---|---|---|
| contents | read | Fetch the PR diff at review time. |
| pull_requests | write | Post inline review comments in one batched review. |
| checks | write | Create and complete postil/gate and postil/review. |
| issues | write | Reply to explicit commands on pull requests and issues. |
| members | read | Verify organization admins before recording approvals. |
| metadata | read | Resolve repository identity. |
| contents: write | never requested | A 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 architectural: 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
The gate never marks an unreviewed head as passing.
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/gateon operational error: failurepostil/reviewon operational error: neutral, with the error summaryClean 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 are sent only in the provider authentication headers required by the configured API interface. An optional private-gateway credential is sent in the additional configured header.
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
- BYOK inference
- 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
- Full diff or repository snapshot
- not persisted
04 — Data handling
The full diff is ephemeral; review records persist.
The retained source-derived review artifact is the envelope, a JSON document with the summary, findings, token usage, and gate verdict. The control plane also keeps the operational records required for check delivery, trigger audit, per-attempt usage accounting, account access, installations, and billing. The full diff is fetched at review time, sent to the applicable model endpoint, and discarded with the process. GitHub App BYOK reviews route through the worker to your configured provider. Hosted plans use Postil's configured provider path. Self-hosted deployments send nothing to us: no telemetry, no license pings, no update checks. There is no full diff cache, embedding index, or repository clone on our infrastructure.
The persisted envelope can include a relevant code excerpt in review output when it is needed to explain a finding.
A configured BYOK endpoint receives review input, including private code. Use only a provider you trust with that code.
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. 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.
We do not publish a separate GPG release key. Sigstore keyless signing keeps release identity tied to GitHub OIDC and avoids a static private key that would need storage, rotation, revocation, and out-of-band trust distribution.