Move the server to car.k8s.stinnesbeck.com

The server's host is car.k8s.stinnesbeck.com, not car.stinnesbeck.com:
IngressRoute, PUBLIC_URL (so the pairing QR code points there), the
app's pre-filled server URL and hint, and the docs. Grafana's
placeholder host follows to car-grafana.k8s.stinnesbeck.com, so one
*.k8s.stinnesbeck.com default certificate covers both. App 2.4.1 for the
new default; the server image is unchanged (its URL comes from the
manifest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
2026-10-17 18:08:35 +02:00
co-authored by Claude Opus 5.5
parent 59ee2cae3b
commit 315586f94c
10 changed files with 19 additions and 19 deletions
+4 -4
View File
@@ -7,17 +7,17 @@ what has been changed, and what is still to do.
- Upstream: https://github.com/fatihdonmezdev/FatihsMG4 (itself a fork of malys / EVABRPUploader - Upstream: https://github.com/fatihdonmezdev/FatihsMG4 (itself a fork of malys / EVABRPUploader
and Leon Kernan / ABRP_Uploader). Credited in the app's About dialog. and Leon Kernan / ABRP_Uploader). Credited in the app's About dialog.
## Architecture: car server at car.stinnesbeck.com ## Architecture: car server at car.k8s.stinnesbeck.com
The car sends everything to our own server. The server stores it in InfluxDB 2 and forwards live The car sends everything to our own server. The server stores it in InfluxDB 2 and forwards live
telemetry to ABRP, so the ABRP token lives only on the server, never in the APK. Grafana reads telemetry to ABRP, so the ABRP token lives only on the server, never in the APK. Grafana reads
InfluxDB directly for the dashboards. InfluxDB directly for the dashboards.
``` ```
car ──live telemetry───▶ car.stinnesbeck.com (Go) ──▶ ABRP api.iternio.com/1/tlm/send car ──live telemetry───▶ car.k8s.stinnesbeck.com (Go) ──▶ ABRP api.iternio.com/1/tlm/send
──daily chunks─────▶ │ ──daily chunks─────▶ │
──charging sessions▶ ▼ ──charging sessions▶ ▼
InfluxDB 2 ◀── Grafana (car-grafana.stinnesbeck.com) InfluxDB 2 ◀── Grafana (car-grafana.k8s.stinnesbeck.com)
``` ```
- **Server:** `server/`, Go 1.26, API only — see `server/README.md` for endpoints, configuration - **Server:** `server/`, Go 1.26, API only — see `server/README.md` for endpoints, configuration
@@ -42,7 +42,7 @@ car ──live telemetry───▶ car.stinnesbeck.com (Go) ──▶ ABRP ap
- [x] Image `mg4-server:2.1.0` (with pairing) pushed to git.stinnesbeck.com (public; pinned by - [x] Image `mg4-server:2.1.0` (with pairing) pushed to git.stinnesbeck.com (public; pinned by
digest `sha256:e5f6e2d2…` 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.k8s.stinnesbeck.com`).
- [ ] First pairing and drive with 2.4.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
+2 -2
View File
@@ -34,8 +34,8 @@ android {
applicationId "com.stinnesbeck.nilsmg4" applicationId "com.stinnesbeck.nilsmg4"
minSdk 27 minSdk 27
targetSdk 36 targetSdk 36
versionCode 58 versionCode 59
versionName "2.4.0" versionName "2.4.1"
// Nothing about the server ships in the APK: its URL and ingest token are entered in // Nothing about the server ships in the APK: its URL and ingest token are entered in
// the app's Server tab (ServerConfig), and the database is only reachable by the server. // the app's Server tab (ServerConfig), and the database is only reachable by the server.
@@ -7,7 +7,7 @@ package com.evsuite.abrp;
* *
* <pre> * <pre>
* # Nils' MG4 config * # Nils' MG4 config
* server_url = https://car.stinnesbeck.com * server_url = https://car.k8s.stinnesbeck.com
* token = 0123abcd... (the server's INGEST_TOKEN) * token = 0123abcd... (the server's INGEST_TOKEN)
* installation_id = 6f1a2b3c-4d5e-4f60-8a9b-0c1d2e3f4a5b * installation_id = 6f1a2b3c-4d5e-4f60-8a9b-0c1d2e3f4a5b
* interval_sec = 60 * interval_sec = 60
@@ -28,7 +28,7 @@ final class ServerConfig {
Pattern.CASE_INSENSITIVE); Pattern.CASE_INSENSITIVE);
/** Pre-filled so pairing needs no typing in the car; can still be changed. */ /** Pre-filled so pairing needs no typing in the car; can still be changed. */
static final String DEFAULT_URL = "https://car.stinnesbeck.com"; static final String DEFAULT_URL = "https://car.k8s.stinnesbeck.com";
/** The server refuses shorter ingest tokens outright. */ /** The server refuses shorter ingest tokens outright. */
static final int MIN_TOKEN_LENGTH = 24; static final int MIN_TOKEN_LENGTH = 24;
+1 -1
View File
@@ -49,7 +49,7 @@
<!-- Server page --> <!-- Server page -->
<string name="server_card_title">Car server</string> <string name="server_card_title">Car server</string>
<string name="hint_server_url">Server URL</string> <string name="hint_server_url">Server URL</string>
<string name="helper_server_url">For example https://car.stinnesbeck.com</string> <string name="helper_server_url">For example https://car.k8s.stinnesbeck.com</string>
<string name="hint_server_token">Ingest token</string> <string name="hint_server_token">Ingest token</string>
<string name="helper_server_token">The server\'s INGEST_TOKEN, at least 24 characters</string> <string name="helper_server_token">The server\'s INGEST_TOKEN, at least 24 characters</string>
<string name="hint_installation_id">Installation ID</string> <string name="hint_installation_id">Installation ID</string>
+3 -3
View File
@@ -1,4 +1,4 @@
# Kubernetes manifests — car.stinnesbeck.com # Kubernetes manifests — car.k8s.stinnesbeck.com
Plain manifests for the car server, InfluxDB 2 and Grafana. They are only files: nothing here Plain manifests for the car server, InfluxDB 2 and Grafana. They are only files: nothing here
is applied automatically. Apply them yourself. is applied automatically. Apply them yourself.
@@ -10,7 +10,7 @@ is applied automatically. Apply them yourself.
| `server.yaml` | Go server Deployment (1 replica, non-root, read-only root filesystem) and Service | | `server.yaml` | Go server Deployment (1 replica, non-root, read-only root filesystem) and Service |
| `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.k8s.stinnesbeck.com and Grafana, TLS from Traefik's default certificate |
| `secrets.example.yaml` | **Template** for the Secrets; placeholders only, 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 | | `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 |
@@ -18,7 +18,7 @@ is applied automatically. Apply them yourself.
## Before the first apply ## Before the first apply
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.k8s.stinnesbeck.com`). Both hosts must be
covered by Traefik's default certificate. covered by Traefik's default certificate.
2. **Create the Secrets.** `cp k8s/secrets.example.yaml k8s/secrets.yaml` (gitignored), replace 2. **Create the Secrets.** `cp k8s/secrets.example.yaml k8s/secrets.yaml` (gitignored), replace
every `CHANGE-ME` there (`openssl rand -hex 32`): `influxdb-auth`, `mg4-server` (ingest token, every `CHANGE-ME` there (`openssl rand -hex 32`): `influxdb-auth`, `mg4-server` (ingest token,
+1 -1
View File
@@ -52,7 +52,7 @@ spec:
containerPort: 3000 containerPort: 3000
env: env:
- name: GF_SERVER_ROOT_URL - name: GF_SERVER_ROOT_URL
value: https://car-grafana.stinnesbeck.com value: https://car-grafana.k8s.stinnesbeck.com
- name: GF_SECURITY_ADMIN_USER - name: GF_SECURITY_ADMIN_USER
value: admin value: admin
- name: GF_SECURITY_ADMIN_PASSWORD - name: GF_SECURITY_ADMIN_PASSWORD
+2 -2
View File
@@ -14,7 +14,7 @@ spec:
- websecure - websecure
routes: routes:
- kind: Rule - kind: Rule
match: Host(`car.stinnesbeck.com`) match: Host(`car.k8s.stinnesbeck.com`)
services: services:
- kind: Service - kind: Service
name: mg4-server name: mg4-server
@@ -35,7 +35,7 @@ spec:
routes: routes:
- kind: Rule - kind: Rule
# CHECK: placeholder host. It must be covered by Traefik's default certificate. # CHECK: placeholder host. It must be covered by Traefik's default certificate.
match: Host(`car-grafana.stinnesbeck.com`) match: Host(`car-grafana.k8s.stinnesbeck.com`)
services: services:
- kind: Service - kind: Service
name: grafana name: grafana
+1 -1
View File
@@ -42,7 +42,7 @@ spec:
value: mg4 value: mg4
# Where a phone reaches the server; the pairing QR code points here. # Where a phone reaches the server; the pairing QR code points here.
- name: PUBLIC_URL - name: PUBLIC_URL
value: https://car.stinnesbeck.com value: https://car.k8s.stinnesbeck.com
- name: INFLUX_TOKEN - name: INFLUX_TOKEN
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
+3 -3
View File
@@ -1,6 +1,6 @@
# Nils' MG4 — car server # Nils' MG4 — car server
A small Go service at car.stinnesbeck.com: the API the car writes to, and the ABRP A small Go service at car.k8s.stinnesbeck.com: the API the car writes to, and the ABRP
forwarder. Data lives in InfluxDB 2; Grafana reads it from there for the dashboards forwarder. Data lives in InfluxDB 2; Grafana reads it from there for the dashboards
(`../k8s/grafana/`). (`../k8s/grafana/`).
@@ -61,8 +61,8 @@ the same bucket), and the device is persisted before the car can collect the tok
restart never loses a paired car. Manage cars with the approval password: restart never loses a paired car. Manage cars with the approval password:
```sh ```sh
curl -H "Authorization: Bearer $APPROVAL_PASSWORD" https://car.stinnesbeck.com/v1/devices curl -H "Authorization: Bearer $APPROVAL_PASSWORD" https://car.k8s.stinnesbeck.com/v1/devices
curl -X POST -H "Authorization: Bearer $APPROVAL_PASSWORD" https://car.stinnesbeck.com/v1/devices/<id>/revoke curl -X POST -H "Authorization: Bearer $APPROVAL_PASSWORD" https://car.k8s.stinnesbeck.com/v1/devices/<id>/revoke
``` ```
## Storage ## Storage