Zum Inhalt springen
Prototyp pruefen

Lokales Android-Cloud-Ops-Runbook

Dieses Runbook beschreibt den aktuell verifizierten lokalen Betriebs- und Debug-Pfad fuer den heute belegten Android-plus-Cloud-Lauf: lokales superheld-cloud, tenant-gebundenes Token-Minting, USB-Tablet mit adb reverse, sichtbare Job-/Trace-Pruefung und die heute tatsaechlich vorhandene Operator-Sicht.

  • cmd/server startet lokal im Secure-Mux-Default mit Bootstrap-Token.
  • Tenant-gebundene App-Tokens koennen ueber POST /tokens gemintet werden.
  • Die Android-App kann auf dem USB-Tablet POST /devices, POST /events, GET /status, GET /alerts, GET /incidents, GET /incidents/{id}, PATCH /incidents/{id}, GET /jobs und fuer den letzten sichtbaren Job zusaetzlich GET /jobs/{id} gegen http://127.0.0.1:8080 nutzen, solange adb reverse tcp:8080 tcp:8080 aktiv ist.
  • Auf dem Issues-Tab der App stehen alltagstaugliche Incident-Aktionen bereit: „Start review” und „Mark handled”. Die App fuehrt Incident-Transitionen ueber PATCH /incidents/{id} aus; erledigte Incidents fallen aus der DAU-Zaehlung auf dem Hauptscreen heraus. Dafuer braucht das App-Token zusaetzlich incidents:write.
  • Ein host-seitig angelegter Demo-Job bleibt tenant-scoped ueber GET /jobs sichtbar, wird in der App zusaetzlich ueber GET /jobs/{id} nachgeladen, liefert eine trace_id und triggert in derselben Job-Karte danach zusaetzlich GET /traces/{id} plus GET /traces/{id}/steps.
  • Dieselbe trace_id ist host-seitig ueber GET /traces/{id} und GET /traces/{id}/steps lesbar und wurde auf dem USB-Tablet sichtbar verifiziert.
  • Die App signalisiert jetzt auch einen tenant-seitigen Konfigurationsdrift sichtbar: wenn die Eingabe-tenant_id nicht mehr zur token-gebundenen Runtime passt, zeigt die Konfigurationskarte Last backend tenant: ... plus eine explizite Tenant-Mismatch-Warnung.
  • Zwei reale App-Starts derselben Installation loggen jetzt lokal telemetry flow=app_launch ... method=APP path=/main_activity ... backend_version=app_local nach logcat und lassen sich ueber denselben telemetrycompare-CLI vergleichen.

Ein explizites Data-Dir macht den Lauf reproduzierbarer und erleichtert spaeteres Debugging:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
SUPERHELD_DATA_DIR=/Users/benediktpoller/code/sh/workspace/runtime/android-cloud-ops \
SUPERHELD_BOOTSTRAP_TOKEN=bootstrap-token \
SUPERHELD_BOOTSTRAP_TENANT_ID=tenant-local \
go run ./cmd/server

Alternativ kann der Lauf mit SQLite-Persistenz gestartet werden. SQLite ist heute die bevorzugte Option, wenn Daten ueber Server-Restarts hinweg erhalten bleiben sollen:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
SUPERHELD_SQLITE_PATH=/Users/benediktpoller/code/sh/workspace/runtime/android-cloud-ops/superheld.db \
SUPERHELD_BOOTSTRAP_TOKEN=bootstrap-token \
SUPERHELD_BOOTSTRAP_TENANT_ID=tenant-local \
go run ./cmd/server

Aktuelle Operator-Fakten fuer diesen Pfad:

  • Default-Bind ist 127.0.0.1:8080.
  • Secure-Mux ist Default; ohne SUPERHELD_BOOTSTRAP_TOKEN und SUPERHELD_BOOTSTRAP_TENANT_ID startet der sichere Lauf nicht.
  • SUPERHELD_SQLITE_PATH aktiviert SQLite-Persistenz; ohne diesen Parameter nutzt die Runtime JSON-Dateien unter SUPERHELD_DATA_DIR.
  • Mit SQLite ueberleben Incidents, Tokens, Jobs und andere Daten einen Server-Restart. Mit JSON-Persistenz werden die Dateien beim Start neu geladen, aber aeltere Formate werden transparent migriert.
  • Die Runtime schreibt ihre Basis-Logs nach stdout/stderr; es gibt heute keine separate Log-Datei- oder Metrics-API.
  • Stop und Restart erfolgen im lokalen Dev-Pfad schlicht ueber Prozessabbruch und erneuten Start mit demselben Kommando.

Fuer den heute verifizierten Android-MVP braucht das App-Token mindestens die Read-/Write-Scopes fuer den sichtbaren Pfad. incidents:write ist seit Commit 538e740 fuer die Incident-Aktionen auf dem Issues-Tab noetig. jobs:write ist nur noetig, wenn der Demo-Job aus diesem Runbook wirklich erzeugt werden soll.

Terminal-Fenster
curl -s \
-X POST http://127.0.0.1:8080/tokens \
-H "Authorization: Bearer bootstrap-token" \
-H "Content-Type: application/json" \
-d '{"scopes":["devices:write","events:write","status:read","alerts:read","incidents:read","incidents:write","jobs:read","jobs:write"]}' \
| jq .

Wichtige Hinweise:

  • Das rohe Token wird nur in der Create-Antwort zurueckgegeben.
  • Das Token bleibt tenant-gebunden an tenant-local, solange der Bootstrap-Lauf so gestartet wurde.
  • jobs:read deckt im aktuellen Secure-Mux auch GET /traces/{id} und GET /traces/{id}/steps mit ab.
Terminal-Fenster
adb devices
adb reverse tcp:8080 tcp:8080
cd /Users/benediktpoller/code/sh/superheld-android
ANDROID_USER_HOME=/Users/benediktpoller/code/sh/workspace/.android \
GRADLE_USER_HOME=/Users/benediktpoller/code/sh/workspace/.gradle-home \
./gradlew connectedDebugAndroidTest \
-Pandroid.testInstrumentationRunnerArguments.class=com.superheld.android.DeviceIdentityManagerInstrumentedTest
ANDROID_USER_HOME=/Users/benediktpoller/code/sh/workspace/.android \
GRADLE_USER_HOME=/Users/benediktpoller/code/sh/workspace/.gradle-home \
./gradlew installDebug \
-Psuperheld.baseUrl=http://127.0.0.1:8080 \
-Psuperheld.apiToken=<tenant-app-token> \
-Psuperheld.tenantId=tenant-local
adb shell am start -W -n com.superheld.android/.MainActivity

Der aktuell verifizierte USB-Pfad nutzt bewusst http://127.0.0.1:8080. 10.0.2.2:8080 bleibt ein Emulatorpfad und ist in diesem Workspace derzeit nicht als realer Lauf belegt.

Auf dem Tablet wurde diese Reihenfolge real verifiziert:

  1. Register Device
  2. Refresh Status
  3. Send Demo Event
  4. optional host-seitig Demo-Job anlegen
  5. erneuter Refresh Status

Nach dem Event-Submit bleibt GET /status Teil des sichtbaren status_refresh-Pfads; fuer Alerts startet die App jetzt einen eigenen alert_read-Slice ueber GET /alerts, fuer Incidents einen eigenen incident_read-Slice ueber GET /incidents plus unmittelbar folgendes GET /incidents/{id}, und fuer Jobs einen eigenen job_read-Slice ueber GET /jobs mit den nachgeladenen Job- und Trace-Reads.

Der aktuell verifizierte status_refresh-Slice zeigt auf dem Tablet dabei zusaetzlich:

  • Target, Tenant, Device und App in derselben Konfigurationskarte
  • bei tenant-seitigem Konfigurationsdrift zusaetzlich Last backend tenant plus eine explizite Tenant-Mismatch-Warnung
  • Latest app launch run
  • Window
  • Result
  • Last successful sync
  • Latest refresh run
  • Window
  • Result
  • Backend version
  • bei Scope- oder Backend-Fehlern zusaetzlich Error code

Der aktuell verifizierte alert_read-Slice zeigt in der Alert-Karte auf demselben Tablet sichtbar:

  • No tenant alerts are currently visible. oder den letzten sichtbaren Alert
  • Latest alert run
  • Window
  • Result
  • Backend version
  • bei Auth-, Scope- oder Backend-Fehlern zusaetzlich Error code

