Cosmos (Corporate Site)¶
Scope¶
This page covers Cosmos infrastructure managed from the Aether repo — the
nginx vhost, the cosmos-init bootstrap, and the aether-side deployment pattern.
For Cosmos application specifics (content, pages, the lead form), see the Cosmos
repo's own README.md.
Architecture¶
Cosmos is the Groupe Suffren corporate site (groupe-suffren.com) — a
separate, static-first Next.js app, deliberately distinct from the Helios
practice sites. All page content is local (lib/content.ts); there is no
backend/CMS dependency. The one runtime integration is the contact/lead form.
┌───────────────────────────────────────┐
│ nginx (aether) │
│ cosmos-staging.groupe-suffren.com ──┼── cosmos-staging-web:3000
│ (groupe-suffren.com — prod, later) ──┘
└───────────────────────────────────────┘
│
▼
┌──────────────────────────┐
│ cosmos-{env}-web │
│ (Next.js standalone) │
└────────────┬─────────────┘
│ POST lead (only when configured)
▼
┌──────────────────────────┐
│ LEAD_WEBHOOK_URL │
│ (email relay / Make / │
│ future crm.Prospect) │
└──────────────────────────┘
One Next.js container per environment. Because content is static, the image builds offline (no build args, no API calls at build time).
Managed by Aether¶
Nginx vhosts¶
In nginx/conf.d/:
cosmos-staging.conf.bootstrap— port-80-only, used once bycosmos-initto serve the ACME challenge before the cert existscosmos-staging.conf.full+.conf.temp— the HTTPS vhost and its maintenance variant (proxied bydeploy.sh's container-swap)
The full vhost proxies to cosmos-staging-web:3000 and is basic-auth gated
(shared /etc/nginx/.htpasswd) — staging is private and non-indexable
pre-launch. There is an edge rate-limit on /api/lead (the forms zone).
Environment files¶
Cosmos has no secrets today, so there is no envs/cosmos/*.enc in aether and
no SECRET_FILES entry. Its only config is:
| Variable | Purpose |
|---|---|
LEAD_WEBHOOK_URL |
Where /api/lead POSTs each validated lead (email relay / Make / future crm.Prospect). Unset + NODE_ENV=production → the route returns 503 (fail-loud). |
ENVIRONMENT |
staging/prod — container naming/labels (set by deploy.sh). |
When LEAD_WEBHOOK_URL is set it lives in /opt/docker/cosmos/envs/.env.<env>
and becomes the first thing to bring under aether's SOPS management (add it to
SECRET_FILES and make encrypt).
Bootstrapping a new Cosmos install¶
For a brand-new deployment (fresh server or first Cosmos deploy on an existing server):
This target:
- Creates
/opt/docker/cosmos/{repo,envs}/on the server - Activates the port-80 bootstrap vhost and reloads nginx (ACME reachable)
- Obtains the SSL cert for
cosmos-staging.groupe-suffren.com - Installs the
.full/.tempvariants, activates the HTTPS vhost, reloads nginx
After cosmos-init:
- Clone the Cosmos repo:
git clone git@github.com:baudry-suffren/cosmos.git /opt/docker/cosmos/repo - Deploy:
cd /opt/docker/cosmos/repo && make deploy ENV=staging - (When the delivery target exists) add
LEAD_WEBHOOK_URLto/opt/docker/cosmos/envs/.env.staging, thenmake encryptin aether once it's registered inSECRET_FILES
cosmos-init is required — make deploy cannot replace it
Aether's make deploy copies the .conf.full/.conf.temp variants but
nginx ignores those (only *.conf loads), and it never issues the cert
nor activates the vhost. Activating a full vhost that references a missing
cert would break nginx -t and risk the whole shared proxy on its next
restart. cosmos-init issues the cert before activating HTTPS.
Deploying Cosmos updates¶
Cosmos-side deploys (code, content) happen from the Cosmos repo:
cd /opt/docker/cosmos/repo
make deploy ENV=staging # deploys develop
make deploy ENV=prod REF=v1.0 # deploys a tag (once prod exists)
Aether-side deploys are only needed when the nginx vhost changes:
| Change | How to deploy |
|---|---|
Edit cosmos-staging.conf.full (routing, auth, rate-limit) |
cd aether && make deploy && make restart-nginx |
| First-ever bootstrap / new environment | make cosmos-init (see above) |
LEAD_WEBHOOK_URL change |
Edit the server env file → make restart ENV=staging in cosmos repo → (if secret-managed) make encrypt in aether |
Production — staged, waiting on the DNS cutover¶
Everything for prod is pre-staged: the cosmos-prod vhosts
(cosmos-prod.conf.{bootstrap,full,temp}), the cosmos-prod-init target, and
MANAGED_FILES entries are all in place. The prod vhost serves the apex
publicly (no basic auth) and 301-redirects www → apex.
The one thing that cannot be pre-done is the TLS cert: Let's Encrypt (HTTP-01)
needs groupe-suffren.com to already resolve to this server, and today it
points to 199.60.103.64/164 (elsewhere). So the cert issues at cutover.
Cutover runbook¶
- Repoint DNS — apex
groupe-suffren.com+wwwA records → this server (54.36.99.184). Confirm:dig +short groupe-suffren.com. - Set the webhook (do this first — a live public form with no target 503s):
add
LEAD_WEBHOOK_URL=…to/opt/docker/cosmos/envs/.env.prod. - Cert + vhost (issues the cert, leaves a maintenance page up — no 502 gap):
- Deploy the app (builds, starts
cosmos-prod-web, swaps maintenance → live, smoke): Prod requires a tag (cut one offmain, which now carries the Docker setup:git tag v0.1.0 && git push origin v0.1.0).
That's it — DNS + two commands. If certbot fails at step 3, DNS hasn't
propagated yet (dig +short groupe-suffren.com must show this server); retry.
Troubleshooting¶
cosmos-staging returns 502: the container is down. Check
docker ps --filter name=cosmos-staging and docker logs cosmos-staging-web.
/api/lead returns 503: LEAD_WEBHOOK_URL is unset — by design (fail-loud).
Set it in /opt/docker/cosmos/envs/.env.staging and make restart ENV=staging.
nginx won't reload after a cosmos change: almost always a missing cert —
docker exec nginx-proxy ls /etc/letsencrypt/live/cosmos-staging.groupe-suffren.com/.
If absent, run make cosmos-init (idempotent; certbot skips a valid cert).
Basic-auth prompt won't accept credentials: the shared /etc/nginx/.htpasswd
is used; see environment management.