English · 简体中文 · Deutsch · Português · Español
Give AI agents the GitHub access their assigned bot already has, without giving them GitHub App keys or installation tokens. Pumpkin is a self-hosted gateway on Cloudflare Workers. Agents make signed GitHub REST and GraphQL requests through one Worker; the Worker uses the assigned bot's GitHub App installation and records the call. Codex and Claude Code plugins are available, along with a standalone agent skill.
- Use the App's real capabilities. An agent can call GitHub endpoints available to its bot's installation, including issue, pull request, repository, and organization APIs. GitHub's installation access and permissions set the limit.
- Keep credentials in one place. GitHub App private keys and minted installation tokens stay in the Worker. Agents hold a caller credential, never an App key or installation token.
- Make actions traceable and retries safer. Calls that reach GitHub have a request ID and a pre-action audit entry. Completed small JSON responses can be replayed with the same ID; uncertain writes require inspection before a new request.
Pumpkin is useful when several agents need to work as GitHub bots, but you want to host App credentials once instead of distributing PEM files and tokens to every agent runtime.
pumpkin-demo.mp4
Agent → signed request → Pumpkin Worker → assigned bot's App installation → GitHub API
│
└─ audit and request records in D1
- The caller credential identifies an agent. Worker policy assigns that caller to one bot.
- The signed route names an organization or user account. Pumpkin finds that bot's App installation on the account. A caller can choose any account where its assigned App is installed.
- Pumpkin calls
https://api.github.comwith an installation token kept inside the Worker. GitHub enforces the installation's repository access and permissions. - Pumpkin returns GitHub's response to the caller and records the request. The Worker denies calls if its nonce, request, or audit store is unavailable.
The gateway supports GET, HEAD, POST, PUT, PATCH, and DELETE on bounded REST and GraphQL requests through /v1/github/{org|user}/{account}/{github_path}. Request bodies are limited to 1 MiB. Public repository and organization writes work when the installation permits them. Endpoints that require user authorization, App JWT authentication, HTTP Git, or another credential type are outside the installation-token gateway.
For narrower workflows, POST /v1/operations/<operation> exposes compiled
typed operations. Each requires an exact caller, operation, and private
repository grant in src/policy.config.ts; the Worker verifies repository
privacy before the requested action. Supported operations are
issues.close, issues.reopen, issues.update, issues.add_labels,
issues.remove_labels, issues.list_comments, repos.list_branches,
and repos.list_commits. Bodies are strict JSON containing
repo: "OWNER/REPO"; issue operations also require issue_number.
After setup, the bundled client signs requests with your caller credential. Replace acme/widgets with a repository accessible to your assigned App installation. A REST read:
node "$PUMPKIN_CLI" GET /v1/github/org/acme/repos/acme/widgetsExpect HTTP 200 and a GitHub repository JSON object containing "full_name":"acme/widgets".
A typed comment read uses a JSON body:
printf '%s\n' '{"repo":"acme/widgets","issue_number":123,"page":1,"per_page":30}' > request.json
node "$PUMPKIN_CLI" POST /v1/operations/issues.list_comments --body request.jsonA GraphQL read uses a JSON body:
printf '%s\n' '{"query":"query { repository(owner: \"acme\", name: \"widgets\") { name } }"}' > query.json
node "$PUMPKIN_CLI" POST /v1/github/org/acme/graphql --body query.jsonExpect HTTP 200 and {"data":{"repository":{"name":"widgets"}}}. The client prints HTTP status to stderr and GitHub's response body to stdout. See the agent skill for retries and request IDs.
Pumpkin prunes nonces after 600 seconds and completed request records after 7 days. It never prunes audit_log automatically. The owner can delete old audit entries by choosing a retention period and running a D1 command. For example, to delete entries older than 90 days:
npx wrangler d1 execute pumpkin --remote --command "DELETE FROM audit_log WHERE ts < strftime('%s','now') - 90*86400"For the Pumpkin owner: You need Node.js 22.12+, a Cloudflare account with Workers and D1, and authority to create and install a GitHub App. Clone the tracked repository and run the interactive wizard in a terminal:
git clone https://github.com/shy-tangerine/pumpkin.git
cd pumpkin
npm run setupThe bundled caller-credential helper requires POSIX file ownership and permission support. Linux, macOS, and Windows through WSL are supported; native Windows execution is not. The shell examples below use a POSIX shell; PowerShell users should run them in WSL or Git Bash, or translate them for PowerShell.
The wizard installs locked dependencies, runs local verification, guides Cloudflare and GitHub App setup, applies schema.sql to the new D1 database, deploys the Worker, checks /health, and saves the Worker URL and caller credential in protected user config. It can also install the Pumpkin plugin into a detected Codex or Claude Code CLI. It requires a TTY and asks before using either host CLI.
- Codex and Claude Code plugins: Install the Pumpkin plugin to give your agent the shared skill, a signed-request client, and a session hook that points to the client. The hook tells the agent to use Pumpkin when the target repository designates it.
- Standalone agent skill: Install with
npx skills add shy-tangerine/pumpkin --skill pumpkin, or use Hermes Tap. This installs the request instructions; making calls also requires the Pumpkin client and a configured Worker.
The install guide covers both paths and first use. The skill contains the agent-facing request contract.
For a cloud caller that needs exactly one file commit without receiving the persistent caller key, mint a repository-scoped token locally:
node scripts/mint-commit-token.mjs mint --repo acme/widgets --registration-id build-123 --issue issue-39The command uses the protected Pumpkin URL and caller credential, registers a
random pct1_ token, and prints it once to stdout. It never stores the token;
paste it into the caller that needs it. Use only the narrow Contents route:
curl --fail-with-body -X PUT \
-H 'Authorization: Bearer pct1_…' \
-H 'Content-Type: application/json' \
-H 'X-Pumpkin-Request-Id: commit-123' \
--data '{"message":"Update generated file","content":"aGVsbG8K"}' \
'https://worker.example/v1/commit-tokens/contents/acme/widgets/generated.txt'The repository must have main as its default branch. The request may omit
branch or set it to main; the Worker always writes to main and rejects
repositories with a different default branch. The token is bound to the exact
OWNER/REPO, expires within 900 seconds, and
is spent before the GitHub call. A failed response after that point can be
uncertain: inspect GitHub before requesting another token. Revoke an unused
token by piping it on stdin; it is never accepted as an argument:
printf '%s\n' "$TOKEN" | node scripts/mint-commit-token.mjs revokeCommit tokens are not a substitute for caller authentication. Anyone who obtains a pasted token can race its one permitted commit before expiry.
The caller cannot choose another bot or supply an installation ID. Signed account choice stays within the assigned App's installations. The Worker controls GitHub authorization, never forwards caller-supplied authorization headers, and does not follow redirects. GitHub responses pass through and may themselves contain credentials from endpoints the installation is allowed to call, so grant the App only the permissions and repositories its agents should use.
npm run verify runs type checks, tests, repository and security checks, and a Wrangler deploy dry run. npm test runs the unit suite.
Pumpkin is licensed under MIT.
The Worker runtime remains TypeScript. For the stdlib-only Python repository and security checks, use uv sync --locked, then uv run python scripts/check_repository.py or uv run python scripts/check_security_gate.py.