Der aktuell verifizierte device_register-Slice zeigt nach Register Device auf demselben Tablet sichtbar:

  • Registered for tenant tenant-local at ...
  • Latest registration run
  • Window
  • Result
  • Backend version

Der aktuell verifizierte event_submit-Slice zeigt nach Send Demo Event auf demselben Tablet sichtbar:

  • Submitted demo event ... at ...
  • Latest event run
  • Window
  • Result
  • Backend version

Der aktuell verifizierte incident_read-Slice zeigt in der Incident-Karte auf demselben Tablet sichtbar:

  • Latest incident detail from /incidents/...
  • Latest incident run
  • Window
  • Result
  • Backend version
  • bei Auth-, Scope- oder Backend-Fehlern zusaetzlich Error code

Der aktuell verifizierte job_read-Slice zeigt in der Job-Karte auf demselben Tablet sichtbar:

  • Latest job detail from /jobs/...
  • Trace detail from /traces/...
  • Latest trace step from /traces/.../steps
  • Latest job run
  • Window
  • Result
  • Backend version
  • bei Scope- oder Backend-Fehlern zusaetzlich Error code

Zuerst einen kleinen Demo-Job anlegen:

Terminal-Fenster
JOB_ID=$(curl -s \
-X POST http://127.0.0.1:8080/jobs \
-H "Authorization: Bearer <tenant-app-token>" \
-H "Content-Type: application/json" \
-d '{"job_class":"incident_triage","workflow_id":"incident_triage_v1","input_refs":{"event_ids":["evt-job-read-demo-missing"]}}' \
| jq -r '.job_id')

Danach denselben Job lesen und auf die asynchron gesetzte trace_id warten:

Terminal-Fenster
curl -s http://127.0.0.1:8080/jobs/$JOB_ID \
-H "Authorization: Bearer <tenant-app-token>" | jq .
TRACE_ID=""
for _ in 1 2 3 4 5; do
TRACE_ID=$(curl -s http://127.0.0.1:8080/jobs/$JOB_ID \
-H "Authorization: Bearer <tenant-app-token>" \
| jq -r '.trace_id // empty')
if [ -n "$TRACE_ID" ]; then
break
fi
sleep 1
done
test -n "$TRACE_ID"
curl -s http://127.0.0.1:8080/traces/$TRACE_ID \
-H "Authorization: Bearer <tenant-app-token>" | jq .
curl -s http://127.0.0.1:8080/traces/$TRACE_ID/steps \
-H "Authorization: Bearer <tenant-app-token>" | jq .

Fuer den heute verifizierten Demo-Job gilt:

  • workflow_id ist incident_triage_v1
  • ohne passendes Quellereignis landet der Job erwartbar in failed_terminal
  • die Trace enthaelt mindestens einen Step fuer load_source_events
  • der Step traegt im Fehlerfall error_code=event_refs_missing
  • der reale Tablet-Lauf zeigte in derselben Job-Karte sichtbar Trace detail from /traces/... sowie Latest trace step from /traces/.../steps: Load Source Events is Failed Terminal. Guardrail passed. Error code event_refs_missing.

