Aetherheim is a secure Rust CMS foundation using Axum, SurrealDB, and Fluxheim. The roadmap tracks later Leptos admin UI and Wasmtime plugin runtime milestones.
The development stack runs SurrealDB and Fluxheim in rootless Podman while the Rust app runs locally on the host.
cp .env.podman.example .env.podman
podman compose --env-file .env.podman -f deploy/podman/podman-compose.yml up -dExpected local endpoints:
- Fluxheim:
http://127.0.0.1:8080 - SurrealDB:
http://127.0.0.1:8000 - Rust app upstream expected by Fluxheim:
http://127.0.0.1:3000
When running behind the local Fluxheim container, bind the Rust app to
0.0.0.0:3000; Fluxheim proxies to it through host.containers.internal.
Run the full local checks:
scripts/checks.shThis gate runs formatting, clippy, tests, cargo deny, cargo audit,
cargo license, and scripts/check_latest_crates.sh so direct crate versions
stay aligned with crates.io.
Run checks plus the local app, Fluxheim, and SurrealDB smoke test:
AETHERHEIM_RUN_SMOKE=1 scripts/checks.shDeployment notes for manual operation, Fluxheim reverse proxy operation, and rootless Podman/Wolfi operation are tracked in docs/DEPLOYMENT.md.
The smoke test enables AETHERHEIM_ALLOW_INSECURE_INSTALLER_PASSKEY_FIXTURE=1
for deterministic API testing. Do not set that variable outside local smoke
runs; it is rejected unless
AETHERHEIM_ALLOW_INSECURE_LOCAL_DATABASE_PASSWORD=1 is also set. Normal
installer admin creation expects a browser WebAuthn registration response.
The smoke test does not enable the insecure administrator bypass. It creates a short-lived smoke session directly in the disposable SurrealDB test database after installer completion, then exercises privileged APIs through the normal session middleware.
For a real first launch, Aetherheim creates a high-entropy installer token file
with 0600 permissions and stores only the keyed token hash in SurrealDB. By
default the file is written to AETHERHEIM_STORAGE_DIR/installer-token; set
AETHERHEIM_INSTALLER_TOKEN_FILE to use an explicit operator-owned path.
Aetherheim attempts to remove the token file after the first administrator is
created and the installer is locked.
Set AETHERHEIM_STORAGE_DIR to the local asset storage directory. Uploaded
asset bytes are written below that root with generated uploads/ keys; public
metadata responses never expose storage keys or checksums.
Set AETHERHEIM_INSTANCE_ID_FILE to a persistent operator-owned path when you
want the instance identity outside the storage directory; otherwise Aetherheim
creates AETHERHEIM_STORAGE_DIR/instance-id on first boot.
The first-run installer creates the first administrator with a passkey, returns one-time recovery codes that the operator must store before leaving the page, and then locks itself automatically. Normal administrator login uses:
POST /_aetherheim/auth/passkey/optionsto create a short-lived discoverable WebAuthn challenge.POST /_aetherheim/auth/passkey/finishto verify the assertion, update passkey state, and issue an HttpOnly, SameSite=Strict, Secureaetherheim_admin_sessioncookie scoped to/_aetherheim.GET /_aetherheim/auth/sessionto check the current administrator session.POST /_aetherheim/auth/logoutto revoke the database-backed session and clear the browser cookie.
Session tokens are never stored in plaintext. Aetherheim stores only keyed
token hashes, verifies active/non-expired sessions in SurrealDB, and requires
route-specific capabilities for privileged APIs. system.admin remains the
superuser override. Authorization re-loads the current user and role
capabilities on each privileged request, so disabling a user or changing roles
takes effect without waiting for the session to expire.
User and commenter email addresses are also stored as keyed, normalized hashes
instead of raw addresses or unsalted digests.
Authentication routes are rate-limited and failed passkey/recovery attempts
trigger a temporary client lockout using the configured security policy.
Administrator recovery codes are one-time, high-entropy fallback credentials. The installer creates the first set for the initial administrator. Plaintext codes are returned only in the creation response; Aetherheim stores only keyed hashes and short prefixes for operator identification. Recovery login consumes a matching active code for the requested username before issuing an admin session, and replayed codes are rejected. Operators can revoke unused recovery codes; revocation returns only safe metadata and records an audit event.
The release roadmap is tracked in docs/ROADMAP.md. The 1.0 scope includes a secure first-run installer with one-time setup token, passkey-first administrator creation, environment/proxy validation, safe site defaults, and automatic installer lockout after setup.
Licensing notes are tracked in docs/LICENSING.md. The current core license is EUPL-1.2; a future plugin/theme exception is planned for the stable Wasm extension runtime so independently developed extensions can use their own licenses through the documented API boundary.