Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.1password.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
GITHUB_APP_ID=op://Private/CovenCat/App ID
GITHUB_WEBHOOK_SECRET=op://Private/CovenCat/Webhook Secret
GITHUB_APP_PRIVATE_KEY=op://Private/CovenCat/Private Key
COVEN_PUBLICATION_SIGNING_SECRET=op://Private/CovenCat/Publication Signing Secret
COVEN_GITHUB_POLICY_PATH=./coven-github-policy.json
COVEN_GITHUB_STATE_DIR=./coven-github-state
COVEN_CODE_BIN=coven-code
COVEN_RUNTIME_ISOLATION=bwrap
COVEN_RUNTIME_EXTERNAL_ISOLATION=verify-host-controls-before-setting
COVEN_GITHUB_REVOCATION_EVENTS=verify-live-app-before-setting
COVEN_BWRAP_BIN=/usr/bin/bwrap
COVEN_RUNTIME_ROOTFS=/opt/coven-runtime/rootfs
COVEN_RUNTIME_NETWORK=shared
COVEN_CODE_BIN=/usr/local/bin/coven-code
7 changes: 5 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ pull requests against this repo. This is the agent-specific layer; read

## Repo-specific invariants (don't break these)

- This is a **thin hosted forwarder**, not the app logic. Familiar/authority and
GitHub-App behavior lives in `coven-github` — don't reimplement it here.
- This is the **hosted deployment adapter**, not the canonical product worker.
Familiar/authority rules, result contracts, and GitHub App product behavior
live in `coven-github`; keep only deployment-specific webhook verification,
evidence capture, runtime invocation, and safe GitHub transport here. Do not
fork or redefine the canonical contracts in this repository.
- **Never commit webhook secrets, signing secrets, App private keys, or tokens.**
Configuration comes from the deploy environment, not the repo.
- **Always verify the GitHub webhook signature** (`X-Hub-Signature-256`) before
Expand Down
98 changes: 93 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ The adapter is deployment-specific. It is not the canonical Rust worker
implementation; it exists so hosted webhook behavior can be reviewed,
reproduced, and changed through PRs instead of server-only edits.

Long-running tasks execute outside the HTTP thread so GitHub deliveries are
acknowledged promptly and health endpoints stay responsive. Queued or
interrupted tasks are discovered again when the service starts. Native review
deployments must subscribe the GitHub App to both `pull_request` and `push`, and
must enable `pull_request.synchronize`, `pull_request.edited`,
`pull_request.reopened`, and the actionless `push` trigger. These deliveries
reconcile stale decisive reviews after head changes, base retargeting, reopening,
or new commits on a PR's base branch. A `publication.mode=comment` route that
lacks any of these safety triggers fails closed instead of publishing native PR
reviews.

## Files

- `src/adapter.ts` - webhook handler, task router, task runner, PR evidence
Expand All @@ -32,17 +43,60 @@ The deployment expects secrets and mutable state to be supplied outside git:

- `GITHUB_APP_ID`
- `GITHUB_WEBHOOK_SECRET` or `WEBHOOK_SECRET`
- `COVEN_PUBLICATION_SIGNING_SECRET` - optional dedicated HMAC key for review
identity markers; defaults to the webhook secret for compatibility
- `COVEN_PUBLICATION_PREVIOUS_SIGNING_SECRETS` - comma-separated prior marker
keys retained only during rotation/reconciliation
- `GITHUB_APP_PRIVATE_KEY_PATH` or `.coven-github-private-key.pem`
- `COVEN_GITHUB_STATE_DIR`
- `COVEN_GITHUB_POLICY_PATH`
- `COVEN_CODE_BIN`
- `COVEN_RUNTIME_ISOLATION=bwrap` - required for every non-demo task; unset is
fail-closed and never falls back to direct execution
- `COVEN_RUNTIME_EXTERNAL_ISOLATION=network-egress-and-resource-limits-verified`
- mandatory declaration that the deployment independently enforces egress,
CPU, memory, PID, and disk/scratch limits
- `COVEN_GITHUB_REVOCATION_EVENTS=pull-request-and-push-verified` - set only
after verifying the installed App's live `pull_request` and `push`
subscriptions; native PR publication fails closed without it
- `COVEN_BWRAP_BIN` - absolute host path to bubblewrap (defaults to
`/usr/bin/bwrap`)
- `COVEN_RUNTIME_ROOTFS` - dedicated credential-free runtime rootfs
- `COVEN_CODE_BIN` - absolute coven-code path inside that rootfs
- `COVEN_RUNTIME_NETWORK=shared` - explicit opt-in required when the Codex
provider needs network access; the default is `none`
- `COVEN_REVIEW_FIX_LOOPS` - optional bounded review-fix loop count, clamped
between `0` and `5`; defaults to `0` so hosted repair loops are opt-in
- Codex OAuth tokens under the deployed account's `.coven-code` directory

