Files
pi-car-companion/PLAN.md
T

24 KiB
Raw Blame History

Pi Car Companion — Product and Delivery Plan

1. Purpose

Pi Car Companion is a local-first companion computer for an MG4. A Raspberry Pi 5 runs services, diagnostics, automations, and a touch-friendly dashboard; the car's existing Android infotainment system remains the display and input device.

The project supplements the vehicle. It must never replace or modify the OEM launcher, firmware, vehicle controls, CAN bus, safety systems, or any driving-critical function.

The intended daily flow is simple:

  1. The Pi boots independently and starts the companion service.
  2. The MG4 Android app opens the Pi-hosted dashboard at http://mg4pi.local:8787.
  3. The user sees real system state and can run a small set of audited, server-defined actions.
  4. If the native app is unavailable, the same dashboard works in a browser or as a PWA.

Core operation must work without cloud access. Optional remote access is a later, explicitly configured feature.

2. Product principles

  • Local first: the Pi and dashboard remain useful on an isolated LAN.
  • Safe by construction: privileged actions are allowlisted, validated, confirmed where necessary, and audited.
  • Truthful UI: unavailable information is shown as unavailable with a reason; no fake telemetry, devices, logs, or success states.
  • Headless Pi: routine use and administration require no dedicated Pi display.
  • Touch first: the primary target is a roughly 1920×720 landscape infotainment screen with large controls and no hover-only interactions.
  • Recoverable: service failures, network loss, partial jobs, and interrupted updates have explicit recovery paths.
  • Least privilege: the service runs as a dedicated non-root user and receives only narrowly scoped privileges.
  • Progressive scope: establish a secure end-to-end vertical slice before adding integrations or convenience features.

3. Scope

Version 1

  • First-run administrator creation, login, logout, and session management
  • Real Raspberry Pi health and network status
  • Live connection and job progress updates
  • Server-defined jobs with timeouts, validation, logs, and audit history
  • Initial jobs:
    • refresh system health
    • run network diagnostics
    • generate a sanitized support bundle
  • Touch-focused web dashboard
  • Native Android WebView shell with editable trusted dashboard URL and reconnect controls
  • Read-only ADB connection health and device/display identity for one explicitly configured head unit
  • Raspberry Pi bootstrap, systemd service, health check, update, and uninstall scripts
  • Automated tests, production builds, and operator documentation

Version 1.1

  • Controlled artifact upload, listing, download, and deletion
  • SHA-256 metadata and storage quota reporting
  • Wi-Fi status/reconnect where supported by the host
  • Dual-radio car hotspot with USB-upstream preference, 60-second built-in fallback, and automatic offline recovery
  • Log rotation/pruning job
  • Companion service restart job
  • Reboot and shutdown actions with strong confirmation
  • Audit filtering and diagnostic export improvements
  • Live hotspot client, DNS-query metadata, and active-flow diagnostics
  • Optional PWA installation support

Later, only after real-world validation

  • Tailscale-assisted remote access
  • Additional local services and automations
  • Media-adjacent utilities
  • Read-only vehicle integrations, only with a separate threat model and explicit hardware testing

Explicitly out of scope

  • Arbitrary shell, terminal, SSH, ADB, Docker socket, or unrestricted file-browser access through the dashboard
  • Public-internet exposure by default
  • Hardcoded or default credentials
  • Rooting, flashing, or modifying MG4 infotainment software
  • Sending vehicle-control commands or interacting with safety-critical systems
  • Claiming hardware behavior that has not been tested on the actual Pi/MG4 installation

4. Proposed architecture

MG4 Android app / browser
          |
          | trusted local HTTP(S) origin
          v
Fastify API + static React app  <---->  SQLite
          |
          +---- system status collectors
          +---- allowlisted read-only ADB collectors
          +---- allowlisted job handlers
          +---- controlled artifact storage
          +---- audit and diagnostic services

Repository layout

pi-car-companion/
├── server/                 # Fastify API, auth, jobs, persistence, collectors
├── web/                    # React touch dashboard and PWA assets
├── android-companion/      # Kotlin Android WebView application
├── config/                 # Safe defaults and example configuration
├── docs/                   # Operations, security, networking, validation
├── scripts/                # Pi bootstrap/install/update/uninstall utilities
├── systemd/                # Service and optional watchdog units
├── tests/                  # Cross-package/integration tests and fixtures
├── .env.example
├── docker-compose.dev.yml  # Development dependencies only, if needed
├── package.json            # Workspace commands
└── README.md

Technology baseline

  • Raspberry Pi OS Lite / Debian arm64 on Raspberry Pi 5
  • Node.js 22 and strict TypeScript
  • Fastify, Zod, SQLite, and Argon2id
  • React, Vite, and a PWA-capable frontend
  • Server-Sent Events by default for one-way live status; use WebSockets only if bidirectional live messaging becomes necessary
  • Native Kotlin Android app, minimum SDK 28, targeting a current stable Android SDK
  • systemd for production process supervision

Use a JavaScript workspace setup that keeps server and web commands consistent. Do not introduce containers into the production Pi path unless a measured need emerges.

5. System design

Authentication and first run

  • There is no default account.
  • Setup is available only while the user table is empty; the first successfully created user becomes administrator.
  • Setup and account creation must be transaction-safe so concurrent requests cannot create multiple initial administrators.
  • Passwords are hashed with Argon2id using parameters documented and tested on Pi hardware.
  • Sessions use opaque, revocable identifiers in HttpOnly cookies.
  • Cookies use SameSite=Strict where compatible, a narrow path, and Secure whenever HTTPS is enabled.
  • Every state-changing browser request requires CSRF protection and origin validation.
  • Login and setup endpoints are rate-limited without leaking account existence.

Server and API

  • Bind to loopback by default. LAN binding is an explicit configuration change documented during installation.
  • Provide GET /healthz for process health and a separate authenticated system-status endpoint.
  • Validate request params, query strings, bodies, and relevant configuration with Zod.
  • Use structured logs with a central redaction policy.
  • Return stable machine-readable error codes alongside safe user-facing messages.
  • Keep collection failures isolated: one unavailable sensor must not fail the entire status response.

System status

Collect actual values for:

  • hostname, OS/version, uptime, and current timestamp
  • CPU model, load average, and temperature when exposed by the OS
  • RAM and disk usage
  • network interfaces and addresses
  • Wi-Fi connection and signal when supported
  • companion service health
  • time of the last successful collection

Each field that can fail should carry an availability state and safe reason. Never substitute generated values.

Controlled jobs

Jobs are registered in code through a typed manifest. The client can discover and invoke only registered jobs; it never supplies an executable or command line.

Every job definition includes:

  • stable ID, title, description, and risk level
  • typed input schema and allowed values
  • explicit timeout and concurrency policy
  • confirmation policy
  • server-side handler
  • log sanitization rules

Every run records:

  • unique run ID and job ID
  • authenticated actor
  • validated, safely serializable inputs
  • queued, started, and completed timestamps
  • progress and terminal state
  • duration, result, and stable error code
  • bounded, sanitized stdout/stderr or handler output

Jobs must handle timeout, cancellation caused by service shutdown, duplicate submission, and stale in-progress records after restart. Destructive jobs require a short-lived, action-bound confirmation rather than a reusable client-side checkbox.

Artifacts

Artifacts live under one configured data directory; API callers never provide filesystem paths.

  • Generate storage names server-side and retain a sanitized display name separately.
  • Reject traversal, separators, reserved names, oversize files, and executable content by default.
  • Enforce limits while streaming, not only from request headers.
  • Record size, detected type, SHA-256, uploader, and upload time.
  • Require authentication for list/download and confirmation plus audit logging for deletion.
  • Define retention and quota behavior before enabling uploads in production.

Audit and diagnostics

  • Audit authentication, settings changes, job lifecycle events, artifact changes, and privileged actions.
  • Audit records are append-oriented and distinct from verbose application logs.
  • Redact passwords, cookies, CSRF values, tokens, authorization headers, environment secrets, and sensitive query parameters at ingestion and export.
  • Support bundles contain an explicit manifest and only allowlisted diagnostic files/data.
  • The UI provides filters, job output viewing, and a plaintext “copy diagnostics” view.
  • Retention is configurable and pruning itself is audited.

