some work

9a5b6ec · gmorell · 2026-05-21 22:45

9 files +140 -55

Files changed

modified README.md
+18 −7
@@ -194,9 +194,14 @@ diogramos. The pieces:
194 194 Ingress with cert-manager.
195 195 - **`pg.yml`** — StackGres SGCluster with the `citext` extension
196 196 enabled (`users.email` is a `citext` column).
197 −- **`app-secrets.example.yml`** — template Secret carrying
198 − `secret_key_base` and `erlang_cookie`. The Postgres password comes
199 − from the StackGres-managed `postgres` Secret in the same namespace.
197 +- **`secrets.example.yml.tmpl`** — template Secret (`waxx-secrets`)
198 + carrying `SECRET_KEY_BASE`, `RELEASE_COOKIE`, optional `SENTRY_DSN`,
199 + and the full `SMTP_*` + `MAIL_FROM_*` env set for the mailer. The
200 + `.tmpl` extension keeps `kubectl apply -f .` and Argo CD directory
201 + globs from accidentally applying the placeholder values (which look
202 + valid but crash the pod — `SECRET_KEY_BASE=REPLACE_ME` is only 10
203 + bytes, Phoenix wants ≥64). The Postgres password comes from the
204 + StackGres-managed `postgres-waxx` Secret in the same namespace.
200 205 - **`.gitlab-ci.yml`** — Kaniko build to the container registry on
201 206 pushes to `master`.
202 207
@@ -234,10 +239,16 @@ is visible in real time to a user on replica B.
234 239 # 1. Apply the database
235 240 kubectl apply -f pg.yml
236 241
237 −# 2. Fill in + apply the app secret
238 −cp app-secrets.example.yml app-secrets.yml
239 −$EDITOR app-secrets.yml
240 −kubectl apply -f app-secrets.yml
242 +# 2. Apply the app secret directly with real values (don't edit the
243 +# .tmpl in place — its REPLACE_ME placeholders crash the pod).
244 +SKB=$(openssl rand -base64 64 | tr -d '\n')
245 +COOKIE=$(openssl rand -hex 32)
246 +kubectl -n waxx create secret generic waxx-secrets \
247 + --from-literal=SECRET_KEY_BASE="$SKB" \
248 + --from-literal=RELEASE_COOKIE="$COOKIE" \
249 + --from-literal=SENTRY_DSN="" \
250 + --from-literal=SMTP_RELAY="" \
251 + --dry-run=client -o yaml | kubectl apply -f -
241 252
242 253 # 3. Apply the rest
243 254 kubectl apply -f app.yml
deleted app-secrets.example.yml
+0 −39
@@ -1,39 +0,0 @@
1 −# Example app-level secret for the waxx deployment.
2 −#
3 −# Postgres credentials are NOT in here — they're managed by StackGres
4 −# and pulled from the `postgres` Secret in the waxx namespace (the
5 −# SGCluster from pg.yml). This file only carries the application's own
6 −# secret_key_base and the Erlang distribution cookie.
7 −#
8 −# Generate the secret_key_base with:
9 −#
10 −# mix phx.gen.secret
11 −#
12 −# Or, if you don't have Mix on the host:
13 −#
14 −# openssl rand -base64 64 | tr -d '\n'
15 −#
16 −# Don't commit the populated copy — copy this file to `app-secrets.yml`
17 −# and add it to .gitignore (or rely on the existing `secret.yml` pattern).
18 −
19 −apiVersion: v1
20 −kind: Secret
21 −metadata:
22 − name: elixir-args
23 − namespace: waxx
24 −type: Opaque
25 −stringData:
26 − # `mix phx.gen.secret` output. 64+ bytes of base64 entropy.
27 − secret_key_base: "REPLACE_ME_with_64_bytes_of_base64_entropy_from_mix_phx_gen_secret"
28 −
29 − # Erlang distribution cookie — every replica must agree on this. Any
30 − # 32+ char random string works.
31 − #
32 − # openssl rand -hex 32
33 − #
34 − erlang_cookie: "REPLACE_ME_with_a_long_random_string"
35 −
36 − # Sentry DSN — copy from your project at https://sentry.io/settings/.../keys/.
37 − # Leave the line out (or set the Secret key empty) to disable reporting;
38 − # the `optional: true` flag in app.yml means the pod still starts.
39 − sentry_dsn: "https://PUBLIC_KEY@oXXXXXXX.ingest.sentry.io/PROJECT_ID"
modified app.yml
+33 −9
@@ -88,30 +88,54 @@ spec:
88 88 - name: RELEASE_COOKIE
89 89 valueFrom:
90 90 secretKeyRef:
91 − name: elixir-args
92 − key: erlang_cookie
91 + name: waxx-secrets
92 + key: RELEASE_COOKIE
93 93
94 94 - name: SECRET_KEY_BASE
95 95 valueFrom:
96 96 secretKeyRef:
97 − name: elixir-args
98 − key: secret_key_base
97 + name: waxx-secrets
98 + key: SECRET_KEY_BASE
99 99
100 100 # Sentry DSN is optional — when the key is empty or absent
101 101 # the pod still starts and Sentry quietly no-ops.
102 102 - name: SENTRY_DSN
103 103 valueFrom:
104 104 secretKeyRef:
105 − name: elixir-args
106 − key: sentry_dsn
105 + name: waxx-secrets
106 + key: SENTRY_DSN
107 107 optional: true
108 108
109 + # SMTP relay. With SMTP_RELAY unset/empty the mailer falls
110 + # back to Swoosh.Adapters.Logger — no mail sent, but the
111 + # rendered magic-link email shows up in pod logs at :info.
112 + - name: SMTP_RELAY
113 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_RELAY, optional: true } }
114 + - name: SMTP_PORT
115 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_PORT, optional: true } }
116 + - name: SMTP_USERNAME
117 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_USERNAME, optional: true } }
118 + - name: SMTP_PASSWORD
119 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_PASSWORD, optional: true } }
120 + - name: SMTP_TLS
121 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_TLS, optional: true } }
122 + - name: SMTP_SSL
123 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_SSL, optional: true } }
124 + - name: SMTP_AUTH
125 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_AUTH, optional: true } }
126 + - name: SMTP_TLS_VERIFY
127 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: SMTP_TLS_VERIFY, optional: true } }
128 + - name: MAIL_FROM_ADDRESS
129 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: MAIL_FROM_ADDRESS, optional: true } }
130 + - name: MAIL_FROM_NAME
131 + valueFrom: { secretKeyRef: { name: waxx-secrets, key: MAIL_FROM_NAME, optional: true } }
132 +
109 133 # Postgres creds come from the StackGres-managed auth secret
110 134 # (named after the SGCluster in pg.yml). runtime.exs reads
111 135 # these directly — no URL composition — so passwords with
112 136 # special characters don't get mangled by URL parsing.
113 137 - name: POSTGRES_HOST
114 − value: postgres
138 + value: postgres-waxx
115 139 - name: POSTGRES_PORT
116 140 value: "5432"
117 141 - name: POSTGRES_DB
@@ -119,12 +143,12 @@ spec:
119 143 - name: POSTGRES_USER
120 144 valueFrom:
121 145 secretKeyRef:
122 − name: postgres
146 + name: postgres-waxx
123 147 key: superuser-username
124 148 - name: POSTGRES_PASSWORD
125 149 valueFrom:
126 150 secretKeyRef:
127 − name: postgres
151 + name: postgres-waxx
128 152 key: superuser-password
129 153 readinessProbe:
130 154 httpGet:
modified lib/waxx_web/controllers/page_html/home.html.heex
+18 −0
@@ -216,5 +216,23 @@
216 216 palette. Open source.
217 217 </p>
218 218 </section>
219 +
220 + <footer class="py-10 sm:py-12 mt-8 border-t border-base-300 text-center">
221 + <p class="font-mono text-xs uppercase tracking-[0.25em] text-base-content/70 flex items-center justify-center gap-2">
222 + <span class="font-bold tracking-[0.16em]">WAXX</span>
223 + <span class="opacity-50">·</span>
224 + <span>a</span>
225 + <%!--
226 + Hardcoded oklch from the neiam-co accent (the value every
227 + ported theme assigns to --color-accent). Inline so the
228 + NEIAM mark stays yellow regardless of which theme the
229 + viewer has picked.
230 + --%>
231 + <span class="font-bold tracking-[0.16em]" style="color: oklch(96% 0.058 96);">
232 + <a href="https://neiam.org"> NEIAM </a>
233 + </span>
234 + <span>production</span>
235 + </p>
236 + </footer>
219 237 </div>
220 238 </Layouts.app>
added secrets.example.yml.tmpl
+71 −0
@@ -0,0 +1,71 @@
1 +# ─────────────────────────────────────────────────────────────────────
2 +# TEMPLATE — do NOT `kubectl apply` this file as-is.
3 +#
4 +# The `REPLACE_ME` placeholders pass YAML validation and produce a
5 +# working Secret object the pod can mount, but `SECRET_KEY_BASE` is
6 +# only 10 bytes — Phoenix's endpoint requires ≥64 — and pods will
7 +# enter CrashLoopBackOff with no useful log output on the way down.
8 +#
9 +# This file is named `.yml.tmpl` (not `.yml`) on purpose so
10 +# `kubectl apply -f .` and Argo CD directory globs skip it.
11 +#
12 +# Apply real values via:
13 +#
14 +# SKB=$(openssl rand -base64 64 | tr -d '\n')
15 +# COOKIE=$(openssl rand -hex 32)
16 +# kubectl -n waxx create secret generic waxx-secrets \
17 +# --from-literal=SECRET_KEY_BASE="$SKB" \
18 +# --from-literal=RELEASE_COOKIE="$COOKIE" \
19 +# --from-literal=SENTRY_DSN="" \
20 +# --from-literal=SMTP_RELAY="smtp.example.com" \
21 +# --from-literal=SMTP_PORT="587" \
22 +# --from-literal=SMTP_USERNAME="apikey-or-user" \
23 +# --from-literal=SMTP_PASSWORD="redacted" \
24 +# --from-literal=SMTP_TLS="if_available" \
25 +# --from-literal=SMTP_AUTH="if_available" \
26 +# --from-literal=MAIL_FROM_ADDRESS="noreply@waxx.neiam.co" \
27 +# --from-literal=MAIL_FROM_NAME="Waxx" \
28 +# --dry-run=client -o yaml | kubectl apply -f -
29 +#
30 +# Postgres credentials: NOT in here — pulled at runtime from the
31 +# StackGres-managed `postgres-waxx` Secret (auto-created by SGCluster
32 +# in pg.yml).
33 +# ─────────────────────────────────────────────────────────────────────
34 +
35 +apiVersion: v1
36 +kind: Secret
37 +metadata:
38 + name: waxx-secrets
39 + namespace: waxx
40 +type: Opaque
41 +stringData:
42 + # `mix phx.gen.secret` output, or: openssl rand -base64 64 | tr -d '\n'
43 + # 64+ bytes of base64 entropy. Anything shorter crashes the endpoint.
44 + SECRET_KEY_BASE: REPLACE_ME
45 +
46 + # Erlang distribution cookie — every replica must agree on this.
47 + # openssl rand -hex 32
48 + RELEASE_COOKIE: REPLACE_ME
49 +
50 + # Optional. Leave empty to disable Sentry reporting; the pod boots
51 + # with Sentry quietly no-op'd.
52 + SENTRY_DSN: ""
53 +
54 + # Optional SMTP relay. Leave SMTP_RELAY unset / blank to fall back
55 + # to the Logger adapter (no mail sent, but rendered email lands in
56 + # the pod log at :info so the magic-link flow still leaves a trail).
57 + SMTP_RELAY: ""
58 + SMTP_PORT: "587"
59 + SMTP_USERNAME: ""
60 + SMTP_PASSWORD: ""
61 + # gen_smtp options: always | if_available | never
62 + SMTP_TLS: "if_available"
63 + SMTP_AUTH: "if_available"
64 + # "true" for implicit TLS on port 465; otherwise STARTTLS upgrade.
65 + SMTP_SSL: "false"
66 + # verify_peer | verify_none. Use verify_none for self-signed relays.
67 + SMTP_TLS_VERIFY: "verify_peer"
68 +
69 + # From-line on outgoing mail. Defaults to "Waxx" / noreply@$PHX_HOST.
70 + MAIL_FROM_NAME: "Waxx"
71 + MAIL_FROM_ADDRESS: ""

Parents: 37cd9a6