Introduction

## Introduction

`pallet-game` is the Proof of Personhood pallet for personhood-gated on-chain games and randomized selection flows. Its core property is _fair participation_: when the pallet selects from a set of entrants, it can guarantee each real person has exactly one entry, regardless of how many accounts they control — because the entrants are scoped by alias, not by account address.

Typical use cases include lottery-style draws, raffles, one-person-one-entry contests, randomized airdrops, and any flow where "one entry per real human" is part of the fairness contract.

!!! warning "Provisional"
    The exact dispatch surface of `pallet-game` — extrinsics, parameter shapes, storage layout, supported game types, and its integration with the forthcoming `pallet-airdrop` — is still being finalized. This page documents the conceptual responsibilities; the per-extrinsic reference will be added once the surface confirms.


What the Pallet Guarantees

## What the Pallet Guarantees

Three properties the pallet enforces:

- **One entry per alias**: When a user enters under their per-game alias (resolved via `under_alias`), the pallet stores the alias. A second entry from the same user produces the same alias and is rejected or replaces the first, depending on the game's mode.
- **Verifiable randomness for selection**: When the pallet selects a winner (or a set of winners), the randomness is sourced from a verifiable source — a block hash, a VRF output, a beacon — that an external party can later check. The selection is not chosen by a privileged operator after the fact.
- **Account unlinkability for state**: The pallet's storage maps from alias to game-side state. The state cannot be linked back to a specific user account without that user's own cooperation.


What a Product Uses It For

## What a Product Uses It For

A Product can dispatch into `pallet-game` to:

- Register entries in a game on behalf of the current user — a call wrapped in `under_alias` so the pallet sees the alias.
- Read game state — current entrant count, current prize pool, outcome of a completed draw — through the standard chain client surface.
- Subscribe to outcomes when a game completes, so the Product UI can react in real time.

A Product does _not_ run the game itself; the pallet does. The Product is the UI that lets users enter and see results.


Where to Go Next

## Where to Go Next

- Learn **pallet-people**

    ---

    The foundational pallet that issues the aliases this pallet keys on.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/pallet-people/)

- Learn **TrUAPI Entropy**

    ---

    The verifiable-randomness primitives that pair with `pallet-game` when a Product also wants its own randomness on the same chain state. *(Reference page forthcoming.)*


Introduction

## Introduction

`pallet-identity` is the People Chain pallet that holds the on-chain identity record associated with an account. It is not part of the alias machinery (aliases live in `pallet-people`); it is the place where a verified account's _direct_ identity — the fields associated with the account itself, not its per-context pseudonyms — lives.

For Product developers, the distinction matters: most personhood-gated flows reach for aliases (`under_alias`, `getAnonymousAlias`) because aliases preserve unlinkability. `pallet-identity` is the relevant pallet when the Product genuinely needs to read or write the user's _direct_ identity record — for example, to display a username the user has explicitly set on their account.

!!! warning "Provisional"
    The exact dispatch surface of `pallet-identity` on the current build — the supported identity fields, the extrinsics that set them, and the verification rules — is still being finalized. This page documents the conceptual responsibilities; per-extrinsic specifics will be added once confirmed.


What the Pallet Records

## What the Pallet Records

An identity record typically contains:

- **A username**: The human-readable name the user has set on their account, useful for display in cases where the user has opted in to a public-facing handle.
- **Display fields**: Additional administrative or display-oriented fields the user has chosen to associate with their account on-chain.
- **Verification metadata**: Markers indicating which of the fields have been verified by an authorized verifier, if applicable.

The fields are public — anyone reading chain state can see them. The user is the only party that can write to their own record (subject to the standard signing model).


Identity vs. Aliases

## Identity vs. Aliases

A useful framing for picking the right pallet:

| You need to…                                                                    | Use…                                                                  |
|:--------------------------------------------------------------------------------|:----------------------------------------------------------------------|
| Recognize "the same real person" in your Product without revealing who they are | An _alias_ from `pallet-people`                                       |
| Display a username the user has chosen publicly                                 | `pallet-identity`'s username field                                    |
| Check whether a user is a verified person (yes/no, not who)                     | A Ring-VRF proof from `pallet-people`                                 |
| Display the user's verified display name in a public context                    | `pallet-identity` fields                                              |
| Gate access on personhood                                                       | `under_alias` against a PoP pallet                                    |
| Gate access on the specific user behind an alias                                | This is what aliases _cannot_ do — that link is intentionally severed |


When to Use From a Product

## When to Use From a Product

A Product reaches for `pallet-identity` rarely. Most Products preserve unlinkability and never touch the user's direct identity. The pallet is the right tool when:

- The user has opted in to a public-facing presence in your Product context (a profile name, a public display name, a verified-handle badge), and you want to read or set that.
- Your Product bridges to off-chain identity systems that need the user's direct identity rather than a per-Product alias.

When in doubt, prefer aliases — they preserve the privacy default.


Where to Go Next

## Where to Go Next

- Learn **pallet-people**

    ---

    The pallet that issues aliases — the unlinkable alternative to direct identity that most Products should prefer.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/pallet-people/)

- Learn **Ring-VRF and Aliases**

    ---

    The mechanism that makes aliases unlinkable and why most Product use cases prefer aliases over direct identity reads.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/ring-vrf-and-aliases/)


Introduction

## Introduction

`pallet-people` is the foundation of [Polkadot's Proof of Personhood](/reference/apps/infrastructure/pop/) on the People Chain. It's the pallet that records who has completed personhood verification, issues the aliases other Products gate on, and exposes the `under_alias` runtime origin every other PoP pallet (game, score, identity, ubc, coinage) builds on top of.

Its responsibilities cluster into three:

- **Maintaining the membership rings**: The full personhood ring (PoP Full) and the lite-people ring (PoP Lite) are this pallet's storage. Joining a ring means an entry is added; the ring's root is what subsequent Ring-VRF proofs are checked against.
- **Issuing aliases**: When a Product (via the App) requests an alias for a specific context, `pallet-people` is the runtime side that produces the alias and registers it as valid against the ring.
- **Implementing the `under_alias` origin**: When a runtime call comes in wrapped in `under_alias`, this is the pallet whose verification logic the runtime runs to decide whether to admit the call.

!!! warning "Provisional"
    The exact dispatch surface of `pallet-people` — the precise extrinsics, their parameter shapes, the storage map names, and the events emitted — is still being finalized. This page documents the conceptual responsibilities; the per-extrinsic reference will be added once the surface confirms.


Ring Membership

## Ring Membership

Two storage structures on `pallet-people` hold the rings:

- **The full personhood ring**: Holds the public keys of accounts that completed biometric PoP. Joining requires the App-side verification flow; the result is an extrinsic that adds the key.
- **The lite-people ring**: Holds accounts registered via a governance-authorized attestation. Attestations are submitted by approved attesters; the pallet validates and records the registration.

Each ring exposes a _ring root_ — the cryptographic commitment used to verify Ring-VRF proofs. The root updates when membership changes; consumers of proofs read the current root from this pallet's storage when validating.


Alias Issuance

## Alias Issuance

`pallet-people` is the issuing authority for aliases. When the App produces a Ring-VRF proof for a specific context (a `.dot` domain), the proof is generated against the pallet's ring root and the context. The alias the verifier sees is deterministic for *(user, context)* — same user, same context, same alias every time.

For cross-domain aliases — a Product requesting an alias scoped to another Product's `.dot` — the pallet has additional checks to ensure the request was user-consented and the destination context is valid.


`under_alias` Dispatch

## `under_alias` Dispatch

When the runtime sees a call wrapped in `under_alias`, the validator path is:

1. Read the relevant ring root from `pallet-people`'s storage.
2. Verify the included Ring-VRF proof against the ring root and the dispatched context.
3. On valid proof, dispatch the inner call with the origin resolved to the user's alias for that context.
4. The receiving pallet sees the alias, not the underlying account.

This is the property that lets downstream pallets (game, score, identity, ubc) gate their logic on alias identity while keeping their state unlinkable to user accounts.


Pallets That Depend on This One

## Pallets That Depend on This One

Every other pallet in the PoP set ultimately reads `pallet-people`'s ring root or accepts `under_alias` calls validated against it. The downstream pallets are listed in the [Proof of Personhood overview](/reference/apps/infrastructure/pop/); the foundational guarantee they all rely on is that this pallet's rings represent the current set of real persons on the network.


Where to Go Next

## Where to Go Next

- Learn **Ring-VRF and Aliases**

    ---

    The cryptographic and runtime mechanism this pallet implements — what Ring-VRF is, what an alias is, how `under_alias` works at the runtime layer.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/ring-vrf-and-aliases/)


Introduction

## Introduction

`pallet-score` is the Proof of Personhood pallet for personhood-anchored reputation and scoring. It records per-alias scores that accumulate from on-chain activity, providing a primitive that a Product (or another pallet) can read to make decisions: "has this real person built up enough reputation in this context to be trusted with X?"

The pallet's storage is keyed by alias, so two scores belonging to the same user across different Products do not link to each other or to the user's account.

!!! warning "Provisional"
    The exact dispatch surface of `pallet-score` — what extrinsics adjust scores, what events cause automatic adjustments, the scoring rules, the storage shape — is still being finalized. This page documents the conceptual responsibilities; the per-extrinsic reference will be added once the surface confirms.


What the Pallet Records

## What the Pallet Records

The conceptual storage is (alias, context) → score:

- A user's _alias_ in a specific context — typically a Product's `.dot` domain — has a per-context score.
- The same user has different aliases in different contexts, so their scores in different contexts are stored under different keys and cannot be linked.
- Scores can be positive (reputation earned) or negative (penalties), depending on the rules the context defines.

The pallet does not impose a single scoring rule; it provides the storage and the alias-scoped key. Other pallets and Products configure how scores adjust.


Read and Write Paths

## Read and Write Paths

