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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ dist/
.pigeon/
.venv/
coverage/
.worktrees/
*.log
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

Tiny, approval-gated delegation between teammates' Codex agents.

Pigeon packages an MCP server, an embedded delegation inbox, signed authority envelopes, scope narrowing, and a native MCP confirmation before work can begin. The relay never receives Codex or OpenAI credentials.
Pigeon packages an MCP server, an embedded delegation inbox, scope narrowing, and native MCP confirmation. EvalOps Agent Kit owns authentication, durable Agent Runtime state, local authority, and Codex app-server execution; Pigeon never receives Platform refresh credentials or OpenAI credentials.

## What works

- `delegate_to_teammate` creates a bounded request.
- `open_pigeon_inbox` renders the embedded inbox.
- The recipient can reject or narrow `workspace_write` → `read_only` → `discuss_only`.
- Approve triggers MCP `elicitation/create`; cancelling it starts nothing.
- Confirmation capabilities are short-lived and single-use.
- Delegation state transitions and Ed25519 envelopes are tested.
- Agent Kit records the confirmed scope and performs execution under a fenced Platform lease.
- Pigeon production mode never falls back to process-local state.

The included transport is intentionally an in-process evaluation relay. It proves the full approval and execution contract without exposing a machine on the network. A production deployment must replace `InMemoryRelay` with an authenticated durable transport and connect `CodexAdapter` to the local Codex app-server; those boundaries are explicit in `src/gateway.ts`.
Pigeon connects only to the owner-only Agent Kit Unix socket. Platform Agent Runtime is the durable ledger, and the Agent Kit daemon connects outbound to Platform and Codex app-server.

## Run

Expand All @@ -25,6 +25,16 @@ pnpm validate:plugin
node dist/server.js
```

The MCP process requires stable coordinates from Agent Kit enrollment:

```bash
export EVALOPS_AGENT_SOCKET=/tmp/evalops-agent-kit.sock
export PIGEON_ORGANIZATION_ID=org_...
export PIGEON_WORKSPACE_ID=workspace_...
export PIGEON_USER_PRINCIPAL_ID=user_...
export PIGEON_DEVICE_PRINCIPAL_ID=device_...
```

The MCP process uses stdio. Install the repo-local marketplace, then install `pigeon` and start a new Codex task so tools and skills reload.

## Approval contract
Expand All @@ -33,15 +43,15 @@ The MCP process uses stdio. Install the repo-local marketplace, then install `pi
2. Recipient reviews the request in the embedded inbox.
3. Recipient may narrow, never widen, authority.
4. The host displays a native confirmation containing sender, objective, workspace, and final scope.
5. Only the resulting one-use capability can start the adapter.
5. Pigeon sends the narrowed decision to Agent Kit; the daemon records evidence and executes only after all Platform and local gates pass.

## Development

Node 20+ is supported. Tests are intentionally deterministic and require no API keys.
Node 22+ is supported. Tests are deterministic and require no API keys.

```bash
./node_modules/.bin/vitest run
./node_modules/.bin/tsc -p tsconfig.json --noEmit
```

See [SECURITY.md](SECURITY.md) before replacing the in-process relay.
See [SECURITY.md](SECURITY.md) for the daemon and Platform authority boundaries.
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

Please report vulnerabilities privately through GitHub's security-advisory flow.

Pigeon treats the recipient gateway as the authority boundary. Effective scope cannot exceed requested scope; confirmation tokens are short-lived, single-use, and stored as hashes. Never expose a Codex app-server directly to the internet, forward OpenAI credentials through a relay, log private keys, or enable non-interactive production approval.
Pigeon treats the Agent Kit daemon as the machine authority boundary. Effective scope cannot exceed requested scope, and native confirmation is required before Pigeon sends an elevated approval decision. Never expose the Agent Kit socket or Codex app-server to the network, put Platform credentials in plugin configuration, or enable non-interactive workspace writes.

The current relay is evaluation-only and in-process. A network transport must add authenticated membership, replay protection, durable monotonic event sequences, rate limits, encryption in transit, and an append-only redacted audit log before production use.
Platform Agent Runtime owns authenticated membership, replay protection, leases, durable events, and audit evidence. The local socket is owner-only; integrations receive scoped capabilities from the daemon. Pigeon must not log objectives, absolute paths, credentials, model responses, or raw daemon frames.
140 changes: 140 additions & 0 deletions docs/superpowers/plans/2026-07-11-company-relay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Pigeon Company Relay Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Ship a deployable, Postgres-backed Pigeon relay with signed device authentication, Slack notifications and low-risk approvals, and a Codex gateway client for durable cross-window delegation.

**Architecture:** A stateless TypeScript HTTP service owns durable delegation state in Postgres and records an append-only event stream plus transactional delivery outbox. The local MCP gateway signs relay commands, polls recipient events, performs elevated approval in Codex, and never sends workspace content to the relay. A Slack adapter posts requests and accepts only reject or `discuss_only` approval actions.

**Tech Stack:** Node.js 20+, TypeScript, Express 5, Postgres 15+, `pg`, Zod, Slack Web API over HTTPS, Vitest, Docker Compose.

## Global Constraints

- Slack may approve only `discuss_only`; `read_only` and `workspace_write` require native Codex confirmation.
- Scope may be narrowed and never widened.
- Relay records must not contain absolute workspace paths, file contents, prompts, tool transcripts, secrets, or model reasoning.
- Capabilities are audience-bound, short-lived, single-use, and stored as hashes.
- Postgres is authoritative; delivery is at least once and command effects are idempotent.
- Do not add Kafka, Redis, or a policy language.

---

### Task 1: Durable protocol and store

**Files:**
- Create: `src/relay/types.ts`
- Create: `src/relay/store.ts`
- Create: `src/relay/memory-store.ts`
- Create: `src/relay/store.test.ts`
- Modify: `src/protocol.ts`

**Interfaces:**
- Produces: `RelayStore`, `RelayCommand`, `RelayEvent`, `DeviceIdentity`, `CreateDelegationInput`, and `MemoryRelayStore`.
- `RelayStore.createDelegation(input, actor)` returns the stored delegation and event atomically.
- `RelayStore.transition(id, expectedVersion, command, actor)` enforces state and scope rules.

- [ ] Write tests for idempotent creation, recipient isolation, narrowing, invalid widening, optimistic concurrency, expiry, terminal states, and redacted workspace labels.
- [ ] Run `pnpm vitest run src/relay/store.test.ts` and verify the new module is missing.
- [ ] Add exact Zod schemas and the `RelayStore` interface; implement the deterministic memory store used by unit tests.
- [ ] Run `pnpm vitest run src/relay/store.test.ts` and verify all store tests pass.
- [ ] Commit with `git commit -m "feat: define durable relay store"`.

### Task 2: Postgres persistence and transactional outbox

**Files:**
- Create: `src/relay/postgres.ts`
- Create: `src/relay/migrations/001_relay.sql`
- Create: `src/relay/postgres.test.ts`
- Modify: `package.json`
- Modify: `pnpm-lock.yaml`

**Interfaces:**
- Consumes: `RelayStore` from Task 1.
- Produces: `PostgresRelayStore`, `migrate(pool)`, `claimOutbox(limit)`, `completeOutbox(id)`, and `failOutbox(id, error)`.

- [ ] Write integration tests gated by `PIGEON_TEST_DATABASE_URL` for atomic create/event/outbox writes, duplicate idempotency keys, concurrent transitions, event cursors, and retry claims.
- [ ] Run the tests without the database variable and verify they skip cleanly; run them against Docker Postgres and verify they fail before implementation.
- [ ] Add `pg`, the migration, parameterized queries, transactions, row locking, uniqueness constraints, and `FOR UPDATE SKIP LOCKED` outbox claims.
- [ ] Run Postgres integration tests and verify all cases pass.
- [ ] Commit with `git commit -m "feat: persist relay state in postgres"`.

### Task 3: Signed relay HTTP API

**Files:**
- Create: `src/relay/auth.ts`
- Create: `src/relay/auth.test.ts`
- Create: `src/relay/app.ts`
- Create: `src/relay/app.test.ts`
- Create: `src/relay/main.ts`
- Modify: `package.json`

**Interfaces:**
- Consumes: `RelayStore` and device public keys.
- Produces: `createRelayApp({ store, devices, clock })`, `verifyDeviceRequest`, and HTTP endpoints `POST /v1/delegations`, `GET /v1/events`, `POST /v1/delegations/:id/approve`, `POST /v1/delegations/:id/reject`, `POST /v1/delegations/:id/start`, `POST /v1/delegations/:id/complete`, plus `GET /healthz`.

