cc.me
Small Axum service for OAuth callbacks and encrypted webhook inboxes:
-
GET /?at=<url>&code=...&state=...redirects toat, appending every query parameter exceptat. -
POST /ccreates or returns the short OAuth callback alias for a target. -
POST /i/<ed25519-public-key>captures a webhook request, encrypts it for that public key, and stores it for later handling. -
POST /i/<key-a>.<key-b>fans out one encrypted copy per recipient. -
/i/<key>/webmention,/websub,/slack,/pingback,/meta,/cloudevents, and/discord/<app-public-key>add small protocol handling before storing deliveries in the same inbox. -
GET /i/<key>looks ahead without removing deliveries. -
POST /i/<key>/claimreserves deliveries for handling. -
POST /i/<key>/ackremoves handled deliveries in batches. -
POST /i/<key>/releasemakes claimed deliveries ready again.
The inbox URL contains an Ed25519 public key. Deliveries are encrypted with the
X25519 key derived from it. GET, claim, ack, and release must include a
valid x-cc-me-timestamp and x-cc-me-signature header signed by the
corresponding Ed25519 private key.
Requests are only stored when captured method, path, query, headers, and body fit within 64 KiB. GET and claim responses return only as many items as fit within 1 MiB. If an inbox is full, new captures are rejected and existing deliveries are not removed.
Run
process-compose upThat starts Postgres and the app. The service listens on 0.0.0.0:3000 by
default.
To run the packaged service binary directly:
nix run github:xmit-co/cc.meConfiguration
| Variable | Default | Meaning |
|---|---|---|
BIND_ADDR |
127.0.0.1:3000 |
HTTP bind address |
DATABASE_URL |
postgres://127.0.0.1:5432/postgres |
Postgres connection URL |
INBOX_MAX_REQUESTS |
100 |
Stored encrypted requests per public key |
INBOX_DEFAULT_GET_LIMIT |
1 |
Default GET/claim batch size when a limit is omitted |
INBOX_MAX_GET_LIMIT |
1000 |
Cap for requested batch sizes |
INBOX_LONG_POLL_SECONDS |
25 |
Long-poll wait used by ?p and claim({ poll: true })
|
SHOT_CHROME_BIN |
chromium |
Chromium binary managed for /shot screenshots |
SHOT_CHROME_ARGS |
empty | Extra whitespace-separated Chromium flags |
SHOT_POW_LEVEL |
22 |
Proof-of-work level required by /shot
|
SHOT_TS_WINDOW_SECONDS |
300 |
Accepted ts skew in /shot documents |
SHOT_NAV_TIMEOUT_SECONDS |
10 |
Page-load timeout per screenshot |
SHOT_CACHE_SECONDS |
3600 |
Screenshot cache lifetime |
SHOT_CHROME_MAX_RENDERS |
64 |
Renders before the managed Chrome is recycled |
Chrome's memory is bounded in layers: renderers are limited to one per
concurrent render with a capped V8 heap, the browser is respawned every
SHOT_CHROME_MAX_RENDERS renders, and deployments should add a hard ceiling
around the service (e.g. systemd MemoryMax=2G, MemorySwapMax=0) — under
cgroup OOM the kernel kills the fattest process, a Chrome renderer, and the
service respawns the browser on the next request.
GET /pow documents a JWT-shaped proof-of-work token format with an in-browser
solver and verifier; reference CPU (Node) and GPU (Metal, OpenCL) solvers live
in pow/. GET /shot renders screenshots in a managed headless Chrome —
disposable incognito-style contexts, up to 2048×2048 with Chrome-side
downscaling, cached for an hour — gated by proof-of-work tokens; the
required level and other expectations are served at GET /shot/config.
Static docs live in docs/. The Go client is the module at the repository root
(go install cc.me@latest for the CLI, import "cc.me/ccme" for the library;
the server answers the go-import handshake on ?go-get=1). The other clients
live under client/: the JavaScript package in client/js/, plus ports in
client/python/, client/rust/, and client/ruby/.
The client CLI can forward or inspect inbox deliveries:
npx cc-me http://example.local:8080/webhook
npx cc-me inspectBy xmit dev team. We respect privacy: HTTP deliveries are encrypted for your key and we don't inspect your emails.