Headless pairing + signing hosts#264
Draft
pgherveou wants to merge 6 commits into
Draft
Conversation
Add a `truapi-host` binary with three roles that replace the external signing-bot for local end-to-end testing: - `relay`: an in-memory statement-store the two hosts pair over. - `pairing-host`: a seedless host (PairingHostRuntime) that presents a pairing deeplink and bridges product byte-frames over WebSocket. - `signing-host`: a wallet-local host (SigningHostRuntime) that answers the handshake and auto-signs, driven by the new SSO responder. truapi-server gains the §B responder half and the signing-host operations it needs: encode-side handshake helpers and responder codecs in `host_logic/sso/`, v4 extrinsic assembly (`host_logic/transaction.rs`), `runtime/signing_host/sso_responder.rs`, and `sign_payload` / `create_transaction` on the signing host. Product accounts and `rootUserAccountId` derive from `//wallet` (host-spec C.0/C.5) via a new sr25519 hard-junction helper. Bandersnatch ring-VRF product-account aliases (`get_account_alias`) use the `verifiable` crate, and lite-username attestation (`host_logic/attestation.rs` + CLI) registers a People-chain username so `get_user_id` resolves; the pairing host can resolve usernames from the real People chain while SSO stays on the relay (`identity_chain_genesis_hash`). The bun e2e driver (`e2e/`) runs the playground's own generated example sources against the pairing host and writes `explorer/diagnosis-reports/ headless.md`. With `E2E_LIVE_CHAIN=1` and a registered signer account it matches the browser host on every method it passes (43/44; the lone failure is a chain example that sends a deliberately-invalid operation id).
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The stop_transaction playground example used a hardcoded placeholder operationId; it now broadcasts a transaction and stops it with the returned id, the correct usage. Also emit captured example output for passing methods in the headless diagnosis report and load the e2e signer mnemonic from a gitignored e2e/.env. Note: against the public paseo Asset Hub RPC, transaction_v1_stop rejects broadcast operation ids (-32602), so this method stays red there; it passes on hosts whose transaction API honors stop (e.g. smoldot in the browser host).
…' into headless-host
The headless hosts now connect to the statement store the same way an iOS/web client does: the real paseo-next-v2 People chain, which serves both the statement-store RPC and the chain RPC over one WebSocket. The in-memory relay (relay.rs, the `relay` subcommand, and the relay orchestration in the e2e runner) is removed; both hosts default `--statement-store` to the People node. Statement-store allowance is obtained on-chain. New native module `src/alloc/` ports `Resources.set_statement_store_account` (pallet 63 call 10): metadata-driven signed-extension encoding + inherited-implication proof message (pinned to the live-verified known answer), bandersnatch ring-VRF membership proof via the `verifiable` prover, the unsigned General (v5) extrinsic, `Members` ring fetch, SSS_SLOT slot scan, a minimal WS JSON-RPC client, and `author_submitAndWatchExtrinsic`. Before pairing, the signing host registers allowance for both its own `//wallet//sso` account and the pairing host's device key, proving its LitePeople ring membership. A new `alloc-check` subcommand diagnoses (or `--submit`s) allowance. statement_store_rpc::submit now inspects the SubmitResult: only `new`/ `known` count as accepted, so `NoAllowance`/`BadProof` rejections surface instead of being silently dropped.
…ations
The pairing host is now driven by a product script: `pairing-host --script
foo.ts --product-id p.dot [--auto-accept]` starts the frame server and runs the
script with a global `truapi` injected (the `@parity/truapi` client, scoped to
the product) plus a `host` helper. The Rust CLI spawns the script via an
embedded `js/runner.ts`, and the command exits with the script's status, so the
host command is the test — no separate bun orchestrator.
Both hosts take `--auto-accept`; without it every confirmation a web/iOS host
would show as a modal is prompted y/n on the CLI (ApprovalPolicy::Prompt).
`make headless` builds the CLI + client; `e2e/run.sh` is a thin launcher that
runs a product script through a pairing host and pipes the emitted deeplink to a
signing host. Product scripts live in `js/scripts/`: `battery.ts` (curated
signer gate, 7/7) and `diagnosis.ts` (playground generated examples ->
explorer/diagnosis-reports/headless.md, 43/1/20 with a live chain). The former
`e2e/{run-e2e,driver,diagnosis,ws-provider}.ts` and `wait-and-sign.sh` are gone.
Product ids must be a `.dot`/`localhost` identifier; the default is
`headless-playground.dot`.
decrypto21
added a commit
that referenced
this pull request
Jul 8, 2026
Draft
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
truapi-hostCLI withpairing-hostandsigning-hostcommands:a TrUAPI host — the product side and the wallet side — you can spin up from the
command line and drive with a JS/TS script. It pairs over the real
paseo-next-v2 People-chain statement store (the same node an iOS/web client
uses); no local relay or node. (Plus the truapi-server work it needs — see below.)
Usage
Launch the two hosts (in two terminals, or background the first). The pairing
host serves a product and runs your script; the signing host answers the deeplink
it prints and signs:
The script is top-level code; a thrown error fails the run:
Expected output (the signing host registers on-chain allowance, then both pair
and the script runs):
Both commands take
--auto-accept; without it, every confirmation a web/iOS hostshows as a modal is prompted
y/non the CLI. The pairing host exits with thescript's status, so the command is the test.
Diagnose on-chain statement-store allowance for a mnemonic:
truapi-host alloc-check --lookback 100 # ring membership + free slot (read-only)E2E
Two ready-made product scripts live in
js/scripts/.battery.ts— a signer gate (login + raw/payload signing, create-transaction,entropy):
diagnosis.ts— runs the playground's own generated examples and writesexplorer/diagnosis-reports/headless.md. The examples are baked totruapi-playground.dot, so serve that product:truapi-host pairing-host --product-id truapi-playground.dot \ --script rust/crates/truapi-host-cli/js/scripts/diagnosis.ts --auto-accept # → 43 passed, 1 failed, 20 skipped (only the Chain/stop_transaction node limitation)make headlessbuilds the CLI + client;rust/crates/truapi-host-cli/e2e/run.shwraps a pairing host + signer (deeplink piped) for one-shot runs.
Roles (one
truapi-hostbinary)PairingHostRuntime; serves product frames andruns your
--scriptwithtruapiinjected.SigningHostRuntime; registers statementallowance on-chain, answers the handshake, and signs via the SSO responder.
--submit) on-chain statement-store allowance.Both hosts default
--statement-storetowss://paseo-people-next-system-rpc.polkadot.io.Product ids must be a
.dot/localhostidentifier; the default isheadless-playground.dot.Statement-store allowance (
src/alloc/)The real store enforces per-account allowance. The signing host grants it
on-chain exactly as a real client does, via an unsigned General (v5)
Resources.set_statement_store_accountextrinsic (pallet 63 call 10) carrying abandersnatch ring-VRF membership proof. The native port covers:
message (
0x00 ‖ call ‖ Σtail.extra ‖ Σtail.implicit), pinned by aknown-answer test to the live-verified vector;
open/createvia theverifiableprover);AsResourcesInfo+ the General-v5 envelope;Membersring fetch (current index, pagedRingKeys,includedslice,ring_size→ domain) and the SSS_SLOT slot scan;author_submitAndWatchExtrinsic.Before pairing, the signing host registers allowance for the two accounts that
submit statements — its own
//wallet//ssoaccount and the pairing host'sper-pairing device key — proving its LitePeople ring membership once, and waits
for the registrations to finalize (the store does not honor a fresh allowance
until then). The signing account must be an attested member; because a
long-lived account sits in an older ring, the host scans back from the current
ring index to find it.
statement_store_rpc::submitinspects theSubmitResult: onlynew/knowncount as accepted, so
NoAllowance/BadProofrejections surface instead ofbeing silently dropped.
truapi-server additions
host_logic/sso/,runtime/signing_host/sso_responder.rs.sign_payloadandcreate_transactionon the signing host, with v4 extrinsicassembly (
host_logic/transaction.rs).rootUserAccountIdderive from//wallet(host-specC.0/C.5) via an sr25519 hard-junction helper.
get_account_alias) via theverifiablecrate(native-only; wasm unaffected).
host_logic/attestation.rs+ CLI);get_user_idresolves a real People-chain username, and since SSO and identity share the
People chain it always resolves.
Verification:
cargo build/clippy -D warnings/testworkspace-wide,cargo +nightly fmt --check, andmake wasmall green; the full pairing + signer flowpasses live against paseo-next-v2, driven through the injected product script.
Notes for reviewers
--mnemonic/$HOST_CLI_SIGNER_MNEMONIC/a gitignored
e2e/.env; it is not committed. The dev mnemonic works out of thebox (ring member, username
guestnrun.93).make e2e-dotli.