Folgende Negativfaelle sind fuer den lokalen Android-Pfad bereits sichtbar und reproduzierbar:

  • Token ohne events:write, aber mit den Read-Scopes und devices:write: Die App zeigt nach Send Demo Event im sichtbaren Banner Event token is missing events:write. Mint a token with events:write for the intended tenant, then retry the demo event. plus Latest event run ... Error code: insufficient_scope und den rohen HTTP 403 insufficient_scope-Detailtext.
  • Token ohne alerts:read: Die App zeigt Alert read token is missing alerts:read. plus Mint a token with alerts:read for the intended tenant, then refresh again., den rohen HTTP 403 insufficient_scope-Detailtext sowie Latest alert run ... Error code: insufficient_scope.
  • Token ohne incidents:read: Die App zeigt Incident read token is missing incidents:read. plus Mint a token with incidents:read for the intended tenant, then refresh again. und den rohen HTTP 403 insufficient_scope-Detailtext.
  • Token ohne jobs:read: Die App zeigt Job read token is missing jobs:read. plus Mint a token with jobs:read for the intended tenant, then refresh again. und den rohen HTTP 403 insufficient_scope-Detailtext.
  • komplett ungueltiger API-Token: Der sichtbare Refresh-Banner zeigt Refresh rejected by backend authentication. plus Error code: unauthorized; die Alert-, Incident- und Job-Karten zeigen im selben Lauf jeweils ... rejected by backend authentication. mit ihren eigenen Latest ... run-IDs und dem rohen HTTP 401 unauthorized: Invalid token.
  • Token bleibt tenant-local-gebunden, aber die App startet mit tenant-debug: Die Konfigurationskarte zeigt sichtbar Last backend tenant: tenant-local plus Configured tenant tenant-debug does not match backend tenant tenant-local. Update the app tenant field or mint a token for the intended tenant.
  • Netzwerkausfall nach zuvor erfolgreichem Refresh: Die App haelt Last successful sync: ... aus dem vorigen Lauf. Alle Read-Karten zeigen ... could not reach the backend. mit Error code: network_error. Dieser Pfad ist als wiederholbarer Geraetetest verifiziert, bei dem der lokale Server nach einem erfolgreichen ersten Refresh verschwindet.
  • Downstream-Detailfehler GET /incidents/{id} 404 nach erfolgreichem Listenread: Die App zeigt Incident list loaded, but the latest incident detail read failed. plus Incident read failed with an unexpected backend response. und HTTP 404 not_found. Verifiziert als Geraetetest mit geraetelokalem Sequenzserver.
  • Downstream-Detailfehler GET /traces/{id} 403 tenant_mismatch nach erfolgreichem Job-Read: Die App zeigt Job detail loaded, but linked trace ... could not be read. plus Trace read is using the wrong tenant context. und HTTP 403 tenant_mismatch. Verifiziert als Geraetetest mit geraetelokalem Sequenzserver.
  • Partieller Scope-Fehler bei erneutem Refresh: Nach einem erfolgreichen ersten Refresh kann ein zweiter Refresh nur GET /alerts mit 403 insufficient_scope scheitern lassen, waehrend Status, Incidents und Jobs weiter erfolgreich laden. Die App zeigt dann die dedizierte Alert-Scope-Guidance neben den erfolgreichen anderen Karten. Verifiziert als Geraetetest.
  • fehlendes adb reverse: Das USB-Tablet erreicht 127.0.0.1:8080 nicht.
  • fehlende oder falsche Geraetesignatur auf Schreibpfaden: POST /devices und POST /events scheitern sichtbar am Signatur-Contract.

6. Logging, Monitoring und SIEM im aktuellen Ist-Stand

Abschnitt betitelt „6. Logging, Monitoring und SIEM im aktuellen Ist-Stand“

