Codex referrals, coordinated. A small Telegram bot that pairs people who have OpenAI Codex referral slots with people who don't yet have Codex access — so both sides get a banked rate-limit reset through the official OpenAI flow.
→ Try it: t.me/codexHuddbot
OpenAI is running a time-bounded Codex referral promotion through 2026-06-24. Plus/Pro subscribers get up to 3 invites; every time a new person accepts an invite and sends their first Codex message, both sides earn one banked rate-limit reset they can spend whenever they want.
The catch: if you don't already know someone outside the Codex bubble, you have no one to invite. And if you're not yet on Codex, you have no easy way to find someone with a free slot.
codex-buddy solves exactly that coordination problem.
- Start a chat with the bot, send
/give, tell it how many slots you're offering. - When a real seeker shows up, the bot relays their email to you once. You manually send the official invite from your Codex app or extension.
- After both sides confirm the loop closed, you both have a banked reset.
- Start a chat with the bot, send
/seek, give it the email you'd like to receive the invite at. - The bot waits for a real giver to take your slot. You'll get a normal OpenAI invite by email — no funny business.
- Accept it, send your first Codex message, hit
/confirmedto close the loop. The giver does the same.
- ❌ Not an invite marketplace, not a code reseller, no money involved.
- ❌ Not an automated referral sender — the giver manually uses OpenAI's official invite flow.
- ❌ Does not call any unofficial OpenAI mutation endpoints. Email is the only thing relayed.
- ❌ Not a way around OpenAI's ToS — both sides are real people doing the real flow.
Seeker email is encrypted at rest with AES-GCM, deduped by HMAC-SHA-256 (keyed with a private pepper, so the raw email never has to be stored to dedupe). Admin UI only ever shows a masked email. After the bot relays the email to a matched giver once, the encrypted blob is wiped. The bot never logs full email addresses. Details in docs/privacy-retention.md.
If you already have a banked reset waiting and are stuck on Linux / a server / a terminal where the redeem button doesn't appear (see openai/codex#27915), the companion CLI tool aaamosh/codex-reset talks to the same /wham/rate-limit-reset-credits/consume endpoint the Codex desktop app uses, so you can spend the credit from the command line.
codex-buddy is the acquisition half (find a referral partner). codex-reset is the redemption half (spend the credit afterwards).
codex-hud is a small TypeScript monorepo whose first app is codex-buddy. Two runtimes are scaffolded; only the first is currently in production:
apps/buddy-server— Node/Hono + native SQLite, deployed on Server4 under systemd ascodex-buddy.service. This is the live path.apps/buddy-bot— Cloudflare Worker/Hono + D1 + Durable Object. Scaffolded for a higher-volume future promo wave; not currently deployed.
Server4 activation state (as of last deploy):
- Telegram bot token and admin Telegram ID allowlist live in
/etc/codex-buddy/codex-buddy.env. - Telegram webhook registered for
@codexHuddbotat the operator-configured HTTPS endpoint with the Server4 self-signed certificate. - Bot name, command list, and EN/RU descriptions are set in the Telegram profile via
server:set-telegram-profile. - Server4 runtime is rechecked after behavior changes via systemd/nginx, loopback + public health, cron, Telegram webhook info, admin auth, and Server4 health-monitor evidence.
- Admin HTML is
lang=en; the live admin response has no Cyrillic characters. - A real operator click-through of
/giveand/seekreached Server4. The resulting state has one giver offer and one seeker request on the same Telegram user, so no match is created by design — self-referrals are blocked. apps/buddy-bot/wrangler.tomlhas real Cloudflare D1 IDs ready for the future path.
apps/buddy-server: current Server4-native Node/Hono runtime, Telegram webhook, admin HTML/API, SQLite runtime, cron command.apps/buddy-bot: future Cloudflare Worker, Hono routes, Telegram webhook, Matchmaker Durable Object, admin HTML/API, cron handler.packages/buddy: shared business service for giver/seeker intake, matching, confirmations, cleanup, archive.packages/core: enums, state machine, matching, privacy, anti-abuse, archive, metrics.packages/telegram: Telegram API wrapper, callback parser, English bot copy, and inline keyboards.packages/db: D1 repository and migrations.packages/db-sqlite: D1-compatible SQLite adapter for Server4.packages/config: remote-config defaults and validation.docs: deployment checklist, operator runbook, admin docs, privacy notes, migration notes.fixtures/telegram: sample Telegram update payloads.scripts/seed-local-config.ts: seeds D1 config rows fromconfig.sample.json.
Telegram sends HTTPS webhook updates to /telegram/webhook. Both runtimes validate the optional Telegram secret token, store only a SHA-256 payload hash by update_id, and process commands or callbacks idempotently. Creating a giver offer immediately attempts to match currently pending seekers; cron remains the backup retry path.
In the Cloudflare runtime, critical match decisions go through one MatchmakerDurableObject instance named codex-buddy-global-matchmaker. The Durable Object serializes:
- pending seeker to active giver reservation
- one-time email relay callback
- giver/seeker confirmation actions
- reservation expiry
- ciphertext cleanup and archive actions
D1 is the source of persistent truth. Durable Object state is not the canonical data store.
In the Server4 runtime, the same shared service runs in a single Node process with a process-local serial queue for match-critical actions. SQLite is the source of persistent truth. The deployed service is expected to run as one systemd instance; do not scale it horizontally without replacing the queue with a database-backed lock.
The shared SQL migration creates:
usersgiver_offersseeker_requestsmatchesconfirmationsabuse_flagsaudit_eventsconfigarchived_recordstelegram_updatesconversation_states
telegram_updates and conversation_states are operational additions for webhook retry idempotency and multi-step Telegram intake.
The schema still contains plan_type, language, region, and timezone fields for compatibility and future promo waves. The current Telegram MVP writes neutral defaults and does not expose those fields in the bot intake.
- Seeker email is normalized and validated.
- Deduplication uses a keyed HMAC-SHA-256 hash with
EMAIL_HASH_PEPPER. - The admin UI shows only masked email.
- Full email is encrypted at rest with
EMAIL_ENCRYPTION_KEYusing AES-GCM. - Giver initially sees only masked email and must press
Show emailfor one-time relay. - After successful Telegram delivery of the relay message, the runtime clears
email_ciphertext. - Closing or completing a seeker request also clears
email_ciphertext. - Cron also clears old ciphertext after
plaintext_relay_ttl_minutes. - Full email is never intentionally written to logs or audit metadata.
Install:
npm installTypecheck:
npm run typecheckTests:
npm testLocal D1 migration:
npm run migrate:localSeed local config:
npm run seed:localLocal Worker:
npm run devLocal Server4-style runtime:
npm run dev:serverServer4 SQLite migration:
npm run server:migrateServer4 seed:
npm run server:seedDeploy:
npm run deployFor the current Server4 path, see docs/server4-deployment.md. For the future Cloudflare path, replace database_id in apps/buddy-bot/wrangler.toml with the real D1 database ID and set Worker secrets from .env.example.
TELEGRAM_BOT_TOKENTELEGRAM_SECRET_TOKENADMIN_TOKENADMIN_TELEGRAM_IDSEMAIL_ENCRYPTION_KEYEMAIL_HASH_PEPPER
Use Wrangler secrets:
npx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put TELEGRAM_SECRET_TOKEN
npx wrangler secret put ADMIN_TOKEN
npx wrangler secret put ADMIN_TELEGRAM_IDS
npx wrangler secret put EMAIL_ENCRYPTION_KEY
npx wrangler secret put EMAIL_HASH_PEPPERServer4 also needs:
BUDDY_DB_PATHBUDDY_HOSTBUDDY_PORTBUDDY_PUBLIC_BASE_URLTELEGRAM_WEBHOOK_CERT_PATHwhen using a Telegram self-signed webhook certificate
Store Server4 values in /etc/codex-buddy/codex-buddy.env, not in git.
Cloudflare after deploy:
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-H "content-type: application/json" \
-d '{"url":"https://<worker-host>/telegram/webhook","secret_token":"<TELEGRAM_SECRET_TOKEN>"}'The bot uses webhook delivery only, not polling.
Server4 webhook registration:
set -a
. /etc/codex-buddy/codex-buddy.env
set +a
npm run server:set-webhook
npm run server:set-telegram-profileFor the no-domain MVP path, Server4 uses nginx TLS on 8443/tcp and uploads /etc/codex-buddy/webhook-cert.pem to Telegram.
Open:
https://<public-bot-host>/admin?token=<ADMIN_TOKEN>
The admin surface supports:
- active giver offers
- active seeker requests
- match list
- manual cancel/resolve
- user block
- promo/config edits
- JSON/CSV export
- aggregate metrics
The browser companion linked from relay messages now lives in the Codex Reset engineering repo:
- Repo:
https://github.com/aaamosh/codex-reset - Download:
https://github.com/aaamosh/codex-reset/raw/main/codex-reset.html - Source:
https://github.com/aaamosh/codex-reset/blob/main/codex-reset.html
codex-hud only relays those GitHub links after Show email. It does not host,
embed, or own the helper HTML. The legacy assets/codex-buddy-invite-helper.html
path is kept only as a small pointer so old Telegram messages do not break. The
companion remains a readable single-file tool with no external dependencies, no
network calls, no cookies/tokens access, no auth-file reads, and no OpenAI invite
or reset mutation endpoint calls. The separate aaamosh/codex-invite-helper
repository remains available as a minimal overlapping helper, but current
codex-hud relay copy points to Codex Reset.
See docs/admin.md.
Server4 uses codex-buddy-cron.timer to run:
npm run server:cronThe future Cloudflare path is configured in apps/buddy-bot/wrangler.toml:
*/10 * * * *0 * * * *
The scheduled handler:
- silently retries pending seeker matching; no-match status text is sent only during explicit user actions
- expires stale reservations
- clears old ciphertext
- archives active intents after promo end/archive mode
- sends one-time reminder nudges for open matches
Cloudflare cron propagation can lag after deploy; do not assume schedule changes apply instantly.
- Product and runtime source of truth: this
README.md. - DB schema:
packages/db/migrations/0001_initial.sql. - Server4 deployment:
docs/server4-deployment.md. - Admin UI visual contract:
DESIGN.md. - Deferred repairs:
REPAIR_QUEUE.md. - Local repeatable lessons:
BEST_PRACTICES.md. - Operator docs:
docs/operator-runbook.md. - Deployment checklist:
docs/deployment-checklist.md. - Privacy/data retention:
docs/privacy-retention.md.
The public repository is intentionally kept as a single-current-state history
for this MVP. The current checkout and reachable Git history must not contain
Cyrillic characters outside third-party dependency installs such as
node_modules, real Server4 host/IP values, Telegram bot tokens, admin IDs, or
runtime secrets.
Use these checks before publishing:
git grep -n -I -P '\p{Cyrillic}' -- .
PUBLIC_BOT_HOST='<actual-public-host-or-ip>' git grep -n -I -e "$PUBLIC_BOT_HOST" -- .
git grep -n -I -P '[0-9]{9,10}:[A-Za-z0-9_-]{30,}' -- .
find . \( -path '*/.git/*' -o -path '*/node_modules/*' \) -prune -o -type f -print0 | xargs -0 rg -n '\p{Cyrillic}' --no-headingAdmin UI is browser-facing. Deterministic harness for the Cloudflare app:
npm run playwright:testIf the exact Playwright-managed browser revision is not installed on this workstation, use an existing local Chromium:
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/home/amosh/.cache/ms-playwright/chromium-1228/chrome-linux64/chrome npm run playwright:testProjects final acceptance should use the canonical isolated browser path for the intended URL when technically possible:
agent-isolated-browser --session codex-buddy-admin start --replace --notify-user --url http://localhost:8787/admin?token=<ADMIN_TOKEN>For Server4 acceptance, use the operator-configured Server4 admin URL:
agent-isolated-browser --session codex-buddy-server4-admin start --replace --notify-user --url https://<public-bot-host>/admin?token=<ADMIN_TOKEN>No native desktop/mobile app runtime exists in this MVP. The only user-facing runtime surfaces are Telegram chat and the browser admin surface.
Meaningful changes to behavior, architecture, config, privacy, deployment, data format, admin workflow, Telegram UX, matching, cron/timer, Server4 runtime, Cloudflare runtime, or verification must update this README or the nearest docs in the same logical slice.