Android companion

  • First launch asks for a dashboard URL, defaulting to http://mg4pi.local:8787; the saved value remains editable.
  • Only the configured origin is trusted. External navigation is blocked or opened only after explicit confirmation.
  • Cleartext traffic is permitted only through a narrow network-security configuration suitable for the configured local origin; production HTTPS remains supported.
  • JavaScript, cookies, and DOM storage are enabled only as required for the trusted dashboard.
  • Use WebViewClient and WebChromeClient with native loading, offline, certificate, and connection-error states.
  • Provide a persistent native reconnect/refresh action.
  • Preserve Android soft-keyboard behavior and support file chooser/download flows without broad storage permissions.
  • Optimize for landscape and offer immersive mode without making navigation recovery difficult.

MG4 infotainment and ADB boundary

The hardware baseline is sourced from the captured MG4 Luxury 2022 profile at https://git.molberg.cloud/alex/mg4-luxury-2022-info (capture dated 2026-07-30; reference commit 162a6636df89e1df2bba7ffc14550a37b3f9e9d5). Treat it as a device-specific reference, not proof for other model years, trims, regions, or software builds.

  • The tested head unit is Android 9/API 28 on arm64, with ADB enabled but no root, no su, and SELinux enforcing. The companion must not depend on root or system partition changes.
  • Display 0 is 1920×720 at 160 dpi, but the usable app area is 1778×640 because of an 80 px top status bar and 142 px left navigation bar. Use Android window insets; do not model the navigation bar as a bottom inset.
  • The physical pixels are non-square. Emulator checks cannot validate the apparent shape of circles, gauges, artwork, or logos; those require the real display.
  • Car UX restrictions are enforced while moving. Version 1 is parked-use software unless a later native activity is explicitly designed, declared, and validated as distraction optimized.
  • The Pi is the only intended ADB host. The bridge targets either one configured serial or exactly one automatically discovered device, refuses ambiguous multi-device connections, uses argument-array process execution, enforces timeouts and output bounds, expose unavailable/unauthorized/offline states, and register every operation in a typed server-side allowlist. No API accepts a shell string, arbitrary ADB arguments, a package/component supplied by a client, or an unrestricted file path.
  • Begin with read-only device identity, connection health, display, package/version, and carefully selected dumpsys collectors. Any app launch, input injection, file transfer, or settings change is a separate state-changing capability requiring an explicit threat model, parked-state policy, confirmation policy, and audit trail.
  • Do not use ADB or the exported SAIC vehicle service to actuate vehicle hardware in this project. Vehicle telemetry and vendor APIs remain a later, read-only research track until permissions, semantics, privacy, and parked-vehicle tests are documented.

Web experience

The web app must remain usable for desktop/mobile setup, but the primary runtime layout targets 1920×720 landscape.

  • Minimum 48dp touch targets; prefer 5672dp for primary actions.
  • Dark, high-contrast UI with large readable type and restrained use of cards.
  • Persistent connection state and obvious reconnect action.
  • Keyboard-accessible controls and visible focus states.
  • No hover-only behavior.
  • Dialogs, logs, menus, and settings panes must fit and scroll at 720px height.
  • Dangerous actions are visually distinct without relying on color alone.

Primary areas:

  1. Home: connection, health, time, network, warnings, recent runs, and safe quick actions.
  2. Jobs: server-discovered actions, validated inputs, confirmations, progress, history, and logs.
  3. Artifacts: upload, usage, metadata, download, and confirmed deletion.
  4. Audit: filterable event history, job output, and support-bundle export.
  5. Settings: access mode, mDNS, optional Tailscale state, retention, sessions, and danger zone.

6. Delivery roadmap

The repository contains a working MVP and tracks remaining delivery work here. Status markers are: [ ] pending, [~] in progress, [x] complete, [!] blocked.

Milestone 0 — Decisions and scaffold

  • Record architecture decisions for workspace tooling, SQLite library/migrations, sessions, CSRF, and live events.
  • Create the monorepo layout and shared TypeScript/tooling configuration.
  • Add repeatable dev, lint, typecheck, test, and build workspace commands.
  • Establish environment parsing, structured logging/redaction, migrations, and test database helpers.
  • Add CI for all host-runnable quality gates.

Exit: a clean checkout installs reproducibly and all empty/scaffolded packages build and test through root commands.