Two paths interact with `pallet-score`:

- **Adjustment dispatch**: A call (typically under `under_alias`, but other patterns are possible) increments or decrements the score for the calling alias. Whether a Product can adjust scores directly, or whether adjustments are gated to specific privileged callers, depends on the context's configuration.
- **Read access**: Anyone can read scores from chain state. The score is associated with an alias, so a reader cannot trivially identify the underlying user — but the alias is, by design, observable to anyone who knows the context.


Use Cases

## Use Cases

Two common patterns:

- **Sybil-resistant reputation**: A reputation system that needs "real-person reputation" rather than "account reputation" reads from `pallet-score` to confirm the entity it is evaluating is a verified person and to retrieve their cumulative score.
- **Gating with thresholds**: A Product gates a premium feature on a score threshold ("you need at least N reputation in this context to do X"); the threshold check reads from the pallet and the Product's UI only unlocks the feature if it passes.


Where to Go Next

## Where to Go Next

- Learn **pallet-people**

    ---

    The foundational pallet that issues the aliases this pallet keys on.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/pallet-people/)

- Learn **Ring-VRF and Aliases**

    ---

    The mechanism that makes the alias-scoping property work — why scores under different contexts cannot be linked.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/ring-vrf-and-aliases/)


Introduction

## Introduction

`pallet-ubc` is the Universal Basic Capacity pallet. Its job is to give every verified person a periodic on-chain _capacity allotment_ — most notably, a quota of fee-free transactions per period. The motivating use case is the "first interaction is free" pattern: a new user can sign and submit one or more transactions without first acquiring DOT, removing the cold-start friction that gates Web3 onboarding.

For a Product developer, `pallet-ubc` is the mechanism that lets your Product be the first thing a new user touches without forcing them to fund their account before they can do anything.

!!! warning "Provisional"
    The exact dispatch surface, the per-period quota amount, the period length, and the precise eligibility rules of `pallet-ubc` on the current build are still being finalized. The "Q11" open question on `pallet-ubc` stable flows is one of the items being firmed up. This page documents the conceptual model; per-extrinsic specifics will be added once confirmed.


What the Pallet Provides

## What the Pallet Provides

Three primitives, organized around the per-period capacity model:

- **The capacity allotment**: Each verified person receives a quota per period (the period length and quota amount are runtime parameters). Within the quota, the user can perform certain operations without paying transaction fees.
- **Consumption tracking**: The pallet records each person's consumption against their current period's quota so subsequent operations correctly account for what is remaining.
- **Eligibility checks**: Operations that _can_ be performed under UBC have their eligibility verified at dispatch — a non-verified account, or an account that has already exhausted the period's quota, falls back to the normal fee path.


When a Product Reaches for UBC

## When a Product Reaches for UBC

The canonical pattern is the first-interaction onboarding flow:

1. A new user opens a Polkadot Product.
2. The Product has the user complete (or confirm) PoP via the App.
3. The Product invokes a UBC-eligible action — typically a small initial setup transaction, a "welcome" claim, a vote, or an action that establishes the user's presence in your Product.
4. The action goes through under the user's UBC quota; the user has not yet acquired any DOT and is still able to participate.
5. After the UBC quota is exhausted (within the period), subsequent transactions follow the normal fee path; by then the user has had a chance to fund their account through the normal channels.

The pallet's value is largely UX: removing the requirement to acquire and hold DOT before doing anything in the Product.


Eligibility Constraints

## Eligibility Constraints

Not every transaction is eligible to consume UBC quota. Typical constraints:

- The user must be a verified person — accounts without PoP cannot consume.
- The operation must be one the runtime has declared UBC-eligible — this is a runtime configuration, not an arbitrary application choice.
- The user must have remaining quota in the current period.

When any of these fail, the transaction does not silently fail — it follows the normal fee path, and the user pays in DOT.


Where to Go Next

## Where to Go Next

- Learn **pallet-people**

    ---

    The pallet that records PoP status — the eligibility input UBC checks against.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/pallet-people/)

- Guide **Verify Your Identity**

    ---

    The setup step that produces the PoP status UBC requires.

    [:octicons-arrow-right-24: Get Started](/reference/apps/infrastructure/pop/)


Introduction

## Introduction

Polkadot's Proof of Personhood delivers a key privacy guarantee: _two Products cannot correlate that they share a user without an explicit cross-domain grant from that user_. That guarantee rests on two pieces of machinery — _Ring-VRF_, the cryptographic primitive that produces context-specific pseudonyms (aliases), and the _`under_alias` runtime origin_, the mechanism that lets pallets gate logic on those pseudonyms instead of on raw accounts.

This page is the conceptual deep dive on both. The App-side reference for _where_ PoP runs is the [Polkadot App's Proof of Personhood](/reference/apps/hosts/polkadot-app/pop/) page.


What PoP Produces

## What PoP Produces

When a user completes Proof of Personhood in the Polkadot App, the result is membership in a ring on the People Chain. A "ring" here is the cryptographic group of all currently-verified persons; being in the ring is what makes the user's later proofs valid.

Two rings coexist on the People Chain:

- **The full personhood ring**: Accounts that completed the biometric PoP flow (PoP Full). Membership requires the full verification ceremony.
- **The lite-people ring**: Accounts that received a governance-authorized attestation (PoP Lite). A separate ring; supply is bounded by governance.

Both rings produce Ring-VRF proofs; what differs is the strength of the membership claim each ring represents.


What an Alias Is

## What an Alias Is

An _alias_ is a context-specific pseudonym derived from the user's ring membership. Three properties define it:

- **Anchored in personhood**: A valid Ring-VRF proof over an alias is also a valid proof of personhood for the user behind it — the alias _is_ the personhood claim, scoped to the specific context.
- **Scoped to a context**: The context is typically a Product's `.dot` domain. The same user produces a consistent alias every time they return to the same Product, but a different alias when they use any other Product.
- **Verifiable without revealing the account**: A verifier confirms "this alias belongs to a real person in the ring" using a Ring-VRF proof. The verifier learns the alias, not the account behind it.

That last property is the load-bearing one for privacy: aliases are unlinkable across Products by design. If two Products could compare their alias lists, every Product would turn into a tracking surface. The alias model makes that combination impossible by default — and the cross-domain alias path (where Product A asks for an alias scoped to Product B's `.dot`) is gated by an explicit user-consent modal.


The `under_alias` Runtime Origin

## The `under_alias` Runtime Origin

For on-chain operations, Polkadot's runtime exposes the `under_alias` dispatchable origin. A user dispatches a call wrapped in `under_alias` and the runtime verifies the underlying Ring-VRF proof before letting the inner call execute. Inside the called pallet, the origin resolves to the user's alias — not the user's account — so the pallet's state stays unlinkable to the account that triggered it.

Two kinds of pallets consume `under_alias` differently:

- Alias-gated pallets key on the alias value itself. The pallet's storage maps from alias to some per-alias state (a vote, a score, a balance, a registration). Two visits from the same user produce the same alias, so the pallet recognizes "the same person came back," but the pallet's storage carries no link back to the user's account.
- Personhood-gated pallets key on the proof itself, not the alias value. The pallet only needs to know that _some_ verified person performed the action; the alias is discarded after the proof check. Useful for "any verified person gets a fee allowance" patterns.

Both shapes are documented per-pallet in the rest of this section.


Cross-Product Aliases

## Cross-Product Aliases

A Product can request an alias scoped to another Product's `.dot` domain. This is the only path by which two Products can cryptographically confirm they share a user. The request triggers a user-consent modal — the user has to explicitly grant the cross-domain alias before the request succeeds. Without that grant, the unlinkability default holds.


Where to Go Next

## Where to Go Next

- Learn **pallet-people**

    ---

    The pallet that issues aliases and implements the `under_alias` origin — the foundation of the PoP pallet set.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/pop/pallet-people/)

- Learn **Proof of Personhood in the Polkadot App**

    ---

    The App-side reference for _where_ PoP verification happens, including the Full and Lite tier model.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/pop/)


Introduction

## Introduction

Submissions to the Statement Store are _not_ priced by transaction fees. The People Chain doesn't charge per statement; instead, every account that wants to publish needs an on-chain allowance record that caps how many live statements it may have at once and how many bytes those statements may total.

The allowance is the spam-resistance mechanism. Without it, a misbehaving account could flood the network's gossip layer with valid (and validly signed) statements that no fee model would meaningfully limit. With it, the network has a deterministic upper bound on per-account traffic that subscribers don't have to defend against at the application layer.


What an Allowance Records

## What an Allowance Records

`pallet-statement-store` stores a per-account `StatementAllowance` record with two limits:

- **`max_count`**: The maximum number of statements the account may have live in the pool at once. As statements expire (TTL elapses, channel replacement evicts an older value), they free up count for new submissions.
- **`max_size`**: The maximum total bytes across the account's currently-live statements. Each submission's payload counts against this; as statements expire, their bytes free up.

Both limits are checked at validation (step 2 of the [lifecycle](/reference/apps/infrastructure/statement-store/lifecycle/)). A submission that would exceed either is rejected at the pallet boundary before it ever enters the gossip layer.


How the Limits Interact

## How the Limits Interact

A typical Product hits the _count_ limit before the size limit if it publishes many small messages, and the _size_ limit before the count limit if it publishes fewer but larger payloads. Two practical consequences:

- A chat-like Product (lots of small statements) should budget primarily against `max_count`. Each message stays live until its TTL elapses, so a chatty user can occupy several `max_count` slots simultaneously.
- A Product that publishes occasional large payloads (configuration blobs, large status updates) should budget primarily against `max_size`. The size limit is total-across-live, not per-statement, so a few big payloads can saturate the quota even if `max_count` is far from full.

A Product can lower its size pressure by moving content to the Bulletin Chain and publishing only a small statement carrying the resulting CID. The CID is short; the bytes live on Bulletin and don't count against the Statement Store allowance.