Do not commit private keys, webhook secrets, OAuth tokens, generated task state,
workspaces, or attempt artifacts.

Real tasks never execute directly as the webhook account. Before minting a
GitHub token, the adapter runs a bubblewrap probe and verifies read-only input,
writable workspace/output mounts, a private PID namespace, no network for
validation, and a dedicated rootfs that does not contain adapter state or
credentials. A missing binary, rootfs, executable, or usable user namespace
records the task as `runtime_isolation_unavailable` with no direct fallback.
`publication.mode=record_only` is not an isolation control.

The runtime rootfs must contain the configured `coven-code`, `git`, and shell
executables plus their libraries, CA/DNS files, and approved runtime assets. It
must not contain the GitHub App key, webhook state, policy, parent home, or Codex
token store. The runtime receives only its dedicated model credential; it never
receives a GitHub token or Git askpass helper. Shared networking is not an
egress-confidentiality boundary, so use a dedicated, revocable model credential
and an externally enforced allowlist that blocks loopback, LAN, and metadata
services. The adapter refuses real tasks unless the external network/resource
isolation declaration is present.

The current runtime passes that model credential to `coven-code`; an untrusted
checkout can therefore try to consume or encode it through the model channel
even when ordinary egress is filtered. Treat this release as trusted-repository
only. Public/untrusted pull requests require a separately constrained worker and
a quota-limited credential broker that never exposes a reusable model token to
the repository process. Do not set the external-isolation declaration for that
use case until those controls exist.

## Local runtime

Install dependencies, build TypeScript, and start the webhook service:
Expand Down Expand Up @@ -95,6 +149,16 @@ Deployments should provide `coven-github-policy.json` through
`COVEN_GITHUB_POLICY_PATH`. That file is intentionally ignored because it is
environment-specific.

Every route with `publication.mode=comment` must include all native-review
safety triggers: `pull_request.synchronize`, `pull_request.edited`,
`pull_request.reopened`, and `push`. The `push` key is actionless; do not write
`push.`. Both the `pull_request` and `push` webhook events must also be enabled
on the installed GitHub App. Missing policy coverage is a configuration error
and native publication remains fail closed. After verifying the live App
registration, set
`COVEN_GITHUB_REVOCATION_EVENTS=pull-request-and-push-verified`; this declaration
is required in addition to the policy trigger list.

