# Echo Nearby protocol foundation

The durable object is a signed payment intent. Clients and transports are replaceable.

This draft adds an open, bounded OPI grammar, independent TypeScript and Rust
inspectors, a Rust/WASM build, hostile compatibility vectors and a web/QR test
client. It does not replace existing HAP-1 or Echo Pay signatures, connect a real
settlement rail, or claim production payment readiness.

Read [SPEC.md](SPEC.md) before integrating. In particular, a valid signature is
not funds, identity verification, authority, receipt truth or settlement.

The companion [signed-request profile](REQUESTS.md) adds a second signature:
the receiver requests exact terms, the payer explicitly approves a bound intent,
and either side can compare both objects against a separately retained request
ID. Open `requests.html` for the two-person reference flow. Its local response
journal returns identical signed bytes on retries; it is not global replay
protection. Existing OPI signatures and wire formats are unchanged.

## Exact permission evidence

The original Nearby page now includes **Check authority evidence**. A separate
local Node signer uses the existing HAP gate to sign the exact receiver-requested
intent under a fresh one-use permit. The browser only inspects exported evidence;
no website-to-signer connection is opened. The [readable local signing guide](authority-guide.html)
opens in a separate tab so the checker stays in place. It is included in the
prepared offline copy and independent kit. Read [AUTHORITY.md](AUTHORITY.md) for
the full test-only profile, trust pins and explicit limits.
The evidence is historical, not a current execution capability or settlement proof.


## Run and reproduce

From `nearby-protocol/`:

```sh
npm ci --ignore-scripts
npm run build
npm run check
cargo test --locked --manifest-path rust/Cargo.toml
cargo build --locked --release --manifest-path rust/Cargo.toml
rustup target add wasm32-unknown-unknown
cargo build --locked --release --lib --target wasm32-unknown-unknown --manifest-path rust/Cargo.toml
node tests/interop.mjs
npx playwright install chromium firefox webkit --only-shell --with-deps
npm run browser:all
python3 pack.py --check
```

