Pre-RFC / discussion-opener from the DIM2 (Jollity) web product team. Full write-up (with alternatives, security, migration, and the long-run direction): https://github.com/paritytech/dim2-spa-fable/blob/main/docs/proposals/game-scoped-session-signer.md — issue refs like #23/#16/#26 below are in the dim2-spa-fable repo.
The gap
DIM2 runs sandboxed under the Host API and follows the no-keys model (product addresses accounts, host holds keys). But the Game pallet requires the statement signer to BE the registered player — the //candidate account (a hard derivation of the user's root), which sign-up registers and grants a PlayerStatementLimit statement-store allowance.
//candidate (sr25519) must sign on two surfaces during a game:
- Statements — the signaling stream (high-frequency): every SDP offer/answer, each ICE candidate, and the request/ACK handshake is a statement whose proof is signed by
//candidate — dozens per minute, continuously. Must be promptless.
- Extrinsics (a handful/game):
Game.report at end (in the web product, mid-session), plus sign_up (can be native/phone) and occasional offboard.
What hosts offer today doesn't fit — the gap is which identity signs, not whether the host can sign promptlessly:
create_proof_authorized (RFC-0010): promptless, but signs with the host's session/allowance account — not //candidate, and it has no PlayerStatementLimit allowance, so the Game pallet rejects it.
- Product accounts: soft-derived, product-scoped; the runtime doesn't recognize them as players.
sign_payload / create_transaction: per-signature confirmation — fine for ~2 extrinsics, unusable for a signaling stream.
Entropy.derive (RFC-0007): derived bytes for product-held keys — the opposite of what's needed.
The product must never see key material, so there's no product-side workaround that isn't a custody regression.
The ask (one new capability)
A session-scoped, promptless signer bound to a specific user-scoped, host-owned derivation, under an explicit bounded scope:
open_scoped_session({
identity: Derivation("pop-candidate") | IdentityRef([u8;32]), // which host-owned key
scope: {
statements: { topics, channels, max_per_minute }, // promptless within scope
calls: ["Game.report"], // tiny extrinsic allowlist
expires_at,
},
}) -> { session_id, public_key }
// then: session_sign_statement / session_create_transaction (promptless, in-scope only)
// close_scoped_session (visible + revocable)
One consent sheet at open; everything in-scope then promptless; out-of-scope is a typed error, never a silent fallback. Framed as a generalization of create_proof_authorized/RFC-0010 along the identity axis — not a new signing engine. Mobile hosts already do exactly this natively (the native game signs statements + report with //candidate); this exposes existing behavior as a wire capability so hosted web/desktop products reach parity.
Long-run context (opens a second discussion)
This is deliberately the identity primitive. Orthogonally, we think the transport should eventually become a host primitive too — connect(peer) → media tracks + native call UI, never touching WebRTC (the ext.media direction). The two compose: even a fully host-owned connection would still need to sign DIM2's signaling as //candidate, because those statements are the on-chain player record. Generic 1:1 calling apps would use only the connection primitive; personhood/on-chain apps like DIM2 use both. Tracked separately (dim2-spa-fable#26); the ext.media blocker for us is a product-facing data channel + an identity hook (this proposal's IdentitySelector).
Open questions for the host-sdk / truapi team
- Identity selector: RFC-vetted named-derivation registry vs opaque
identity_ref minted by a prior host flow (e.g. native sign-up)?
- Home: new trait, extension of
Signing, or a new RFC-0010 allowance kind under ResourceAllocation?
- dotli cross-device: the keystore is the paired phone — does the phone hold the session (relay latency per statement?) or delegate a session key to the web host for the scope?
- Statement-scope vocabulary: topics/channels, or reference the on-chain
PlayerStatementLimit?
- Consent copy ownership: host heuristics vs RFC-registered use-case strings?
- Contract of record: truapi RFC first, or prototype as a useragent-kit/host-container extension and graduate?
Happy to prototype the product side against @parity/host-api-test-sdk and be the first consumer — our M5 is blocked on the direction chosen here.
Pre-RFC / discussion-opener from the DIM2 (Jollity) web product team. Full write-up (with alternatives, security, migration, and the long-run direction): https://github.com/paritytech/dim2-spa-fable/blob/main/docs/proposals/game-scoped-session-signer.md — issue refs like #23/#16/#26 below are in the
dim2-spa-fablerepo.The gap
DIM2 runs sandboxed under the Host API and follows the no-keys model (product addresses accounts, host holds keys). But the Game pallet requires the statement signer to BE the registered player — the
//candidateaccount (a hard derivation of the user's root), which sign-up registers and grants aPlayerStatementLimitstatement-store allowance.//candidate(sr25519) must sign on two surfaces during a game://candidate— dozens per minute, continuously. Must be promptless.Game.reportat end (in the web product, mid-session), plussign_up(can be native/phone) and occasionaloffboard.What hosts offer today doesn't fit — the gap is which identity signs, not whether the host can sign promptlessly:
create_proof_authorized(RFC-0010): promptless, but signs with the host's session/allowance account — not//candidate, and it has noPlayerStatementLimitallowance, so the Game pallet rejects it.sign_payload/create_transaction: per-signature confirmation — fine for ~2 extrinsics, unusable for a signaling stream.Entropy.derive(RFC-0007): derived bytes for product-held keys — the opposite of what's needed.The product must never see key material, so there's no product-side workaround that isn't a custody regression.
The ask (one new capability)
A session-scoped, promptless signer bound to a specific user-scoped, host-owned derivation, under an explicit bounded scope:
One consent sheet at open; everything in-scope then promptless; out-of-scope is a typed error, never a silent fallback. Framed as a generalization of
create_proof_authorized/RFC-0010 along the identity axis — not a new signing engine. Mobile hosts already do exactly this natively (the native game signs statements + report with//candidate); this exposes existing behavior as a wire capability so hosted web/desktop products reach parity.Long-run context (opens a second discussion)
This is deliberately the identity primitive. Orthogonally, we think the transport should eventually become a host primitive too —
connect(peer)→ media tracks + native call UI, never touching WebRTC (theext.mediadirection). The two compose: even a fully host-owned connection would still need to sign DIM2's signaling as//candidate, because those statements are the on-chain player record. Generic 1:1 calling apps would use only the connection primitive; personhood/on-chain apps like DIM2 use both. Tracked separately (dim2-spa-fable#26); theext.mediablocker for us is a product-facing data channel + an identity hook (this proposal'sIdentitySelector).Open questions for the host-sdk / truapi team
identity_refminted by a prior host flow (e.g. native sign-up)?Signing, or a new RFC-0010 allowance kind underResourceAllocation?PlayerStatementLimit?Happy to prototype the product side against
@parity/host-api-test-sdkand be the first consumer — our M5 is blocked on the direction chosen here.