The agent-native RFC platform for teams.
- Draft
- Discuss
- Decide
RFC123 turns GitHub pull requests into a real RFC workflow. Markdown in, RFC out. Every proposal is a PR in your existing repo, with the same auth, the same permissions, and the same git history. Walk away tomorrow and the record stays in git.
There's a canonical hosted instance at rfc123.com. The rest of this README is for running your own.
The license (FSL-1.1-MIT) lets you run RFC123 for your team, your company, or yourself, on any infrastructure you like. The one thing it does not let you do is wrap it up and sell it back as a hosted RFC product. Two years after each release ships, that release converts to plain MIT.
Practically that means: if your goal is "RFC123, but for my team only," you're in the right place.
The bundled docker-compose.yml stands up the whole stack: the Next.js app, a self-hosted Convex backend, the Convex dashboard, and a Redis cache. The only thing you have to register on the outside is a GitHub OAuth App.
It takes about five minutes.
- Docker and Docker Compose (
docker compose versionshould print v2.x or newer) - Node.js 20+, only because the optional Slack step uses it
git clone https://github.com/Twixes/rfc123.git
cd rfc123
cp .env.example .envOpen .env and fill in the required values. There are five secrets to generate:
openssl rand -base64 32 # for AUTH_SECRET
openssl rand -base64 32 # for SECRET_KEY
openssl rand -base64 32 # for TOKEN_ENCRYPTION_KEY
openssl rand -hex 32 # for CONVEX_INSTANCE_SECRET
openssl rand -base64 32 # for UPSTASH_REDIS_REST_TOKEN (any random string works)Plus two URLs. For a local play, use:
NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_CONVEX_URL=http://localhost:3210
For a real deploy behind a real domain, set NEXTAUTH_URL to your public app URL and NEXT_PUBLIC_CONVEX_URL to the public URL where you'll expose the Convex backend. Both are bundled into the client at build time, so changing them later means rebuilding the app image.
Go to GitHub Settings β Developer settings β OAuth Apps β New OAuth App. Fill in:
- Application name: RFC123 (or whatever you want to call it)
- Homepage URL: your
NEXTAUTH_URLfrom step 1 - Authorization callback URL:
<your NEXTAUTH_URL>/api/auth/callback/github
Generate a client secret. Paste both values into .env:
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
The app asks for read:user user:email repo read:org. The read:org scope is what lets RFC123 resolve "review requested from team X" assignments.
This part is two phases, because the Convex admin key is generated by the running backend container and has to be pasted into the app's env before it can push schema.
Phase 1: backend up, admin key out.
docker compose up -d backend dashboard redis srh
docker compose exec backend ./generate_admin_key.shCopy the key it prints. Paste it into .env:
CONVEX_SELF_HOSTED_URL=http://backend:3210
CONVEX_SELF_HOSTED_ADMIN_KEY=<the key you just copied>
(http://backend:3210 is the in-network hostname between containers. The browser uses NEXT_PUBLIC_CONVEX_URL instead.)
Phase 2: app up.
docker compose up -d appThe app container waits for Convex to be reachable, pushes the schema and functions, then starts serving on port 3000.
Open http://localhost:3000. Sign in with GitHub. Pick a repo. Write an RFC.
The Convex dashboard is at http://localhost:6791 if you ever need to look inside the database. The admin key from above is what you log in with.
git pull
docker compose build app
docker compose up -d appThe app re-pushes Convex on every start, so schema migrations come along for free. The Convex backend and dashboard images update with docker compose pull && docker compose up -d.
State lives in two named Docker volumes:
convex_data: Convex SQLite plus uploaded files. Back this up.redis_data: just a cache. Don't bother.
You can snapshot the volume directly, or use Convex's own export:
docker compose exec backend convex export --path /convex/data/backup.zip
docker compose cp backend:/convex/data/backup.zip ./backup-$(date +%F).zipRFC123 can DM every user a daily summary of RFCs awaiting their review. To turn it on, create a Slack app.
-
Render the manifest with your URL filled in:
node --env-file=.env scripts/render-slack-manifest.mjs
-
Go to https://api.slack.com/apps, "Create New App", "From a manifest". Paste the output.
-
From the app's "Basic Information" page, copy the Client ID and Client Secret into
.env:SLACK_CLIENT_ID=... SLACK_CLIENT_SECRET=... -
Restart the app:
docker compose up -d app
Users connect their own Slack workspace from /settings. The hourly Convex cron fires the briefing and respects each user's timezone and weekday preferences. No briefing on a day with no open RFCs.
Set OPENAI_API_KEY in .env to get one-sentence summaries on new PR bodies and AI-generated first-commit messages. Without it, both features fall back to plain non-AI defaults.
Set NEXT_PUBLIC_POSTHOG_KEY and NEXT_PUBLIC_POSTHOG_HOST to point at your PostHog project for product analytics and error tracking. Source map upload at build time is opt-in via POSTHOG_PERSONAL_API_KEY and POSTHOG_PROJECT_ID.
If you're hacking on the code itself, running outside Docker is much faster. This path uses Convex Cloud (free tier) instead of self-hosted Convex.
pnpm install
cp .env.example .env.local
npx convex dev # provisions a Convex project and prints NEXT_PUBLIC_CONVEX_URL
# paste that URL into .env.local
pnpm devGenerate the three secrets (AUTH_SECRET, SECRET_KEY, TOKEN_ENCRYPTION_KEY) the same way as the Docker setup. Then mirror two of them to Convex so the cron worker can authenticate back:
npx convex env set SECRET_KEY "<value>"
npx convex env set NEXTAUTH_URL "http://localhost:3000"You'll also need an Upstash Redis (their free tier covers it) for the GitHub response cache, or run a local Redis next to Hiett's serverless-redis-http shim like the bundled compose does.
A few things worth knowing before you point this at a production team:
- The Convex backend is single-instance. Crons would double-fire under multiple replicas. The Next.js
appcontainer is stateless and scales horizontally to as many replicas as you want. - HTTPS in front. NextAuth and Slack OAuth both refuse non-HTTPS callbacks unless the host is
localhost. Put Caddy or your platform's managed TLS in front. - Persistent disk for
convex_data. Attach at least 5 GB and grow it as your RFC archive does. NEXTAUTH_URLandNEXT_PUBLIC_CONVEX_URLare baked into the client at build time. Change them and you needdocker compose build appagain.- Keep the secrets stable.
AUTH_SECRETrotation invalidates every session;TOKEN_ENCRYPTION_KEYrotation requires re-encrypting every stored GitHub token;CONVEX_INSTANCE_SECRETrotation invalidates the admin key.
pnpm dev # next dev with React Compiler
pnpm build # production build
pnpm start # next start, after pnpm build
pnpm lint # biome check
pnpm format # biome format --write
pnpm typecheck # tsc --noEmit
pnpm test # vitest, single run
pnpm test:watch # vitest, watch mode
Bug reports and PRs welcome. Before pushing, run pnpm lint && pnpm typecheck && pnpm test. And review every single line of code from a coding agent - yes, you, as a human. Slop not accepted here.
FSL-1.1-MIT. Run it for any purpose, including commercially within your own org. Just don't offer it to third parties as a hosted RFC service.