439 lines
23 KiB
Markdown
439 lines
23 KiB
Markdown
# Architecture
|
|
|
|
## Scope and trust boundaries
|
|
|
|
`libchimera-gfx` is split so that host-testable policy and state validation do
|
|
not depend on PS5 headers or undocumented ABI assumptions.
|
|
|
|
```text
|
|
RetroArch adapter (Phase 3) ----+
|
|
+--> public C API --> validated core
|
|
SDL2 renderer (Phase 4) --------+ |
|
|
+--> mock backend
|
|
+--> PS5 backend (refuses)
|
|
|
|
Phase-0 capability sample --> probe logic --> symbol resolver --> system loader
|
|
^ |
|
|
| +-- no resolved pointer escapes
|
|
+-- generated read-only symbol table
|
|
```
|
|
|
|
The capability probe is intentionally separate from the normal context API.
|
|
Discovering exports does not make the PS5 backend usable and cannot set a
|
|
rendering capability flag.
|
|
|
|
## Public API contract
|
|
|
|
The public API is C11 and uses opaque handles, explicit structure sizes, and an
|
|
API version. This supports compatible growth without copying upstream or
|
|
proprietary structures into the interface.
|
|
|
|
Phase-0 guarantees:
|
|
|
|
1. `chimera_gfx_create()` creates only the mock backend; the separately
|
|
compiled PS5 backend always returns `SAFETY_POLICY`.
|
|
2. Any rendering-request flag fails with `CHIMERA_GFX_STATUS_SAFETY_POLICY`.
|
|
3. Mock capabilities claim only a host-memory lifecycle and present model,
|
|
always together with `NON_RENDERING` and `HOST_TEST_ONLY`.
|
|
4. Context destruction accepts `NULL` and otherwise returns `RESOURCE_BUSY`
|
|
until every child surface and texture is destroyed.
|
|
5. Unknown backend IDs, API versions, flags, or undersized structures fail
|
|
before backend code is entered.
|
|
|
|
The contract includes opaque surface and texture handles, validated RGBA8
|
|
upload, a deterministic mock present serial, and cleanup status. It does not
|
|
yet include buffers, queues, shaders, fences, swapchains, display ownership,
|
|
hardware contexts, or hardware presentation.
|
|
|
|
## Backend contract
|
|
|
|
Backends receive only validated inputs. A backend must populate capability
|
|
flags from evidence, never from platform names or compile-time assumptions.
|
|
Future mutating operations must include bounded lifetimes, timeout behavior,
|
|
idempotent cleanup, and a safe fallback before entering the public API.
|
|
|
|
The mock backend is deterministic and advertises only:
|
|
|
|
- backend availability;
|
|
- non-rendering operation;
|
|
- host-test support;
|
|
- bounded surface and texture lifecycle;
|
|
- packed host-memory upload and non-graphical present-state tracking.
|
|
|
|
It allocates no display or GPU resource. A present validates ownership,
|
|
dimensions, format, and upload state, then records a serial and FNV-1a content
|
|
hash for test inspection.
|
|
|
|
## PS5 discovery boundary
|
|
|
|
The PS5 probe has two layers:
|
|
|
|
- portable logic that iterates an immutable symbol-name table and records only
|
|
`resolved: true|false`;
|
|
- a PS5 loader shim limited to `dlopen`, `dlsym`, and `dlclose`.
|
|
|
|
Resolved addresses are compared with `NULL`, then discarded. They are never
|
|
logged, returned, cast to a function type, or invoked. The probe does not call
|
|
even apparently read-only exports because their signatures and ABI remain
|
|
unverified.
|
|
|
|
## Phase-0.5 startup boundary
|
|
|
|
The stock SDK v0.41 `crt1.o` is outside the trusted runtime boundary: its
|
|
reachable `_start` graph performs kernel credential and syscall-bound writes
|
|
and initializes a module-capable rtld. A compiler trace proves that
|
|
`-nostartfiles -nodefaultlibs` can omit that object, but the exact public loader
|
|
caller needed to prove entry stack, argument ownership, safe return, cleanup,
|
|
and crash behavior is absent from the pinned local evidence.
|
|
|
|
Accordingly, `CHIMERA_GFX_BUILD_PS5_MINIMAL_STARTUP=ON` always fails
|
|
configuration. There is no candidate source or artifact. The design may be
|
|
reopened only after the evidence listed in
|
|
`docs/runtime/minimal-crt-feasibility.md` is available and reviewed.
|
|
|
|
Artifact execution policy is a separate, fail-closed boundary. Static
|
|
eligibility requires `execution_eligible=true` and a SHA-256 absent from the
|
|
permanent denylist. Passing that layer never grants execution authority.
|
|
|
|
## Phase-0.6 controlled runtime boundary
|
|
|
|
Phase 0.6 identified the exact installed Payload Manager v0.3.1 and
|
|
`ps5-payload-dev/elfldr` v0.23. The normal SDK CRT is acceptable for design
|
|
only when every temporary effect is documented and bounded; `__patch_init`
|
|
alone is no longer an automatic blocker.
|
|
|
|
The exact chain is not bounded: ptrace single-step loops and detached payload
|
|
runtime have no deadline, cleanup and credential failure paths are unresolved,
|
|
Payload Manager launch is not SHA-256-bound, and manager upload writes
|
|
persistent storage. The historical Phase-0.6 profile therefore remained
|
|
`execution_eligible=false` and no Phase-0.6 lifecycle artifact exists.
|
|
|
|
Future consumers must validate the artifact manifest, permanent denylist,
|
|
controlled runtime profile, exact device firmware, exact local bytes, and
|
|
budgets together. The static gate always returns
|
|
`execution_authorized=false`. See ADR-0010.
|
|
|
|
## Phase-0.7 hardened deployment boundary
|
|
|
|
Phase 0.7 preserves the Phase-0.6 record and creates new private hardened
|
|
runtime artifacts. elfldr now bounds ptrace and child lifetime, restores and
|
|
checks all credential fields, centralizes cleanup, reaps every controlled
|
|
child, and rehashes received payload bytes. The controlled Payload Manager
|
|
hashes and streams one no-follow file descriptor and is compiled for one exact
|
|
firmware/artifact identity.
|
|
|
|
The lifecycle payload uses normal SDK v0.41 startup, sends one notification,
|
|
and calls `_exit`. It contains no GNM, VideoOut, SDL, payload-network, retry,
|
|
or autoload path. Complete source/map/disassembly callgraphs remain part of the
|
|
audit because static linkage means imports alone are insufficient evidence.
|
|
|
|
The current architecture state is
|
|
`READY_FOR_HARDENED_RUNTIME_DEPLOYMENT` offline only. It permits packaging and
|
|
review; it does not permit connection, installation, transfer, or execution.
|
|
See ADR-0011.
|
|
|
|
The candidate runtime module name `libSceGnmDriver.sprx` is an inference from
|
|
the public stub filename and the SDK's documented dynamic-loading convention;
|
|
it is not treated as proven on any firmware.
|
|
|
|
## Phase-0.9A anti-brick design boundary
|
|
|
|
Phase 0.9A adds only an offline transaction contract and a virtual host
|
|
simulator. It does not add an installer, PS5 path, device transport, target
|
|
artifact or production runtime code. The simulator holds logical objects,
|
|
mounts, hashes and writes in memory and marks every result as non-hardware
|
|
evidence.
|
|
|
|
Hardened elfldr and controlled Payload Manager have independent transactions,
|
|
backups, target mappings, approvals, switches and rollbacks. No combined action
|
|
or component order is inferred. The lifecycle probe is excluded. In-place
|
|
overwrite and a two-rename interval with a missing live name are forbidden.
|
|
All fourteen PS5 power-loss boundaries and all filesystem atomicity and
|
|
durability properties remain `UNPROVEN`; any one blocks installation. See
|
|
`docs/runtime/phase-0.9-installation-transaction-design.md`.
|
|
|
|
## Phase-0.9B bounded observer boundary
|
|
|
|
Phase 0.9B stops before target implementation. The normal SDK v0.41 `_start`
|
|
graph reaches `__patch_init()` before `main` and therefore violates this
|
|
phase's kernelwrite-free requirement. `-nostartfiles -nodefaultlibs` can omit
|
|
that graph, but the bare-entry return/exit, crash and loader-cleanup contract
|
|
is still unproven.
|
|
|
|
The hardened controlled route also passes `stdio=-1` to the payload. The
|
|
controlled manager provides connect, send and close operations but no receive
|
|
operation; the loader's fixed exit response carries no payload data. Therefore
|
|
there is no proven non-persistent observer output channel. No observer source,
|
|
target, ELF, map or artifact package exists. Host-only mocks validate the
|
|
result contract and fail-closed BLOCKED record, not PS5 behavior. See
|
|
`docs/runtime/phase-0.9b-bounded-observer-design.md`.
|
|
|
|
## Phase-0.9C observer execution-feasibility boundary
|
|
|
|
Phase 0.9C closes the offline design question with
|
|
`BLOCKED_MULTIPLE_FOUNDATIONAL_CONTRACTS`. The loader sets `RIP`, `RDI`, and a
|
|
synthetic return address, but it does not prove entry stack alignment or a
|
|
safe post-return continuation. Normal SDK startup reaches `__patch_init`;
|
|
omitting that runtime also removes the only reviewed syscall, import, time,
|
|
heap, and libc initialization path. No safe normal/error/deadline exit and
|
|
joint cleanup contract remains.
|
|
|
|
The host-only D1 model defines a fixed 4096-byte caller-owned record with
|
|
nonce/request binding, two firmware fields, capability bitmaps, checksums,
|
|
deadline, cleanup status, and a final completion marker. Hardened elfldr does
|
|
not own such a buffer: its current `payloadout` lives in the child mapping.
|
|
The controlled manager connects, sends, and closes without receiving; the
|
|
loader discards wait status. The protocol is therefore a future contract, not
|
|
a target implementation or output channel. No observer source, target,
|
|
artifact, map, package, device operation, or authorization exists.
|
|
|
|
## Phase-0.9D existing-stack readback boundary
|
|
|
|
Phase 0.9D does not revisit target startup or create an observer. It audits the
|
|
already present elfldr and Payload Manager request surfaces. Their file reads
|
|
feed listing, metadata, hash-before-launch, or host-to-elfldr transmission; no
|
|
route frames and returns an arbitrary file to the host. The result is
|
|
`BLOCKED_NO_READBACK_PATH`.
|
|
|
|
The host backup state machine therefore remains a contract only: two separate
|
|
exclusive local files, exact byte counts, close/reopen/hash, and full
|
|
size/hash/byte comparison. Matching copies could establish
|
|
`OFF_DEVICE_BACKUP_VALID`, never `RECOVERY_PROVEN`. Current live component
|
|
paths, external exploit repeatability, durable restoration, and power-loss
|
|
recovery remain unproven. See
|
|
`docs/runtime/phase-0.9d-readback-feasibility.md`.
|
|
|
|
## Phase-0.9E external bootstrap boundary
|
|
|
|
The architecture before hardened elfldr is not locally source-bound. The
|
|
bounded inventory found an exact Y2JB-named ZIP and byte-exact opaque `SIECAF`
|
|
member, but no acceptable parser, exact-used deployment evidence, port-9020
|
|
listener source/binary, or exact sender implementation. The port-9020 material
|
|
in the elfldr README describes only a host stream into a pre-existing
|
|
rudimentary loader; it is not that loader's protocol or implementation.
|
|
|
|
Consequently host-to-memory, filesystem staging, entrypoint selection,
|
|
duplex output, crash cleanup, post-reboot restart, and independence from
|
|
elfldr and Payload Manager remain `UNPROVEN`. Phase 0.9E adds no parser,
|
|
emulator, target code, client, rescue payload, or artifact. Its fail-closed
|
|
classification is `BOOTSTRAP_IMPLEMENTATION_MISSING`.
|
|
|
|
## Phase-0.9E-R official-release correlation boundary
|
|
|
|
The local outer Y2JB ZIP is not byte-identical to any current official
|
|
`Gezine/Y2JB` release asset: all official sizes and GitHub SHA-256 digests
|
|
differ. Official tag source establishes a dynamic Remote JS Loader that seeks
|
|
port 50000 and evaluates bounded JavaScript, while 9020 appears only in sender
|
|
examples. The embedded port-9021 elfldr has no source or generator in release
|
|
1.6. These source facts do not bind the local opaque `SIECAF` backup or prove
|
|
deployed behavior. Classification remains `LOCAL_BACKUP_NOT_CORRELATED`; no
|
|
Phase-0.9F design gate opens.
|
|
|
|
## Phase-0.9E-R2 inner-correlation boundary
|
|
|
|
The local MediaFire URL and object page are now metadata-bound, but their
|
|
maker/source is not. A read-only host parser, bound to the public fixed-width
|
|
SIECAF header layout, fingerprints header, metadata, and hash tables without
|
|
decrypting or extracting content. The local inner differs from both the
|
|
mandatory official Y2JB 1.6 4.03 inner and one date/size-motivated community
|
|
autoloader inner in complete bytes and normalized structure. Classification is
|
|
`LOCAL_BACKUP_UNCORRELATED`; the parser and fingerprints create no bootstrap,
|
|
target, recovery, deployment, or runtime evidence.
|
|
|
|
## Adapter boundaries
|
|
|
|
### RetroArch
|
|
|
|
Phase 1.0A is implemented in a separate private `chimera-retroarch` fork. It
|
|
links the real RetroArch frontend/runloop, a PS5 platform frontend and a
|
|
deterministic static software smoke core. The headless profile uses null
|
|
drivers; the second profile links RGUI and RetroArch's SDL2 video, input and
|
|
audio drivers to the public PS5 SDL fork. Neither profile uses
|
|
`libchimera-gfx`, GNM, a hardware-rendered libretro context or dynamic cores.
|
|
Target artifacts remain ignored local outputs with execution eligibility
|
|
disabled. Later hardware-rendered cores still require proven context,
|
|
synchronization, memory-ownership and shader contracts.
|
|
|
|
Phase 1.0E adds a narrowly scoped result path to the Phase-1.0D diagnostic
|
|
profile. Hardened elfldr's legacy raw-ELF route duplicates the accepted socket
|
|
to payload stdout, so the payload can emit fixed 64-byte checksummed D-stage
|
|
frames without creating a target-side socket or connection. The host performs
|
|
one send and a write-half-close, then bounded receive on that same connection.
|
|
This contract does not apply to the controlled route (`stdio=-1`), grants no
|
|
device authority, and leaves the SDK CRT, SDL/VideoOut path and GNM boundary
|
|
unchanged.
|
|
|
|
Phase 1.0I adds no runtime component. It binds the consumed H trace to the
|
|
existing RetroArch/SDL source, linker map and disassembly. The architecture
|
|
boundary remains at the failed first VideoOut submit: the exact call tuple is
|
|
known, while errno, argument semantics, visible presentation and cleanup are
|
|
not. The write-firewall status contains more state than the transmitted D12
|
|
frame, so future observability must expose the exact operation before another
|
|
artifact can be reviewed.
|
|
|
|
Phase 1.0P similarly adds no runtime component. It binds the consumed O result
|
|
to the exact M source, map, dynamic relocations and disassembly. The submit
|
|
failure site and D12-before-D04 source order are proven, while VideoOut
|
|
argument/layout semantics and the root cause remain unresolved. A future
|
|
protocol needs a distinct post-D04 terminal; accepting the earlier D12 would
|
|
confuse a shutdown request with lifecycle completion.
|
|
|
|
Phase 1.0Q adds only public-source provenance. The PS5 submit declarations,
|
|
opaque records and constants all trace to one SDL commit lineage; the SDK has
|
|
export names only and the relevant official ports consume that same SDL fork.
|
|
PS4's similar API is retained as analogue evidence, never promoted to a PS5
|
|
contract. The architecture therefore remains blocked before any argument or
|
|
ownership-state experiment.
|
|
|
|
Phase 1.0R adds no runtime path. It proves that the pinned PS5 SDL2main does
|
|
not create an application or display context: its only pre-entry action is
|
|
splash hiding and its exit action occurs after `SDL_main` returns. SDL's video
|
|
backend already performs the splash action before VideoOut open in the tested
|
|
artifact. Direct and Payload Manager transfers converge on the same hardened
|
|
elfldr spawn routine. PacBrew is packaging metadata, and the distinct
|
|
`hbldr`/shsrv launcher implementation remains unbound; it cannot be promoted
|
|
to a target fix.
|
|
|
|
Phase 1.0S binds that official implementation and proves an architectural
|
|
difference: hbldr substitutes an ELF into a SystemService-launched BigApp,
|
|
whereas raw elfldr substitutes it into SceSpZeroConf. It does not prove that
|
|
the BigApp supplies usable VideoOut ownership on firmware 9.60. The available
|
|
hbldr path is also outside the Chimera execution model because it requires a
|
|
device file and includes BigApp termination, runtime kernel/ptrace mutation,
|
|
unbounded waits and, in current releases, possible persistent fake-app setup
|
|
under `/system_ex`. It remains research evidence, never a runtime dependency.
|
|
|
|
Phase 1.0T adds only an offline sanitization and classification boundary for
|
|
text that an operator might supply later. It has no socket implementation.
|
|
The model drops serial, model, temperature, frequency, raw transcript and
|
|
unknown paths; it fingerprints `help` command names and labels `sum` as a
|
|
non-cryptographic 16-bit checksum. Its output schema hard-codes
|
|
`exact_identity=false`. Any future collector remains a separately reviewed,
|
|
inactive design problem because merely connecting already spawns shsrv's
|
|
shell and emits sensitive greeting data.
|
|
|
|
Phase 1.0U adds no runtime component. It records a bounded local artifact
|
|
inventory and distinguishes official source, a host telnet wrapper and an
|
|
unpinned package recipe from a deployed target binary. No candidate was found
|
|
inside the declared scope. That negative result cannot be generalized to the
|
|
whole host or device and cannot satisfy the launch-context identity gate.
|
|
|
|
Phase 1.0V adds a host-only byte-stream model, not a transport. An incremental
|
|
Telnet filter feeds the existing Phase-1.0T sanitizer under fixed byte/chunk
|
|
limits and a one-shot state machine. The CLI exposes only an explicit offline
|
|
stdin mode and literal metadata paths. Logical buffer clearing limits retained
|
|
application state but does not prove physical memory erasure. No network or
|
|
device boundary is crossed.
|
|
|
|
Phase 1.0W hardens the collector's Telnet, chunk, path and metadata edges and
|
|
places an immutable policy object above it. The policy consumes two synthetic
|
|
matching records and produces a frozen session-plan value. A test-only fake
|
|
transport models one open/command/receive/close sequence. Neither component
|
|
contains a live transport, persistence path or executable CLI, so the network
|
|
boundary remains a separately blocked implementation concern.
|
|
|
|
Phase 1.0X places exclusive host evidence and one-shot orchestration around
|
|
that immutable plan. The adapter and monotonic clock are dependency-injected;
|
|
the repository provides only fakes. Receipt creation precedes adapter open,
|
|
and sanitized output follows collector completion. Both files use exclusive
|
|
leaf creation, file flush, close and reopen verification. There is deliberately
|
|
no command-byte layer or live prompt boundary: those contracts remain blocked
|
|
until exact source framing is established. File flush does not prove containing
|
|
directory durability, and pre/post deadline checks cannot preempt a blocking
|
|
adapter implementation.
|
|
|
|
Phase 1.0Y separates protocol evidence into legacy raw and current
|
|
`libtelnet`/NVT source families. The framing model is deliberately independent
|
|
of X orchestration: it transforms only supplied synthetic bytes and reports
|
|
prompt candidates without creating boundaries for a live adapter. This avoids
|
|
silently binding current-source CRLF and negotiation behavior to an unknown
|
|
deployed version. A later passive contract can use plain LF, which both audited
|
|
families accept, while treating all received negotiation and incomplete output
|
|
as bounded failure.
|
|
|
|
Phase 1.0Z implements that later layer as a pure data contract. An immutable,
|
|
target-free object contains one ASCII/LF batch and its exact completion policy.
|
|
The receive accumulator consumes supplied chunks, rejects IAC and delegates
|
|
sanitization to V, but it cannot seal on prompt or EOF. An externally injected
|
|
synthetic hard-deadline event is the only sealing boundary. This separation
|
|
keeps byte formatting and completeness checks independently testable without
|
|
quietly introducing a network adapter, clock or live completion claim.
|
|
|
|
Phase 1.0AA composes X evidence and Z framing behind a closed fake boundary.
|
|
Unlike X's abstract injected adapter, AA accepts only exact built-in fake types;
|
|
there is no protocol a live implementation can satisfy. A synthetic event
|
|
advances an explicit fake clock, while receipt, one-batch send, deadline seal,
|
|
close and sanitized output form a testable order. This proves model composition
|
|
only, not socket preemption, deployment identity or firmware behavior.
|
|
|
|
Phase 1.0AB separates OS feasibility from implementation. It binds the local
|
|
Python `_socket`, `select`, `socket.py`, `selectors.py` and monotonic-clock
|
|
identities, then validates only synthetic syscall ordering. The required future
|
|
architecture is one nonblocking descriptor, readiness before every I/O call,
|
|
an explicit partial-send offset, a single absolute deadline and `finally`-based
|
|
local cleanup. Runtime source makes that sequence feasible but cannot turn
|
|
selector timeouts into a hard scheduling guarantee or attest remote cleanup.
|
|
|
|
Phase 1.0AC realizes that ordering behind a dormant, target-free component.
|
|
Only the exact built-in fake facade and fake clock are accepted, so no live
|
|
implementation can satisfy an injected protocol. The adapter composes the Z
|
|
batch/result contract with synthetic create, readiness, partial progress,
|
|
deadline and close outcomes. This is integration-test infrastructure only: it
|
|
adds no socket factory, address model, activation path or firmware evidence.
|
|
|
|
Phase 1.0AU closes the subsequent result-channel source-feasibility question.
|
|
The official SDK exposes every primitive signature needed by the AT ownership
|
|
model, but official shsrv composes none of them into a bounded channel. Its
|
|
worker closes inherited descriptors and its service restarts automatically.
|
|
The architecture therefore permits only a target-free canary contract next;
|
|
live channel code, a target build and every device action remain outside the
|
|
boundary.
|
|
|
|
Phase 1.0AV turns the unresolved launch-context claim into a causal offline
|
|
contract. It holds payload and protocol identity constant while varying only
|
|
the launcher identity, and requires independent one-shot approvals. Results
|
|
are comparable only after submit, D04 and a distinct cleanup terminal. The
|
|
contract deliberately cannot interpret a zero submit return as presentation;
|
|
it contains no target implementation or activation surface.
|
|
|
|
Phase 1.0AW maps that contract onto exact historical source. A future single
|
|
ELF needs a new magic and a D14 terminal guarded by completed RetroArch teardown,
|
|
an empty initialized mask, valid cleanup order and an independent cleanup-error
|
|
counter. Official v0.7 can duplicate raw stdout into its BigApp, but that source
|
|
candidate does not make its unbounded and mutating launcher admissible.
|
|
|
|
Phase 1.0AX freezes the proposed wire and cleanup semantics independently of
|
|
target code. D14 reuses the 64-byte frame's raw/result/auxiliary fields to bind
|
|
initialized, cleaned, cleanup-error and `rarch_main` state. Only canonical D14
|
|
after D07 and D04 is terminal. This host reference cannot establish that a
|
|
future RetroArch build or firmware implements the same behavior.
|
|
|
|
Phase 1.0AY fixes repository lineage as another trust boundary. The selected N
|
|
tip is an exact descendant of M with unchanged diagnostic target sources and an
|
|
inactive runner. Future source work must occur in a separate worktree so the
|
|
historical loader checkout and consumed-run records are not rewritten.
|
|
|
|
### SDL2
|
|
|
|
The future SDL2 integration will be a renderer backend that can satisfy
|
|
`SDL_RENDERER_ACCELERATED`; it will not replace the existing PS5 window,
|
|
VideoOut, audio, input, IME, or filesystem backends. This separation avoids
|
|
forking unrelated platform support. The compiled scaffold reports acceleration
|
|
unavailable and requires the existing software fallback.
|
|
|
|
## Offline Phase-1 boundary
|
|
|
|
The VideoOut candidate is a separate CMake option that is off by default and
|
|
valid only under the PS5 toolchain. Project code calls public SDL2 APIs; a
|
|
staged Zlib-licensed overlay removes upstream keyboard/IME initialization.
|
|
SDL retains ownership of its public-source VideoOut declarations and layouts.
|
|
The target does not link SDL2main, so the firmware gate runs before SDL or
|
|
system-service initialization. See ADR-0006 and `docs/phase1/`.
|
|
|
|
## Failure model
|
|
|
|
All unknown firmware, missing exports, version mismatches, partial
|
|
initialization, or unsupported requests fail closed. Later hardware phases must
|
|
add bounded waits, watchdog-visible progress, crash logs without secrets or
|
|
addresses, and cleanup that can be audited independently of the success path.
|