# ECHO-OPI/HAP/1 — exact intent signing under human authority

Experimental test-credit adapter and detached historical evidence profile.
OPI, request and HAP-1/1 signed bytes are unchanged. This profile authorizes the
creation of an OPI signature. It does not authorize settlement, issue funds,
prove identity or convert an expired HAP permit into current authority.

## Trust boundary and first implementation

The website imports and inspects public evidence. It has no connection to the
local signer, no signing command bridge and no access to its keys. The Node
reference signer reads a receiver's exported request, displays the exact intent
in the owner's terminal, and requires explicit confirmation. It opens no HTTP
server or network connection. Run a reviewed source revision locally; executing
untrusted downloaded code defeats this boundary. The local machine, Node runtime,
terminal, encrypted key files and storage remain trusted. This is not hardware
key isolation or protection against local malware.

The reference CLI keeps the owner/issuer and payer/executor as separate encrypted
Ed25519 keys on the same machine; the owner also signs the delegate request. It
does not claim separation between these roles against a compromised local host.
The adapter API also supports a separately paired delegate and permit transport.
No new radio transport, phone signer, WebAuthn presence or PULSE policy is implied.

## Exact mapping

This first adapter accepts only `echo-demo-local`, `test-credit`, scale `0` and
amounts at most `9007199254740991`, because HAP amounts are safe integer minor
units. It rejects other rails and larger OPI amounts without rounding.

| HAP field | Derivation |
| --- | --- |
| capability | `echo-nearby-sign` |
| resource | `echo-nearby/` + the full 64-hex payee public key |
| unit | `echo-test-credit` |
| amount | Exact integer OPI amount |
| audience | HAP key ID of the payer/executor key |
| digest | HAP SHA-256 of canonical `{schema:"ECHO-OPI/HAP/1/action",intent}` |
| OPI authority | HAP SHA-256 of the complete signed ticket |

The payload includes **every** intent member: network, asset, scale, both keys,
amount, nonce, request reference, authority reference, memo commitment and both
timestamps. The ticket precedes the intent; its hash is placed in the intent
before the exact-action digest is calculated. There is no circular commitment.
The required receiver-signed ECHO-REQ/1 must match independently retained request
ID and payee pins, the payer restriction if present, all terms, distinct nonce
and the entire intent time window.

Only one exact HAP ticket is supported. Unknown profiles, extra delegation chains
or unimplemented attenuation semantics are refused. An OPI cannot widen a grant.

## Signing and durability

1. The owner reviews the exact intent in the local terminal. The CLI creates a
   one-use ticket for exactly the request's amount; approval happens before the
   challenge clock starts. The adapter requires the approved full payment ID.
2. The CLI durably reserves that request ID in a local export journal before
   requesting a permit. A missing journal or state file is an error, not a fresh
   account. This reservation is never automatically refunded.
3. The existing HAP executor creates a pending challenge with at most five
   seconds of freshness. The owner verifies the delegate request and atomically
   consumes allowance before returning a permit. Lost replies do not refund it.
4. The executor verifies history, scope, received revocation, exact payload,
   time and pending challenge, and durably consumes the challenge before entering
   the signing callback. It retains its existing final dispatch check.
5. The callback signs with a non-extractable WebCrypto key. It checks the clock
   before signing, after the OPI signature and after the signer statement. A
   deadline or backward-clock failure exports nothing; consumption remains spent.
6. Completed public evidence is durably retained before file export. If export
   fails, use `export` with the same request ID. It returns identical bytes and
   never signs again. Reserved/uncertain outcomes block new signing for that ID.

The five-second permit applies to **signing**. File transfer and later historical
inspection need not complete in five seconds. A real settlement adapter must
define and enforce its own current authority, funds and replay rules at its final
effect boundary. Local request reservations are not global double-spend prevention.
Received revocation prevents future permitted dispatch; it cannot undo a completed
signature. No disconnected device can infer an undelivered revocation.

Normal restart preserves journals and encrypted keys. Restoring an old disk image,
forking stores, losing a passphrase or recovering a stable Echo ID is not solved
by this adapter. Independent retained checkpoints or stronger storage are needed
for rollback detection; production key recovery remains separate work. The local
reference limits both grants and request records to 128 and stops when full.

## Detached evidence wire format

An `.eauth` is canonical printable-ASCII JSON, without trailing whitespace,
at most 32 KiB, with exactly:

`version, intentWire, requestWire, ticket, authorization, permit, execution`.

- `version`: `ECHO-OPI/HAP/1`.
- `intentWire`, `requestWire`: the original canonical signed OPI and request JSON
  strings. Embedded signatures are not reinterpreted or re-signed on export.
