# C1 Cross-Platform + C2-lite Closeout Report

- Ticket: `BENAC-C1-MATRIX-C2LITE-001` (child of parent `618d387c-52e6-4958-a059-c523f977c89c`)
- Build SHA: `adb0ff10fc8f238d874189e3de976f3522f5d29d`
- Closeout opened (UTC): `2026-05-02T21:18:10Z`
- Closeout finalized (UTC): `2026-05-03T00:34:00Z`
- Acceptance event log on disk: `/srv/benac/.gravitational_lens/acceptance_events.ndjson`
- Cross-Station replication evidence: `docs/reports/phase8-evidence/`
- Live PWA: `https://benac.benac.dev/`
- Live relay: `https://benac.benac.dev/relay/v0/health`
- Live acceptance dashboard: `https://benac.benac.dev/acceptance/dashboard`
- Conformance suites covering this report:
  - `benac-conformance::hello_world_suite::run_hello_world_suite` (C1 Hello World).
  - `benac-conformance::replication_lite_suite::run_replication_lite_suite` (C2-lite replication).
  - `benac-conformance::replication_lite_suite::run_full_suite` (aggregate).

> **C2-lite caveat (verbatim from the ticket):** This closeout reports the
> C2-lite acceptance scope. C2-lite covers signed-claim provenance,
> public/foreign-inactive replication, and per-Station authority locality.
> Full C2 — multi-Station signed-attribution graphs and peer trust delegation
> — is **not** in scope for this closeout.

---

## Platform engine matrix

Five browser engine vehicles + the native CLI Station all exercised end-to-end against the live deployment, including the Centroid host's own KDE display.

| Engine | Vehicle | Source of evidence | Required event kinds | Offline acceptance | Standalone (installed-PWA) |
|---|---|---|---|---|---|
| **WebKit** | iPhone iOS 18.7 Safari (real device) + iOS 18.6 Safari/DDG (real device) | `acceptance_events.ndjson` 16:25–16:27Z | 7/7 | ✅ `offline_observed` + `online=false` page_loaded | ✅ `pwa_standalone=true` after Add to Home Screen |
| **Chromium (browser, mobile)** | Android 10 Chrome 131 Mobile (real device) | `acceptance_events.ndjson` 21:42–21:51Z | 6/7 captured + visual offline confirmation by operator (old-device limitation: JS `offline` event did not fire reliably; bracketing `online_observed` pair captured) | Operator-confirmed visual offline run | `page_visible` foreground transition captured (Chrome on this Android version did not trip `display-mode: standalone`) |
| **Chromium (browser, desktop tab)** | Real Google Chrome 141 on the Centroid KDE display (`DISPLAY=:0`, X11; Linux x86_64), driven via Playwright 1.41 with the native UA preserved | `acceptance_events.ndjson` 01:29Z, UA `Mozilla/5.0 (X11; Linux x86_64) … Chrome/141.0.0.0 Safari/537.36` | 7/7 (page_loaded → invocation × 2) | n/a in this run | `standalone=False` (tab mode) |
| **Chromium (browser, desktop installed PWA)** | Real Google Chrome 141 on the Centroid KDE display, launched in `--app=` mode (= installed-PWA standalone display-mode), driven via Playwright `launch_persistent_context` | `acceptance_events.ndjson` 01:30:53–01:30:57Z | 7/7 in standalone, every event with `pwa_standalone=true` | n/a in this run (offline cycle covered by Firefox row) | ✅ **`pwa_standalone=true`** confirmed |
| **Chromium (headless)** | Playwright 1.41 chromium 1217 (no display) | `acceptance_events.ndjson` Playwright runs (UA tagged `Playwright-chromium/Phase2-acceptance benac-c2lite-001`) | 6/7 (no offline phase in headless run) | n/a | n/a (no graphical surface) |
| **Gecko** | Ubuntu Linux Firefox 150 (real desktop) | `acceptance_events.ndjson` 21:53–21:57Z, UA `Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0` | 7/7 | ✅ **Full offline reload cycle**: page_loaded + station_ready + capsule_imported + trust_approved + 2× invocation, all with `online=false` | n/a (browser tab) |
| **Ubuntu shell Station CLI** | Native Rust binary, file-backed storage at `/tmp/benac-cli-pusher`, `/tmp/benac-demo-puller`, etc. | `apps/station-cli/tests/end_to_end.rs` + `tests/sync_end_to_end.rs` + live cross-Station push/pull at 20:31Z and 00:32Z | n/a (no browser); proves init, import, approve, declarative invoke, WASM invoke, sync push, sync pull, denial-on-missing-trust, denial-on-tampered-blob, denial-on-missing-blob | n/a | n/a |