Per-Statement Size Limit

## Per-Statement Size Limit

Independent of the per-account allowance, each statement payload has a hard 512-byte limit (after JSON encoding) enforced by the SDK. Larger payloads throw `StatementDataTooLargeError` before submission. For content bigger than 512 bytes, the recommended pattern is the Bulletin Chain + CID composition above.


Provisioning on TestNet

## Provisioning on TestNet

!!! warning "Provisional"
    The process for obtaining a Statement Store allowance on TestNet is still being defined. The [Get TestNet Tokens](/apps/get-started/get-testnet-tokens/) guide is the source of truth as the provisioning flow stabilizes.

On TestNet today, allowances are provisioned by calling the `increase_allowance_by` extrinsic — typically through a governance or sudo route, since the call is privileged.


Where to Go Next

## Where to Go Next

- Learn **Lifecycle**

    ---

    Where allowance validation sits in the statement lifecycle (step 2 — before gossip propagation).

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/lifecycle/)

- Guide **Get TestNet Funds**

    ---

    The setup step where TestNet allowances are provisioned.

    [:octicons-arrow-right-24: Get Started](/apps/get-started/get-testnet-tokens/)


Introduction

## Introduction

`pallet-statement-store` supports an optional channel field on every statement. When a new submission carries the same `channel` as an existing live statement from the same account, the pallet replaces the older one in the node's pool — and the replacement propagates to subscribed peers, who drop the old statement in favor of the new one.

The result is a last-write-wins primitive on top of the standard pub/sub: each channel name, scoped per-account, maps to a single live value at any moment.

The Product SDK abstracts this as `ChannelStore<T>` in `@parity/product-sdk-statement-store`. The underlying storage primitive is the same — every channel value is still a statement going through the same lifecycle — but the read surface looks like a key-value map keyed by channel name.


When to Reach for Channels

## When to Reach for Channels

Three patterns are the canonical fit:

- **Presence indicators**: _"Alice is online."_ Only the latest value matters; older states should be replaced as the user goes idle and active again.
- **Multiplayer cursors and other live UI state**: The user has moved their cursor to position N; the previous position is no longer relevant.
- **"Now playing" / live status**: Only the current track matters, not the history of what was playing earlier in the session.

For append-only events — chat messages, action logs, social-feed posts — channels are the wrong primitive. Each event needs to live independently; using a channel would erase the previous message every time the user sent a new one. For those use cases, keep using `client.publish` directly.


Scope and Replacement Rules

## Scope and Replacement Rules

Channels are scoped per-account. The pallet's replacement rule only matches statements from the same signer — one user cannot overwrite another user's channel value. Two users posting on the same channel name produce two independent live values, one per account.

The replacement is atomic from a subscriber's perspective: the older statement is evicted from the pool and the newer one inserted in the same step. A subscriber sees the new value; the old value, if it was in flight to the subscriber's node, is dropped before it reaches the application callback.


The Channel-Store API

## The Channel-Store API

The SDK's `ChannelStore<T>` exposes the standard surface:

- **`channels.write(name, value)`**: Publish a new value on the named channel. The SDK hashes the channel name with Blake2b-256, attaches the per-account scoping, and submits as a statement.
- **`channels.read(name)`**: Return the latest value seen on that channel locally.
- **`channels.readAll()`**: Return the full map of `(channel name → latest value)` the local node has seen.
- **`channels.onChange(callback)`**: Fire on every transition (a new write replacing an older value).

`ChannelStore` automatically timestamps the value if your `T` omits a timestamp, so consumers can reason about ordering when needed.

!!! warning "Provisional"
    The maximum number of live channels per account, the interaction between channels and the per-account `max_count` allowance limit, and any cross-account aggregation primitives are still being finalized.


Where to Go Next

## Where to Go Next

- Learn **Subscriptions**

    ---

    The subscription surface that delivers channel transitions to subscribers, including the per-channel dedup behavior.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/subscriptions/)

- Guide **Exchange Ephemeral Messages**

    ---

    The Product-side how-to including the `ChannelStore` walkthrough.

    [:octicons-arrow-right-24: Get Started](/apps/build/pub-sub-off-chain-data/)


Introduction

## Introduction

The Statement Store is Polkadot's publish/subscribe layer for short-lived, signed messages between Product users. A Product publishes a small statement; the People Chain validates it; the network gossips it out to anyone subscribed to a matching topic. Statements never enter the chain's block storage — they live briefly in nodes' local pools and decay after a short TTL.

The split is the point: pallet rules on chain, payloads in gossip. The chain enforces who can publish and how much; the gossip layer carries the data. That is what makes the Statement Store the right tool for _real-time signaling between users_ (chat messages, presence, multiplayer cursors, typing indicators, "now playing" status), and for anything else that has to propagate fast and does not need to be permanent.

For when to reach for this versus the [Bulletin Chain](/reference/apps/infrastructure/bulletin-chain/) or local storage, see the [Storage options for your Product](/apps/build/pub-sub-off-chain-data/) callout.


How a Statement Reaches Subscribers

## How a Statement Reaches Subscribers

Three properties define the path from a publish call to a subscriber's callback firing:

- **Signed at the source**: Every submission carries an authentication proof (Sr25519 by default). The Host requests the proof through the App; the Product never handles keys.
- **Allowance-gated by the pallet**: The People Chain's `pallet-statement-store` enforces a per-account quota — `max_count` live statements and `max_size` total bytes. Submissions exceeding the quota are rejected at the pallet boundary, so spam does not reach subscribers. See [Allowance](/reference/apps/infrastructure/statement-store/allowance/).
- **Gossiped, not block-stored**: Once accepted into the node's statement pool, the submission propagates peer-to-peer through the People Chain network's gossip layer. Subscribers see it via their node's filtered statement subscription. See [Subscriptions](/reference/apps/infrastructure/statement-store/subscriptions/).

For the full statement lifecycle (validation, propagation, expiry, maintenance) see [Lifecycle](/reference/apps/infrastructure/statement-store/lifecycle/).


Delivery Semantics

## Delivery Semantics

The Statement Store does not retry, acknowledge, or guarantee ordering — those are network-layer guarantees the gossip protocol does not provide. Delivery is best-effort. If your Product needs to know a message reached a peer, the peer publishes an acknowledgment statement; reliability is composed at the application layer, not at the protocol.

If the content needs to outlive the TTL, store it on the Bulletin Chain and publish a CID as the statement payload. The composition — Statement Store for the live signal, Bulletin Chain for content that has to be there later — is documented in the Polkadot App's [Chat reference](/reference/apps/hosts/polkadot-app/chat/).


Where to Go Next

## Where to Go Next

- Learn **Lifecycle**

    ---

    How a statement progresses from submission through validation, gossip, TTL, and eviction — and what each step guarantees.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/lifecycle/)

- Learn **Subscriptions**

    ---

    How a subscriber registers a topic filter, what `topic2` adds, and the dedup behavior on the receiving side.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/subscriptions/)

- Learn **Channels**

    ---

    The last-write-wins primitive built on top of statements — useful for soft state like presence indicators where only the latest version matters.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/channels/)

- Learn **Allowance**

    ---

    The allowance model that gates submissions — `max_count`, `max_size`, and how a Product budgets traffic against an account's quota.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/allowance/)

- Guide **Exchange Ephemeral Messages**

    ---

    The Product-side how-to: setting up the client, subscribing, publishing typed statements, using channels.

    [:octicons-arrow-right-24: Get Started](/apps/build/pub-sub-off-chain-data/)


Introduction

## Introduction

A Statement Store submission passes through five recognizable steps between a `publish` call and being dropped from the network. Knowing where in that lifecycle a given statement is helps you reason about delivery: some failure modes can only happen at one step, and some guarantees only exist between two of them.


The Five Steps

## The Five Steps

1. **Submission**: The Product calls `publish` on its Statement Store client (or the equivalent TrUAPI call). The Host obtains an authentication proof from the paired App and forwards the request to the People Chain node's `statement_submit` JSON-RPC.

2. **Validation**: `pallet-statement-store` checks the submission against the per-account allowance (see [Allowance](/reference/apps/infrastructure/statement-store/allowance/)). If `max_count` or `max_size` would be exceeded, or the authentication proof is invalid, the submission is _rejected_ at this step and never reaches any subscriber. The publish call returns `false` (statement was rejected) rather than throwing.

3. **Acceptance into the pool**: A validated submission enters the local node's statement pool, the in-memory data structure that holds live statements. The publish call returns `true` at this point — the statement is now live on this node.

4. **Gossip propagation**: The node propagates accepted statements to peers that have registered a matching topic filter. Each peer's node decides whether the statement matches and forwards or drops accordingly. This is best-effort: peers may not be listening, may drop the statement under load, or may have already seen a duplicate.

5. **Expiry and eviction**: Each statement carries a TTL (default 30 seconds, configurable per submission up to the pallet-enforced cap). When the TTL elapses, the statement is evicted from each node's pool. After eviction, subscribers who join late will not see it — there is no "fetch by hash" recovery for an expired statement, and there is no chain block to look it up in.


What Each Step Guarantees

## What Each Step Guarantees

A few load-bearing facts that fall out of the lifecycle:

- **Between submission and acceptance, the chain enforces validity**: A spam-flood attempt against your Product's topic does not reach subscribers because invalid or quota-exceeding submissions are rejected at step 2.
- **Between acceptance and expiry, propagation is best-effort**: A subscriber connected to the same node *will* see an accepted statement; a subscriber connected to a different node may or may not, depending on the gossip path.
- **After expiry there is no recovery**: If your Product needs the bytes to be retrievable later, the canonical pattern is to write them to the Bulletin Chain and publish the resulting CID as the statement payload. The statement carries the live signal; the Bulletin Chain holds the durable content.


On-Chain Trigger (Forthcoming)

## On-Chain Trigger (Forthcoming)

!!! warning "Forthcoming"
    On-chain-triggered statements — submissions originated by an OCW (off-chain worker) rather than a Product — are a forthcoming surface for runtime developers, not Product developers. The reference for this path will be added separately.


Where to Go Next

## Where to Go Next

- Learn **Allowance**

    ---

    The validation step's quota mechanism — what gets rejected at step 2 and why.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/allowance/)

- Learn **Subscriptions**

    ---

    How a subscriber attaches to the gossip stream at step 4 and the dedup behavior on the receiving side.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/subscriptions/)


Introduction

## Introduction

A Product subscribes to the Statement Store by registering a topic filter with a People Chain node. The node forwards only the statements matching that filter to the Product's callback; everything else on the gossip stream is dropped before it reaches the Product. The filter is the Product's lever — pick it correctly and the Product sees exactly the traffic it wants.

The Product SDK wraps this as `client.subscribe<T>(callback, options?)` in `@parity/product-sdk-statement-store`. Underneath, it calls the node's `statement_subscribeStatement` JSON-RPC with the appropriate filter, decodes payloads into the typed `T`, and dedupes by content hash before invoking the callback.


How Topics Filter

## How Topics Filter

Two topics shape every subscription:

- **The primary topic**: The Product's identity on the gossip layer. The SDK derives it as `blake2b(appName)` and tags every submission your Product publishes with that primary topic. Subscribers from _other_ Products on the same network see only their own traffic, and your subscribers see only yours — the node filters traffic from other Products at the boundary before it reaches your callback.
- **The optional `topic2`**: Scopes a subscription further within your Product's primary topic. Pass it as a room id, a document id, or any other secondary key. The SDK hashes it with Blake2b-256 and adds it to the filter; only statements that match both topics are forwarded.

A Product subscribing without `topic2` sees every statement published anywhere in your Product. With `topic2`, it sees only the slice that matches the secondary key. Both shapes are routinely useful — global Product chat needs the full stream; a specific room needs the scoped one.


What the Callback Receives

## What the Callback Receives

The subscription delivers each matching statement to your callback decoded into your typed `T` and deduped by channel and content hash. The dedup means:

- A statement that propagates to the Product's node via multiple gossip paths fires the callback only once.
- A statement replaced on the same channel by a newer one (see [Channels](/reference/apps/infrastructure/statement-store/channels/)) fires the callback for the newer value only.


Subscription Lifecycle

## Subscription Lifecycle

`client.subscribe` returns an `Unsubscribable` whose `unsubscribe()` method tears down the JSON-RPC subscription on the node and stops the callback from receiving further events. Tear subscriptions down when the Product no longer needs them — typically on view unmount or process shutdown — so the node doesn't hold open a filter your Product has stopped reading.


Initial-State Behavior

## Initial-State Behavior

Some `subscribe` implementations return the current statement pool matching the filter (a backfill of statements that haven't expired yet) plus an ongoing stream of new matches; some return only the ongoing stream. The Product SDK's default returns the current pool plus the ongoing stream, so a subscriber that joins late still sees recently published statements that haven't aged out.

!!! warning "Provisional"
    The exact backfill behavior, the per-node pool size limits visible to subscribers, and the reconnection / missed-events semantics when a Product's node loses gossip connectivity are still being finalized.


Where to Go Next

## Where to Go Next

- Learn **Channels**

    ---

    The last-write-wins primitive built on the same gossip layer — `topic2` filters by scope; channels filter by replacement semantics.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/channels/)

- Learn **Lifecycle**

    ---

    Where in the lifecycle a subscription attaches (step 4: gossip propagation) and what the dedup behavior protects against.

    [:octicons-arrow-right-24: Reference](/reference/apps/infrastructure/statement-store/lifecycle/)


Introduction

## Introduction

The Account Management method group lets a Product discover which account it should operate against. A Polkadot Product never sees the user's root identity; it sees a per-Product sub-account derived from the user's identity and the Product's `.dot` domain. See [Sandbox and Sub-Accounts](/reference/apps/protocol/truapi/sandbox/) for the derivation model. This method group is the surface that resolves that sub-account, exposes its address, and returns related state.

The Product SDK wraps this surface as `SignerManager.getProductAccount(dotNsIdentifier, derivationIndex)` in [`@parity/product-sdk-signer`](https://www.npmjs.com/package/@parity/product-sdk-signer).


Conceptual Contract

## Conceptual Contract

The group covers:

- **Resolving a Product-scoped account**: Given the Product's dotNS identifier and a derivation index (`0` is the conventional default), the Host returns a `SignerAccount` with the SS58-formatted address, the raw public key, and an optional display name.
- **Listing accounts available to the Product**: The Host enumerates the sub-accounts the user has approved for this Product to see.
- **Selecting an active account**: Before the Product invokes any signing operation, it tells the Host which account it intends to use for the rest of the session or until it switches accounts.

Account resolution is local and instantaneous. The Host computes the derived address without needing to round-trip to the paired App. The private key never leaves the App; the address derivation uses public information only.

!!! warning "Provisional"
    Multi-account scenarios (a Product asking for several sub-accounts at non-default indexes for distinct flows), cross-domain account requests (alias-scoped accounts spanning multiple `.dot` domains), and the precise list-accounts surface are still being finalized.


Where to Go Next

## Where to Go Next

- Learn **Sandbox and Sub-Accounts**

    ---

    The conceptual model for why a Product gets a derived sub-account in the first place, and how isolation works.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/sandbox/)

- Guide **Accounts and Signing**

    ---

    The Product-side how-to: getting an account, building a transaction, and submitting it.

    [:octicons-arrow-right-24: Get Started](/apps/build/sign-and-submit/)


Introduction

## Introduction

The TrUAPI Calls method group is the lifecycle handshake between a Product and its Host. When a Product loads, it identifies itself to the Host; the Host responds with the TrUAPI surface the Product is allowed to use. Every other method group sits on top of this one — a Product cannot invoke any capability until the handshake completes.

For Product developers, this is mostly implicit. The [`@parity/product-sdk`](https://www.npmjs.com/package/@parity/product-sdk) core handles the handshake in `createApp()` and exposes the resulting `App` object that the rest of the SDK relies on. For Host developers, this is the entry point that every Host implementation has to honor.

!!! warning "Provisional"
    The exact set of lifecycle calls in this group, their names, parameters, and the precise sequencing the Host enforces during Product load are still being finalized. The conceptual contract below is stable; per-call signatures will be added as the surface confirms.


Conceptual Contract

## Conceptual Contract

Three things have to happen between a Product loading and that Product being able to use the rest of TrUAPI:

- **Product identifies itself**: The Product declares its identity, typically its `.dot` name, so the Host can scope sub-accounts, storage, and topic filters consistently.
- **Host advertises capabilities**: The Host responds with the TrUAPI version it implements and the set of method groups available. A Product can branch on the returned capabilities if it needs to support multiple Hosts with different capability sets.
- **Product completes setup-time configuration**: The Product configures session-level objects, such as the chain client, storage handle, and signer, against the Host context the lifecycle call returned.

After the lifecycle handshake completes, the Product is in steady state: every other call (signing, storage, chat, chain reads) goes through its own method group.


Where to Go Next

## Where to Go Next

- Learn **Permissions**

    ---

    The next layer after the lifecycle handshake: what capabilities the Host will let the Product actually invoke, based on declared and granted permissions.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/permissions/)


Introduction

## Introduction

The Chain Interaction method group lets a Product read on-chain state, including account balances, storage items, and runtime constants, and subscribe to changes on those values. It is the foundation most Products build on. Reading state is usually the first thing a Product does after lifecycle setup.

The Product SDK wraps this surface as [`@parity/product-sdk-chain-client`](https://www.npmjs.com/package/@parity/product-sdk-chain-client), which gives the Product a typed PAPI client per connected chain. The same code works whether the Product runs inside a Host, where calls route through the Host's chain client, or outside one in development, where the client falls back to a direct WebSocket connection.


Conceptual Contract

## Conceptual Contract

The group exposes the chain-client surface a Product expects, with the Host handling the routing:

- **Connecting to one or more chains**: A preset path (`getChainAPI(env)`) returns a ready-to-use client for a known environment. A Bring Your Own Descriptors path (`createChainClient`) lets the Product compose any set of chains. Both paths produce the same client shape.
- **Reading storage**: Every connected chain exposes a typed `.query` surface for storage items and a `.constants` surface for runtime constants via PAPI's typed descriptors.
- **Subscribing to changes**: The underlying PAPI client exposes `watchValue(address)` and similar primitives for live subscriptions. The Host mediates the connection but the subscription surface is the standard PAPI one.
- **Reading multiple chains in parallel**: Independent connections per chain let reads compose with `Promise.all`.
- **Dropping to the raw PAPI client**: `.raw` exposes the underlying `PolkadotClient` for advanced flows the typed surface does not cover, such as custom remote procedure call methods, low-level submissions, and raw storage subscriptions.

For the conceptual model and the Product-side how-to, see [Read Chain State](/apps/build/read-chain-state/).

!!! warning "Provisional"
    Subscription error semantics (reconnection, missed-events recovery), per-Host differences in how the underlying connection is routed, and the precise set of presets shipped with the SDK are still being finalized.


Where to Go Next

## Where to Go Next

- Guide **Read Chain State**

    ---

    The Product-side how-to: preset and BYOD connection paths, reading storage / constants / account state, multi-chain reads.

    [:octicons-arrow-right-24: Get Started](/apps/build/read-chain-state/)

- Learn **Signing**

    ---

    The signing method group that complements chain reads and lets a Product react to state changes on-chain.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/signing/)


Introduction

## Introduction

The Chat method group lets a Product participate in the Polkadot App's chat surface by registering as a bot in a room, publishing typed messages, and rendering custom message types that the App displays inline. The underlying transport composes the Statement Store for signaling with the Bulletin Chain for content that has to outlive the gossip time to live (TTL). The Chat group exposes a higher-level surface on top of that pattern.

For the conceptual model of how Chat composes the two layers, see the [Chat in the Polkadot App](/reference/apps/hosts/polkadot-app/chat/) reference. This page documents the TrUAPI surface that powers it.


Conceptual Contract

## Conceptual Contract

Methods in this group fall into three families:

- **Room and bot registration**: A Product registers as a bot inside a room and creates simple group chats via `host_chat_create_simple_group`, introduced in `v0.2`. The registration call is gated by the standard Permissions surface.
- **Publishing typed messages**: The Host accepts `Text`, `RichText`, `Actions`, `File`, `Reaction`, and `Custom` message types. It handles encoding, attaches the per-Product account context, and dispatches to the Statement Store and Bulletin Chain underneath.
- **Custom-message rendering (reverse subscription)**: `product_chat_custom_message_render_subscribe` lets the App ask the Product to render its own `Custom` message types in place. The Host opens a subscription against the Product, and the Product responds with rendered output for each message it owns.

Messages a Product submits before bot registration completes are queued automatically. The Product does not need to track registration state.

!!! warning "Provisional"
    Lifecycle semantics around room membership, the precise shape of typed-message payloads, and the rendering-subscription contract are still being finalized. The capabilities above are stable; per-method signatures will be added as the surface confirms.


Where to Go Next

## Where to Go Next

- Learn **Chat in the Polkadot App**

    ---

    The conceptual reference for how Chat composes Statement Store and Bulletin Chain, and what each message type is for.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/chat/)


Introduction

## Introduction

The Entropy method group lets a Product obtain a stable secret derived from the user's root key, scoped to that Product. A Product uses it to seed key material it needs to hold itself — an encryption key for local data, a deterministic identifier, a seed for a Product-side keypair — without ever touching the user's root entropy or asking the user to manage a separate secret.

The value is deterministic: it is derived from the user's root [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) entropy through a three-layer BLAKE2b-256 keyed-hashing scheme, so the same input always produces the same output. A Product that asks for the same derivation context tomorrow, or on another device the user has restored, gets the same 32 bytes back. This is what makes it useful for seeding: the Product can re-derive its key material instead of storing it.

!!! danger "Not a randomness source"
    Host-derived entropy is deterministic and pre-computable — it is key-derivation, not randomness. Do **not** use it for lottery draws, fair-shuffle games, randomized selection, or any flow where a value must be unpredictable to the parties involved. Anyone who can reproduce the derivation can predict the output. For unpredictable values, use the browser's `crypto.getRandomValues`; for randomness that an external party must be able to verify against the chain, use an on-chain VRF or beacon directly, not this group.

This is the same `entropy = randomness` confusion behind the RFC-7 bug, where a Host minted a random `rootAccountSecret` instead of deriving it. The point of this group is the opposite of randomness: reproducible derivation.


Conceptual Contract

## Conceptual Contract

The group exposes a single derivation primitive:

- **Deriving entropy for a context**: The Product requests 32 bytes of entropy derived from the user's root entropy and bound to a derivation context. The same context always yields the same bytes; two different contexts yield independent values, so a Product cannot accidentally reuse one secret where it meant to use another.

There is no subscription, no per-block or per-epoch update, and no randomness beacon. The group is one `host_derive_entropy` request and its deterministic response.

!!! warning "Provisional"
    The exact derivation-context surface (how a Product names and scopes a derivation) and the SDK wrapper around `host_derive_entropy` are still being finalized. The deterministic-derivation contract described here matches the TrUAPI v0.2 spec and is stable.


Where to Go Next

## Where to Go Next

- Learn **Sandbox and Sub-Accounts**

    ---

    Why a Product is scoped to derived material rather than the user's root identity — the same derivation principle this group exposes for arbitrary secrets.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/sandbox/)


Introduction

## Introduction

TrUAPI is the protocol every Polkadot Product uses to talk to the Host running it. It is the _only_ interface a Product has to the rest of the world. Every chain interaction, storage write, outbound request, signing operation, and Proof of Personhood proof crosses this boundary, and the Host on the other side enforces the sandbox.

