Own the platform. Own the data. Own the DNS.
Rootminster is the self-hosted management platform behind Open Domains. It brings DNS, domain requests, user accounts, staff tooling, security controls, integrations, APIs and background jobs together in one deployable stack.
Rootminster is built for running a real shared-domain platform without stitching together a pile of disconnected admin tools.
It gives operators one place to manage the full lifecycle of a domain service:
| Capability | What it does | |
|---|---|---|
| π | DNS management | Manage zones and DNS records through Cloudflare with ownership and permission checks enforced server-side. |
| π¨ | Domain requests | Accept, review, approve, reject and audit subdomain requests with staff workflows. |
| π₯ | Users & roles | Local accounts, OAuth login, sessions, email verification, staff roles and administrator controls. |
| π‘οΈ | Safety screening | Explainable request risk scoring, protected-brand signals, velocity checks and staff overrides. |
| π | Scoped API tokens | Restrict tokens by permission, hostname, DNS type and expiry. |
| π‘ | Dynamic DNS | Dedicated DDNS endpoints for controlled A and AAAA record updates. |
| βοΈ | Background automation | DNS checks, synchronisation, cleanup and scheduled platform maintenance. |
| π | Analytics | Optional per-subdomain analytics through Umami. |
| π¬ | Discord tooling | Signed slash commands for user and staff workflows. |
| π³ | Donations | Optional Stripe-backed donations and donation-gated features. |
| π | Audit trail | Keep an operational record of sensitive platform actions. |
Rootminster is deliberately modular. Core platform functions stay lean while optional services can be switched on, configured and replaced from the admin interface.
Rootminster is intentionally compact:
- React 18 + Vite powers the web interface.
- Fastify on Node.js 24 serves the API and production frontend.
- PostgreSQL 17 stores identities, sessions, operational entities, settings and audit data.
- A separate job runner handles scheduled maintenance and synchronisation.
- Cloudflare provides authoritative DNS integration.
- Optional modules add SMTP, Discord, Stripe, Umami, OAuth and other services.
PostgreSQL advisory locks prevent duplicate scheduled jobs when multiple job runners are accidentally started.
For a deeper look, see ARCHITECTURE.md.
git clone https://github.com/open-domains/Rootminster.git
cd Rootminstercp .env.example .envAt minimum, configure:
APP_URL=https://rootminster.example.com
POSTGRES_PASSWORD=replace-this-with-a-strong-passworddocker compose up -d --buildThe stack starts:
appβ Rootminster web interface + APIjobsβ scheduled jobs and maintenancepostgresβ PostgreSQL 17
By default the application is bound to:
127.0.0.1:3000
You can seed an administrator directly:
docker compose exec \
-e ADMIN_EMAIL=admin@example.com \
-e ADMIN_PASSWORD='replace-with-a-long-password' \
app npm run db:seed-adminOr, on a brand-new installation, configure INITIAL_SETUP_KEY, open /setup, create the first administrator and then remove the setup key from the environment.
Database migrations run automatically when the application container starts.
Rootminster includes an optional Traefik overlay.
Create or reuse the external Docker network configured by TRAEFIK_NETWORK, then run:
docker compose \
-f compose.yml \
-f compose.traefik.yml \
up -d --buildThe overlay creates an HTTPS router for APP_HOST and forwards traffic to Rootminster inside Docker.
For internet-facing deployments, HTTPS should be considered mandatory before enabling authentication or API access.
Optional integrations are configured from:
Admin β Module Settings
Rootminster currently exposes modules for:
- GlitchTip error monitoring
- Cloudflare DNS
- SMTP email
- Cloudflare Turnstile
- Stripe donations
- Google OAuth
- GitHub OAuth
- Discord
- automated safety screening
- MCP server
- Umami analytics
Secrets stored through Module Settings are encrypted with AES-256-GCM and are never returned to the browser.
Existing environment-based integration settings can be imported once into the database. After checking the imported configuration, the matching optional environment variables can be removed.
Only bootstrap and runtime values need to remain in the environment, such as database connectivity, application URL, encryption keys and initial setup settings.
Rootminster ships with a versioned REST API under:
/api/v1
Interactive documentation is available at:
/api-docs
The OpenAPI 3.1 document is exposed at:
/api/v1/openapi.json
Users can create hashed bearer tokens under:
Settings β API Tokens
API tokens can be restricted by:
- permission
- exact hostname
- DNS record type
- expiry date
Dynamic DNS uses the dedicated dns:dynamic permission and:
POST /api/v1/dynamic-dns
DDNS tokens must be restricted to owned hostnames and A/AAAA record types. The endpoint can use the caller's public IP or an explicitly supplied public IPv4/IPv6 address.
It will not silently create a new DNS record if the target record does not already exist.
Default limits include:
| Access type | Default limit |
|---|---|
| Public reads | 60 requests/minute/IP |
| Authenticated reads | 120 requests/minute/token |
| Authenticated writes | 30 requests/minute/token |
Responses expose standard limit, remaining, reset and retry headers.
The legacy /functions/publicApi?action=β¦ endpoint remains available for compatibility.
Rootminster is built around server-side authorization rather than trusting the browser.
Key protections include:
- Argon2id password hashing
- database-backed sessions
- role checks on protected actions
- narrowly scoped API tokens
- rate limiting
- encrypted module secrets
- Cloudflare Turnstile support
- audited administrative actions
- request risk screening
Accepted requests receive a versioned, explainable risk assessment covering signals such as:
- suspicious wording
- preview URL structure
- protected brands
- account age
- request velocity
- rejection history
- sensitive DNS record types
- shared DNS targets
The score helps staff prioritise reviews. It never approves requests automatically.
Staff can inspect individual signals, re-run screening or override a verdict with a mandatory audited reason.
Rootminster can optionally expose its role-aware remote control endpoint at:
https://your-host/mcp
The endpoint uses OAuth 2.1 authorization-code flow with PKCE, dynamic client registration, short-lived access tokens and rotating refresh tokens.
Permissions are evaluated against the user's current Rootminster role on every call. Changing or disabling a staff account therefore takes effect immediately on subsequent requests rather than leaving stale authorization inside long-lived tokens.
All authenticated users can inspect their own account, requests and subdomains. Staff and administrators can additionally review pending requests and perform normal approval or rejection operations, with the same audit and notification behaviour used by the web interface.
The importer accepts either an object keyed by entity name or an object containing an entities property.
Example:
{
"entities": {
"User": [],
"Domain": [],
"DnsRecord": [],
"SubdomainRequest": []
}
}Import from a local file:
npm run db:import -- ./export.jsonOr stream the export into the running application container:
docker compose exec -T app node scripts/import-data.js - < export.jsonLegacy IDs are retained in legacy_id while new UUIDs are generated for Rootminster.
Imported users are marked as verified, but passwords are not imported. Existing users must use the password-reset flow before signing in with email and password.
Install dependencies:
npm installPoint DATABASE_URL at a PostgreSQL database, then run:
npm run db:migrate
npm run devVite serves the web interface and proxies API requests to the local Fastify process.
| Command | Purpose |
|---|---|
npm run dev |
Start the web and API development processes |
npm run dev:web |
Start only the Vite frontend |
npm run dev:api |
Start only the Fastify API |
npm run build |
Build the production frontend |
npm start |
Start the production API and frontend |
npm run jobs |
Start scheduled jobs |
npm run db:migrate |
Apply database migrations |
npm run db:seed-admin |
Create or update an administrator |
npm run db:import -- FILE |
Import an existing data export |
npm run lint |
Run code-quality checks |
npm run lint:fix |
Fix supported lint problems |
npm run typecheck |
Type-check the JavaScript/JSX project |
Before exposing Rootminster publicly:
- Use a strong PostgreSQL password.
- Keep PostgreSQL on the internal Docker network.
- Put the application behind HTTPS.
- Configure a persistent module encryption key.
- Use a narrowly scoped Cloudflare API token restricted to the required zones.
- Enable Turnstile for public forms where appropriate.
- Never commit
.env, database exports or production secrets. - Back up the
postgres_datavolume before upgrades or bulk imports. - Remove
INITIAL_SETUP_KEYafter the first administrator is created. - Review installed module permissions before enabling them.
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Rootminster β
ββββββββββββββββββββββββββββββββββββββββββββββββ€
β app Web UI + Fastify API β
β jobs Scheduled maintenance β
β postgres Durable platform data β
ββββββββββββββββββββββββββββββββββββββββββββββββ
The default Compose network is internal, keeping PostgreSQL and background services away from the public network surface.
Rootminster is designed to handle the less glamorous parts of running a shared domain service too: approvals, DNS drift, authentication, moderation signals, auditability, background cleanup and safe extensibility.
The result is one control plane that can start small, run comfortably in Docker and grow into a much larger domain platform without turning into infrastructure spaghetti. ππ«