# @leumas/imperium-server — host Imperium on your own infra

Standalone, white-label Imperium production server — the packageable 'run your own Imperium ecosystem' edition. Reuses the @leumas/hosting core (resolve/serve/SEO/mode-endpoints/shield) behind a...


The standalone, white-label edition of Imperium: point a wildcard domain at it and every subdomain
resolves to its own config (`mode` + `data`) and renders. It is **the same engine the Leumas
platform runs** — `@leumas/hosting` — assembled into a small Express app, so a self-hosted instance
gets identical resolve, publish gating, SEO, storage endpoints and anti-scrape behaviour.

```
visitor → wildcard edge (Caddy) → imperium-server worker(s) → @leumas/hosting → your config store
```

## Quick start

```bash
node bin/create-imperium.mjs --dir ./my-imperium --domain example.com
cd ./my-imperium
node node_modules/@leumas/imperium-server/src/server.js
```

The scaffolder creates the runtime layout, generates instance secrets, and seeds the shared
`__global` layer plus one published site so the first boot serves a real page. Re-running it never
overwrites existing configs.

## What it serves

| Surface | Path |
|---|---|
| Every subdomain's page | `GET *` — SEO injected into the SPA bundle (or a minimal shell without one) |
| Config resolution | `GET /api/hosting/resolve?host=…` |
| Custom view types | `GET /api/hosting/view-modes` (public — anonymous visitors need these to render `x-…` sites) |
| Storage-backed modes | `GET /api/hosting/modes/:siteId/{folder,gallery,exe}/…` |
| SEO | `/robots.txt`, `/sitemap.xml` (this host), `/sitemap-index.xml` (all servable sites) |
| Probes | `/healthz`, `/readyz` |

Publishing, editing and the member UI live in **Leumas Studio** — this server is the read/serve
plane. Point both at the same store, or run Studio's appliance EXE alongside it.

## Configuration

All optional except the base domain.

| Env | Meaning |
|---|---|
| `PORT` | listen port (default `1000`) |
| `IMPERIUM_BASE_DOMAIN` | the domain subdomains hang off (`example.com`) |
| `IMPERIUM_WEB_DIST` | a built `leumas-web` bundle → full SPA rendering |
| `LEUMAS_DATA_ROOT` | the config store (libSQL) — the connector reads this one |
| `IMPERIUM_DATA_ROOT` | server-local runtime files (`license.json`) |
| `IMPERIUM_STORAGE_ROOT` | per-site upload roots for folder/gallery/exe modes |
| `IMPERIUM_PREVIEW_SECRET` | **must be identical across workers** or draft-preview links fail to verify |
| `IMPERIUM_FOLDER_ROOTS` | `;`-separated absolute prefixes a `folder` site may read (self-host only) |
| `IMPERIUM_RESOLVE_CACHE_MS` | resolve read-cache TTL (default `3000`, `0` disables) |
| `IMPERIUM_ALLOW_LEGACY_CONFIGS` | serve pre-`firstParty` configs during a migration |
| `LEUMAS_LICENSE_SERVER` | licensed appliance: entitlement policies come from the signed token |

## Entitlements

Two modes, no configuration branching in your code:

- **Self-host** — the instance's own `entitlement_policies` docs, layered over the committed
  defaults, so hosting works out of the box.
- **Licensed appliance** (`LEUMAS_LICENSE_SERVER`) — the policy ceiling is a Leumas-signed Ed25519
  token verified **offline**; an unreachable platform never bricks the instance before the token's
  own expiry. Membership assignment stays local to your instance.

Set `IMPERIUM_NO_ENTITLEMENTS=1` to serve every published site without the per-owner capability
re-check (single-tenant installs).

## Scaling

`deploy/Caddyfile` is a wildcard edge that forwards **all** of `*.example.com` to an identical
worker pool, preserving the `Host` header — no per-site routes, ever. Start N copies on
`1001…100N`, list them in the `reverse_proxy` block, and Caddy health-checks `/healthz`.

Each worker is stateless apart from its store; the resolve cache is per-process and TTL-bounded, so
workers converge within `IMPERIUM_RESOLVE_CACHE_MS` of a publish.

## Security posture

- Configs serve only when **published** (hosted) or explicitly flagged **`firstParty`** (operator).
- Storage endpoints are traversal-guarded and scoped to `storage/imperium/<siteId>/`; raw filesystem
  roots are self-host-only and must be allowlisted via `IMPERIUM_FOLDER_ROOTS`.
- Custom view types render inside a **sandboxed iframe** with no same-origin access.
- The reverse proxy performs SSRF checks (private/loopback/metadata ranges denied, DNS-rebind pinned).
- The optional per-site shield applies rate limits, user-agent denylists and honeypot paths at the edge.


---
Source: shared/services/imperium-server/README.md
Canonical: https://docs.leumas.tech/p/services/imperium-server
