Skip to content

Repository files navigation

Infra

Infra

A centralized, self-hosted authentication server,
the one thing every app you build can point at.

Runs on your own Cloudflare account. No per-user bill, no third party holding your users' data.

📖 Documentation

Technologies we use & love

TanStack Start Cloudflare Turborepo Better Auth Bun Tailwind CSS Biome shadcn/ui Zod Claude Code Git


Infra is open source, published by Pherus. Pherus uses it, but Infra is not Pherus's product: it's its own project, and anyone can deploy their own instance

What's in this repo

This is a Turborepo monorepo (bun workspaces):

Package What it is
infra/ The auth engine. The real betterAuth() instance (all plugins, D1/KV/R2), the admin dashboard, and only the admin/owner-facing pages: /setup, admin /sign-in, /forgot-password. See infra/README.md.
www/ ("Infraccount") The end-user "my account" app, the same idea as myaccount.google.com. It runs no auth server of its own; it's a pure client of infra via better-auth/react's createAuthClient. Hosts the OAuth provider's pages (/sign-in, /create-account, /two-factor, /consent, /forgot-password, /reset-password) plus profile, security (2FA, passkeys, active sessions), and wallets (saved mobile-money numbers, transaction history, receipts).
shared/ui (@infra/ui) The one UI kit, consumed by both apps. Neither app owns its own copy of components.
plugins/r2 (@infra/r2) Object storage, extracted into a standalone package with server and client exports so www (or any third-party consumer) can call it too, not just infra's own in-process auth.api.*.
plugins/payment (@infra/payment) PawaPay mobile-money payments, extracted the same way.

Why two apps

Infra used to be a single app doing everything: the admin dashboard and the hosted OAuth login/consent/sign-up pages a connected app's users see. That coupled two very different audiences, the instance owner managing the platform and any end user of any connected app, into one surface.

Splitting them means infra only ever needs to authenticate its own admins/owners, and www/Infraccount is the one place an end user (of any app pointed at this instance) signs in, manages passkeys/2FA, and manages payments. The same shape as how Google separates the internal admin console from myaccount.google.com.

Running locally

bun install                              # installs every workspace package
cp infra/.env.example infra/.env.local   # infra's secrets, see comments in the file
cp www/.env.example www/.env.local       # www's config (points at infra's URL)
bun run dev                              # starts infra (:3000) and www (:3001) together

Whenever you edit an .env/.env.local file, re-run bun run type-gen and fully restart the dev server. Vite's own file-watcher restart isn't enough for Cloudflare Worker bindings/secrets to pick up the change.

bun run build       # production build, every package
bun run typecheck   # tsc --noEmit, every package

See Getting Started for prerequisites and first-run setup, or the full docs site for architecture, the OAuth provider, and payments.

Deploying

infra and www are each their own Cloudflare Worker, deployed independently:

bun run --cwd infra deploy
bun run --cwd www deploy

docs/ stays at the repo root, not nested under infra/, since GitHub Pages serves it as a root-level /docs folder.

Contributing

Contributions are welcome. See CONTRIBUTING.md for setup, code conventions, and how to open a pull request. This project follows a Code of Conduct. Found a security issue? See SECURITY.md, please don't open a public issue for it.

License

MIT

About

A centralized, self-hosted authentication server, the one thing every app you build can point at.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages