Summary
An AutoSigning grant (RFC-0010) transfers the product-subtree secret to the host, but the runtime consumes it only for account.sign_vrf (RFC-0023). Every other product-account signing API still consults a per-call confirmation and, on pairing hosts, round-trips to the signing host. The capability's key material already covers those signatures, so the runtime can serve them locally and prompt-free under the same grant, with the same guardrails as the VRF path.
Legacy hosts never consume the capability at all (host-papp defines the wire codec but has no consumer; the iOS in-app container discards the payload), so this is purely host-local behavior in truapi-server: no wire or Account Holder changes.
APIs to cover
| API |
Today (with an active grant) |
Target |
signing.sign_raw (product account) |
per-call prompt + remote relay |
local, prompt-free |
signing.sign_payload (product account) |
per-call prompt + remote relay |
local, prompt-free |
signing.create_transaction (product account) |
per-call prompt + remote relay |
local, prompt-free |
statement_store.create_proof (product-signed) |
signing host: works; pairing host: Unavailable (no SSO raw-statement protocol) |
local on pairing hosts via the capability (feature unlock, no prompt change) |
Out of scope, by design (key material outside the product subtree): the *_with_legacy_account variants, ring-VRF aliases/proofs (person keys), entropy.derive (root entropy), create_proof_authorized (allowance slot keys, own capability).
Implementation sketch
ProductAuthority::auto_signing_covers(session, calling_product, account) -> bool; implemented on SigningHost via the activation-generation grant set and on PairingHost via the owner-bound persisted capability.
- Short-circuit the role-neutral confirmation gates in
runtime.rs (sign_payload :1316, sign_raw :1369, create_transaction :1422) when the predicate holds.
- Pairing-host fast paths mirror the existing
sign_vrf block: capability → derive_product_keypair_from_subtree_secret → sign. create_transaction needs no chain-state assembly because RFC-0020 has the product supply call_data/extensions/tx_ext_version; the fast path is the same build_local_transaction call the signing host uses.
- Move
sign_extrinsic_payload, raw_payload_bytes, and build_local_transaction from runtime/signing_host.rs into shared host_logic so both roles use one implementation.
- Replace the
sign_statement_store_product_payload Unavailable stub on the pairing host with a capability-backed local path.
- Guardrails identical to the VRF path: calling product must equal the account's product (normalized), owner-bound capability validation, session-lifecycle epoch fencing.
- Rustdoc on the
truapi trait methods gains the RFC-0023 sentence ("local when AutoSigning covers the account, otherwise a per-call user confirmation"); regenerate codegen so the TS client and playground docs match.
Verification
Extend the CLI battery's Resource Allocation/auto_signing_e2e case: after the grant, each covered call must succeed while appending zero confirmation lines to the approvals transcript (TRUAPI_APPROVALS_LOG), on both host roles. The transcript already records sign raw data, sign payload, and create transaction actions, so the harness needs only the extra calls and window assertions. The pairing-host diagnosis report's Statement Store/create_proof row flips to pass.
Estimate
~550–750 authored LOC total: ~50 shared groundwork (+~90 relocated helper lines), then per API roughly 130–190 including tests and battery coverage. Details in the table above; implementation is ~200 of it, the rest is tests/harness/docs.
Open decision
RFC-0010's consent text already frames the grant broadly ("sign on the product's behalf without per-call user prompts", with the explicit "sign arbitrary transactions as me" warning), and the Account Holder transfers the full subtree secret today regardless. Extending consumption does not widen the granted authority, but it does change what products observe under a standing grant, so it should get an explicit product/security sign-off before landing, and Account Holder consent UIs must present AutoSigning distinctly from slot allowances (RFC-0010 §authorization UI).
Builds on #329 (first consumer of the capability: sign_vrf, plus the owner-binding, lifecycle fencing, and the approvals-transcript e2e harness this issue reuses).
Summary
An AutoSigning grant (RFC-0010) transfers the product-subtree secret to the host, but the runtime consumes it only for
account.sign_vrf(RFC-0023). Every other product-account signing API still consults a per-call confirmation and, on pairing hosts, round-trips to the signing host. The capability's key material already covers those signatures, so the runtime can serve them locally and prompt-free under the same grant, with the same guardrails as the VRF path.Legacy hosts never consume the capability at all (
host-pappdefines the wire codec but has no consumer; the iOS in-app container discards the payload), so this is purely host-local behavior intruapi-server: no wire or Account Holder changes.APIs to cover
signing.sign_raw(product account)signing.sign_payload(product account)signing.create_transaction(product account)statement_store.create_proof(product-signed)Unavailable(no SSO raw-statement protocol)Out of scope, by design (key material outside the product subtree): the
*_with_legacy_accountvariants, ring-VRF aliases/proofs (person keys),entropy.derive(root entropy),create_proof_authorized(allowance slot keys, own capability).Implementation sketch
ProductAuthority::auto_signing_covers(session, calling_product, account) -> bool; implemented onSigningHostvia the activation-generation grant set and onPairingHostvia the owner-bound persisted capability.runtime.rs(sign_payload:1316,sign_raw:1369,create_transaction:1422) when the predicate holds.sign_vrfblock: capability →derive_product_keypair_from_subtree_secret→ sign.create_transactionneeds no chain-state assembly because RFC-0020 has the product supplycall_data/extensions/tx_ext_version; the fast path is the samebuild_local_transactioncall the signing host uses.sign_extrinsic_payload,raw_payload_bytes, andbuild_local_transactionfromruntime/signing_host.rsinto sharedhost_logicso both roles use one implementation.sign_statement_store_product_payloadUnavailablestub on the pairing host with a capability-backed local path.truapitrait methods gains the RFC-0023 sentence ("local whenAutoSigningcovers the account, otherwise a per-call user confirmation"); regenerate codegen so the TS client and playground docs match.Verification
Extend the CLI battery's
Resource Allocation/auto_signing_e2ecase: after the grant, each covered call must succeed while appending zero confirmation lines to the approvals transcript (TRUAPI_APPROVALS_LOG), on both host roles. The transcript already recordssign raw data,sign payload, andcreate transactionactions, so the harness needs only the extra calls and window assertions. The pairing-host diagnosis report'sStatement Store/create_proofrow flips to pass.Estimate
~550–750 authored LOC total: ~50 shared groundwork (+~90 relocated helper lines), then per API roughly 130–190 including tests and battery coverage. Details in the table above; implementation is ~200 of it, the rest is tests/harness/docs.
Open decision
RFC-0010's consent text already frames the grant broadly ("sign on the product's behalf without per-call user prompts", with the explicit "sign arbitrary transactions as me" warning), and the Account Holder transfers the full subtree secret today regardless. Extending consumption does not widen the granted authority, but it does change what products observe under a standing grant, so it should get an explicit product/security sign-off before landing, and Account Holder consent UIs must present AutoSigning distinctly from slot allowances (RFC-0010 §authorization UI).
Builds on #329 (first consumer of the capability:
sign_vrf, plus the owner-binding, lifecycle fencing, and the approvals-transcript e2e harness this issue reuses).