In the Polkadot Triangle, three Hosts run Products. TrUAPI is the contract by which a Product talks to whichever Host is loading it. Most Product developers will never call TrUAPI directly. The [Product SDK](/reference/glossary/#polkadot-sdk) (the `@parity/product-sdk` family of packages) wraps it in typed methods. The methods you call still go through TrUAPI, and the Host validates and routes everything.

!!! note "TrUAPI and its packages"
    TrUAPI is the name of the protocol specification maintained at [`paritytech/truapi`](https://github.com/paritytech/truapi). It ships as two layers of packages: [`@parity/truapi`](https://www.npmjs.com/package/@parity/truapi) is the low-level generated protocol client (the wire), and the [`@parity/product-sdk`](https://www.npmjs.com/package/@parity/product-sdk) family is the higher-level SDK most Products use. This reference uses "TrUAPI" for the protocol surface and names the `@parity/product-sdk-*` package in the [Packages](/reference/apps/protocol/truapi/packages/) table that wraps each group.

This reference documents the protocol surface in three layers:

1. **Conceptual model**: Why the Host-Product boundary exists, what derived sub-accounts mean, and how the sandbox enforces isolation. See [Sandbox and Sub-Accounts](/reference/apps/protocol/truapi/sandbox/).
2. **Operational properties**: The protocol's versioning model and the packages that implement it. See [Versioning](/reference/apps/protocol/truapi/versioning/) and [Packages](/reference/apps/protocol/truapi/packages/).
3. **Method groups**: Eleven groups covering the capabilities a Product reaches for. One page per group is linked in [The Eleven Method Groups](#the-eleven-method-groups). A few host-lifecycle methods sit outside these groups; see the note below.

!!! warning "Provisional"
    The complete per-method reference for TrUAPI remains provisional. The method-group pages below cover what each group is for and the conceptual contract; the exhaustive per-method signatures, parameter shapes, and return types will be added as the surface stabilizes.


Two Audiences

## Two Audiences

TrUAPI has two distinct readerships, and the per-method pages are written to serve both at once:

- **Product developers**: You build Products that call the TrUAPI surface. You need to know what each method does, what permissions gate it, what it returns, and how the Product SDK wraps it.
- **Host developers**: You build Hosts that implement the TrUAPI surface. You need to know what each method must guarantee, how to validate the Product's call, and how to enforce the sandbox at every entry point.

A Product developer can usually rely on the Product SDK (the [`@parity/product-sdk`](https://www.npmjs.com/package/@parity/product-sdk) family of packages) and never call TrUAPI directly. A Host developer is reading the same pages to know what the SDK calls underneath, and what their Host has to honor.


The Eleven Method Groups

## The Eleven Method Groups

| Group                                                                                | Covers                                                                                |
|:-----------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------------:|
| [TrUAPI Calls](/reference/apps/protocol/truapi/calls/)                    | The lifecycle calls Products use to identify themselves and the Host uses to introduce capabilities. |
| [Permissions](/reference/apps/protocol/truapi/permissions/)               | Declaring, querying, and gating on Product capabilities.                                             |
| [Local Storage](/reference/apps/protocol/truapi/local-storage/)           | The `KvStore` API for per-Product, per-device key-value persistence.                                 |
| [Account Management](/reference/apps/protocol/truapi/account-management/) | Resolving per-Product sub-accounts and reading their state.                                          |
| [Signing](/reference/apps/protocol/truapi/signing/)                       | The mediated signing flow for building transactions and dispatching them through the paired App.     |
| [Chat](/reference/apps/protocol/truapi/chat/)                             | Room and bot registration, typed message publishing, custom message rendering.                       |
| [Statement Store](/reference/apps/protocol/truapi/statement-store/)       | Publishing and subscribing to gossip-distributed signed statements on People Chain.                  |
| [Preimage](/reference/apps/protocol/truapi/preimage/)                     | Off-chain content addressed by hash for on-chain operations to dereference.                          |
| [Chain Interaction](/reference/apps/protocol/truapi/chain-interaction/)   | Reading chain state and subscribing to changes through the Host.                                     |
| [Payment](/reference/apps/protocol/truapi/payment/)                       | Payment-flow primitives (general `Balances` transfers and the Pocket flow).                          |
| [Entropy](/reference/apps/protocol/truapi/entropy/)                       | Deterministic per-Product entropy derived from the user's root key (key-derivation, not randomness). |

!!! note "Methods outside the eleven groups"
    A small number of host-lifecycle methods in the TrUAPI v0.2 spec are not part of any of the eleven groups above — `host_navigate_to`, `host_push_notification`, and `host_feature_supported`. They are Host-environment hooks rather than Product capabilities, and are documented per-Host rather than in this protocol surface.

!!! warning "Cross-Host conformance is the target, not yet the guarantee"
    TrUAPI is specified as a single canonical surface, and the intent is that a Product runs unchanged across every Host. Today that holds between Polkadot Web and Polkadot Desktop, which share the TypeScript host-container (SCALE, `host_*` calls). The mobile Polkadot App — the canonical signer for transactions — currently exposes a hand-written JSON bridge whose method names differ from the spec and, in places, between iOS and Android. Treat "the same surface on every Host" as the conformance goal the spec defines, not a settled property of every shipping Host. See the cross-host conformance tracking in the SDK team's work.


High-Level Diagram

## High-Level Diagram

!!! warning "Provisional"
    The canonical TrUAPI architecture diagram showing the Host, the Product, the SDK layers in between, and the eleven method groups as a single map — is still being finalized. It will be embedded here and referenced from the section overview once confirmed.


Where to Go Next

## Where to Go Next

- Learn **Sandbox and Sub-Accounts**

    ---

    Why a Product gets a derived sub-account rather than the user's root identity, and why the Host API is the only egress.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/sandbox/)

- Learn **Versioning**

    ---

    How TrUAPI versions evolve and what compatibility a Product can rely on across them.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/versioning/)

- Learn **Packages**

    ---

    The package map: which SDK package wraps which TrUAPI method group, and where the boundary between Product code and Host code lives.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/packages/)


Introduction

## Introduction

The Local Storage method group exposes the per-Product, per-device key-value store a Product uses to remember values between sessions, such as user preferences, drafts, and cached values. The Product SDK wraps this group as a `KvStore` in the [`@parity/product-sdk-local-storage`](https://www.npmjs.com/package/@parity/product-sdk-local-storage) package.

Two properties matter for how this group is shaped:

- **Per-Product namespacing**: The Host prepends every key with a prefix derived from the Product's `.dot` domain. A Product cannot accidentally read or write another Product's keys; the boundary is at the Host, not in the Product's code.
- **Host-dependent backend**: Inside Polkadot Desktop, reads and writes go to the Host's on-device storage. Inside Polkadot Web, the Host backs them with browser-managed storage. The SDK abstracts the difference; the Product's code is identical either way.


Conceptual Contract

## Conceptual Contract

The group exposes the standard key-value surface plus a few helpers:

- **`get(key)`, `set(key, value)`, and `remove(key)`**: Read, write, and delete a string-valued key.
- **`getJSON(key)` and `setJSON(key, value)`**: Read and write JSON-serializable values, with the Host handling encoding.
- **Prefixed-namespace creation**: A Product can carve out further sub-namespaces inside its own per-Product space, useful when multiple Product features share the store and want to avoid collisions.

Reads return `null` for absent keys; deletes are idempotent (no error on missing keys).

!!! warning "Provisional"
    Quota policy (per-Product byte caps, eviction rules), the exact per-Host backend behavior (especially around Web's browser-managed storage), and any subscription-style key-change notifications are still being finalized. The basic key-value contract above is stable.


Where to Go Next

## Where to Go Next

- Guide **Persist Data Locally**

    ---

    The Product-side how-to: initializing the store, reading and writing values, prefix namespaces, error handling.

    [:octicons-arrow-right-24: Get Started](/apps/build/persist-data-locally/)


Introduction

## Introduction

A Product developer rarely calls TrUAPI directly. The [`@parity/product-sdk`](https://www.npmjs.com/package/@parity/product-sdk) family of packages is the typed wrapper, and most Products will install one of them per capability they consume. This page maps which package wraps which TrUAPI method group, and which Product-side how-to guide demonstrates each.

!!! warning "Provisional"
    Package names and version pins remain provisional while the SDK matures. This page documents known packages and boundaries; exact versions and future package splits are outside the current scope.


Package Map

## Package Map

|                            TrUAPI Method Group                            |                                                                                SDK Package                                                                                |                              Product-Side How-To                               |
|:-------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------:|
|          [TrUAPI Calls](/reference/apps/protocol/truapi/calls/)           |                                                                             `@parity/product-sdk` (core)                                                                              |                           N/A (lifecycle, implicit)                            |
|        [Permissions](/reference/apps/protocol/truapi/permissions/)        |                                                                             `@parity/product-sdk` (core)                                                                              |                                      N/A                                       |
|      [Local Storage](/reference/apps/protocol/truapi/local-storage/)      |                                        [`@parity/product-sdk-local-storage`](https://www.npmjs.com/package/@parity/product-sdk-local-storage)                                         |           [Persist Data Locally](/apps/build/persist-data-locally/)            |
| [Account Management](/reference/apps/protocol/truapi/account-management/) | [`@parity/product-sdk-signer`](https://www.npmjs.com/package/@parity/product-sdk-signer) / [`@parity/product-sdk-address`](https://www.npmjs.com/package/@parity/product-sdk-address) |          [Sign and Submit Transactions](/apps/build/sign-and-submit/)          |
|            [Signing](/reference/apps/protocol/truapi/signing/)            |                                    `@parity/product-sdk-signer` / [`@parity/product-sdk-tx`](https://www.npmjs.com/package/@parity/product-sdk-tx)                                    |          [Sign and Submit Transactions](/apps/build/sign-and-submit/)          |
|               [Chat](/reference/apps/protocol/truapi/chat/)               |                                                                            `@parity/product-sdk` chat APIs                                                                            |                                      N/A                                       |
|    [Statement Store](/reference/apps/protocol/truapi/statement-store/)    |                                      [`@parity/product-sdk-statement-store`](https://www.npmjs.com/package/@parity/product-sdk-statement-store)                                       | [Publish and Subscribe to Off-Chain Data](/apps/build/pub-sub-off-chain-data/) |
|           [Preimage](/reference/apps/protocol/truapi/preimage/)           |                                        [`@parity/product-sdk-host`](https://www.npmjs.com/package/@parity/product-sdk-host) (Preimage manager)                                        |                       (covered in Bulletin Chain how-to)                       |
|  [Chain Interaction](/reference/apps/protocol/truapi/chain-interaction/)  |     [`@parity/product-sdk-chain-client`](https://www.npmjs.com/package/@parity/product-sdk-chain-client) and [`polkadot-api`](https://www.npmjs.com/package/polkadot-api) (PAPI)      |               [Read Chain State](/apps/build/read-chain-state/)                |
|            [Payment](/reference/apps/protocol/truapi/payment/)            |                                  `@parity/product-sdk-tx` and [`@parity/product-sdk-utils`](https://www.npmjs.com/package/@parity/product-sdk-utils)                                  |                                      N/A                                       |
|            [Entropy](/reference/apps/protocol/truapi/entropy/)            |                                                               `@parity/product-sdk` (deterministic entropy derivation)                                                                |                               n/a (forthcoming)                                |


## The Boundary Between Product Code and Host Code

A useful way to read the package map is by where the package executes:

- **Pure Product packages**: Packages such as `@parity/product-sdk-local-storage`, `@parity/product-sdk-signer`, `@parity/product-sdk-chain-client`, and `@parity/product-sdk-tx` execute inside the Product sandbox. They are typed wrappers that build a TrUAPI call and dispatch it through the Host API. Your Product depends on them at compile time.
- **Product-SDK umbrella**: `@parity/product-sdk` ties them together at the top of a Product and wires up the App / chain / storage surfaces with consistent defaults.
- **Host implementations of TrUAPI**: Host implementations live in the Hosts themselves, not in any Product-side package. A Product does not depend on Host code at compile time; it depends on the Host honoring the TrUAPI contract at run time.


Where to Go Next

## Where to Go Next

- Learn **Versioning**

    ---

    How package versions map to TrUAPI protocol versions.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/versioning/)


Introduction

## Introduction

The Payment method group lets a Product accept or initiate payments. Payments are signed transactions like any other transaction, but this group collects the two payment shapes a Product is most likely to need, the standard `Balances.transfer_keep_alive` flow and the personhood-aware Pocket flow.

For most Product use cases, this group is consumed through `@parity/product-sdk-tx` and helpers in `@parity/product-sdk-utils` (formatting, validation), with the same `signAndSubmit` round trip every other signed call goes through.


Conceptual Contract

## Conceptual Contract

The group covers:

- **Building a payment request**: The Product encodes what a merchant is asking for, including recipient, amount, and optional memo, into a payload the payer's Product can parse and pre-fill into a transfer form. No on-chain interaction happens at request-build time; the request is an application-layer payload.
- **Constructing a transfer extrinsic**: Given a validated recipient and a planck-denominated amount, building a `Balances.transfer_keep_alive` (or `transfer_allow_death` when the sender intends to drain the account) ready to sign.
- **Submitting and watching the payment**: The Product passes the extrinsic through the standard Signing surface and observes status transitions of `signing`, `broadcasting`, `in-block`, and `finalized`.
- **Pocket variant**: Pocket is personhood-aware and two-sided. The sender acts on Desktop, the receiver acts on the App, and the receiver explicitly accepts or declines. See [Pocket on Polkadot Desktop](/reference/apps/hosts/polkadot-desktop/pocket/) for the conceptual model.

The Payment group does not introduce new signing semantics. Every payment-side transaction still routes through the per-transaction approval flow on the paired App.

!!! warning "Provisional"
    The Pocket-specific TrUAPI methods (whether and how a Product can initiate a Pocket transfer programmatically versus relying on Host UI), payment-request URL formats as a canonical wire format, and any asset-aware extensions (multi-asset transfers, pUSD-specific calls) are still being finalized.


Where to Go Next

## Where to Go Next

- Learn **Pocket on Polkadot Desktop**

    ---

    The Pocket send flow's conceptual model and how it pairs with the App-side recipient view.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/pocket/)


Introduction

## Introduction

The Permissions method group is the gate every other method group implicitly passes through. Before a Product can sign a transaction, make an outbound request, or access the microphone, the Host checks two things: did the Product declare the required permission, and did the user grant it? This method group is how a Product reads its own permission state, and how the Host enforces it.

Most Products do not call these methods explicitly. Declarations live in the Product's manifest, and gating happens at the boundary of the methods that actually need a permission, such as signing, network, and media. The Permissions method group exposes the runtime query surface for checking whether a permission is granted.


Conceptual Contract

## Conceptual Contract

Three things sit in this group:

- **Permission queries**: A Product can check whether a given capability is granted, such as with `isGranted("ChainSubmit")`. The shape lets a Product branch on permission state without trying the capability and catching `PermissionDenied`.
- **Permission grants and re-prompts**: The Host can expose runtime prompts for capabilities the user can elect at runtime rather than at install. The set of runtime-elective permissions versus install-time-only permissions is part of the Host's policy.
- **Permission-denied error reporting**: Every capability-gated method group bubbles up consistently, so a Product can treat `PermissionDenied` as a uniform failure mode wherever it happens.

!!! note "Permission names: spec vs. Host-side aliases"
    Some permission names in this documentation are Host-side normalizations, not the literal identifiers in the TrUAPI v0.2 spec. In particular, `ExternalRequest` (outbound network access) is a Polkadot Desktop alias; the protocol-level construct is `RemotePermission::Remote(Vec<String>)`, which carries the list of permitted hosts. A Host implementer reading the spec should map the friendly names used here to their spec enum variants.

For the conceptual model, see the [Polkadot Desktop Permissions](/reference/apps/hosts/polkadot-desktop/permissions/) reference.

!!! warning "Provisional"
    The exhaustive permission taxonomy and the exact runtime-query surface (method names, parameter shapes, return values) are still being finalized. This page documents the conceptual contract; the per-method specifics will be added as the surface confirms.


Where to Go Next

## Where to Go Next

- Learn **Polkadot Desktop Permissions**

    ---

    Host-side enforcement: capability types, manifest declaration, and the denial UX.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/permissions/)


Introduction

## Introduction

The Preimage method group lets a Product submit off-chain content that on-chain operations dereference by hash. A typical use case is a governance proposal, where the on-chain referendum points at a hash, the preimage is the bytes of the proposal call, and the bytes must be available when the referendum executes.

The Statement Store and Preimage surfaces look superficially similar because both submit bytes, return a hash, and let on-chain logic reference that hash. They are not interchangeable. See the [Statement vs Preimage](/reference/apps/hosts/polkadot-desktop/preimage/#statement-vs-preimage) decision table for which to use when.


Conceptual Contract

## Conceptual Contract

The group covers:

- **Submitting a preimage**: The Product submits bytes for an on-chain operation that will reference them. The Host signs the submission with the user's per-Product account, subject to the standard signing model, the `ChainSubmit` permission, and per-transaction approval on the paired App.
- **Hosting the bytes**: The preimage layer hosts the referenced bytes so the on-chain operation can dereference them when it executes. In practice, the Host often routes storage through Bulletin Chain.
- **Lifecycle management**: Preimages are durable until the referenced operation completes or until they are explicitly cleaned up, unlike statements, which expire after their time to live (TTL).

!!! warning "Provisional"
    The exact set of host calls in this group, supported preimage size limits, the relationship with Bulletin Chain storage, and any preimage-availability hooks for runtime introspection are still being finalized.


Where to Go Next

## Where to Go Next

- Learn **Preimage on Polkadot Desktop**

    ---

    The Host-side reference including the Statement-vs-Preimage decision table.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/preimage/)

- Guide **Store Data on Chain**

    ---

    The Product-side how-to for Bulletin Chain storage that the preimage surface often delegates to underneath.

    [:octicons-arrow-right-24: Get Started](/apps/build/store-data-on-chain/)


Introduction

## Introduction

A Polkadot Product runs inside a sandbox enforced by the Host. The sandbox is what makes the platform meaningfully different from "run third-party code in a browser tab" — three deliberate constraints, none of which a Product can bypass:

- A Product gets a derived sub-account, not the user's root identity.
- The Host API is the only egress, meaning the only way a Product can reach the world outside its sandbox.
- Permissions selectively relax isolation for the capabilities the user has explicitly granted.

This page is the deep dive on what each constraint means and how it is enforced.


## Why Derived Sub-Accounts, Not Root Identity

A user has one root identity on the People Chain. If a Product could ask the Host for the user's root key, or for a single account that represents the user across everything, every Product would see the same user, and every Product would be a tracking surface.

Instead, each Product gets a per-domain sub-account, derived deterministically from the user's identity and the Product's `.dot` domain:

- The same user opening `app-a.dot` and `app-b.dot` sees a different account in each Product.
- The two Products cannot link those accounts back to a shared user without an explicit cross-domain permission grant.
- The derivation is local (no round trip to the paired App is needed to compute the address), so getting an account is instantaneous from the Product's perspective.

The trade-off is that the Product cannot accumulate "the user's full identity." It can only see the user's behavior in this Product. Cross-Product correlation is a permission the user grants explicitly, not a default.

For the Product-side how-to that consumes this surface, see [Sign and Submit Transactions](/apps/build/sign-and-submit/).


Why the Host API Is the Only Egress

## Why the Host API Is the Only Egress

A Product running inside a Host has no other way to reach the world. The browser-level network stack, the chain-client, the storage backend, and the signing primitive are all behind the Host API. The Product asks the Host, and the Host decides.

This matters for two reasons:

- **Consistent sandbox boundary**: Any capability a Product attempts to use crosses TrUAPI. The Host applies permission checks, account scoping, and policy enforcement at that single point. There is no side channel to leak data through.
- **Backend substitution**: A Product calling `app.wallet.getAnonymousAlias()` gets the same answer whether the Host fetches it from a local cache, a paired phone, or a future Host implementation that resolves it some other way. The Product targets the API, not the implementation.

The corollary: a Product that wants to do anything not exposed through TrUAPI cannot. There is no escape hatch by design.


## How Permissions Selectively Relax Isolation

Isolation is the _default_, not the only mode. The user can grant permissions that selectively widen what a Product can do:

- A Product can request a cross-domain alias, which is an alias scoped to another Product's `.dot` domain. The user sees a consent modal and explicitly approves or denies.
- A Product can declare an `ExternalRequest` permission with a URL pattern, opening a specific outbound network path the sandbox would otherwise block.
- A Product can request `ChainSubmit` for the ability to ask the Host to present a signing prompt to the user. The grant covers only the ability to ask; per-transaction approval is still required.

Every permission is a narrow, declared widening, not a broad "trust this Product" toggle. Full isolation is the default when the user installs a new Product without granting anything.


Where to Go Next

## Where to Go Next

- Guide **Accounts and Signing**

    ---

    The Product-side how-to that consumes the sub-account surface: setting up the signer, building transactions, and submitting them through the Host.

    [:octicons-arrow-right-24: Get Started](/apps/build/sign-and-submit/)

- Learn **Signing in Polkadot Desktop**

    ---

    How the `ChainSubmit` permission grants ability-to-request without granting auto-sign, and how Desktop enforces that distinction.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/signing/)


Introduction

## Introduction

The Signing method group lets a Product submit a signed transaction. The Host on the other side does not sign. It mediates by rendering the signing modal, forwarding the payload to the paired Polkadot App, waiting for the user's approval on their phone, and returning the result as a signature, rejection, or timeout.

The Product SDK wraps this as `tx.signAndSubmit(signer)` on a Polkadot API (PAPI) transaction object plus a signer returned by `SignerManager.getSigner()`. From the Product's perspective, it is a single awaited promise. Underneath, it is a TrUAPI dispatch and a round trip to the user's phone.


Conceptual Contract

## Conceptual Contract

The group covers:

- **Submitting a payload for signing**: The Product hands the Host a pre-built, account-bound transaction. The Host renders the signing modal and forwards the payload to the paired App.
- **Watching the result**: The Host returns the outcome — signature obtained (Host then broadcasts), explicit user rejection (`HostRejectedError`), or session timeout (`TimeoutError`).
- **Surfacing status transitions**: Through PAPI's `submitAndWatch` companion surface, the Product sees status events (`signing`, `broadcasting`, `in-block`, and `finalized`) as the transaction progresses.

The default contract is the one that makes the model trustworthy: every transaction requires a per-request approval on the phone, with no session-scoped consent and no path for a Product to obtain a signature without the user approving the specific payload they were shown. This per-request flow is what a Product gets unless an Auto-Signing resource has been explicitly allocated.


Auto-Signing

### Auto-Signing

The shipping [`@parity/truapi`](https://www.npmjs.com/package/@parity/truapi) wire carries an **Auto-Signing** capability on top of the per-request default. A Product requests it as an allocatable resource through `host_request_resource_allocation`; the user must explicitly grant the allocation, and the Host scopes what it covers. Polkadot Desktop ships this as `signing-bot-autopair`, which pairs a signing bot so that allocated transactions are signed without a per-transaction prompt. Auto-Signing is opt-in, user-granted, and bounded — it does not remove the approval requirement for ordinary signing; it replaces it, within the allocated scope, with an up-front consent the user can revoke.

!!! note "Newer than the v0.2 method-group surface"
    Auto-Signing is carried by the `@parity/truapi` wire but is not part of the v0.2 method-group surface documented in these pages, so it is a place where the shipping protocol is ahead of the documented groups. A Host implementer working only from the v0.2 method groups will not find it there; a Product running on today's Desktop can use it.

For the full mediated-signing flow (the three-actor model, the `ChainSubmit` permission, failure-mode UX), see the [Signing in Polkadot Desktop](/reference/apps/hosts/polkadot-desktop/signing/) reference.

!!! warning "Provisional"
    Edge cases still being defined include multi-signature flows that aggregate across multiple Hosts, batched transactions that present as a single user prompt, and custom signing policies (delegation, time-windowed allowances). The single-transaction, single-prompt contract described in this section is stable.


Where to Go Next

## Where to Go Next

- Learn **Signing in Polkadot Desktop**

    ---

    The mediated-signing flow in detail: how the Host renders the modal, forwards to the paired App, and surfaces timeouts and rejections.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/signing/)

- Guide **Accounts and Signing**

    ---

    The Product-side how-to with the `try`/`catch` patterns for `HostRejectedError` and `TimeoutError`.

    [:octicons-arrow-right-24: Get Started](/apps/build/sign-and-submit/)


Introduction

## Introduction

The Statement Store method group lets a Product publish and subscribe to signed, gossip-distributed statements on the People Chain. The Statement Store itself, including the pallet, gossip layer, lifecycle, and validation rules, is a network-layer pub/sub primitive. It is short-lived, allowance-gated, and propagated peer-to-peer. This TrUAPI group is how a Product reaches it through the Host.

The Product SDK wraps this surface as `StatementStoreClient` from [`@parity/product-sdk-statement-store`](https://www.npmjs.com/package/@parity/product-sdk-statement-store). For the conceptual model of what statements are good for (versus the Bulletin Chain or local storage), see [Storage options for your Product](/apps/build/pub-sub-off-chain-data/).


Conceptual Contract

## Conceptual Contract

The group exposes the standard pub/sub surface with the Host filling in the parts a Product cannot do safely on its own:

- **Publishing a statement**: The Product hands the Host a payload (under the per-statement 512-byte limit), a topic, a channel, a time to live (TTL), and any options. The Host requests an authentication proof from the paired App, attaches the per-Product account context and primary topic (`blake2b(appName)`), and submits to the People Chain node's `statement_submit` JSON-RPC.
- **Subscribing to a topic filter**: The Product registers a callback for statements matching its primary topic plus any secondary `topic2`. The Host opens the underlying `statement_subscribeStatement` JSON-RPC, decodes payloads, and deduplicates by content hash before invoking the callback.
- **Channel-based last-write-wins**: When a statement carries a `channel`, the pallet replaces older statements from the same account on the same channel. The SDK abstracts this as `ChannelStore<T>` on top of the underlying method group.

Delivery is best-effort gossip, with no retry, acknowledgment, or ordering guarantee. Reliability is composed at the application layer. For example, if a recipient needs to confirm receipt, they publish an acknowledgment statement back.

For the Desktop-side mediation perspective, see [Statement Store via Host API](/reference/apps/hosts/polkadot-desktop/statement-store/).

!!! warning "Provisional"
    Detailed error taxonomy, allowance-management primitives, and any direct topic-walking API remain provisional.


Where to Go Next

## Where to Go Next

- Guide **Exchange Ephemeral Messages**

    ---

    The Product-side how-to: setting up the client, subscribing, publishing typed statements, and using channels.

    [:octicons-arrow-right-24: Get Started](/apps/build/pub-sub-off-chain-data/)

- Learn **Statement Store via Host API**

    ---

    What Polkadot Desktop adds when mediating Statement Store traffic, including proof routing, account context, and per-Product topic scoping.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/statement-store/)


Introduction

## Introduction

TrUAPI is versioned. A Host implements a specific version of the protocol; a Product is built against a specific version range; the SDK abstracts most version differences but cannot bridge across breaking changes. This page documents the versioning model so a Product developer knows what to declare and what to expect.

!!! warning "Provisional"
    The detailed compatibility rules between TrUAPI versions, the canonical version number a Product manifest declares, and the deprecation policy for older versions are still being finalized. This page documents the conceptual model; the operational reference will be added once those details are confirmed.


What a Version Number Identifies

## What a Version Number Identifies

A TrUAPI version identifies the set of method groups, their signatures, and their semantics as a single coherent surface. Two Hosts running the same version of TrUAPI must accept the same calls with the same parameters and return values with the same shape; a Product targeting that version can rely on consistent behavior across them.

A change between two versions can be:

- **Additive**: New methods or new optional parameters. A Product targeting the older version still works against a Host on the newer version.
- **Behavioral**: Same signature, different behavior. This is surfaced as a version bump with a documented behavior change; a Product targeting the older version may need to retest.
- **Breaking**: Removed or renamed methods, changed parameter shapes. A Product targeting an incompatible older version will not run on a Host that has dropped that version.


What a Product Targets

## What a Product Targets

A Product declares the TrUAPI version range it requires. The Host inspects the declaration on load and either runs the Product (compatibility OK) or refuses to load it (compatibility mismatch, with a user-visible explanation).

Most Products will target a single version line and let the SDK abstract the per-call wrapping. Reaching directly into TrUAPI in code that bypasses the SDK is the most common reason a Product breaks across a version change, because the SDK is the layer that smooths over compatible changes.


How the SDK Maps to Versions

## How the SDK Maps to Versions

!!! warning "Roadmap"
    The version-range model and SDK-to-protocol mapping described in this section are the intended design, not what ships today. The shipping protocol is a single version line (`v1`); manifest version-ranges and lockstep SDK versioning are not yet implemented. This section describes where versioning is headed.

The intent is for the [`@parity/product-sdk`](https://www.npmjs.com/package/@parity/product-sdk) family of packages to be versioned in lockstep with the TrUAPI versions they wrap, so that a given SDK version targets a specific TrUAPI version line and pinning your SDK pins your TrUAPI surface implicitly.

Under that model, when TrUAPI introduces a behavioral change the SDK would absorb it where it can; where it cannot (a removed method, an incompatible parameter), it would bump a major version, and a Product's upgrade path would be to update the SDK pin and re-test against the new TrUAPI surface.

For the package map, see [Packages](/reference/apps/protocol/truapi/packages/).


Where to Go Next

## Where to Go Next

- Learn **Packages**

    ---

    Which SDK package wraps which TrUAPI method group, and how SDK versions map to TrUAPI versions.

    [:octicons-arrow-right-24: Reference](/reference/apps/protocol/truapi/packages/)


Introduction

## Introduction

Product Skills are small, modular instruction sets that teach an AI coding agent how to work effectively across specific parts of the Polkadot Products stack. A skill bundles together what the agent needs to know about one slice of the surface (chain connections, bulletin storage, contract interactions, and so on) so that when you ask it for help in that area, it has the right context loaded.

If you are working on a Polkadot Product with an AI coding agent, installing the relevant skills tilts the agent toward correct, idiomatic answers instead of generic boilerplate.

The skills live inside the [`paritytech/product-sdk`](https://github.com/paritytech/product-sdk) monorepo under `product-sdk/skills/`.


Skills List

## Skills List

| Skill                                                                                | What It Covers                                                                                |
|:---------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| [`product-sdk-app-builder`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-app-builder) | End-to-end scaffolding and implementation of Polkadot apps using `@parity/product-sdk` packages. Foundational orchestrator skill — reach for this when starting a new project or scaffolding chain interactions.        |
| [`product-sdk-bulletin`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-bulletin)             | Upload and retrieve data on the Polkadot Bulletin Chain. Covers CID-based content-addressed storage, IPFS gateway access, the `BulletinClient` SDK, batch uploads, and CID computation.                                 |
| [`product-sdk-chain-connection`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-chain-connection) | Typed access to Polkadot chains via `@parity/product-sdk-chain-client` and `@parity/product-sdk-descriptors`. Covers preset and BYOD (bring-your-own-descriptors) paths, state queries, and storage subscriptions. |
| [`product-sdk-contracts`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-contracts)           | Smart contract interaction (PolkaVM / Solidity) on Asset Hub. Covers `ContractManager` with `cdm.json` manifests, ad-hoc `createContract`, `ContractRuntime`, and contract type codegen.                                |
| [`product-sdk-statement-store`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-statement-store) | Publish and subscribe to ephemeral messages on the Polkadot Statement Store. Covers `StatementStoreClient` lifecycle, host and local connection modes, channels with last-write-wins semantics, and the 512-byte size limit. |
| [`product-sdk-transactions`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-transactions)     | Submit transactions, manage signers, and derive keys. Covers `@parity/product-sdk-tx`, `@parity/product-sdk-signer`, and `@parity/product-sdk-keys` — multi-provider wallet accounts, Host API signing (Desktop/Mobile), and dev signers for testnet. |
| [`product-sdk-utilities`](https://github.com/paritytech/product-sdk/tree/main/product-sdk/skills/product-sdk-utilities)           | Foundational utilities — SS58/H160 address encoding, AES/ChaCha/NaCl crypto, HKDF key derivation, byte encoding, token (planck) formatting, key-value storage, and structured logging.                                  |


How to Install

## How to Install

Clone the `paritytech/product-sdk` repo:

```bash
git clone https://github.com/paritytech/product-sdk.git
```

Skills live under `product-sdk/skills/<skill-name>/` in the cloned directory. Register whichever skills are relevant with your agent — Claude Code, Cursor, Codex, or a custom harness — using that agent's skill or context-loading mechanism.

Skills trigger based on context. You can also invoke a skill directly by name before asking a question. For example, before asking the agent for help chunking a large upload to the Bulletin Chain, load the relevant context up front:

```text
/product-sdk-bulletin how do I chunk a 50 MB file across multiple transactions?
```

The agent now has the SDK's API patterns and best practices loaded before it answers, instead of falling back on generic JavaScript knowledge.


Updating

## Updating

!!! tip "Keep skills current"
    Polkadot Products surfaces are in active development. A skill that has not been refreshed for a while will teach the agent stale API patterns, which is worse than no skill at all. Refresh after major releases.

Pull the latest changes from your clone:

```bash
git pull
```

Then re-register any updated skills with your agent as needed.


Where to Go Next

## Where to Go Next

- Guide **App Development How-To**

    ---

    The how-to guides themselves, the same conceptual content the skills above index into.

    [:octicons-arrow-right-24: Get Started](/apps/get-started/)

- External **product-sdk repo**

    ---

    The source of truth for the skills documented above, including per-skill README files and updates.

    [:octicons-arrow-right-24: Visit Repo](https://github.com/paritytech/product-sdk)


Alias

## Alias

A context-specific pseudonym derived from a user's [PoP](#proof-of-personhood-pop) identity via [Ring-VRF](#ring-vrf). Unlinkable across `.dot` domains by default. The same user produces a consistent alias every time they return to the same [Product](#product), but a different alias for any other Product.


Allowance

## Allowance

An on-chain access-control grant. The chain-level permission that gates publishing or storing data. `pallet-statement-store` records per-account `StatementAllowance` entries (a `max_count` cap on live statements and a `max_size` cap on total bytes). [Bulletin Chain](#bulletin-chain) consumes per-account quotas of transactions and bytes for storage. [Coinage](#coinage) issues periodic free-token allowances to persons. Spam resistance comes from the allowance ceiling rather than from per-transaction fees.


Asset Hub

## Asset Hub

[Polkadot](#polkadot)'s system chain for assets, swaps, smart contracts, and EVM-compatible contracts. Hosts the [dotNS](#dotns) contracts, where [`.dot`](#dot-address) names actually live.