Diese drei Sichten sind im lokalen Workspace-Pfad bewusst getrennt:

  • Logging: Basis-Startmeldungen und Store-Fallbacks erscheinen im go run ./cmd/server-Prozess auf stdout/stderr.
  • Logging: Die Android-App schreibt fuer reale App-Starts unter SuperheldTelemetry comparebare telemetry flow=app_launch ...-Zeilen nach logcat.
  • Logging: Wenn die App X-Superheld-Run-Id mitsendet, schreibt der Backend-Prozess zusaetzlich eine Telemetrie-Zeile pro Request, aktuell verifiziert fuer /devices, /events, /status, fuer den dedizierten alert_read-Pfad auf /alerts, fuer den dedizierten incident_read-Pfad auf /incidents und /incidents/{id} sowie fuer den dedizierten job_read-Pfad auf /jobs, /jobs/{id}, /traces/{id} und /traces/{id}/steps; fehlgeschlagene korrelierte Requests haengen dabei error_code=... an dieselbe Logzeile an.
  • Logging: Wenn X-Superheld-Tenant-Id nicht zum tenant-gebundenen Token passt, schreibt der Secure-Mux jetzt zusaetzlich eine grepbare Operator-Zeile tenant_hint_mismatch token_tenant_id=... requested_tenant_id=... token_id=... flow=... run_id=... method=... path=... device_id=... app_version=....
  • Logging: Fuer den verifizierten Alert-Happy-Path teilten die Alert-Karte und der Backend-Prozess denselben alert_read-run_id=alert-read-942bc64f-6cac-4daa-909e-efa64112c556; im Backend erschien dazu GET /alerts status=200 result=success.
  • Logging: Fuer den verifizierten Event-Scope-Fehler teilten Banner, Event-Karte und der Backend-Prozess denselben event_submit-run_id=event-submit-4cec7916-b952-4530-bfcf-d379f71e48c2; im Backend erschien dazu POST /events status=403 result=failure error_code=insufficient_scope.
  • Logging: Fuer den verifizierten Alert-Scope-Fehler teilten die Alert-Karte und der Backend-Prozess denselben alert_read-run_id=alert-read-90df8959-d5b9-41de-9dd5-46bc9db48c86; im Backend erschien dazu GET /alerts status=403 result=failure error_code=insufficient_scope, waehrend /status, /incidents und /jobs unter ihren eigenen Run-IDs erfolgreich blieben.
  • Logging: Fuer den verifizierten Job-Scope-Fehler teilten die Job-Karte und der Backend-Prozess denselben job_read-run_id=job-read-e2d9a9ca-a82b-4bff-8ceb-ba22954039ae; im Backend erschien dazu GET /jobs status=403 result=failure error_code=insufficient_scope, waehrend die nachgelagerten Job- und Trace-Reads erwartbar ausblieben.
  • Logging: Fuer den verifizierten Tenant-Mismatch-Lauf zeigte die App sichtbar Last backend tenant: tenant-local. Ein zusaetzlicher verifizierter lokaler /status-Read mit run_id=status-refresh-tenant-hint-test-1, X-Superheld-Tenant-Id: tenant-debug und tenant-local-gebundenem Token schrieb davor jetzt explizit tenant_hint_mismatch token_tenant_id=tenant-local requested_tenant_id=tenant-debug ..., bevor die normale telemetry flow=status_refresh ... tenant_id=tenant-debug ...-Zeile erschien; die host-seitige Runtime-Wahrheit blieb dabei weiter tenant-local-gebunden.
  • Monitoring: Es gibt derzeit keine separate Metrics- oder /healthz-API; GET /status ist der leichtgewichtige Runtime-Check fuer den app-relevanten Schutzstatus.
  • SIEM: Der aktuell belegte Exportpfad ist event-zentriert ueber GET /events und Webhooks. Jobs, Incidents und Traces sind heute Operator-/Runtime-Reads, kein eigener verifizierter SIEM-Exportstrom.

Fuer den verifizierten Job-/Trace-Pfad sind diese Operator-Sichten heute praktisch:

  • GET /jobs fuer den tenant-scoped Job-Ueberblick
  • GET /jobs/{id} fuer den Einzelstatus
  • GET /traces/{id} fuer Korrelation ueber trace_id
  • GET /traces/{id}/steps fuer den aktuell feinsten belegten Ausfuehrungsblick
  • go run ./cmd/server-Output fuer telemetry flow=status_refresh ..., telemetry flow=alert_read ..., telemetry flow=device_register ..., telemetry flow=event_submit ..., telemetry flow=incident_read ... und telemetry flow=job_read ... run_id=... tenant_id=... device_id=... app_version=... backend_version=...; bei Fehlerlaeufen zusaetzlich error_code=...
  • rg 'tenant_hint_mismatch|<run_id>' /pfad/zum/server.log wenn App-Tenant-Hinweis und token-gebundener Runtime-Tenant auseinanderlaufen sollen

Der erste verifizierte app-lokale Compare-Schnitt nutzt exportierte logcat-Zeilen von zwei realen App-Starts derselben Installation:

Terminal-Fenster
adb logcat -c
adb shell am force-stop com.superheld.android
adb shell am start -W -n com.superheld.android/.MainActivity
adb shell am force-stop com.superheld.android
adb shell am start -W -n com.superheld.android/.MainActivity
adb logcat -d -s SuperheldTelemetry:I \
> /Users/benediktpoller/code/sh/workspace/runtime/app-launch-compare-e2e-20260317/app-launch.log
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/app-launch-compare-e2e-20260317/app-launch.log \
-flow app_launch \
-device-id android-a1cb63bc-5160-4c6c-8ff9-b83819bc7733 \
-app-version 0.1.0

Der verifizierte Bericht zeigte dabei:

  • Run A: app-launch-772767fb-f8dd-42b1-81d4-1558fac5b636
  • Run B: app-launch-efcaa925-326b-47f7-af08-03d52bedbd09
  • denselben stabilen device_id=android-a1cb63bc-5160-4c6c-8ff9-b83819bc7733
  • APP /main_activity als comparebaren Request-Key
  • backend_version=app_local und das Laufzeitdelta von 385ms auf 366ms