- `ticket`: the complete HAP-1/1 signed ticket.
- `authorization`: the delegate's HAP-1/request containing the signed challenge.
- `permit`: the issuer's HAP-1/receipt of kind `permit` for that exact challenge.
- `execution`: exactly `{schema,paymentId,requestId,permitHash,at,signature}`.
  `schema` is `ECHO-OPI/HAP/1/execution`; `permitHash` is the HAP hash of the complete
  permit; `at` is a declared integer UTC millisecond time within the permit and
  ticket windows and no earlier than the permit. The payer signs the UTF-8 bytes
  `ECHO-OPI/HAP/1/execution\n` + canonical execution body without `signature`.

The statement is a signed claim by the payer about signing, not proof of truthful
execution, clock accuracy, settlement or hardware behavior. HAP issuer receipts
also remain issuer statements. No full journal is attached: this avoids exposing
unrelated transactions, but it means cumulative budget history and the absence of
revocation cannot be independently reconstructed from this detached file.

Verification requires a separately configured context with exactly
`issuer, delegate, payer, payee, requestId, now`. The first two are raw 64-hex keys;
the next two are full `ed25519:` addresses; `requestId` is a full `ereq1_` ID;
`now` is the verifier's current integer UTC seconds. Never construct these trust
pins from the incoming evidence. Pinning keys does not identify their human owners.

Inspection checks canonical encoding, all six signatures (OPI, receiver request,
ticket, challenge, delegate request, issuer permit) **and** the payer statement;
exact authority and action binding; issuer-declared bounds; declared windows and
request binding. It rejects a declared signing time in the verifier's future.
An expired intent can still have valid historical evidence and is labelled expired.

Successful output is `AUTHORITY_EVIDENCE_MATCHED`, `historical:true`,
`status:pending-sync`, with `historyChecked`, `revocationChecked`,
`executionConfirmed` and `settlementConfirmed` all **false**. It never returns an
execution capability or persists/consumes authority. Failure returns only `ok:false`
and a reason code; it never falls back to a positive signature-only verdict.

## Run on a laptop

Use Node 24 and a reviewed checkout or independent kit. The prebuilt verifier and
the Node built-ins need no npm install to run these commands. On Linux/macOS, from
the kit root:

```sh
node nearby-protocol/authority-cli.mjs init ./nearby-private
node nearby-protocol/authority-cli.mjs sign ./nearby-private ./request.ereq ./payment.eauth
```

Choose a unique passphrase of at least 16 characters at the hidden terminal prompt.
Never put it in command arguments, environment variables, browser fields or logs.
`init` requires a new directory; it never overwrites existing state. Record the
printed public profile keys separately. The directory and key/state files are
restricted to the local user. This reference is test-only, without a recovery UX.

For `sign`, obtain the `.ereq` from the receiver using the existing signed exchange.
The CLI asks for the full separately retained request ID and confirmed receiver
address, unlocks the local keys, displays the complete proposed intent/ticket and
requires `SIGN`. No piped or automated approval is accepted by this CLI.

If the file write fails or a response is lost, do not create a new request or
nonce as a workaround. Re-export the retained result:

```sh
node nearby-protocol/authority-cli.mjs export ./nearby-private FULL_REQUEST_ID ./retained.eauth
```

If it reports `OUTCOME_UNKNOWN`, signing for that request remains blocked. Keep
the state and investigate; deleting the journal is not a recovery procedure.
Output files are created exclusively, never overwritten.

To inspect without a website, prepare `context.json` from your separately retained
keys, recipient and request ID, plus the current UTC seconds:

```sh
node nearby-protocol/authority-cli.mjs inspect ./payment.eauth ./context.json
node nearby-protocol/authority-cli.mjs extract-opi ./payment.eauth ./payment.opi
```

`extract-opi` only extracts the original bytes and makes no authority verdict.
The `.opi` remains usable by existing OPI verifiers and QR clients. The larger
`.eauth` travels as a file or text; no single-QR or new radio transport is claimed.
Use the website's **Check authority evidence** view for the same inspection, with
manually confirmed pins. Preparing its standalone offline copy caches the verifier,
not the surrounding site, evidence or trust pins. Reimport after reload.

## Reproduce and challenge it

`authority-vectors.json` publishes 32 deterministic cases with public synthetic
keys. `npm run check` verifies them and exercises the real HAP gate, state races,
restart, refusal and byte-preserving export. Browser tests cover the inspector,
tampering, changed pins, offline reload and the existing Twin. The authority
inspector currently has one project implementation; existing independent Rust/WASM
OPI/request inspection does not imply Rust/WASM support for this new evidence.
Independent implementations, an external security review and actual physical
device acceptance remain separate milestones.
