Summary
On wasm32, allocate_statement_store_allowance and allocate_bulletin_allowance are hard stubs. Because those functions are what derive and return the statement-store secret, no browser-hosted product can create a statement proof at all — statement_store.create_proof_authorized fails UnableToSign.
Still present on main (rust/crates/truapi-server/src/runtime/signing_host/sso_responder.rs, unchanged from vendored revision b0e70f91):
// native, line 852 — real work, returns the key material:
Ok(allowance.secret.to_bytes().to_vec())
// wasm32, line 1014:
#[cfg(target_arch = "wasm32")]
pub(super) async fn allocate_statement_store_allowance(
_services: &Arc<RuntimeServices>,
_signing_host: &SigningHost,
_product_id: &str,
_policy: OnExistingAllowancePolicy,
) -> Result<Vec<u8>, AllowanceAllocationError> {
Err(AllowanceAllocationError::NativeOnly { resource: "statement-store" })
}
// wasm32, line 1026: same stub for `allocate_bulletin_allowance`
Observed behaviour
A product calls RFC-0010 resource_allocation.request for
[StatementStoreAllowance, BulletinAllowance, SmartContractAllowance, AutoSigning] and gets:
["NotAvailable", "NotAvailable", "NotAvailable", "Allocated"]
with, per refused item:
WARN truapi_server::runtime::signing_host: direct resource allocation item failed
{product_id=…, reason=signing host: statement-store allowance allocation is native-only}
Then statement_store.create_proof_authorized returns UnableToSign (i.e. RemoteStatementStoreCreateProofError::UnableToSign — signing failed, not UnknownAccount), and statement_store.submit is never reached. These are one defect, not two: no allocation ⇒ no ss_secret ⇒ nothing to sign with.
Not account state
Reproduced against live paseo-next-v2 with a freshly minted BIP-39 identity registered as a lite person via the DotSpark attester, holding a confirmed on-chain statement-store allowance (ring committed at index 156). Under full TRACE the resource_allocation.request span opens and closes with no nested chain query — the refusal is unconditional. Sibling capabilities on the same transport all work: local_storage.*, statement_store.subscribe, permissions.request_remote_permission, account.get_user_id, chain.*.
Impact
Any wasm32 embedder is affected. Concretely, Epoca (Firefox fork, vendors @useragent-kit/wasm@0.6.13) cannot serve statement writes for any product: its own nightly packaged-live-contracts reports statement-store=non-passing / chain-query=non-passing against production dot://t3ams-spa.dot, and the T3ams SPA loses DM/group/channel messaging entirely. Embedders cannot compensate — the missing artifact is secret key material produced inside these functions, so there is no host callback or product-side workaround.
Porting notes
The wasm-hostile parts of the native implementation (852-1011) look bounded:
current_unix_secs() uses std::time::SystemTime (also cfg(not(wasm32)), ~line 1036) — chain-derived time is already available in the runtime;
- the Bulletin/People RPC path (
BulletinRpcClient, wait_bulletin_authorization) — host_rpc_client already serves chain RPC on wasm for chain.* and statement_store.subscribe;
- the
MissingLitePeopleMembership ring check — same RPC surface.
Alternatively, accept an already-confirmed on-chain allowance on wasm instead of performing the allocation, and/or expose a host hook so an embedder that can already claim it (Epoca does, in EpocaAllowance.ensure()) can satisfy the slot.
Related
Summary
On
wasm32,allocate_statement_store_allowanceandallocate_bulletin_allowanceare hard stubs. Because those functions are what derive and return the statement-store secret, no browser-hosted product can create a statement proof at all —statement_store.create_proof_authorizedfailsUnableToSign.Still present on
main(rust/crates/truapi-server/src/runtime/signing_host/sso_responder.rs, unchanged from vendored revisionb0e70f91):Observed behaviour
A product calls RFC-0010
resource_allocation.requestfor[StatementStoreAllowance, BulletinAllowance, SmartContractAllowance, AutoSigning]and gets:with, per refused item:
Then
statement_store.create_proof_authorizedreturnsUnableToSign(i.e.RemoteStatementStoreCreateProofError::UnableToSign— signing failed, notUnknownAccount), andstatement_store.submitis never reached. These are one defect, not two: no allocation ⇒ noss_secret⇒ nothing to sign with.Not account state
Reproduced against live
paseo-next-v2with a freshly minted BIP-39 identity registered as a lite person via the DotSpark attester, holding a confirmed on-chain statement-store allowance (ring committed at index 156). Under fullTRACEtheresource_allocation.requestspan opens and closes with no nested chain query — the refusal is unconditional. Sibling capabilities on the same transport all work:local_storage.*,statement_store.subscribe,permissions.request_remote_permission,account.get_user_id,chain.*.Impact
Any
wasm32embedder is affected. Concretely, Epoca (Firefox fork, vendors@useragent-kit/wasm@0.6.13) cannot serve statement writes for any product: its own nightlypackaged-live-contractsreportsstatement-store=non-passing/chain-query=non-passingagainst productiondot://t3ams-spa.dot, and the T3ams SPA loses DM/group/channel messaging entirely. Embedders cannot compensate — the missing artifact is secret key material produced inside these functions, so there is no host callback or product-side workaround.Porting notes
The wasm-hostile parts of the native implementation (852-1011) look bounded:
current_unix_secs()usesstd::time::SystemTime(alsocfg(not(wasm32)), ~line 1036) — chain-derived time is already available in the runtime;BulletinRpcClient,wait_bulletin_authorization) —host_rpc_clientalready serves chain RPC on wasm forchain.*andstatement_store.subscribe;MissingLitePeopleMembershipring check — same RPC surface.Alternatively, accept an already-confirmed on-chain allowance on wasm instead of performing the allocation, and/or expose a host hook so an embedder that can already claim it (Epoca does, in
EpocaAllowance.ensure()) can satisfy the slot.Related