# Rekey Self-hostable, multi-tenant auth and billing for the apps you run. User auth and provider-agnostic billing share one tenant model, behind one API, in one `docker compose --profile full up`. > **Status:** 2.1.0 is the latest stable release. 2.2.0 is in release candidates, with its stable release to follow. [MIT licensed](LICENSE). ## For AI agents If you're an AI coding agent reading this repo, start at [AGENTS.md](AGENTS.md). ## Quick start **Building an app on Rekey Cloud?** You need none of the self-host setup below. [rekey.dev/docs/quickstart](https://rekey.dev/docs/quickstart) goes from a free workspace (no card) to a signed-in user in a Next.js app, with the drop-in `` form and two environment variables. Or clone a starter: [Next.js](https://github.com/rekey-dev/nextjs-starter), [Astro](https://github.com/rekey-dev/astro-starter), [digital shop](https://github.com/rekey-dev/nextjs-commerce). ### Self-host The whole stack boots with one command — the API auto-migrates on start: ```bash cp .env.example .env # fill POSTGRES_PASSWORD, REDIS_PASSWORD, # JWT_SECRET, SUPER_ADMIN_KEY, ENCRYPTION_KEY # then paste the datastore passwords into # DATABASE_URL/REDIS_URL — compose does not do it for you docker compose --profile full up # Postgres + Redis + API (:3030) + panel (:3031) # + portal (:3050) ``` Every published port binds to `127.0.0.1`, so this is a **local** stack — bringing it up on a VPS does not put it on the internet. `BIND_ADDRESS=0.0.0.0` overrides that, and if you set it, set `OPERATOR_SIGNUP_MODE=invite` in the same edit or the first stranger to find the host can create an operator account. The internet-facing file is [`docker-compose.prod.yml`](docker-compose.prod.yml) (Traefik, no published ports) — see [DEPLOY.md](DEPLOY.md). Prefer to run from source (watch mode)? Start just the datastores in Docker: ```bash pnpm install cp .env.example .env # same five values as above docker compose up -d postgres redis pnpm db:migrate:deploy pnpm dev ``` Both paths read that one root `.env`. `pnpm dev` generates the Prisma client and builds the workspace packages the apps import before starting anything, so a fresh clone needs no separate build step. API at `http://localhost:3030`, interactive docs at `/docs`, operator panel at `http://localhost:3031`. Then bootstrap the first Tenant, Application and API key in one command. The CLI is self-host only: it authenticates with `SUPER_ADMIN_KEY`, which a Rekey Cloud workspace does not have. ```bash export REKEY_URL=http://localhost:3030 export SUPER_ADMIN_KEY=$(grep ^SUPER_ADMIN_KEY .env | cut -d= -f2) npx @rekey.dev/cli init --tenant-name "Acme Co" --owner-email ops@acme.example \ --app-name "Acme Prod" --app-slug acme-prod ``` See [docs/quickstart.md](docs/quickstart.md), the self-host quickstart, for the full walkthrough (boot → bootstrap → call from your app → sign up an end-user), and [DEPLOY.md](DEPLOY.md) to run it in production (Traefik + TLS). ## Structure Apps (each runs on a fixed dev port): | App | Package | Port | What | |---|---|---|---| | `apps/api` | `@rekey.dev/api` | 3030 | Fastify monolith — auth + billing + admin API | | `apps/panel` | `@rekey.dev/panel` | 3031 | Next.js admin panel (panel.rekey.dev) | | `apps/portal` | `@rekey.dev/portal` | 3050 | Hosted customer portal V2 (portal.rekey.dev) | There is no `examples/` directory: the demo apps that lived there were removed in #261 because they had drifted from the API they demonstrated. Working integrations live in their own repositories instead, the three starters linked under [Quick start](#quick-start), alongside [docs/quickstart.md](docs/quickstart.md) and [docs/react-components.md](docs/react-components.md). Packages: - `packages/shared-types` — Zod schemas shared between API and SDKs - `packages/sdk-node` — `@rekey.dev/node`, the server SDK - `packages/sdk-react`, `packages/sdk-nextjs` — client SDKs - `packages/cli` — the `rekey` CLI - `packages/mcp` — MCP server - `prisma/schema.prisma` — owned by `apps/api` Docs (`docs/`): | Doc | What | |---|---| | [quickstart.md](docs/quickstart.md) | Self-host quickstart: fresh clone → running API → first Application → first end-user. The Cloud quickstart is [rekey.dev/docs/quickstart](https://rekey.dev/docs/quickstart) | | [api-url.md](docs/api-url.md) | Which URL to point the SDK at — Rekey Cloud vs self-hosted | | [concepts.md](docs/concepts.md) | Tenant / Application / EndUser data model | | [api-keys.md](docs/api-keys.md) | The three credential types, and environments | | [api-key-rotation.md](docs/api-key-rotation.md) | Leaked-key drill + rotation cadence | | [auth.md](docs/auth.md) | End-user auth: tokens, MFA, passkeys, OAuth | | [react-components.md](docs/react-components.md) | The drop-in React component library | | [billing.md](docs/billing.md) · [billing-providers.md](docs/billing-providers.md) · [coupons.md](docs/coupons.md) | Plans, checkout, providers, discounts | | [webhooks.md](docs/webhooks.md) | Outbound events, signature verification, retries | | [email-templates.md](docs/email-templates.md) | Custom transactional email: register, publish, preview, send by key from your backend | | [devices.md](docs/devices.md) | Device-bound sessions, the `max_devices` entitlement, licence seats | | [external-billing.md](docs/external-billing.md) | Bring your own billing: an inbound-only provider fed by your own system's events | | [portal.md](docs/portal.md) | Hosted customer self-service billing portal | | [errors.md](docs/errors.md) | Error envelope + the complete code reference | | [migrating-to-2.4.md](docs/migrating-to-2.4.md) · [migrating-to-2.2.md](docs/migrating-to-2.2.md) | Upgrading a deployment and its integrations to 2.4 from 2.3, and to 2.2 from 2.1 | | [jwks.md](docs/jwks.md) · [oidc-provider.md](docs/oidc-provider.md) · [tenant-auth.md](docs/tenant-auth.md) · [mcp.md](docs/mcp.md) · [data-erasure.md](docs/data-erasure.md) | Everything else | ## Billing providers Stripe, PayPal, and Razorpay ship in the box, all behind one `BillingProvider` interface with geographic routing (Razorpay for India, Stripe elsewhere, by default — configurable per Application). Need a different processor? Providers are self-describing modules — one directory describes checkout, webhook verification, and event mapping, and the registry wires up the rest. See [docs/billing-providers.md](docs/billing-providers.md) for the how-to. Billing is **off** on a new Application (`billingConfig.enabled` defaults to `false`) — every billing endpoint answers `403 BILLING_DISABLED` until an operator turns it on in Panel → Application → Billing. The core modules under `apps/api/src/modules/` (including `auth`, `billing`, `plans`, `api-keys` and `applications`) ship their own `AGENTS.md` describing what the module is for and what an agent should not do there. If a module has none, start from its `*.routes.ts` file. ## Known dev-only behaviours Things that are deliberately unsafe outside local development, documented here rather than left for you to discover. None are enabled by default. **`REKEY_DEV_ECHO_AUTH_TOKENS=true`** makes the operator password-reset and magic-link endpoints return the raw token in the API response, and the panel then puts that token in a URL query string (`?demoToken=…`) to render a working link. Query strings land in browser history, `Referer` headers, and access logs, so a token that reaches one is best treated as disclosed. The flag exists because those endpoints are unauthenticated by necessity — you cannot require a session to recover a forgotten password — so with no mail transport configured there is otherwise no way to complete the flow locally. Guards: the API **refuses to boot** if the flag is set with `NODE_ENV=production`, and the token is withheld unless `NODE_ENV` is exactly `development`. Unset or unknown values withhold it. We chose to leave the query-string hand-off in place rather than re-engineer a dev convenience, and to document it instead — if this turns out to bite someone, the fix is a one-shot httpOnly cookie and we'll take it. ## Contributing Setup, the dev loop, and PR conventions are in [CONTRIBUTING.md](CONTRIBUTING.md). AI coding agents should read [AGENTS.md](AGENTS.md). ## License [MIT](LICENSE).