- [ ] Write tests for signatures, timestamp skew, nonce replay, tenant/recipient isolation, idempotency, Slack approval restrictions, Codex elevated approval, redacted responses, and stable error codes.
- [ ] Run the focused tests and verify failure before implementation.
- [ ] Implement canonical request signing, in-memory nonce replay protection, Zod request validation, actor/source authorization, and JSON error middleware.
- [ ] Run focused API tests and verify they pass.
- [ ] Commit with `git commit -m "feat: add signed relay api"`.

### Task 4: Slack notifications and safe interactive actions

**Files:**
- Create: `src/slack/client.ts`
- Create: `src/slack/client.test.ts`
- Create: `src/slack/actions.ts`
- Create: `src/slack/actions.test.ts`
- Modify: `src/relay/app.ts`
- Modify: `src/relay/postgres.ts`

**Interfaces:**
- Produces: `SlackClient.postDelegation`, `SlackClient.updateDelegation`, `verifySlackRequest`, `handleSlackAction`, and an outbox delivery handler.
- Slack approval calls the relay transition only when the requested and effective scopes are `discuss_only`; rejection is permitted for every scope.

- [ ] Write tests using a fake Slack HTTP server for Block Kit payload redaction, immediate action acknowledgement, request signature verification, stale actions, rejection, low-risk approval, and elevated-scope denial.
- [ ] Run focused Slack tests and verify failure before implementation.
- [ ] Implement minimal Slack Web API calls, verified interactive routes, opaque IDs, DM request blocks, and outbox retry classification.
- [ ] Run focused Slack and API tests and verify they pass.
- [ ] Commit with `git commit -m "feat: connect slack approval workflow"`.

### Task 5: Codex gateway relay client

**Files:**
- Create: `src/relay/client.ts`
- Create: `src/relay/client.test.ts`
- Modify: `src/server.ts`
- Modify: `src/gateway.ts`
- Modify: `plugins/pigeon/.mcp.json`

**Interfaces:**
- Produces: `RelayClient.createDelegation`, `RelayClient.events`, `RelayClient.approve`, `RelayClient.reject`, and `RelayClient.complete`.
- The existing MCP tools use the remote client when `PIGEON_RELAY_URL`, `PIGEON_DEVICE_ID`, and `PIGEON_DEVICE_PRIVATE_KEY` are present; otherwise they retain the evaluation-only memory relay.

- [ ] Write tests for canonical signing, event cursors, retry-safe commands, remote inbox rendering, Slack-approved discussion work, and mandatory native confirmation for elevated work.
- [ ] Run focused tests and verify failure before implementation.
- [ ] Implement the signed HTTP client and gateway adapter, keeping private keys local and sending only workspace labels.
- [ ] Run gateway, widget, client, and protocol tests and verify they pass.
- [ ] Commit with `git commit -m "feat: connect codex gateway to relay"`.

### Task 6: Deployment, operations, and release verification

**Files:**
- Create: `Dockerfile`
- Create: `docker-compose.yml`
- Create: `.env.example`
- Create: `docs/company-relay.md`
- Create: `scripts/smoke-relay.mjs`
- Modify: `README.md`
- Modify: `.github/workflows/ci.yml`
- Modify: `package.json`

**Interfaces:**
- Produces: `pnpm relay`, `pnpm migrate`, `pnpm smoke:relay`, a health endpoint, container image, local Postgres stack, and operator setup documentation.

- [ ] Add a smoke script that registers two fixture devices, creates a delegation, reads it as the recipient, approves it through the allowed surface, and observes completion.
- [ ] Add container/deployment files with non-secret examples, health checks, migration startup, retention and revocation guidance, and Slack app configuration.
- [ ] Run `pnpm test`, `pnpm typecheck`, `pnpm build`, `pnpm validate:plugin`, and the Docker-backed smoke test.
- [ ] Inspect tracked files and build output for credentials, private keys, absolute personal paths, and raw workspace content.
- [ ] Commit with `git commit -m "docs: ship company relay deployment"`.
- [ ] Push the branch, open a draft pull request against `evalops/pigeon:main`, and report validation evidence and remaining production prerequisites.
Loading
Loading