SENTR now lets a person act on an alert: revoke permissions, move selected assets to a destination they control, create and recover a private destination, use a small local burner wallet, and check a recipient before sending. These are working browser flows with real transactions on local forks of Robinhood mainnet and Ethereum. The main wallet signs its own transactions. SENTR never requests or stores its private key.

The original helmet, moving eye, watch inbox, encrypted push, wallet discovery, public observation feed and English/Chinese pages remain. Home now leads with “Watch, then act” and a visible “Secure my wallet” button. A new Protect page presents five tools. Nothing was deployed, pushed or sent as a transaction to a public chain.

The remaining limits matter: automatic moves cover native coin and standard ERC-20 balances, history discovery is bounded, and a stealth destination does not hide the public transfer from the source wallet. This is a tested local build, not a safety guarantee or an independent security audit.

## What changed

1. **Act from the alert.** Approval cards open an exact transaction review for `approve(spender, 0)` or `setApprovalForAll(spender, false)`. Poisoning cards confirm a local blocklist entry without a signature. Outgoing-transfer cards open protection on the matching chain. Main-wallet account and network are checked before signing.
2. **Secure my wallet.** Find current permissions and balances, choose standard tokens and a destination, review every call, then sign. Every discovered active permission is included for revocation. Dust is skipped. Native coin moves last after simulated gas costs and an extra reserve. The app checks `wallet_getCapabilities`, uses `wallet_sendCalls` where supported, or submits sequentially with progress and receipts. Rejections, reverts and ambiguous submissions stop safely; an uncertain submission is not blindly retried. Receipts remain downloadable and can be checked again after reload.
3. **Private escape.** The real secp256k1 stealth derivation and recovery approach was ported from the read-only YONDER implementation. WebCrypto generates the local spend/view secrets and encrypts them with a passphrase. A one-time destination and its recovery record are saved in that encrypted vault. The user exports a backup before moving assets, can scan saved destinations, and can recover received tokens/native coin through the local signer. Importing a backup after clearing browser storage was exercised on both forks.
4. **Burner shield.** Generate a separate local wallet, give it a name, save an encrypted backup, and fund a chosen small amount from the main wallet. Creation registers a watch. The burner appears in the site's EIP-6963 picker and exposes an EIP-1193 provider for native sending, token approval and generic contract calls, including a tested mint. Sweep revokes discovered residual approvals and returns standard tokens/native coin. Burn requires a confirmed sweep, a fresh balance/permission check and backup acknowledgement. It deletes the browser's encrypted key record and explicitly shows the remaining gas reserve. Downloaded backups still contain that key.
5. **Safe Send.** Compare the complete recipient with successful outgoing chain history, a local address book and the local poison list. Same first four and last four hex characters with a different middle blocks signing. Incoming dust never becomes a trusted contact. First-time recipients offer a small test transaction; the remaining amount is reduced after that test confirms. Users must independently verify the complete address.
6. **Less retained data.** Choose 7, 30 or 90 days at registration or update existing watches. The whole watch, subscription, inbox and recipient/spender history expire together. API requests and scanner passes prune expired data. Browser data expires on use and its timer. One action, after acknowledging key backup, removes this browser's watches and local SENTR data, including its vault, receipts, address book, blocklist, push registration and caches. Other open SENTR tabs lock their in-memory vault. A partial server failure retains the capability needed to retry deletion.

The application stores no emails or IP logs and adds no analytics. Watch data remains limited to the address, selected chains, optional push subscription, hashed inbox capability, scan state and bounded alert/history records. Vault keys do not automatically expire, so watch expiry does not strand funds. The Trust page links to the actual client encryption code and a generated, verified copy of the retention/API implementation. Hosting, RPC and push infrastructure may retain connection metadata independently.

## Signing and local keys

Transaction construction, calldata encoding, simulation and fee estimation happen in the browser. The review shows network, source, destination, full spender/contract addresses, readable amounts, raw calldata, permission changes and gas limits. Plans expire after five minutes. Account/network changes invalidate submission. Token transfer and revoke effects are checked after successful receipts; a token that returns success without the expected result is reported as a verification failure.

Only newly generated burner and stealth recovery keys are handled locally by SENTR. They are encrypted at rest using AES-256-GCM with a fresh 12-byte IV, a 16-byte salt and PBKDF2-SHA-256 with 310,000 iterations. The minimum passphrase length is 12 characters. The vault locks after five minutes of inactivity. Unlocking keeps secrets in memory until lock, idle expiry or cross-tab deletion. Export is ciphertext; import checks its structure and key/address consistency. A compromised device or compromised page can still expose unlocked secrets. The app is not an audited hardware wallet.

The burner provider only signs for its own local address after explicit consent. It does not implement arbitrary signing methods. It is available to this site's own flows, not injected into unrelated websites. The main wallet continues to use its external provider.

The stealth math follows the [ERC-5564 scheme](https://eips.ethereum.org/EIPS/eip-5564), but this private escape intentionally publishes no announcement. The recovery record is in the encrypted backup. This avoids a public announcement tying the destination to the user's stealth identity. It does not hide the source-to-destination transfer: amounts, assets and timing remain public and someone watching the old wallet can follow that transfer. There is no public announcement to rediscover if the backup is lost.

Wallet batching follows [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792); provider discovery uses [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963). Capability absence can fall back to sequential submission. User rejection and uncertain submission do not trigger a second attempt through another method.

## Tests and evidence

| Check | Result | Evidence |
| --- | --- | --- |
| Unit and API suite | 35 passed, including 19 new protection/retention cases | [unit-tests.txt](evidence/v2/unit-tests.txt) |
| Robinhood mainnet fork, chain 4663 | 16 browser flow checks; 9 main-wallet and 8 local-wallet transactions | [fork-browser-proof.json](evidence/v2/fork-browser-proof.json) |
| Ethereum mainnet fork, chain 1 | Same 16 checks and 17 transactions at mobile width | [fork-browser-proof.json](evidence/v2/fork-browser-proof.json) |
| Original browser regression | 31 cases passed | [original-browser-regression.txt](evidence/v2/original-browser-regression.txt) |
| Local links and inspectable privacy source | 295 references verified; source copy matches implementation | [local-links.json](evidence/v2/local-links.json) |
| JavaScript syntax and copy restrictions | All source files checked with `node -c` and copy scanner | [final-check.txt](evidence/v2/final-check.txt) |
| New screens | 32 static EN/zh images and 36 transaction-flow images at 1440/390, plus six focused receipts | [screenshots.json](evidence/v2/screenshots.json), [detail-captures.json](evidence/v2/detail-captures.json) |
| Visual inspection | Final image hashes and review notes | [visual-review.json](evidence/v2/visual-review.json) |

The fork run uses mainnet state forked into Anvil, newly deployed local fixture contracts and injected EIP-6963 providers. Browser RPC requests are routed exclusively to those local forks. The main-wallet fixture account signs through Anvil; browser code never receives its private key. Locally generated burner and recovery wallets sign real transactions client-side. Each chain also delivers five authenticated, encrypted Web Push payloads to the local receiver. The receiver validates VAPID and decrypts the payloads. No browser script errors were recorded.

The final UI rerun is recorded in [fork-browser-final.json](evidence/v2/fork-browser-final.json). It repeats both complete fork flows and asserts that batch receipt links appear once. [receipt-render-check.json](evidence/v2/receipt-render-check.json) also verifies the final renderer against saved fork receipts.

The batch test uses an injected EIP-5792 adapter that issues actual ordered, non-atomic Anvil transactions. It proves capability negotiation, protocol handling, receipts and resulting on-chain effects. It does not prove atomic execution or compatibility with every commercial wallet. Unit cases additionally exercise rejection without fallback, partial batch receipts, reverted transactions, stale plans, ambiguous submission, token-effect mismatch, cancellation between steps, vault tampering and unsupported signing requests.

Screenshots use the mandated `node ~/shot/shot.mjs` helper against a Python local server. Transaction screenshots replay sanitized DOM snapshots saved from the successful fork browser flows. Live form values are preserved; password/file values and execution scripts are excluded. They are presentation evidence alongside the live browser assertions, not a substitute for those assertions. Scrollable review dialogs have additional focused receipt captures. Some section captures include the fixed navigation where the screenshot helper scrolled the selected section. No private keys, inbox capabilities or signed raw transaction payloads are in the public evidence.

The original round-one report is preserved at [evidence/v1/BUILD-REPORT-round1.md](evidence/v1/BUILD-REPORT-round1.md). Its detector, store, race, retry, reorganisation and push tests remain in the suite. The earlier dependency audit is historical, not a fresh security certification. This round adds no npm dependencies; the existing Ethers 6.17.0 ESM build and MIT license are vendored locally for the browser.

## Coverage and operational limits

- Protection runs on one selected chain at a time. The default history window is the latest 64 blocks. A user can request a range of up to 2,000 blocks, repeat earlier ranges and supply extra token contracts. Saved discoveries and watch history are retained and current permissions/balances are rechecked. This is not a complete lifetime approval or recipient index.
- Automatic moves cover native coin and standard ERC-20 tokens. Operator permissions can be revoked, but NFT/ERC-1155 asset transfers are not automatically swept. Unreadable or nonstandard contracts are reported and skipped. Off-chain permits, internal native transfers requiring traces, unsupported networks and unobserved approvals can be missed.
- Dust thresholds are 1,000 raw token units and 0.000001 native coin. These do not estimate market value. Gas reserves remain at the source, including after burner sweep. Burn explicitly shows that reserve and requires a backup; later deposits and unknown assets require manual recovery from the backup.
- A compromised main private key can still race every transaction. Revocation cannot recover funds already taken. Receipt inclusion is not finality. The watcher retains its two-block confirmation lag and bounded reorganisation handling.
- Encrypted payload delivery is proved through the local push receiver. Actual operating-system notification display through a commercial push service on a final HTTPS origin remains unproved.
- No public deployment, production credential provisioning, independent audit or unrestricted-scale capacity test was performed. The local pilot limits and production prerequisites below still apply.

## Local runtime and boundaries

The existing API was reloaded after the implementation and remains loopback-only on port 8787. The existing scanner timer remains active. No service environment or private key was printed or changed. [local-runtime.json](evidence/v2/local-runtime.json) records the healthy local state. Watch expiry is applied on API/scanner execution; it does not depend on the user's browser staying open. Infrastructure logging is separate from application logging.

YONDER was read only to port the stealth calculation. Its unchanged reference fingerprint is in [frozen-reference.json](evidence/v2/frozen-reference.json). SNAG was not modified. All written files are inside the authorized workspace, under SENTR or its sibling test/runtime area. Checkpoints are recorded in [checkpoints.json](evidence/v2/checkpoints.json). The temporary Python preview server is stopped after final verification, while the pre-existing API and scanner remain running.

## Exact launch prerequisites

Nothing in this section was deployed or installed outside the authorized workspace.

1. Choose the production HTTPS origin. Serve only the static allowlist generated by `node scripts/export-static.mjs` from `build/static`. Keep the API, dependencies, tests, evidence and private runtime directory outside the public document root. Route `/api/*` on that origin to the loopback Node API. Preserve the Authorization header. Disable reverse-proxy access logging if the production privacy statement is to remain accurate; application logging is already disabled.
2. Set `SENTR_SITE_ORIGIN` to that exact origin in the private environment file. Keep `SENTR_VAPID_PUBLIC` and `SENTR_VAPID_PRIVATE` paired and stable. Set `SENTR_VAPID_SUBJECT` to an owned HTTPS contact URL or monitored mailto contact. Changing VAPID keys requires fresh browser subscriptions. Never put the private key in frontend JavaScript.
3. For a shared store, create a PRIVATE Vercel Blob store through the account dashboard and supply its `BLOB_READ_WRITE_TOKEN` securely to both the VM scanner and API. Set `SENTR_STORE=blob` in both environments. The SDK adapter is present, but a real Blob store was not provisioned or credential-tested. No Vercel CLI was run. New watches can start in an empty shared store; migrating existing private local records requires an explicit controlled transfer and must not be done through public static files. Alternatively, a single VM can keep the already-tested file store on durable storage with the API and watcher sharing `SENTR_DATA_DIR`.
4. Install the provided user units from `ops/` for reboot persistence. The current units are transient runtime units, deliberately created through the user manager without writing user configuration outside this workspace. They do not survive a VM reboot. An operator must replace them with persistent units and arrange user-manager startup. The templates already contain this workspace's absolute paths. Stop the runtime units before replacing them, then enable the API service and scanner timer. Run the API and scanner with the same private environment.
5. Repeat watch, actual-browser push, alert-link, revoke and unwatch checks through the final HTTPS origin. Check cache freshness, delayed-scanner reporting and recovery after a service restart. Public RPC throughput and whole-document Blob updates must be capacity-tested before accepting more than the local pilot limit.
6. Add the approved mark. A token contract is not needed to monitor wallets. Once a real $SENTR contract exists, `node scripts/sentr-live.mjs --ca 0x... --dry-run` verifies chain 4663, deployed code and the SENTR symbol. Remove `--dry-run` to change only local token configuration. Optional `--buy` and `--lock` values must be HTTPS links. `--off` restores the prelaunch display. This script never deploys. After changing the mark or token configuration, regenerate the pages when needed and rerun `node scripts/export-static.mjs` so the public static copy contains the final assets and settings.

## Reproduce locally

Run these from this SENTR directory with the existing dependencies installed:

```sh
python3 scripts/build-pages.py
npm test
npm run check
node scripts/v2-verify.mjs
```

For browser checks, run the required preview server in one terminal:

```sh
python3 -m http.server 8786 --bind 127.0.0.1
```

Then, in another terminal:

```sh
npm run test:fork:v2
node tests/browser.mjs
npm run screenshots:v2
node scripts/v2-detail-captures.mjs
node scripts/v2-receipt-review.mjs
```

The fork test needs Anvil, the existing local Playwright installation and reachable public fork RPCs. Tests keep scratch files in the authorized sibling ops directory. Stop the Python preview server afterward. Export the static allowlist with:

```sh
node scripts/export-static.mjs
```

`build/static` excludes the API implementation, dependencies, tests, evidence and private runtime data. Export only writes the local delivery copy. It does not deploy anything.
