Skip to content

Proposal: game-scoped session signer — promptless host signing as a user-scoped identity (//candidate) #274

Description

@peetzweg

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 //candidatedozens 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

  1. Identity selector: RFC-vetted named-derivation registry vs opaque identity_ref minted by a prior host flow (e.g. native sign-up)?
  2. Home: new trait, extension of Signing, or a new RFC-0010 allowance kind under ResourceAllocation?
  3. 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?
  4. Statement-scope vocabulary: topics/channels, or reference the on-chain PlayerStatementLimit?
  5. Consent copy ownership: host heuristics vs RFC-registered use-case strings?
  6. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions