Skip to content

fix: derive the reserved person and identity keys under the network suffix - #627

Merged
pgherveou merged 5 commits into
mainfrom
fix/person-keys-follow-network-suffix
Sep 7, 2026
Merged

fix: derive the reserved person and identity keys under the network suffix#627
pgherveou merged 5 commits into
mainfrom
fix/person-keys-follow-network-suffix

Conversation

@peetzweg

@peetzweg peetzweg commented Sep 7, 2026

Copy link
Copy Markdown
Member

Fixes #619

The reserved RFC-0022 identities follow the network: uid.<suffix> and peopl.<suffix> instead of the pinned uid.dot / peopl.dot.

Why: A product is opened as peopl.paseo on 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 on uid.paseo, the CLI looked on uid.dot and reported no username, and a peopl.<tld> session could never own the keys that make its user a person.

How: SigningHostConfig gains network_suffix, supplied by the shell next to the genesis hashes (CLI preset, NativeHostRuntimeConfig, wasm runtimeConfig.networkSuffix) and validated as one bare label. product_account.rs keeps peopl / uid as 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 same NetworkSuffix (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 reports paseo, previewnet testnet, and identity-check --network paseo-next-v2 now finds a phone-made lite username.

Breaking for embedders: SigningHostConfig::new, the Swift/Kotlin HostRuntimeConfig and the wasm signing-host runtimeConfig require the suffix; the iOS bindings are regenerated. CLI persons made before this on a test network hold .dot keys and are no longer found; delete the base path and onboard again. RFC-0022/0024 amended. New vectors for .paseo / .testnet are cross-checked against an independent RFC-0022 implementation (@web3-citizenship/accounts), and an ignored live test holds the preset suffix against the chain.

…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.
@peetzweg
peetzweg requested a review from a team September 7, 2026 14:02
@peetzweg

peetzweg commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

Verified end to end against paseo-next-v2 with the truapi-host CLI built from this branch, hosting humanity-spa (npm run dev:host -- --network paseo-next-v2) as an identity that was onboarded by the iOS app.

  • The host pairs with the identity's lite username. The released 0.13.1 build reports attested signer has no dotNS lite username for the same mnemonic, since it looks under uid.dot.
  • The keys the host registers for the peopl.paseo product are the ones the chain holds: the index 1 key is in the Lite ring and the index 0 key is in the People ring, so the card resolves as a Member.
  • The same two keys come out of the TypeScript RFC-0022 implementation in web3-citizenship-web for that mnemonic, so the core, the SPA and the TS derivation agree.

@peetzweg
peetzweg requested a review from pgherveou September 7, 2026 14:55
@pgherveou

Copy link
Copy Markdown
Collaborator

@lore-bot-app review

@lore-bot-app

lore-bot-app Bot commented Sep 7, 2026

Copy link
Copy Markdown

Reading the diff and checking what the record says. Back in a few minutes.

@lore-bot-app lore-bot-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 from uid.dot to uid.paseo, registered_lite_username fails with a message that does not mention the TLD, and wait_for_ring_membership burns its attempts on a key that was never admitted. Requested fix: store the TLD on AccountRecord with a serde default of dot and 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.NetworkSuffix from the People chain in statement_allowance/slot.rs for 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) via builtInProductId(PERSONHOOD_LABEL, tld) (personhood-keys.ts).
  • Downstream still carries the old convention: playground-app's config states "hosts pin product ids to .dot and the truapi core rejects anything else" (PR #523). That is stale relative to DOTNS_TLDS in this repo, and it is worth knowing products still assume .dot when 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

  1. Blocking: existing CLI state is silently re-keyed with misleading failures. rust/crates/truapi-host-cli/src/accounts.rs:641-664. AccountRecord has a network field but no suffix, so every record created before this change under uid.dot now derives uid.paseo. An attested auto account hits registered_lite_username and fails with "attested signer has no dotNS lite username" (attestation.rs:375). An imported account goes straight to wait_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_pairing in rust/crates/truapi-server/src/runtime/signing_host/sso_responder.rs:334-337 re-derives the responder identity under the new suffix, so peers paired against the .dot statement account no longer match, with no detection. Since both presets are test networks, all existing CLI state is affected.

  2. Minor: the wasm signing host now requires a field the TS package does not expose. rust/crates/truapi-server/src/wasm.rs:584-585 reads runtimeConfig.networkSuffix as required. ProductRuntimeConfig in js/packages/truapi-host/src/runtime.ts:63-109 has no such field, and nothing under js/ or playground/ constructs WasmSigningHostRuntime. Any JS consumer of that constructor breaks with no type-level hint. Either the config type needs a signing-host variant with networkSuffix, or the constructor is unreachable from the shipped package and the change should say so.

  3. Minor: native README examples no longer compile. ios/truapi-host/README.md:123-127 and :360-364, android/truapi-host/README.md:95-99 and :330-334 construct HostRuntimeConfig without networkSuffix, which has no default in either language. CLAUDE.md treats stale top-level docs as a regression.

  4. Minor: stale uid.dot module doc. rust/crates/truapi-host-cli/src/attestation.rs:9 still says the CLI registers the uid.dot identity account.

  5. Minor: suffix validation is looser than product-id validation. rust/crates/truapi-platform/src/lib.rs:246-258 accepts any lowercase alphanumeric label up to 16 bytes, while product ids are restricted to DOTNS_TLDS (lib.rs:305). A typo such as pasoe passes 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 to DOTNS_TLDS, so the two layers disagree on intent.

No instructions addressed to the reviewer were found in the diff.

Questions for the author

  1. Why host configuration rather than reading the suffix from the People chain, as read_network_suffix already 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.
  2. 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.
  3. The Kotlin HostRuntimeConfig gains 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

Comment thread rust/crates/truapi-host-cli/src/accounts.rs
Comment thread rust/crates/truapi-server/src/wasm.rs
Comment thread rust/crates/truapi-platform/src/lib.rs Outdated
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.
@pgherveou
pgherveou enabled auto-merge September 7, 2026 20:58
Keep generated Swift bindings untracked and regenerate the suffix API
from Rust sources.
@pgherveou
pgherveou disabled auto-merge September 7, 2026 21:15
@pgherveou
pgherveou added this pull request to the merge queue Sep 7, 2026
Merged via the queue into main with commit 002241e Sep 7, 2026
20 checks passed
@pgherveou
pgherveou deleted the fix/person-keys-follow-network-suffix branch September 7, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On test networks the personhood product cannot use its own person keys: product ids carry the network TLD, the keys are pinned to peopl.dot

2 participants