`npm run browser` runs Chromium by default. For one other engine, set
`ECHO_BROWSER=firefox` or `ECHO_BROWSER=webkit`. Unknown engine names fail;
there is no silent fallback. The all-engine runner reports every engine's result
and exits unsuccessfully if any suite fails.
Screenshots are isolated under `test-results/<engine>/`.
Playwright WebKit is a patched browser build, not installed Apple Safari;
see the [upstream browser notes](https://playwright.dev/docs/browsers#webkit).
The test runner is pinned to Playwright 1.64.0. This includes the upstream
[WebKit service-worker offline-navigation fix](https://github.com/microsoft/playwright/pull/42894);
the same offline-emulation assertions run in every engine without a skip or
an origin-outage substitute.

The build emits the static client under `public/nearby-protocol/`. Serve `public/`
over localhost HTTP for development or HTTPS for devices. No external scripts,
provider calls, accounts or API credentials are needed. Opening the page does
not create a key, access a camera, sign or send an intent. Choose **Prepare offline
copy** before disconnecting. Browser eviction can remove cached software and
local data: keep exported files. The downloadable ZIP contains the source,
verifier sources, prebuilt JavaScript client and licenses; it can be served
independently of Money Musk. Rust and WASM binaries are built from the pinned
source/toolchain; compiler and package downloads are required before an offline
build unless you have already cached them. The existing QR codec is included.

`wasm.mjs` exports `createInspector(wasmBytes)`. Pass the bytes built at
`rust/target/wasm32-unknown-unknown/release/echo_opi.wasm`; the returned function
accepts the same wire and optional context as the TypeScript inspector. It does
not fetch code, derive trusted context from a packet or authorize execution.
The browser lab uses the bundled TypeScript implementation, not downloaded WASM.

Native inspector input on stdin: `{"wire":"<exact canonical JSON>","context":{...}}`.
Omit context for signature-only historical inspection. Reports are JSON. The
process reports data refusals as JSON; callers must check `ok` and `code`, never
infer success from the process exit status.

## Reference-client design and assets

The portable pages reuse the main Money Musk Cyber colors, Echo coin logo,
Space Grotesk, JetBrains Mono and Major Mono Display. Fonts, logo and font
licenses are bundled beside the client and included in its opt-in offline cache
and build manifest. No third-party font or image request is required. Font
licenses are in `client/*-OFL.txt`; the protocol code license does not relicense
the Money Musk name or logo. Independent clients may provide their own branding.

Navigation between the four reference pages works in the standalone kit.
The header's CYBER label describes appearance, not network or security status.
Signing and verification retain explicit text labels; color alone conveys no
result. QR canvases have no decorative overlay. Fields use 16px text and controls
have at least 44px targets. The static grid and header texture have no flashing
or continuous animation. The original `/nearby` page hosts the three interactive clients in its existing site layout, with
the signed exchange selected first. The older balance demo mounts only when
opened; its state and wire format remain separate. The native companion is
optional, and its introduction no longer announces an App Store launch.

The interactive clients adapt the existing Echo Twin core, wireframe shell,
crossed rings and satellites as local SVG. The display follows explicit request,
intent or pair-verification results and names which check it reflects. Refusals
show the reason; a checked request never implies payment authorization. Valid
intents and matching pairs keep a visible **Pending Sync** label. Edits, imports,
page hiding and reload clear the display; context-checked results also clear when
their signed time window ends. Signature-only inspection explicitly leaves the
signer and unchecked context unconfirmed. A brief ring movement respects reduced
motion. There are no frame loops, external assets, WebGL or voice/provider calls.
The visualization only renders results. The separate local authority signer is
described in AUTHORITY.md; no settlement adapter is connected.

The [original Nearby comparison](ORIGINAL-NEARBY-REVIEW.md) records what carries
forward and which authority, identity, transport and settlement integrations remain
separate. Both clients include readable four-question evidence cards and an
altered-copy exercise using the actual verifier. Every retained intent can be
exported with its original bytes after reload.

## Inside the original Nearby page

`components/nearby-protocol-experience.tsx` mounts one same-origin client at a
time with `?embed=1`. Embedded presentation removes duplicate site navigation,
introductory copy and footer. The actual signer, inspectors, QR codec and journals
are the same as the standalone clients. This is presentation reuse, not a sandbox
or a new cryptographic security boundary. A compromised same-origin host remains
inside the threat model.

The typed `embed-host.ts` bridge accepts bounded integer height/scroll messages
only from the currently mounted frame at the exact origin. Unknown fields,
actions, wrong origins/sources and out-of-range dimensions are ignored. No packet,
key, trust pin, verification result or signing command crosses this bridge.
Temporary keys are never lifted into React state. Switching tools unmounts the
previous client, discarding its temporary key and unfinished state; users are
instructed to export first. Navigation inside the exchange scrolls the host page.

The opt-in service worker caches only the portable client directory. It does not
cache the surrounding Next.js site. Embedded mode therefore hides its preparation
button and directs users to open the standalone tools and prepare there before
going offline. If `?embed=1` is opened as a top-level page, the normal standalone
navigation and offline preparation remain visible. The existing SAMEORIGIN and
frame-ancestors response headers remain unchanged.

## Try two devices

Use the iPhone and laptop guide at `client/device-check.html` in the source
checkout, or open `device-check.html` beside the deployed client.
The guide covers both roles, real QR capture, export/import, refusal and offline
reload. It records no results and cannot mark an unperformed test as passed.

1. Load and prepare the client on each device while connected. On the receiver,
   explicitly create a temporary test key and copy its full receiving address.
2. Confirm the recipient address through a channel you trust. On the sender,
   create a different temporary key, paste the recipient and review the exact
   amount and expiry. Sign only after reviewing.
3. Transfer by QR, exported file or copied packet. Importing never navigates to
   a packet URL and never executes a payment. The same bytes can pass through a
   third carrier without modification.
4. On the receiver, enter the independently confirmed recipient and optional
   payer key. Verify. The result stays **Pending Sync — no settlement connected**.
5. Change a copy of the amount and verify: refusal. Try the wrong recipient,
   wrong payer, expiry, denied camera/storage, offline reload and exported-file
   reimport. No path may display final settlement.

The camera/image decoder is bundled JavaScript, avoiding a dependency on native
BarcodeDetector support. Physical iPhone/Android camera interoperability remains
an acceptance test; automated QR pixels and browser emulation cannot certify it.

## Further work, with explicit boundaries

- Stable Echo identity-to-device-key bindings, rotation and recovery.
- Authenticated acknowledgement and confidential-attachment profiles. Signed
  request/intent matching is implemented in the experimental ECHO-REQ/1 profile.
- Independent review and physical acceptance of the local HAP signing adapter;
  a separate enforcement profile for Border Control and settlement.
- A chosen settlement rail with atomic admission, durable idempotency, pinned
  final receipts and outcome-unknown reconciliation. The existing demo log is
  not such a rail. OpenTimestamps is downstream evidence, not settlement.
- Optional NFC tag and local-network adapters with measured platform support.

These are engineering requirements, not features activated by this draft.

## Verification evidence

The protocol corpus contains 155 OPI/request tests plus the authority evidence
and durable-signer tests. The 147 OPI/request cases have TypeScript, native Rust
and Rust/WASM reports; authority evidence currently has one project inspector.
The browser suite exercises 112 checks per engine (31 intent, 43 request-exchange,
16 embedded-host and 22 authority-evidence checks)
at desktop and 390-pixel viewports. CI runs the same suite in Chromium, Firefox
and WebKit and fails if any engine fails. Consult the checks for the exact
revision; coverage declarations are not evidence that a particular run passed.
Browser checks include QR image transfer between isolated
profiles, exported-file byte equality, clipboard/camera denial, corrupt/denied
storage, lost write acknowledgement, duplicate response reuse, payer-key changes,
stale approval while waiting for a lock, request/recipient/amount substitution,
key discard while preserving signed exports, offline reload, and truthful Twin
states after refusal, changed input, expiry and reduced-motion selection. They also
check full-key evidence cards, copy-only tampering and retained exports offline.
Embedded-host checks use the real host bridge and clients to test message
validation, resizing, exports, step navigation, tool replacement and standalone
fallback. The full React/site mount is reviewed separately on the hosted preview. The independent
kit is checked byte-for-byte. Mobile viewport emulation and a Linux WebKit run
are not iPhone Safari or physical-camera tests. These results are not an audit.