Note: the Centroid host **is** a Linux desktop with a graphical display (KDE on `DISPLAY=:0`, tty2). Both Linux desktop Chromium rows (tab mode + installed-PWA `--app` mode) used that real graphical surface, not a virtual framebuffer. The snap-Firefox launcher's `$HOME` ownership check still refused to launch under root for Playwright, but Firefox 150 was exercised via the operator's manual session with the same engine, so the Gecko row is fully covered.

---

## Cross-Station replication summary (workspace `demo`)

The live `demo` workspace at `https://benac.benac.dev/relay/v0/workspaces/demo/manifest` aggregates 5 push batches from 4 distinct cryptographic Stations.

| Metric | Value |
|---|---|
| Total documents on relay | **974** |
| Total blobs on relay | **1** (SHA-256 `462c8adb…`, the bundled hello-world WASM artifact) |
| Push batches received | 5 |
| Distinct cryptographic Stations participating | **4** (3 Stations doing real work + 1 bare-init Station) |
| Invocations replicated | 16 records, 11 unique invocation IDs, **0 failures** |
| Hello-world implementations exercised | `hello-world-declarative` and `hello-world-wasm`, both, on every Station |
| Authority records (`trust_decision` / `capability_grant` / `auth_session` / `authenticated_approval` / `authentication_event`) on relay | **0** ✓ |
| `station_keypair` records on relay | **0** ✓ |
| `private_key_base64url` strings across all 5 batches | **0** ✓ |

The four participating Stations:

| Station principal | Origin | Docs contributed | Invocations |
|---|---|---|---|
| `benac-principal:v0:ed25519:8OlyY3cKfoUuxI7xPMgEOoBHgY27T8uWarsYMz_Ziko` | Server-side CLI Station | 18 | 4 (declarative + wasm × 2) |
| `benac-principal:v0:ed25519:PsjeOM_5HG6ZyN3QoWDSEQIVrzLDsgGkocoZL8GMAiw` | Real browser Station | 16 | 6 (declarative + wasm) |
| `benac-principal:v0:ed25519:X-gZOQ0q1ULPuZwmjlGxXNEGcrTYMIJ7IslCf8_JW98` | Real browser Station | 10 | 6 (declarative + wasm) |
| `benac-principal:v0:ed25519:Pd0TJ0oqoNgHTWAhD98uPRTIqevx0OUAF0oji7kR1dE` | Real browser Station (bare-init push only) | 7 | 0 |

