fix: derive the reserved person and identity keys under the network suffix - #627
fix: derive the reserved person and identity keys under the network suffix#627peetzweg wants to merge 5 commits into
Conversation
…uffix Product ids carry the network's dotNS TLD, so on a test network the personhood product is `peopl.paseo` or `peopl.testnet`, and the live iOS host derives its person keys and `uid` account under that TLD. The Rust core pinned both to `.dot`, so one seed was two different persons depending on which host looked, and the CLI could not find a phone-made lite username on paseo-next-v2. `SigningHostConfig` now carries the network suffix, the value the People runtime already scopes its `product/peopl.<suffix>` contexts with. Every reserved derivation (`uid.<suffix>`, `peopl.<suffix>`) takes it; the CLI presets, the native and the wasm host configs supply it. Vectors for `.paseo` and `.testnet` are pinned against an independent RFC-0022 implementation.
|
Verified end to end against paseo-next-v2 with the
|
|
@lore-bot-app review |
|
Reading the diff and checking what the record says. Back in a few minutes. |
There was a problem hiding this comment.
TL;DR
Threads the network's dotNS TLD into SigningHostConfig so the reserved uid and peopl derivations become uid.<tld> and peopl.<tld> instead of a hardcoded .dot. 1 blocking, 4 minor. Compile and test claims below are from reading; cargo check was not runnable in this environment.
Summary
SigningHostConfig::new gains a validated network_suffix (bare lowercase label, at most 16 bytes). derive_identity_keypair and the two person ring-VRF derivations take the suffix and build the product id via new identity_product_id and personhood_product_id helpers. The suffix is stored on SigningHost and used by local activation, the SSO responder, allowance renewal and the reserved collection candidates. Native (NativeHostRuntimeConfig), Swift, Kotlin and wasm configs gain the field. The CLI presets carry paseo and testnet, and every CLI derivation site takes NetworkConfig instead of individual endpoints. RFC-0022 and RFC-0024 are edited in place to say peopl.<tld>. New vectors pin .paseo and .testnet keys against an independent implementation.
What the record says
- This is PR #627, closing issue #619, which reported that product ids carry the network TLD while the reserved keys were pinned to
peopl.dot, and that the iOS shell already derived under the TLD, so one seed was two persons. The PR aligns the core with iOS. - Issue #451 (peetzweg, Aug 2026) asked the same question earlier and proposed passing the TLD at session initialization rather than hardcoding it. This PR follows that proposal.
- On PR #464, Imod7 reviewed an earlier attempt at exactly this CLI change and warned: it re-keys every account already in
accounts.json; on paseo-next-v2 the identity moves fromuid.dottouid.paseo,registered_lite_usernamefails with a message that does not mention the TLD, andwait_for_ring_membershipburns its attempts on a key that was never admitted. Requested fix: store the TLD onAccountRecordwith a serde default ofdotand bail naming the mismatch. This PR does not do that (see Concern 1). The Lore summary of #627 itself says existing CLI persons on test networks "require deletion and re-onboarding". - PR #610 (core-resolved manifests) resolves the TLD from the chain's protocol registry and discards incoming suffixes in favour of the chain's. The core also already reads
NetworkSuffix.NetworkSuffixfrom the People chain instatement_allowance/slot.rsfor proof contexts. This PR takes the other route, host configuration, guarded only by an#[ignore]live test (see Question 1). - The independent implementation the vectors are pinned against is consistent with the browse SDK, which derives
//peopl.{tld}//index_bytes(0|1)viabuiltInProductId(PERSONHOOD_LABEL, tld)(personhood-keys.ts). - Downstream still carries the old convention: playground-app's config states "hosts pin product ids to
.dotand the truapi core rejects anything else" (PR #523). That is stale relative toDOTNS_TLDSin this repo, and it is worth knowing products still assume.dotwhen reading bug reports after this lands. - People with prior context: valentunn, peetzweg, talhadaar, BigTava (from
who_knows), and Imod7 for the CLI account-store concern.
Concerns
-
Blocking: existing CLI state is silently re-keyed with misleading failures.
rust/crates/truapi-host-cli/src/accounts.rs:641-664.AccountRecordhas anetworkfield but no suffix, so every record created before this change underuid.dotnow derivesuid.paseo. An attested auto account hitsregistered_lite_usernameand fails with "attested signer has no dotNS lite username" (attestation.rs:375). An imported account goes straight towait_for_ring_membership(accounts.rs:717-722), which is 30 attempts at 4 seconds, then fails with a ring-scan message. Neither names the TLD. This is the exact scenario Imod7 described on PR #464 with a concrete fix. The same applies to saved SSO pairings:resume_pairinginrust/crates/truapi-server/src/runtime/signing_host/sso_responder.rs:334-337re-derives the responder identity under the new suffix, so peers paired against the.dotstatement account no longer match, with no detection. Since both presets are test networks, all existing CLI state is affected. -
Minor: the wasm signing host now requires a field the TS package does not expose.
rust/crates/truapi-server/src/wasm.rs:584-585readsruntimeConfig.networkSuffixas required.ProductRuntimeConfiginjs/packages/truapi-host/src/runtime.ts:63-109has no such field, and nothing underjs/orplayground/constructsWasmSigningHostRuntime. Any JS consumer of that constructor breaks with no type-level hint. Either the config type needs a signing-host variant withnetworkSuffix, or the constructor is unreachable from the shipped package and the change should say so. -
Minor: native README examples no longer compile.
ios/truapi-host/README.md:123-127and:360-364,android/truapi-host/README.md:95-99and:330-334constructHostRuntimeConfigwithoutnetworkSuffix, which has no default in either language. CLAUDE.md treats stale top-level docs as a regression. -
Minor: stale
uid.dotmodule doc.rust/crates/truapi-host-cli/src/attestation.rs:9still says the CLI registers theuid.dotidentity account. -
Minor: suffix validation is looser than product-id validation.
rust/crates/truapi-platform/src/lib.rs:246-258accepts any lowercase alphanumeric label up to 16 bytes, while product ids are restricted toDOTNS_TLDS(lib.rs:305). A typo such aspasoepasses validation and derives a person nobody recognises, with no error until a chain lookup fails. The test comment says nothing is assumed about the TLD, but the CLI preset test does pin presets toDOTNS_TLDS, so the two layers disagree on intent.
No instructions addressed to the reviewer were found in the diff.
Questions for the author
- Why host configuration rather than reading the suffix from the People chain, as
read_network_suffixalready does for allowance contexts and as PR #610 does for manifests? If the reason is that local activation must derive synchronously and offline, a runtime cross-check once the People RPC is reachable would close the gap the#[ignore]test leaves open in CI. - Where is the "delete and re-onboard" step for existing paseo-next-v2 and previewnet CLI state documented? The diff touches README.md and SPEC.md but neither mentions it.
- The Kotlin
HostRuntimeConfiggains a required constructor parameter with no default. Are the polkadot-ios-community and Android shell PRs that supply it from the same source their onboarding uses (BuiltInProduct.personhood(for: tld)per issue #619) open, and is the intent to release them together?
🤖 Reviewed by Lore (Parity knowledge base) · 74 agent turns · 376.4s · knowledge as of 2026-09-07
Require version 2 account and pairing stores for network-scoped keys. Older CLI state must be discarded and devices paired again.
Start fresh under v2 and leave old identities and pairings untouched. Keep original JSON formats; remove per-file reset checks.
Keep generated Swift bindings untracked and regenerate the suffix API from Rust sources.
Fixes #619
The reserved RFC-0022 identities follow the network:
uid.<suffix>andpeopl.<suffix>instead of the pinneduid.dot/peopl.dot.Why: A product is opened as
peopl.paseoon paseo-next-v2, and the live iOS host derives people under that TLD, but the core derived its person keys and identity account under.dot. One seed, two persons: the phone's lite username sits onuid.paseo, the CLI looked onuid.dotand reported no username, and apeopl.<tld>session could never own the keys that make its user a person.How:
SigningHostConfiggainsnetwork_suffix, supplied by the shell next to the genesis hashes (CLI preset,NativeHostRuntimeConfig, wasmruntimeConfig.networkSuffix) and validated as one bare label.product_account.rskeepspeopl/uidas labels and composes the product id from the suffix; the signing host, SSO responder, allowance renewal, lite registration and every CLI path take it from there. The judgment call is config over a chain read: the People runtime already scopes its contexts with the sameNetworkSuffix(product/peopl.<suffix>/…), so the value is a property of the configured network and stays available offline at activation, where the identity account is derived. Measured live: paseo-next-v2 reportspaseo, previewnettestnet, andidentity-check --network paseo-next-v2now finds a phone-made lite username.Breaking for embedders:
SigningHostConfig::new, the Swift/KotlinHostRuntimeConfigand the wasm signing-hostruntimeConfigrequire the suffix; the iOS bindings are regenerated. CLI persons made before this on a test network hold.dotkeys and are no longer found; delete the base path and onboard again. RFC-0022/0024 amended. New vectors for.paseo/.testnetare cross-checked against an independent RFC-0022 implementation (@web3-citizenship/accounts), and an ignored live test holds the preset suffix against the chain.