# Wallet outbox archive restore rehearsal

This command accepts the exact `data.ndjson.gz`, `manifest.json`, and `_SUCCESS`
files from one private COS batch. It verifies `_SUCCESS` before opening MySQL,
then checks manifest/data SHA-256, COS CRC64-ECMA, gzip integrity, row count,
cursor bounds, UTC time bounds, unique source PKs, and both delta control totals.

On a pay node, place the three files in a mode `0700` temporary directory using
the attached `HyAppWalletOutboxArchiveRole`; do not copy the operator CSV key or
long-lived credentials onto the node. Keep the files mode `0600` and remove the
temporary directory after the report is captured.

Verify files without database writes:

```bash
go run ./services/wallet-service/cmd/wallet-outbox-archive-restore \
  --data /secure/batch/data.ndjson.gz \
  --manifest /secure/batch/manifest.json \
  --success /secure/batch/_SUCCESS
```

Restore to the standard isolation contract. The DSN is read from an environment
variable so it does not appear in shell history or process arguments. The exact
database check prevents a mistyped DSN from silently selecting another schema.

```bash
read -rsp 'Restore MySQL DSN: ' WALLET_OUTBOX_ARCHIVE_RESTORE_DSN && export WALLET_OUTBOX_ARCHIVE_RESTORE_DSN
go run ./services/wallet-service/cmd/wallet-outbox-archive-restore \
  --data /secure/batch/data.ndjson.gz \
  --manifest /secure/batch/manifest.json \
  --success /secure/batch/_SUCCESS \
  --restore \
  --expected-database wallet_archive_restore \
  --rehearsal-id rehearsal-20260721-001 \
  --confirm RESTORE:rehearsal-20260721-001:<batch-id>
```

The command creates only these versioned isolation tables:

- `wallet_outbox_restore_rehearsals_v1`: one atomic state/evidence row per rehearsal.
- `wallet_outbox_restore_rows_v1`: wallet_outbox-compatible fields, keyed by
  `(rehearsal_id, app_code, event_id)` and tagged with `source_batch_id`.

`RESTORE_VERIFIED` means the three files were cryptographically verified, every
row was inserted into the isolation table, and SQL readback reproduced the
manifest controls. It is not purge authorization. Any later purge workflow must
also match the computed data/manifest/success CRC values to the owner receipt,
prove all consumer watermarks are beyond the batch cursor, and delete only small
exact `(app_code,event_id)` PK sets under its own concurrency gate.

The batch states are intentionally monotonic: manifest `DATA_VERIFIED` -> marker
`VERIFIED` -> CLI `FILES_VERIFIED` -> transactional `RESTORING` -> persisted
`RESTORE_VERIFIED`. No state in this command maps to `PURGE_ELIGIBLE`.