Milestone 1 — Secure authenticated vertical slice

  • [~] Implement SQLite migrations for users, sessions, settings, audit events, and job runs. User, session, audit, and job-run tables are complete; settings remain pending.
  • Implement atomic first-admin setup, login, logout, session expiry/revocation, CSRF, and throttling.
  • Build setup/login screens and authenticated application shell.
  • Implement GET /healthz and authenticated system status with per-field availability.
  • Show real connection and health state in the dashboard with loading, error, empty, and reconnect behavior.
  • Stream status updates with reconnect/backoff semantics.

Exit: a new local installation can create one admin, authenticate securely, and view truthful host status end to end.

Milestone 2 — Jobs, audit, and diagnostics

  • Implement the typed job registry, runner, timeouts, progress events, persistence, and restart recovery.
  • Add refresh-health, network-diagnostics, and sanitized-support-bundle jobs.
  • Implement action-bound confirmation infrastructure for higher-risk future jobs.
  • Build the jobs/history UI and audit timeline.
  • [~] Add bounded output, central redaction, support-bundle manifest, and retention behavior. Output bounding, central redaction, and the support-bundle manifest are complete; retention remains pending.

Exit: the three initial jobs run only through the allowlist, expose live progress, persist safe results, and create attributable audit events.

Milestone 3 — ADB transport and Android client

  • Implement one-device ADB configuration, connection/authorization state, timeouts, output bounds, and test fixtures.
  • Add allowlisted, read-only device identity and display collectors with per-field availability.
  • Create the Kotlin project with minimum SDK 28 and landscape support.
  • Implement URL setup/storage, trusted-origin navigation, and narrow cleartext policy.
  • Implement WebView session behavior, soft keyboard, refresh, loading, and offline/error screens.
  • Implement safe file chooser/download behavior where supported.
  • Produce a debug APK in CI or a documented compatible Android build environment.

Exit: the Pi reports truthful ADB/head-unit connection state through read-only allowlisted collectors, and the app builds and can load/authenticate against the local dashboard in an emulator or test device; actual MG4 behavior remains explicitly unverified until hardware testing.

Milestone 4 — Pi deployment

  • [~] Add idempotent bootstrap, install-service, healthcheck, update, and uninstall scripts. Installation, health verification, and atomic updates are complete; uninstall remains pending.
  • Create a dedicated non-root user, /var/lib/pi-car-companion data directory, and /etc/pi-car-companion configuration directory.
  • [~] Add hardened systemd unit(s), restart policy, graceful shutdown, and optional watchdog. Service hardening and restart behavior are complete; an optional watchdog remains pending.
  • [~] Document LAN opt-in, firewall expectations, mg4pi.local through Avahi/mDNS, backups, updates, rollback, and recovery. LAN access, mDNS service discovery, updates, and release safety are documented; backup and recovery procedures remain pending.
  • Test a clean install and upgrade on Debian arm64 or Raspberry Pi OS.

Exit: a fresh supported Pi image can be installed, rebooted, updated, and removed using documented commands without leaving an insecure default service.

Milestone 5 — Artifacts and operational actions

  • Implement controlled uploads/downloads/deletion, hashing, quotas, and metadata UI.
  • [~] Add Wi-Fi refresh/reconnect with capability detection. A disabled-by-default dual-radio NetworkManager controller, 60-second recovery watchdog, fixed dashboard actions, hotspot credential configuration, and status UI are implemented; destructive failover and unplug/replug behavior still require staged Pi and MG4 validation.
  • Add log pruning and companion-service restart jobs.
  • Add reboot and shutdown with explicit action-bound confirmation and narrowly scoped privilege configuration.
  • Exercise storage exhaustion, job timeout, service restart, and network-loss recovery.

Exit: operational features remain path-safe and allowlisted, destructive actions are hard to trigger accidentally, and failure modes are documented and tested.

Milestone 6 — Vehicle validation and release

  • Validate Pi boot, power-loss behavior, thermals, storage durability, Wi-Fi, and mDNS in the intended installation.
  • Validate the Android app on the MG4: resolution, touch sizing, keyboard, scrolling, downloads, reconnect, and immersive-mode recovery.
  • Run a parked-vehicle distraction/usability review; remove or gate flows unsuitable while driving.
  • Complete security review, dependency audit, backup/restore drill, and release checklist.
  • Clearly record tested hardware/software versions and all remaining limitations.

Exit: all release gates pass on the actual target hardware, documentation matches observed behavior, and no hardware-dependent claim exceeds the evidence.

