# Web Automation Methodology

Use this workflow for an AI-generated dashboard or an HTTP/SSE/WebSocket client.
BMCLI owns hardware acquisition, buffering, DBC decoding, and the Web transport;
the generated project owns presentation and application-specific behavior.

## Place BMCLI in the application architecture

BMCLI Web is the bus-domain service, not a complete application backend. Keep
analyzer discovery and configuration, CAN/LIN traffic, decoded signals,
DBC/LDF state, logging, replay, routes, TX tasks, and bus-related status in
BMCLI. There are two valid shapes:

- A lightweight dashboard may connect directly to BMCLI Web, especially for a
  local, mostly read-only engineering view.
- A composed Web system may use BMCLI alongside one or more ordinary application
  services and a same-origin reverse proxy. Those services should own concerns
  such as users and authorization policy, durable application or time-series
  data, uploaded files and assets, reports, workflows, and other business logic.

The external application backend may call or proxy BMCLI's typed
REST/SSE/WebSocket interfaces and may persist or aggregate their results. It
must not start a second BMAPI reader, reimplement BMCLI's live bus state or
DBC/LDF decoding, or turn browser input into shell commands. BMCLI remains the
authority for current hardware state and bus semantics.

Do not add a database, upload system, general authentication platform, or
full-service Web framework to BMCLI merely because the larger application needs
one. CORS being disabled is not a reason to enlarge BMCLI; a composed system can
use a same-origin reverse proxy while BMCLI remains on loopback. An external
file service may validate and store an upload, then place an approved project
artifact within BMCLI's allowed project boundary before invoking the typed
database API. General service supervision and deployment orchestration also
belong to the surrounding application unless the lightweight dashboard simply
starts and stops its own BMCLI session.

## Discover the live contract

1. Resolve and validate the project bench first.
2. Query `bmcli help web start --format=json` and open the required daemon
   channels with explicit stable `SN/port`, role, timing, and electrical state.
3. Start loopback service with `bmcli web start --listen=127.0.0.1:8080
   --format=json`.
4. Read `/api/v1/capabilities`, `/api/v1/openapi.json`, and
   `/api/v1/operations`. Generate requests from the live OpenAPI document.
5. Treat an operation whose registry metadata says `migrated=false` or
   `http_policy=disabled` as CLI-only. Do not route it through `/execute`.

The v1 Web surface currently covers service discovery, bench/device snapshots,
bounded firmware-check jobs, typed channel list/info/open/config/close and
statistics, database load/list/info/unload, TX tasks, routes, logging, replay,
remote-BMAPI gateway lifecycle, CAN/CAN FD/LIN message send, raw history, SSE,
binary WebSocket, DBC signal plans, latest values, signal history, and signal
SSE. It does not claim HTTP coverage for every CLI command.

## Use XCP as another bus-domain resource

When the dashboard explicitly needs ECU internal measurements or calibration,
keep the same service boundary. Discover `/api/v1/capabilities`,
`/api/v1/openapi.json`, and `/api/v1/operations`, then use `/api/v1/xcp`.
`GET /api/v1/xcp` returns bounded endpoint status. `POST /api/v1/xcp` accepts a
tokenized `argv` array whose first item is `xcp`; it is not a shell string.
Resolve exact tokens from runtime Help before sending them. Prefer XCP DAQ for
charts, synchronous A2L measurement reads for bounded status cards, and
characteristic writes only behind explicit authorization and audit controls.

BMCLI owns XCP-on-CAN transport, A2L address/conversion semantics, DTO decoding,
coexistence with UDS/logging, and analyzer ownership. The surrounding
application owns durable history, calibration approval workflows, users,
uploaded A2L lifecycle, reports, and business-specific limits. Do not add a
second BMAPI reader or reimplement XCP inside the dashboard backend merely to
obtain chart data.

## Select the data path

- Use `POST /api/v1/messages` for a bounded send or LIN transaction. Add a
  unique `Idempotency-Key` when retry behavior matters.
- Use `/messages/history?after=<cursor>` for deterministic pagination and
  reconnect. A `409 cursor_expired` means retained history was overwritten.
- Use `/messages/stream?from=now` for ordinary JSON event rates. Persist each SSE
  event ID only after the application accepts the event; reconnect with
  `Last-Event-ID`.
- Use `/messages/ws?from=now` with subprotocol `bmcli.frames.v1` for high-rate raw
  frames. Parse BMF1 with explicit big-endian fields from OpenAPI/reference data;
  never map the bytes to a compiler C struct.
- Create `/signals/plans` for the specific DBC message needed by the UI, then use
  `/signals/latest`, `/signals/history`, or `/signals/stream`. Keep database
  identity/revision visible in engineering diagnostics.
- Use `/channels`, `/databases`, `/tx-tasks`, and `/routes` for their typed
  lifecycle operations. Percent-encode `/` in a channel path selector such as
  `10356%2F0`; keep `10356/0` unchanged in query parameters and JSON bodies.
- Database load accepts only existing files inside the BMCLI project working
  directory. A surrounding file service may stage an approved artifact there,
  but must not bypass that boundary or give BMCLI arbitrary file access.
- Use `/logging`, `/replay`, and `/gateway` for service lifecycle instead of
  constructing CLI text. Logging outputs and replay inputs are confined to the
  project root, including resolved Windows junction and Linux symlink targets.
- Read `/channels/{selector}/stats` and `/metrics` before sizing retention.
  Monitor retained bytes/duration plus each consumer's backlog and pending gap;
  event capacity alone is not a time guarantee.
- Submit `/devices/{selector}/firmware-check` only for a version check and poll
  `/jobs/{job_id}`. Actual firmware transfer remains a local-only,
  device-disruptive CLI operation.
- Send only one hardware setter group per channel PATCH. Treat CAN timing and
  LIN protocol pairs as their documented groups; split other desired-state
  changes into separate idempotent requests.

For a lossless curve, append every unique sequence in order, reject duplicates,
detect gaps, and recover from history before resuming live display. UI refresh
may downsample, but retained engineering data must not silently drop or repeat a
sample.

## Preserve ownership and safety

One RxBroker owner reads each daemon channel. CLI receive, logging, gateway,
raw Web streams, and signal decoding have independent cursors; do not start a
second BMAPI reader in the generated application. Keep the service loopback by
default. A non-loopback listener requires an explicit bearer token,
`--allow-insecure-http`, and network authorization; CORS remains disabled. The
current embedded server is plain HTTP:
the token authenticates but does not encrypt credentials, API payloads, or bus
traffic. Use non-loopback only on a trusted isolated engineering network with a
host firewall, or behind an approved HTTPS reverse proxy. Never present a token
as transport encryption or expose the listener directly to an untrusted network.
The package includes `share/bmcli/packaging/https/configure-nginx.sh`; use a
certificate trusted by the browser and keep BMCLI bound to loopback. Do not use
`--ignore-certificate-errors`, `curl -k`, or a disabled HTTPS-first policy as a
deployment solution.

Sending traffic and creating active decode state require the same reviewed
bench context as direct BMCLI. Do not expose firmware update, device reset, UDS
programming, arbitrary file access, or daemon administration through a custom
browser proxy. Query `web status` for clients/counters and call `web stop` in
cleanup without stopping a daemon that predated the workflow.