Wenn derselbe lokale Cloud-Lauf nach stdout oder stderr zusaetzlich per tee in eine Datei gespiegelt wird, kann der erste kleine Compare-Schnitt direkt auf diesen echten Backend-Logzeilen laufen:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/status-refresh-compare-e2e-20260317/server.log \
-flow status_refresh \
-run-a status-refresh-d8a9ae69-972f-45de-818f-3541d79e92c4 \
-run-b status-refresh-5c829384-e399-4b81-aee8-9c931389e08b

Der aktuell verifizierte Lauf nutzte fuer den status_refresh-Vergleich explizite Run-IDs, weil die beiden Tablet-Laeufe nach pm clear unterschiedliche lokale device_id-Werte erzeugten. Sichtbar war dabei:

  • Run A: status-refresh-d8a9ae69-972f-45de-818f-3541d79e92c4 blieb auf GET /status mit 200 success erfolgreich
  • Run B: status-refresh-5c829384-e399-4b81-aee8-9c931389e08b zeigte auf GET /status den Wechsel auf 403 failure mit error_code=insufficient_scope
  • der Bericht machte denselben Request-Key GET /status mit dem Delta von 183µs auf 83µs sichtbar
  • derselbe server.log zeigte im Fehlerrun weiterhin getrennte und erfolgreiche alert_read-, incident_read- und job_read-Slices unter eigenen run_id-Werten

Ein weiterer verifizierter Lauf nutzte denselben CLI-Schnitt fuer den dedizierten alert_read-Slice:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/alert-read-telemetry-e2e-20260317/server.log \
-flow alert_read \
-tenant-id tenant-local \
-app-version 0.1.0

Sichtbar war dabei:

  • Run A: alert-read-942bc64f-6cac-4daa-909e-efa64112c556 blieb auf GET /alerts mit 200 success erfolgreich
  • Run B: alert-read-90df8959-d5b9-41de-9dd5-46bc9db48c86 zeigte auf GET /alerts den Wechsel auf 403 failure mit error_code=insufficient_scope
  • der Bericht machte fuer denselben Slice den Delta-Wechsel von 73µs auf 69µs sichtbar

Ein weiterer verifizierter Lauf nutzte denselben CLI-Schnitt fuer den Gold-Flow device_register. Weil der Fehlerrun nach adb shell pm clear mit einer neuen lokalen device_id lief, wurde der Bericht bewusst ueber explizite Run-IDs fixiert:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/device-register-compare-e2e-20260317/server.log \
-flow device_register \
-run-a device-register-05aa2ac5-fc1c-432a-8735-64375aea2844 \
-run-b device-register-83ba260e-6db7-4103-a401-2492a59e7518

Sichtbar war dabei:

  • Run A: device-register-05aa2ac5-fc1c-432a-8735-64375aea2844 blieb auf POST /devices mit 201 success erfolgreich
  • Run B: device-register-83ba260e-6db7-4103-a401-2492a59e7518 zeigte auf POST /devices den Wechsel auf 403 failure mit error_code=insufficient_scope
  • der Bericht machte fuer denselben Gold-Flow den Delta-Wechsel von 1.947ms auf 77µs sichtbar

Ein weiterer verifizierter Lauf nutzte denselben CLI-Schnitt fuer den Gold-Flow event_submit. Weil der Fehlerrun nach adb shell pm clear mit neuer lokaler device_id lief, wurde die automatische Run-Auswahl hier ueber denselben tenant_id und dieselbe app_version eingegrenzt:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/event-submit-compare-e2e-20260317/server.log \
-flow event_submit \
-tenant-id tenant-local \
-app-version 0.1.0

Sichtbar war dabei:

  • Run A: event-submit-76e907bb-731e-43e6-ae96-d7dff8c8acbc blieb auf POST /events mit 202 success erfolgreich
  • Run B: event-submit-114e32c8-ac30-4325-95c5-71a7c364ec6d zeigte auf POST /events den Wechsel auf 403 failure mit error_code=insufficient_scope
  • der Bericht machte fuer denselben Gold-Flow den Delta-Wechsel von 5.826ms auf 44µs sichtbar