7. Verification strategy

Automated tests

  • Authentication: first-admin race, password verification, session expiry/revocation, setup lockout, rate limiting
  • Request security: CSRF rejection, origin checks, schema validation, authorization boundaries
  • Jobs: unknown-job rejection, input validation, confirmation enforcement, timeout, redaction, persistence, restart recovery
  • Artifacts: traversal and filename attacks, streaming size limits, executable rejection, SHA-256, authorization, deletion audit
  • Status: parser/collector fixtures, unavailable fields, partial collection failures
  • UI: setup/login, loading/error/reconnect, live updates, empty states, confirmation flows, and scroll behavior at target viewport sizes
  • Android: URL validation, origin policy, navigation decisions, and lifecycle state where practical
  • Deployment: shell linting plus idempotency checks in a disposable supported environment

Required quality gates

Before merging a milestone:

lint
typecheck
unit and integration tests
web production build
server production build
Android build (from Milestone 3 onward)

Security-sensitive changes also require focused negative tests. Commands and their actual results belong in release notes or CI; documentation must not claim a command passed unless it was run.

Manual target checks

  • 1920×720 layout at normal and enlarged font scale
  • touch targets, focus order, soft keyboard, and scroll containment
  • Pi temperature/status availability across supported kernels
  • Wi-Fi disconnected/reconnected states
  • service crash/restart and stale job recovery
  • browser/app behavior during Pi reboot and network loss
  • support-bundle inspection for secret leakage
  • abrupt power loss and subsequent database integrity

8. Security invariants

These are release blockers, not optional enhancements:

  • No default credentials or post-setup first-run endpoint
  • No arbitrary command or arbitrary filesystem path accepted from a client
  • No privileged action outside the server allowlist
  • No destructive action without strong, action-specific confirmation and audit
  • No secret-bearing data in logs, audit payloads, live events, or support bundles
  • No LAN/public binding silently enabled by application defaults
  • No broad root service; privileged host operations use narrowly scoped mechanisms
  • No unauthenticated artifact or diagnostic access
  • No unsupported claim of HTTPS security for a plain local HTTP deployment
  • No vehicle-control or safety-system integration in this project scope

9. Configuration and operations

Configuration should be explicit, validated at startup, and documented in .env.example. At minimum it covers:

  • listen host and port
  • canonical dashboard origin
  • database, artifact, and support-bundle locations
  • session lifetime and secure-cookie mode
  • upload limits and allowed content policy
  • audit, job-output, and artifact retention
  • log level and redaction-safe diagnostics mode
  • optional mDNS and trusted-proxy behavior

Production secrets are generated during installation and stored outside the repository with restrictive permissions. Database and artifact backups must be consistent, restorable, and versioned with the application schema.

10. Definition of done for Version 1

Version 1 is complete only when:

  • a clean Raspberry Pi installation can be completed from the documentation;
  • the service runs unprivileged under systemd and survives reboot/restart;
  • the first admin can be created exactly once and can log in/out securely;
  • real status is visible without fabricated fallback data;
  • the three initial jobs are allowlisted, validated, bounded, streamed, persisted, and audited;
  • a sanitized support bundle can be generated and inspected;
  • configured ADB device/display identity is read through bounded, read-only collectors with honest offline and unauthorized states;
  • the web UI is usable at 1920×720 and handles connection failure honestly;
  • the Android app builds and loads only its configured trusted origin;
  • lint, typecheck, automated tests, server/web builds, and Android build pass;
  • security and operations documentation is current;
  • Pi/MG4-specific validation results are separated from emulator or desktop results.

11. Open decisions

Resolve these through short architecture decision records before their milestone begins:

  1. Package manager/workspace tooling and lockfile policy
  2. SQLite driver, migration mechanism, and backup approach
  3. Session store and CSRF implementation
  4. Server-Sent Events reconnect/replay contract
  5. Safe privilege boundary for service restart, reboot, shutdown, and Wi-Fi operations
  6. Android handling for local HTTP versus an optional locally trusted HTTPS setup
  7. Pi power supply, graceful shutdown strategy, and storage medium for in-vehicle use
  8. Whether the infotainment app can be installed and persist sessions under the MG4's Android restrictions

Until measured on target hardware, these remain decisions or risks—not assumptions disguised as completed functionality.