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:
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user