# ADR 0063: Physical Evidence Terminal

Date: 2026-08-02
Status: Accepted

## Implementation Status on Main

The evidence-terminal design is implemented on `main`. The repository contains
the evidence-log sources, `core/src/evidence_terminal.rs`, the
`evidence-terminal` Cargo feature, and `scripts/test-evidence-terminal.py`.
COM1 remains the automated QEMU oracle; the framebuffer terminal is an opt-in
visual mirror for serial-less physical evidence capture.

On 2026-08-08, the evidence-terminal path was captured on the disposable O2
Micro `1217:8620` target across five readable framebuffer pages and a continuous
boot video. Every photographed page reports:

```text
count 00000139
drop 00000000
crc 176F4C6E
```

`format_status_line` formats only the page fields as decimal and formats count,
drop, and CRC with `write_hex8`. Therefore `00000139` is hexadecimal and means
313 decimal accepted markers. Two separate physical boots produced the same
count, zero-drop state, and CRC. The physical stream was reconstructed from the
current QEMU stream plus observed hardware-path differences and recomputes to
313 markers with CRC `176F4C6E`. See
[`docs/evidence/2026-08-08-physical-evidence-terminal.md`](../evidence/2026-08-08-physical-evidence-terminal.md).

## Context

ADR 0062 proved the polling SDHCI/eMMC backend through QEMU acceptance and
through target-specific physical evidence on the disposable O2 Micro
`1217:8620` laptop. That physical target does not currently provide a captured
serial log. The existing framebuffer acceptance panel proves that the Phase 10
storage path reached its final physical screen, but it does not show the full
marker transcript in a form a reviewer can inspect from a photo or video.

PythOS still treats COM1 as the automated QEMU oracle. A screenshot remains
insufficient evidence by itself. The physical screen may mirror the marker
stream for serial-less hardware, but it must not replace serial ordering checks
or broaden the hardware claim.

## Decision

Add an opt-in `evidence-terminal` feature for verification images. The feature
renders a terminal-style marker transcript on the framebuffer after the Phase 10
storage proof reaches `PYTHOS:CORE:PHASE_10_COMPLETE`.

The boot ABI minor version moves from `0.2` to `0.3`. `PythBootInfo` consumes
part of the existing reserved area with explicit evidence-log metadata:

```rust
pub evidence_log_phys: u64,
pub evidence_log_len: u32,
pub evidence_log_flags: u32,
pub reserved: [u64; 6],
```

`evidence_log_flags` uses bit `0x0000_0001` as
`PYTH_EVIDENCE_LOG_FLAG_PRESENT`; all other bits are invalid. When the flag is
clear, `evidence_log_phys` and `evidence_log_len` must be zero. When the flag
is set, the evidence buffer must be page-aligned, loader-allocated RAM with a
total length of 64 KiB.

The shared log format is named `PYLOG001`, version `1`. Its payload stores
ASCII marker lines separated by `\n`. The header records payload capacity,
bytes used, accepted line count, dropped line count, and CRC-32/ISO-HDLC over
accepted payload bytes, including the trailing newline for each accepted line.
CRC-32/ISO-HDLC is defined as reflected polynomial `0xEDB88320`, initial state
`0xFFFF_FFFF`, reflected byte updates, and stored/displayed value
`state ^ 0xFFFF_FFFF`.

The loader allocates and initializes the evidence buffer before emitting
`PYTHOS:LOADER:ENTER`, then mirrors loader markers to both COM1 and the
evidence buffer. PythCore validates the buffer, maps it at fixed high kernel
virtual window `0xFFFF_C000_1003_0000` in the PythCore-owned page tables before
the broad loader identity map is removed, keeps that mapping supervisor-only in
user roots, backfills the earliest core markers that were emitted before
attachment, then mirrors subsequent core marker writes through the serial path.

The evidence terminal emits no replacement milestone markers. Existing marker
names and order remain intact. To let the QEMU screendump capture the terminal
after the final page is visible, the opt-in acceptance path emits the additional
post-render marker `PYTHOS:CORE:EVIDENCE_TERMINAL_READY` only when the evidence
log reports zero dropped lines. If `dropped` is nonzero, the terminal renders
with the nonzero drop count, emits `PYTHOS:CORE:EVIDENCE_TERMINAL_DROPPED`,
and enters the panic exit path. The evidence-terminal QEMU harness uses
`PYTHOS:CORE:EVIDENCE_TERMINAL_READY` as its success trigger while still
requiring `PYTHOS:CORE:MILESTONE_1_COMPLETE` to appear first in the transcript.
After emitting the ready marker, PythCore performs one additional bounded
terminal dwell before entering `qemu_exit::success()` so QEMU screendump capture
has a finite guest-side window while the final terminal page remains visible.

On physical hardware the QEMU debug-exit port write is ignored and
`qemu_exit::success()` remains in its non-returning loop, leaving the final
terminal page visible. Page dwell uses the existing PIT tick clock. Any CPU
spin fallback is calibrated and verified only for the O2 Micro `1217:8620`
target evidence image.

The physical transcript can legitimately differ from QEMU where hardware
discovery, audio fallback, or pre-existing persistent-storage state select
different truthful marker branches. Physical acceptance therefore does not
require a bit-for-bit copy of the QEMU marker stream; it requires a complete,
ordered, zero-drop stream consistent with the executed hardware path.

## Consequences

Default builds without `evidence-terminal` are unchanged. The feature creates
a visual mirror of the milestone marker stream for physical evidence, not a
trusted log store, filesystem, USB writer, object-store record, or replacement
serial oracle.

Evidence-terminal builds fail loudly if required evidence metadata is absent,
malformed, unmappable, or internally corrupt. If the transcript exceeds the
64 KiB buffer, the terminal renders with a nonzero `dropped` count and does not
emit the ready marker, so the artifact cannot be misread as a complete
transcript.

This ADR does not add USB mass storage, FAT, partitions, filesystems, DMA/ADMA,
interrupt-driven SDHCI, hotplug, or generic physical SDHCI/eMMC support. The
physical claim remains limited to the disposable O2 Micro `1217:8620` target
until another ADR records a new target and verification boundary.