Start from [`config/example-policy.json`](config/example-policy.json) and the
connection guide in
[`docs/coven-github-connection.md`](docs/coven-github-connection.md).
Expand All @@ -106,10 +170,34 @@ connection guide in
signed delivery -> policy route -> delivery/task/result state without calling
GitHub or `coven-code`.
- Captures PR checkout metadata and changed-file patches before invoking
`coven-code`.
- Publishes visible structured review evidence, including `reviewed_files`,
`supporting_files`, findings, test evidence, no-findings rationale, and
limitations.
`coven-code`, including paginated file lists and patch-completeness checks.
- Publishes PR results as native GitHub reviews: complete no-finding evidence
approves, actionable findings request changes, and incomplete or
contradictory evidence is published as a comment review. Findings are made
inline only when their captured diff location is valid; all other findings
remain in the review body. Decisive reviews are bound to the captured commit
and require full changed-file coverage, a clean matching checkout, and
verified passing test evidence. They are created pending, then submitted only
after fresh head and base checks; a concurrent revision change causes a
COMMENT downgrade or automatic dismissal. Passing claims are decisive only
when they match successful host-captured validation receipts. Validation and
post-run Git checks execute in a second credential-free, network-disabled
sandbox.
- Uses repository-scoped installation tokens: parent Git gets only
`contents:read`, PR evidence gets read authority, and publication write
authority is minted only after isolated execution has finished.
- Persists `publication_pending` before GitHub writes and resumes interrupted
publication on startup or duplicate webhook delivery without rerunning the
agent.
- Persists publication identities and review/comment IDs in the configured
state directory, reconciles HMAC-signed App-authored identities with GitHub,
and serializes publication per PR so retries and concurrent runs do not
duplicate output.
Newer reviews link to superseded covencat output and dismiss its prior
decisive state when GitHub permits it.
- Publishes non-PR task results and operational notices as issue comments,
including structured `reviewed_files`, `supporting_files`, findings, test
evidence, no-findings rationale, and limitations.
- When `COVEN_REVIEW_FIX_LOOPS` is greater than `0`, reruns `coven-code` with
prior structured review findings as explicit repair instructions until no
findings remain or the configured loop count is exhausted.
6 changes: 5 additions & 1 deletion config/example-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
"enabled_triggers": [
"issues.labeled",
"issue_comment.created",
"pull_request_review_comment.created"
"pull_request_review_comment.created",
"pull_request.synchronize",
"pull_request.edited",
"pull_request.reopened",
"push"
],
"trigger_labels": [
"coven:fix",
Expand Down
83 changes: 80 additions & 3 deletions docs/coven-github-connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,40 @@ Set the manifest webhook URL to this service:
https://your-host/webhook
```

The manifest subscribes to the events this adapter can route:
The adapter can route these GitHub App webhook events:

- `issues`
- `issue_comment`
- `pull_request`
- `pull_request_review`
- `pull_request_review_comment`
- `check_suite`
- `check_run`
- `push`

For every route using `publication.mode: comment`, the installed GitHub App must
subscribe to both **Pull request** (`pull_request`) and **Push** (`push`) events.
The route's `enabled_triggers` must contain all four safety triggers:

- `pull_request.synchronize` for new commits or force-pushes on the PR head.
- `pull_request.edited` for base-branch retargeting.
- `pull_request.reopened` to reconcile a PR when it becomes active again.
- `push` to reconcile open PRs when new commits land on their base branch. Push
deliveries have no `action`, so the policy key is exactly `push`, not `push.`.

These deliveries let the adapter dismiss signed, decisive covencat reviews
whose reviewed head/base pair is no longer current. Native publication fails
closed if any required policy trigger is absent. `doctor:app` reports each
missing trigger as an error. Keep the route on `record_only` until both the
policy and App subscriptions are complete. After checking the live App settings,
set `COVEN_GITHUB_REVOCATION_EVENTS=pull-request-and-push-verified`; this explicit
deployment declaration is also required for native PR publication.

Treat the manifest file and the live App registration as separate checks:
ensure `docs/app-manifest.json` in `OpenCoven/coven-github` lists both events,
then verify the installed App's settings also subscribe to **Pull request** and
**Push**. Updating a manifest file does not retroactively update an existing
GitHub App registration.

After creating the App, keep these values outside git:

Expand Down Expand Up @@ -68,6 +94,11 @@ Then replace:
- `bot_usernames` with the GitHub App bot login, for example
`coven-cody[bot]`.
- `trigger_labels` with labels that should start tasks.
- `enabled_triggers` with the exact event/action pairs allowed to spend compute,
plus actionless `push` where needed. Events not listed are acknowledged and
ignored. A `publication.mode: comment` route must include all four
native-review safety triggers listed above; otherwise publication fails
closed.
- `familiar` with the familiar id, display name, model, and skills to pass to
`coven-code`.

Expand All @@ -79,7 +110,14 @@ export COVEN_GITHUB_POLICY_PATH="$PWD/coven-github-policy.json"

Keep `publication.mode` as `record_only` for first smoke runs. Switch it to
`comment` only after you have verified the App installation, `coven-code`
runtime, Codex token, and workspace permissions.
runtime, Codex token, workspace permissions, both required GitHub App webhook
subscriptions, and all four required safety triggers.

`record_only` controls publication, not process isolation. Non-demo work stays
blocked until the mandatory runtime sandbox below passes its executable probe.
For decisive native reviews, also configure a bounded list of trusted
validation commands under `publication.validation_commands`; a runtime-authored
claim without a matching successful sandbox receipt is published as COMMENT.

## Runtime Checklist

Expand All @@ -92,12 +130,51 @@ export GITHUB_WEBHOOK_SECRET="replace-with-github-secret"
export GITHUB_APP_PRIVATE_KEY_PATH="$PWD/keys/coven-github.private-key.pem"
export COVEN_GITHUB_POLICY_PATH="$PWD/coven-github-policy.json"
export COVEN_GITHUB_STATE_DIR="$PWD/coven-github-state"
export COVEN_CODE_BIN="$(command -v coven-code)"
install -d -m 700 "$COVEN_GITHUB_STATE_DIR"
export COVEN_RUNTIME_ISOLATION="bwrap"
export COVEN_RUNTIME_EXTERNAL_ISOLATION="network-egress-and-resource-limits-verified"
export COVEN_GITHUB_REVOCATION_EVENTS="pull-request-and-push-verified"
export COVEN_BWRAP_BIN="/usr/bin/bwrap"
export COVEN_RUNTIME_ROOTFS="/opt/coven-runtime/rootfs"
export COVEN_CODE_BIN="/usr/local/bin/coven-code" # path inside the rootfs
export COVEN_RUNTIME_NETWORK="shared" # explicit Codex egress opt-in

npm run doctor:app
npm start
```

Build the dedicated rootfs as an administrator-controlled deployment artifact.
It must contain the configured `coven-code`, `/usr/bin/git`, `/bin/sh`,
`/bin/true`, their libraries, CA/DNS files, and required runtime assets. Do not
copy the webhook checkout, state directory, policy, App key, hosting-user home,
or `.coven-code` token store into it. Bubblewrap must be able to create user
namespaces on the target host; `doctor:app` runs a real read/write isolation
probe and fails when it cannot.

The external-isolation declaration is intentionally mandatory. Set it only
after the worker host or container enforces allowlisted egress (including no
loopback, LAN, or metadata access) and CPU, memory, PID, workspace/disk, and
scratch limits. Bubblewrap mount namespaces and timeouts do not provide those
controls. A state directory from an older deployment must be owned by the
service user and made inaccessible to group/other (for example, `chmod -R go-rwx
"$COVEN_GITHUB_STATE_DIR"`) before this version starts.

This release passes the model credential to `coven-code`, so an untrusted
checkout can still try to consume or encode it through the allowed model
channel. Limit real execution to trusted repositories. Supporting public or
otherwise untrusted pull requests requires a separately constrained worker and
a quota-limited credential broker that does not expose a reusable model token
to repository code; the declaration above must remain unset until that boundary
is actually deployed.

The adapter mounts only per-task input (read-only), the checkout and result
directory (writable), and the checkout `.git` directory again as read-only. It
passes no GitHub token, askpass helper, App secret, SSH agent, or parent home to
`coven-code`. Publication authority is minted only after the sandbox exits.
Validation commands run again without credentials or network access. If the
host cannot satisfy this boundary, leave real execution disabled and use demo
mode or an externally isolated worker; there is no unsafe direct fallback.

In another shell:

```bash
Expand Down
Loading
Loading