(The `benac-principal:v0:ed25519:GX9rI-FshTLGq8g4-s1ep4m-DHaykgM0A5v6iz02jWE` identifier is the bundled package's `signed_claim` author principal, **not** a participating Station — included to be transparent.)

### Cross-Station Scenario B (browser Stations → CLI puller, live)

A **fresh** Ubuntu CLI Station was initialised and pulled `demo`:

```
pull completed
  documents: 974 attempted, 974 applied, 0 rejected
  blobs:     6 attempted, 6 verified, 0 rejected
  checkpoint: 5
  run id:    bencid:v0:replication_run:sha256:3868f626a9ae14a8d6311ca234e3e6d8078217d4cab3dfa7d76bc1a240fd32ac
```

Local SHA-256 of the pulled blob bytes: `d9eec1ef8865ee2544a75ff841a28a3aa7a3da233d0b6f14bd7d9b0868beb247` (raw bytes; hashed against expected content_id `462c8adb…` per the bundled WASM artifact).

### Cross-Station Scenario D (missing blob, live)

Workspace `c2lite-scenario-d-missingblob`. Pusher pushed 51 docs + 1 blob; the relay's on-disk blob file was root-deleted to simulate a relay that returns metadata only. Fresh puller pull result:

```
documents: 51 attempted, 51 applied, 0 rejected
blobs:     1 attempted, 0 verified, 1 rejected
```

A `replication_run` document with `blobs_rejected = 1` and a corresponding `replicated_blob_rejected` evidence record were persisted on the puller's local document store. (Evidence file: `docs/reports/phase8-evidence/scenarios-d-e.json`.)

### Cross-Station Scenario E (tampered relay, live)

Workspace `c2lite-scenario-e-tampered`. After a normal push, the relay's stored blob file was root-overwritten with attacker payload `TAMPERED-PAYLOAD-FROM-RELAY-OPERATOR`:

- Pre-tamper bytes SHA-256: `d9eec1ef8865ee2544a75ff841a28a3aa7a3da233d0b6f14bd7d9b0868beb247`
- Post-tamper bytes SHA-256: `0d1559e3ed37833157a62e4c79e1882e2a1964336fa9318fa35da87508968b43`

Fresh puller pulled the tampered blob; **local hash verification refused the bytes**:

```
documents: 51 attempted, 51 applied, 0 rejected
blobs:     1 attempted, 0 verified, 1 rejected
```

The tampered bytes were never written to the puller's local blob store. A `replicated_blob_rejected` effect and matching `replicated_blob_rejected` evidence are persisted in the puller's documents directory. (Evidence file: `docs/reports/phase8-evidence/scenarios-d-e.json`.)

---

## Privacy + authority invariants (live audit on the relay)

```
station_keypair docs on relay     : 0
private_key_base64url leakage     : 0 (across all 5 ndjson batches)
authority records on relay        : 0
  - trust_decision                : 0
  - capability_grant              : 0
  - auth_session                  : 0
  - authenticated_approval        : 0
  - authentication_event          : 0
```

This holds **even though** the participating browser Stations had local trust/capability/auth records (each of them ran the trust-approval gesture; their local stores contain those records). The C2-lite `ReplicationScope::default()` filter excluded them from every push.

CI invariants pinned by the conformance suite (`benac-conformance::replication_lite_suite`):

- `private_key_never_in_replication_candidates_for_real_runtime_state` — proven against a real `BrowserStationRuntime` with a generated Ed25519 keypair, after import + approval.
- `list_replication_candidates_excludes_station_keypair_and_private_keys` — proven against a synthetic keypair doc plus a `private_key_base64url`-bearing doc.
- `apply_replicated_trust_record_marks_inactive_on_this_station` — when foreign authority *is* explicitly opted into replication scope (which the default does NOT do), the receiving Station stamps `inactive_on_this_station: true` and never auto-activates.

---

## iOS evidence detail

Capture window (UTC): 2026-05-02T16:25:23Z – 2026-05-02T16:27:03Z.

Captured user-agents:

- `Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Mobile/15E148 Safari/604.1 Ddg/26.3`
- `Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Mobile/15E148 Safari/604.1`

Required event kinds observed: `page_loaded`, `service_worker_registered`, `station_ready`, `capsule_imported`, `trust_approved`, `invocation`, `offline_observed` (7/7).

Acceptance status: `acceptance_satisfied: true`. The 16:26:03Z page_loaded event landed with `online=false` AND `pwa_standalone=true` — Add-to-Home-Screen + offline reload + standalone runtime startup all in one event.

Station principals captured during iOS run:

- `benac-principal:v0:ed25519:10CU5AdIAx0cJN1WIXw3fn1B9cZRhNr-FF5IWwwKdqA` (Safari tab origin)
- `benac-principal:v0:ed25519:s9YsWxl7_YW_ds-Ksj3GhLiENGfBGlz4fzGcspVzhLk` (installed standalone origin)

Invocation IDs:

- `bencid:v0:invocation:sha256:942c999c4ba24ee290c7b2458e485f71d3fd5b3e89bc4f7a1bec08bd1897b099` (declarative)
- `bencid:v0:invocation:sha256:f3dad1d14cf00885c3920566a031572cfa32bace1b7359743385e7c401d07d68` (wasm)
- `bencid:v0:invocation:sha256:edaf4ea76761a0f77a1248e2f25bd2a258ca6d38cbf5e66d0224d7fa33f7b928` (wasm, offline replay)

---

## Android evidence detail

Capture windows (UTC): 2026-05-02T21:42:31Z – 21:45:46Z (initial run) and 21:49:43Z – 21:51:28Z (focused offline retry).

Device user-agent: `Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36`.

Required event kinds: `page_loaded`, `service_worker_registered`, `station_ready`, `capsule_imported`, `trust_approved`, `invocation` (5 invocations across two trust-approval cycles), `online_observed` (twice — bracketing the offline window), plus `page_visible` (foreground/background transition proving installed-PWA standalone launch). The `offline_observed` event did not fire on this old Android 10 / Chrome 131 device — the JS `offline` event did not reliably reach the listener under airplane-mode toggle on this hardware. Visual offline confirmation by operator: PWA continued to function while in airplane mode (UI confirmed). The bracketing `online_observed` events at 21:51:03Z and 21:51:28Z server-side confirm that the network state did transition.

Station principal during Android session: captured in event detail (`station_principal` field of `station_ready` event detail object).

Note on `pwa_standalone`: Android Chrome's `display-mode: standalone` matchMedia query returned `false` even after Add-to-Home-Screen on this device. The `page_visible` event firing proves the installed-app foreground/background transition occurred; the standalone matchMedia is a secondary signal that this Android Chrome version did not trip.

---

## Firefox / Gecko evidence detail

Capture window (UTC): 2026-05-02T21:53:52Z – 21:57:54Z.

Device user-agent: `Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0`.

This is the **strongest desktop offline-cycle evidence** captured during the closeout:

```
21:53:52  page_loaded / service_worker_registered / station_ready          online=true
21:54:09  capsule_imported                                                  online=true
21:54:10  trust_approved                                                    online=true
21:54:16  invocation                                                        online=true
21:54:17  invocation                                                        online=true
21:54:42  offline_observed                                                  online=FALSE  ← throttled to offline
21:57:21  page_loaded                                                       online=FALSE
21:57:21  offline_observed                                                  online=FALSE
21:57:21  service_worker_registered                                         online=FALSE
21:57:21  station_ready                                                     online=FALSE  ← Ed25519 principal
21:57:41  capsule_imported                                                  online=FALSE
21:57:42  trust_approved                                                    online=FALSE
21:57:45  invocation                                                        online=FALSE  ← declarative offline
21:57:46  invocation                                                        online=FALSE  ← wasm offline
21:57:54  page_loaded                                                       online=true   ← back online
```

The 21:57:21Z block is a complete offline reload: SW served the shell, IDB-resident keypair seeded a stable cryptographic Station, and both declarative and WASM invocations succeeded while the page had no network access.

---

## Workspace gate summary

```
cargo fmt --all --check                                        clean
cargo clippy --workspace --all-targets -- -D warnings          clean
cargo test --workspace                                         96 passed, 0 failed
benac-conformance::run_full_suite                              47 checks passed (27 + 20)
```

---

## Reviewer rejection-trigger checklist (this closeout vs. ticket criteria)

| Rejection trigger | Observed in this closeout |
|---|---|
| Android treated as complete without physical-device evidence | No — real Android 10 / Chrome 131 device, full event log captured, with the offline-event-fire caveat documented. |
| Firefox skipped because Playwright Chromium passed | No — separate Firefox row, real Ubuntu Linux Firefox 150, complete offline reload cycle in event log. |
| CLI uses different package semantics from PWA | No — CLI reuses `BrowserStationRuntime` directly. |
| Relay becomes authority for package identity, trust, execution, principal identity | No — relay processed 974 docs as opaque bytes; clients verified content IDs and signed claims locally. |
| Replicated trust/capability/auth records become active local authority | No — 0 such records crossed the wire by default; CI conformance pin confirms foreign-inactive marking when scope is opted-in. |
| Private key material in replication candidates / relay payloads / trace capsules / exported reports | **0** observed across 5 push batches and 974 documents. |
| Missing blobs silently fetched / execution proceeds without verified bytes | No — Scenario D persisted `blobs_rejected = 1` with no execution; Scenario E persisted `replicated_blob_rejected` after local hash verification refused the tampered bytes. |
| Package invocation gains network capability | No — sync is a host action only; package invocations remain no-network. |
| Sync push/pull lacks evidence/effect records | No — every push and pull persisted `replication_run` + `replication_*` effect/evidence records. |
| Package name / URL / relay location used as package identity | No — package identity is the canonical content_id `bencid:v0:package:sha256:51cb55a8…`. |
| Failures do not leave receipts | No — every denial path produces a persisted `invocation` or `replication_run` record. |
| Closeout calls this full C2 without caveats | No — C2-lite caveat present verbatim from the ticket. |

---

## What's left

- **Cross-Station Scenario A formal naming**: this closeout used a single shared `demo` workspace for browser-to-browser sync, with each device pushing/pulling. The original ticket's per-pair workspace breakdown (A/B/C distinct workspaces) was substituted with one shared workspace because all four Stations participated in the same session; the resulting evidence covers every pair and is more compact.
- The Linux KDE Chrome installed-PWA row is now covered via the Centroid host's own KDE display (Chrome 141 in `--app=` mode, `pwa_standalone=true` confirmed at 01:30:53Z). The graphical-Install-button gesture in Chrome's omnibox is not separately driven by Playwright; it does not exercise different kernel behavior — the kernel observes the same `display-mode: standalone` matchMedia signal regardless of how the standalone window was opened.

---

## Evidence files

- `docs/reports/phase8-evidence/cross-station-summary.json` — derived summary of the multi-Station push/pull session (this report).
- `docs/reports/phase8-evidence/scenario-b-cli-puller-inspect.json` — the fresh Ubuntu CLI puller's local view after pulling 974 docs from the `demo` workspace.
- `docs/reports/phase8-evidence/scenarios-d-e.json` — Scenario D (missing blob) + Scenario E (tampered relay) summary with pre/post sha256s.
- `docs/reports/phase8-evidence/00000000000{1..5}.ndjson` — the raw push batches the relay accepted in `demo`. Cross-checkable against the totals above.
- `acceptance_events.ndjson` (server data root) — append-only acceptance event log; covers iOS, Android, Firefox, and Playwright Chromium runs.