Ein weiterer verifizierter Lauf nutzte denselben CLI-Schnitt fuer den dedizierten incident_read-Gold-Flow:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/incident-read-compare-e2e/server.log \
-flow incident_read \
-tenant-id tenant-local \
-app-version 0.1.0

Sichtbar war dabei:

  • Run A: incident-read-ea31daf4-67d0-4a4b-8511-373c4c78bb70 blieb ueber GET /incidents und den nachgeladenen Detail-Read GET /incidents/incident-89092c694dc7a3e9 erfolgreich
  • Run B: incident-read-89ec180d-b38c-4416-bafc-a3efc04ecd1a zeigte auf GET /incidents den Wechsel auf 403 failure mit error_code=insufficient_scope
  • der Bericht machte sichtbar, dass der Detail-Read im Fehlerrun nicht mehr stattfand und deshalb fuer GET /incidents/incident-89092c694dc7a3e9 als not present im Delta auftauchte

Ein weiterer verifizierter Lauf nutzte denselben CLI-Schnitt fuer den dedizierten job_read-Slice:

Terminal-Fenster
cd /Users/benediktpoller/code/sh/superheld-cloud
go run ./cmd/telemetrycompare \
-log /Users/benediktpoller/code/sh/workspace/runtime/job-read-telemetry-e2e-20260317/server.log \
-flow job_read \
-tenant-id tenant-local \
-app-version 0.1.0

Sichtbar war dabei:

  • Run A: job-read-750be845-48f0-4344-9e29-2702a0ffd2ee blieb ueber GET /jobs, GET /jobs/job-1364710f79d6161572c2b0fd, GET /traces/trc-9b532b6c0bb196a4d43b6eda und GET /traces/trc-9b532b6c0bb196a4d43b6eda/steps erfolgreich
  • Run B: job-read-e2d9a9ca-a82b-4bff-8ceb-ba22954039ae zeigte auf GET /jobs den Wechsel auf 403 failure mit error_code=insufficient_scope
  • der Bericht machte sichtbar, dass GET /jobs/{id}, GET /traces/{id} und GET /traces/{id}/steps im Fehlerrun nicht mehr stattfanden und deshalb als not present im Delta auftauchten

Optional kann derselbe CLI-Schnitt die automatische Run-Auswahl zusaetzlich ueber -app-version oder -backend-version auf dieselbe Android- oder Backend-Buildkombination eingrenzen.

Das Tool ist bewusst klein gehalten: Es vergleicht heute vorhandene korrelierte Backend-Logzeilen, ersetzt aber noch keine eigene Telemetrie-API, keine Compare-UI und keinen Langzeit-Store.

Mit SUPERHELD_DATA_DIR=/Users/benediktpoller/code/sh/workspace/runtime/android-cloud-ops schreibt der lokale Lauf seine JSON-Backends unter anderem in diese Dateien:

  • tokens.json
  • devices.json
  • events.json
  • alerts.json
  • incidents.json
  • jobs.json
  • traces.json
  • artifacts.json
  • webhooks.json
  • token_audit.json
  • job_audit.json

Die JSON-Persistenz laedt bestehende Dateien beim Start. Aeltere Formate (z. B. incident_id statt id) werden beim Lesen transparent migriert.

Mit SUPERHELD_SQLITE_PATH=/pfad/zur/superheld.db nutzt die Runtime eine einzelne SQLite-Datenbank statt separater JSON-Dateien. SQLite ist heute die bevorzugte Option fuer Laeufe, bei denen Daten zuverlaessig ueber Server-Restarts hinweg erhalten bleiben sollen:

  • Incidents, Tokens, Jobs, Token-Audit und Job-Audit werden in SQLite-Tabellen persistiert.
  • Aeltere Datensaetze mit fehlenden Feldern werden beim Lesen aus den vorhandenen Spalten rekonstruiert.
  • Incident-Reads nach einem Server-Restart sind mit SQLite verifiziert und stabil.

Wenn ein frischer Dev-Lauf noetig ist, den Server zuerst stoppen und danach nur das lokale Runtime-Verzeichnis oder die SQLite-Datenbank gezielt entfernen oder ersetzen. Die Workspace-Grenze bleibt dabei innerhalb von /Users/benediktpoller/code/sh.