Deploy server 2.1.0; keep filled-in secrets out of git
- kustomization pins mg4-server 2.1.0 by digest; server.yaml sets PUBLIC_URL for the pairing QR code; the secrets template gains APPROVAL_PASSWORD. - Filled-in Secrets now go into k8s/secrets.yaml, which is gitignored; the template stays on placeholders. This follows a leak where real values typed into the template were committed and pushed (amended and force-pushed since; both values regenerated). - SPECS.md records pairing and the incident. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
@@ -39,3 +39,6 @@ mise.local.toml
|
|||||||
/EVHardware/build
|
/EVHardware/build
|
||||||
/EVHardware/.gradle
|
/EVHardware/.gradle
|
||||||
/EVHardware/**/build
|
/EVHardware/**/build
|
||||||
|
|
||||||
|
# Filled-in Kubernetes Secrets (copy of k8s/secrets.example.yaml). Never commit.
|
||||||
|
/k8s/secrets.yaml
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ car ──live telemetry───▶ car.stinnesbeck.com (Go) ──▶ ABRP ap
|
|||||||
- **Dashboards:** Grafana, provisioned from `k8s/grafana/`: Live (stats, SoC/range, power/speed,
|
- **Dashboards:** Grafana, provisioned from `k8s/grafana/`: Live (stats, SoC/range, power/speed,
|
||||||
route map, temperatures, tyres), Consumption (period totals, lifetime, SoH, per-day energy,
|
route map, temperatures, tyres), Consumption (period totals, lifetime, SoH, per-day energy,
|
||||||
distance and efficiency) and Charging (totals, session table, curves). Prices in euro.
|
distance and efficiency) and Charging (totals, session table, curves). Prices in euro.
|
||||||
- **Car app:** Server tab (URL, ingest token, installation id, config import); live telemetry
|
- **Pairing:** the car shows a QR code, a phone approves it with the approval password, and the car
|
||||||
|
gets its own token (RFC 8628-style; `server/README.md`). The shared `INGEST_TOKEN` still works.
|
||||||
|
- **Car app:** Server tab (URL, pairing by QR, ingest token, installation id, config import); live telemetry
|
||||||
every 15 s while driving or charging; consumption chunks and charging sessions as before.
|
every 15 s while driving or charging; consumption chunks and charging sessions as before.
|
||||||
- **Deployment:** `k8s/`, plain manifests. **Nothing is ever applied to the cluster by us** —
|
- **Deployment:** `k8s/`, plain manifests. **Nothing is ever applied to the cluster by us** —
|
||||||
Nils applies them (`k8s/README.md`). TLS comes from Traefik's default certificate.
|
Nils applies them (`k8s/README.md`). TLS comes from Traefik's default certificate.
|
||||||
@@ -36,12 +38,12 @@ car ──live telemetry───▶ car.stinnesbeck.com (Go) ──▶ ABRP ap
|
|||||||
- [x] End-to-end in podman (server + InfluxDB 2.7.12 + Grafana 13.2.3): every endpoint, retry
|
- [x] End-to-end in podman (server + InfluxDB 2.7.12 + Grafana 13.2.3): every endpoint, retry
|
||||||
idempotency, all 25 dashboard queries return data, dashboards rendered and checked.
|
idempotency, all 25 dashboard queries return data, dashboards rendered and checked.
|
||||||
- [x] Manifests validated offline (including Traefik CRDs). **Not applied.**
|
- [x] Manifests validated offline (including Traefik CRDs). **Not applied.**
|
||||||
- [x] Car app 2.3.0: unit tests and lint pass; **not yet tried on the car**.
|
- [x] Car app 2.4.0 (pairing by QR): unit tests pass; **not yet tried on the car**.
|
||||||
- [x] Image `mg4-server:2.0.0` pushed to git.stinnesbeck.com (public; pinned by digest
|
- [x] Image `mg4-server:2.1.0` (with pairing) pushed to git.stinnesbeck.com (public; pinned by
|
||||||
`sha256:6feea777…` in `k8s/kustomization.yaml`).
|
digest `sha256:e5f6e2d2…` in `k8s/kustomization.yaml`).
|
||||||
- [ ] Check in `k8s/ingress.yaml`: the Traefik ingress class (placeholder `traefik-external`) and
|
- [ ] Check in `k8s/ingress.yaml`: the Traefik ingress class (placeholder `traefik-external`) and
|
||||||
Grafana's host (placeholder `car-grafana.stinnesbeck.com`).
|
Grafana's host (placeholder `car-grafana.stinnesbeck.com`).
|
||||||
- [ ] First drive with 2.3.0: telemetry arrives in InfluxDB, Grafana and ABRP.
|
- [ ] First pairing and drive with 2.4.0: telemetry arrives in InfluxDB, Grafana and ABRP.
|
||||||
|
|
||||||
## To do
|
## To do
|
||||||
|
|
||||||
@@ -84,6 +86,11 @@ car ──live telemetry───▶ car.stinnesbeck.com (Go) ──▶ ABRP ap
|
|||||||
- 2026-10-17 — Server rewritten in Go (API only); dashboards moved to Grafana with provisioned
|
- 2026-10-17 — Server rewritten in Go (API only); dashboards moved to Grafana with provisioned
|
||||||
InfluxDB datasource and three dashboards; the Next.js app (`web/`) removed. Per-host
|
InfluxDB datasource and three dashboards; the Next.js app (`web/`) removed. Per-host
|
||||||
cert-manager certificates dropped in favour of Traefik's default certificate.
|
cert-manager certificates dropped in favour of Traefik's default certificate.
|
||||||
|
- 2026-10-17 — Pairing by QR code: server 2.1.0 issues per-car tokens after approval on a phone;
|
||||||
|
app 2.4.0 shows the QR code and collects its token. Tested end to end against InfluxDB.
|
||||||
|
- 2026-10-17 — Incident: a commit swept the filled-in `secrets.example.yaml` (InfluxDB admin token,
|
||||||
|
INGEST_TOKEN) into git and pushed it. Commit amended and force-pushed; both values regenerated in
|
||||||
|
the now gitignored `k8s/secrets.yaml`. Filled-in secrets live only there from now on.
|
||||||
- 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb;
|
- 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb;
|
||||||
removed again (1002962) once values showed on the car. Revert 1002962 to bring them back.
|
removed again (1002962) once values showed on the car. Revert 1002962 to bring them back.
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -11,7 +11,8 @@ is applied automatically. Apply them yourself.
|
|||||||
| `grafana.yaml` | Grafana 13.2.3 Deployment (1Gi PVC), Service; datasource and dashboards provisioned |
|
| `grafana.yaml` | Grafana 13.2.3 Deployment (1Gi PVC), Service; datasource and dashboards provisioned |
|
||||||
| `grafana/` | The provisioned InfluxDB datasource and the MG4 dashboards (Live, Consumption, Charging) |
|
| `grafana/` | The provisioned InfluxDB datasource and the MG4 dashboards (Live, Consumption, Charging) |
|
||||||
| `ingress.yaml` | Traefik `IngressRoute`s for car.stinnesbeck.com and Grafana, TLS from Traefik's default certificate |
|
| `ingress.yaml` | Traefik `IngressRoute`s for car.stinnesbeck.com and Grafana, TLS from Traefik's default certificate |
|
||||||
| `secrets.example.yaml` | **Template** for the Secrets; not in `kustomization.yaml` |
|
| `secrets.example.yaml` | **Template** for the Secrets; placeholders only, not in `kustomization.yaml` |
|
||||||
|
| `secrets.yaml` | Your filled-in copy — **gitignored**, never committed |
|
||||||
| `kustomization.yaml` | Everything except the Secrets; pins the server image, generates the Grafana ConfigMaps |
|
| `kustomization.yaml` | Everything except the Secrets; pins the server image, generates the Grafana ConfigMaps |
|
||||||
|
|
||||||
## Before the first apply
|
## Before the first apply
|
||||||
@@ -19,9 +20,10 @@ is applied automatically. Apply them yourself.
|
|||||||
1. **Check `ingress.yaml`:** the ingress class (placeholder `traefik-external` for the public
|
1. **Check `ingress.yaml`:** the ingress class (placeholder `traefik-external` for the public
|
||||||
Traefik) and Grafana's host (placeholder `car-grafana.stinnesbeck.com`). Both hosts must be
|
Traefik) and Grafana's host (placeholder `car-grafana.stinnesbeck.com`). Both hosts must be
|
||||||
covered by Traefik's default certificate.
|
covered by Traefik's default certificate.
|
||||||
2. **Create the Secrets.** Copy `secrets.example.yaml` outside the repository, replace every
|
2. **Create the Secrets.** `cp k8s/secrets.example.yaml k8s/secrets.yaml` (gitignored), replace
|
||||||
`CHANGE-ME` (`openssl rand -hex 32`), then apply the copy: `influxdb-auth`, `mg4-server`
|
every `CHANGE-ME` there (`openssl rand -hex 32`): `influxdb-auth`, `mg4-server` (ingest token,
|
||||||
(ingest token, your ABRP API key and user token) and `grafana-admin`.
|
approval password for pairing, your ABRP API key and user token) and `grafana-admin`. Keep
|
||||||
|
`secrets.example.yaml` itself on placeholders only.
|
||||||
3. **Registry access.** The `mg4-server` package on git.stinnesbeck.com is public, so no pull
|
3. **Registry access.** The `mg4-server` package on git.stinnesbeck.com is public, so no pull
|
||||||
secret is needed. If you make it private, add an `imagePullSecrets` entry to `server.yaml`.
|
secret is needed. If you make it private, add an `imagePullSecrets` entry to `server.yaml`.
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ is applied automatically. Apply them yourself.
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
kubectl apply -f k8s/namespace.yaml
|
kubectl apply -f k8s/namespace.yaml
|
||||||
kubectl apply -f <your filled-in secrets file>
|
kubectl apply -f k8s/secrets.yaml
|
||||||
kubectl apply -k k8s/
|
kubectl apply -k k8s/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ resources:
|
|||||||
- ingress.yaml
|
- ingress.yaml
|
||||||
images:
|
images:
|
||||||
- name: git.stinnesbeck.com/nils/mg4-server
|
- name: git.stinnesbeck.com/nils/mg4-server
|
||||||
# 2.0.0, pinned by digest so a re-pushed tag cannot change what runs.
|
# 2.1.0, pinned by digest so a re-pushed tag cannot change what runs.
|
||||||
digest: sha256:6feea777c514b9488ffb0b49fad7c15423ee06f0e0e8d609cf31eb85755da815
|
digest: sha256:e5f6e2d2ef9dc2650055a0823a3f86d8903e46918a76e990540482edf819766d
|
||||||
generatorOptions:
|
generatorOptions:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/part-of: mg4
|
app.kubernetes.io/part-of: mg4
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# TEMPLATE ONLY — not part of kustomization.yaml. Copy it outside the repository, fill in
|
# TEMPLATE ONLY — not part of kustomization.yaml. Copy it to k8s/secrets.yaml (gitignored),
|
||||||
# real values, and apply that copy yourself. Never commit the filled-in file.
|
# fill in real values there, and apply that file yourself. Never put real values in here.
|
||||||
#
|
#
|
||||||
# openssl rand -hex 32 for every token and password below
|
# openssl rand -hex 32 for every token and password below
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -22,6 +22,9 @@ stringData:
|
|||||||
# The car's write token, entered in the app. At least 24 characters, or the server
|
# The car's write token, entered in the app. At least 24 characters, or the server
|
||||||
# refuses to start.
|
# refuses to start.
|
||||||
INGEST_TOKEN: CHANGE-ME-openssl-rand-hex-32
|
INGEST_TOKEN: CHANGE-ME-openssl-rand-hex-32
|
||||||
|
# Approves a car on your phone when it pairs by QR code (at least 12 characters). Leave it
|
||||||
|
# empty to switch pairing off; then only the INGEST_TOKEN above works.
|
||||||
|
APPROVAL_PASSWORD: CHANGE-ME-at-least-12-characters
|
||||||
# Your ABRP credentials. Leave either empty to switch forwarding off.
|
# Your ABRP credentials. Leave either empty to switch forwarding off.
|
||||||
ABRP_API_KEY: CHANGE-ME
|
ABRP_API_KEY: CHANGE-ME
|
||||||
ABRP_USER_TOKEN: CHANGE-ME
|
ABRP_USER_TOKEN: CHANGE-ME
|
||||||
|
|||||||
+5
-1
@@ -40,12 +40,16 @@ spec:
|
|||||||
value: mg4
|
value: mg4
|
||||||
- name: INFLUX_BUCKET
|
- name: INFLUX_BUCKET
|
||||||
value: mg4
|
value: mg4
|
||||||
|
# Where a phone reaches the server; the pairing QR code points here.
|
||||||
|
- name: PUBLIC_URL
|
||||||
|
value: https://car.stinnesbeck.com
|
||||||
- name: INFLUX_TOKEN
|
- name: INFLUX_TOKEN
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: influxdb-auth
|
name: influxdb-auth
|
||||||
key: admin-token
|
key: admin-token
|
||||||
# INGEST_TOKEN, ABRP_API_KEY, ABRP_USER_TOKEN, ABRP_MIN_INTERVAL_SECONDS.
|
# INGEST_TOKEN, APPROVAL_PASSWORD, ABRP_API_KEY, ABRP_USER_TOKEN,
|
||||||
|
# ABRP_MIN_INTERVAL_SECONDS.
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: mg4-server
|
name: mg4-server
|
||||||
|
|||||||
Reference in New Issue
Block a user