Skip to content

Repository files navigation

Agentgram

A Cloudflare Worker that gives your company's agents somewhere to put assets.

Writes are gated on GitHub identity: an agent proves it controls an authorized GitHub account by publishing a short-lived gist, and trades that proof for a bearer token. Assets live in an R2 bucket and are served straight back out over HTTP.

The worker serves its own API documentation as Markdown at GET /, so an agent pointed at the root URL can work out how to use it without any other context.

How authorization works

  1. The agent generates a random string (32–128 characters).
  2. It publishes a gist containing the SHA-256 of that string.
  3. It posts [gistUrl, randomString] to /authorize.
  4. The worker fetches the gist, checks the hash matches, and checks the gist's owner is on the authorized list.
  5. It returns a bearer token, valid for 10 minutes.

The gist only proves account ownership, so it can be deleted immediately. No GitHub token is ever handed to the worker.

GIST_URL=$(echo -n "<your random string>" | { sha256sum 2>/dev/null || shasum -a 256; } | gh gist create)
TOKEN=$(curl -s -X POST https://<your-worker-host>/authorize \
  -H "Content-Type: application/json" \
  -d "[\"$GIST_URL\", \"<your random string>\"]")
gh gist delete --yes "$GIST_URL"

Endpoints

Every endpoint except POST /authorize and asset reads requires Authorization: Bearer <token>.

Method Path Description
GET / API documentation as Markdown.
POST /authorize Exchange a gist proof for a token. Body: [gistUrl, str].
DELETE /authorize Revoke the calling token.
GET /users Newline-separated list of authorized GitHub accounts.
PUT /users?user=<name> Authorize a GitHub account.
DELETE /users?user=<name> Deauthorize a GitHub account.
GET <path> Download an asset. No authentication required.
POST <path> Upload or replace an asset.
DELETE <path> Delete an asset.

Uploads require Content-Type and Content-Length; the body is stored verbatim and the content type is echoed back on download. /, /users and /authorize are reserved and cannot be used as asset paths.

Usernames and asset paths are case-sensitive.

Important

Asset reads are public. Anything uploaded is readable by anyone who knows its URL, without a token. Only writes are gated. Do not store secrets here.

Setup

Requires Node.js and a Cloudflare account with R2 enabled.

npm install

Create the bucket that backs the worker:

npx wrangler r2 bucket create agentgram-bucket

Then set the bootstrap account in wrangler.jsonc. INIT_AUTH is the GitHub username that is always authorized — it is implicitly on the user list, cannot be removed via the API, and is how you get your first token:

"vars": {
  "INIT_AUTH": "your-github-username"
}

Deploy:

npm run deploy

Authorize your teammates' accounts with PUT /users?user=<name> once you have a token. To hand the worker over to a different owner, change INIT_AUTH and redeploy.

Development

npm run dev          # local dev server
npm run typecheck    # tsc --noEmit
npm run format       # prettier --write .
npm run format:check # verify formatting
npm run cf-typegen   # regenerate worker-configuration.d.ts from wrangler.jsonc

Observability is enabled in wrangler.jsonc, and source maps are uploaded, so runtime exceptions come back with mapped stack traces:

npx wrangler tail --format json --status error

Storage layout

Everything lives in the one R2 bucket:

Key Contents
users Newline-separated authorized accounts, excluding INIT_AUTH.
authorize JSON array of [token, expiryMs] for live tokens.
anything else An uploaded asset, keyed by its URL path.

Both metadata objects are mutated through a compare-and-swap helper that retries on an ETag mismatch, so concurrent updates from multiple agents will not clobber each other.

License

MIT — see LICENSE. Copyright (c) 2026 Infrawrench LLC.

About

A Cloudflare Worker that gives your company's agents somewhere to put assets, gated on GitHub identity.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages