Skip to content

mlaify/aegis-deploy

Repository files navigation

aegis-deploy

Self-hosted Docker Compose stack for an Aegis email server, fronted by Cloudflare Tunnel + Workers. One docker compose up brings the relay and gateway online; the admin / web SPAs are deployed separately as Cloudflare Workers from their own repos.

v0.3-alpha — reference deployment. Production hardening (HSM-backed tokens, secrets management, multi-instance HA) is out of scope for this repo. Treat it as a reproducible starting point, not a turnkey appliance.

What you get

Service Container Public access Purpose
relay aegis-relay via cloudflared at relay.<your-domain> Encrypted-envelope store + identity directory + admin API
gateway aegis-gateway host ports 25, 993 (direct) Legacy SMTP/IMAP boundary for interop with classic email
cloudflared aegis-cloudflared outbound only Tunnel daemon — connects this host to Cloudflare's edge
(separate) aegis-admin SPA CF Worker admin.<your-domain> Operator console (deploy via aegis-admin/wrangler.jsonc)
(separate) aegis-web SPA CF Worker auth.<your-domain> End-user identity / inbox (deploy via aegis-web/wrangler.jsonc)

Why the SPAs aren't in this stack: they're already publicly-trusted CF Workers via wrangler deploy, with edge caching, CF Access policies, and zero-config TLS. Bundling them into a host-side container would just be re-adding the nginx-and-certbot pain we left behind.

Prerequisites

  • Docker 24+ with Compose v2
  • A Cloudflare account with the domain you want to host on (free tier is fine)
  • A Cloudflare Tunnel created in Zero Trust → Networks → Tunnels, with the connector token in hand
  • The sibling repos cloned next to this one:
    parent/
    ├── aegis-relay/
    ├── aegis-gateway/
    ├── aegis-admin/        ← deployed separately as a CF Worker
    ├── aegis-web/          ← deployed separately as a CF Worker
    └── aegis-deploy/       ← you are here
    
  • Public IPv4 with port 25 (and 993) open only if you want legacy email federation. CF Tunnel can't proxy SMTP — port 25 has to be reachable from other mail servers directly. PQ-native-only deployments can leave the gateway disabled.

Quick start

1. Create the Cloudflare Tunnel

In Cloudflare → Zero Trust → Networks → Tunnels:

  • Create a tunnel named aegis-<your-org> (Cloudflared connector)

  • On "Install and run a connector", copy the token (e.g. eyJhIjoiOWY...). Don't run the install command — docker compose up runs the connector for you.

  • On the Public Hostnames tab, add:

    Subdomain Domain Service
    relay your-domain.com http://relay:8787
    gateway-admin (optional) your-domain.com http://gateway:8788

    These hostnames refer to the docker-compose service names, not host IPs — the cloudflared container can dial them on the internal compose network.

2. Bootstrap the stack

cd aegis-deploy
./scripts/bootstrap.sh
docker compose up -d

The bootstrap script prompts for hostname, primary domain, and your tunnel token, then writes .env with fresh admin tokens (mode 600).

Confirm the tunnel registered:

docker compose logs cloudflared | grep "Registered tunnel connection"

3. Deploy the admin SPA (one-time, separate)

cd ../aegis-admin
npm install
wrangler login
npm run deploy

In the CF dashboard → Workers & Pages → aegis-admin → Settings → Custom Domains, attach admin.<your-domain>.

(Repeat for aegis-web if you want the end-user identity SPA at auth.<your-domain>.)

4. Claim your domain

Open https://admin.<your-domain> (sign in via CF Access if you've enabled it). Connect using:

  • Relay admin URL: https://relay.<your-domain>
  • Relay admin token: the value from .env's AEGIS_RELAY_ADMIN_TOKEN

Go to Domains → Claim domain and enter your domain. The UI shows the DNS TXT record to add (_aegis-verify.<domain>). Add it at your DNS provider, then click Verify.

CLI alternative for a headless workflow:

./scripts/claim-domain.sh example.com

5. (Recommended) Publish discovery records

Once the domain is verified, clients can auto-discover this relay if you publish a DNS SRV record. See docs/dns-discovery.md for the SRV + .well-known walkthrough.

6. Provision your first user

In the admin UI: Users → Provision a user → enter a local part (alice) under your verified domain.

Configuration reference

All configuration lives in .env. See .env.example for the full list.

Variable Purpose
AEGIS_PUBLIC_HOSTNAME Hostname this relay answers on (matches the CF Tunnel ingress)
AEGIS_PRIMARY_DOMAIN Domain you'll claim (informational only — the actual claim happens via the admin UI)
AEGIS_RELAY_PUBLIC_URL Advertised in /.well-known/aegis-config
CLOUDFLARED_TOKEN Tunnel token from CF dashboard
AEGIS_RELAY_ADMIN_TOKEN Bearer token gating /admin/* on the relay
AEGIS_GATEWAY_ADMIN_TOKEN Bearer token gating /admin/* on the gateway
AEGIS_GATEWAY_PASSPHRASE Long-term secret deriving the gateway demo identity
SMTP_PORT / IMAP_PORT Public ports for legacy email federation

Updating

git -C ../aegis-relay   pull
git -C ../aegis-gateway pull
git pull
docker compose build --pull
docker compose up -d

The aegis-admin and aegis-web SPAs update via their own npm run deploy flows.

Storage lives in the relay-data named volume; pulling a new image does not touch your envelopes, identities, or domain claims.

Backup

The relay's complete state is in the relay-data volume:

  • aegis-relay.db — SQLite database (envelopes, identities, domains, users)
  • aegis-relay-runtime.json — runtime config (tokens, retention policy)
  • aegis-relay-audit.jsonl — append-only audit log
docker run --rm -v aegis-deploy_relay-data:/data -v "$PWD":/backup alpine \
    tar czf /backup/aegis-relay-$(date +%F).tar.gz -C /data .

Troubleshooting

  • Tunnel won't registerdocker compose logs cloudflared will show "Unauthorized" if the token is wrong. Pull a fresh token from the CF dashboard and update .env. The token is regenerable.
  • curl https://relay.<your-domain>/healthz returns 502 — tunnel ingress likely points at the wrong service name or port. The Service field must be http://relay:8787 (lowercase, the docker-compose service name, not localhost).
  • curl /.well-known/aegis-config returns 404 — domain not verified yet. Complete the Claim → Verify flow first.
  • Domain verification fails — DNS TTL: wait a few minutes after publishing the TXT record. Check from anywhere: dig TXT _aegis-verify.<domain> @1.1.1.1
  • docker compose build is slow — first build compiles Rust from scratch; subsequent builds are cached. If you change Cargo.toml, expect a full rebuild.

Security notes

  • The admin token is a bearer token over HTTPS. Treat it like a root password. Rotate it via the admin UI ("Add token" → revoke the old).
  • The CF Tunnel token is also a long-term credential. Rotate it from the CF dashboard if it's ever exposed.
  • The gateway admin route (gateway-admin.<your-domain>) sits behind the same tunnel as the relay. Add a CF Access policy restricting it to your operator email — the gateway's bearer-token check is the second line of defense, not the first.
  • The relay enforces self-signed identity documents (Ed25519 + ML-DSA-65). It does not implement key continuity yet — a key rotation appears to clients as a fresh identity (RFC-0002 §11).
  • Federation is not yet implemented — this relay only delivers to identities published on itself or via the gateway's SMTP path.

About

Docker Compose deployment stack for self-hosted Aegis (relay + gateway + admin)

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors