Repository navigation
Conversation
Add `platform::ProductId`, built only by `ProductId::parse`, so holding one means the id is normalized. It encodes as its string and decodes by parsing, which lets `ProductContext` derive `Decode` and carry a `ProductId`. It derives `Hash` and `Ord`, so the AutoSigning grant set, the native worker registry and the CLI's Pocket cards are keyed by it. The string helpers become methods: `normalize_product_identifier` is `ProductId::parse`; `bare_product_label`, `same_product_on_one_network`, `on_one_network`, `manifest_cache_key`, `has_trusted_remote_permissions`, `normalizes_to_trusted_remote_permissions`, `is_localhost_product_identifier` and `ProductContext::is_game_product` move onto `ProductId`. `is_product_identifier` is removed. The permission and manifest paths take `&ProductId`: `AuthorizedAccess`, `scope_grant`, `grants_scope`, `require_context_access`, manifest resolution, `account_access_authorization`, `cross_product_signature_key` and `confirm_signature`. The account gate is `may_use_product_account`, `holds_cross_product_scope` and `holds_cross_product_scope_within`, which take the id a request was parsed into and answer `bool`, so each call parses it once. `ProductStorageKey::new` takes a `ProductId`, removing the runtime's `expect`. Permission administration reports a cross-product signature approval for a target that is not a product id as not determined, and refuses to set one. The CLI parses a local config's `productName` while reading it, so a name that is not a full product id stops startup before anything is stored. Codegen recognises `ProductId` by its rustdoc item id in the `platform` module and emits a string, so the generated clients and host codecs are unchanged. Closes #1385
…on and caches The product-keyed derivations in `host_logic` take `&ProductId`: product subtree and account keys, ring-VRF entropy, product entropy and aliases. The reserved `uid.<suffix>` and `peopl.<suffix>` paths keep a private string form, since the network suffix they are built from is not a validated type. `ProductAuthority` and both of its implementations take `&ProductId` wherever they took a product id string. So do the signing host's allowance cache and AutoSigning grants, the pairing host's subtree, AutoSigning and allowance caches and their storage helpers, the ring VRF registry and the native payment and top-up status relay. Ids are parsed where they enter: requests from a paired host over SSO and status notifications from a native host. Wire messages, review structs and persisted records keep strings. Typing the derivations fixes the places that used a raw id next to a normalized one: the signing host derived AutoSigning ring-VRF entropy and the statement-store and Bulletin allowance accounts from the id a paired host sent, and the pairing host looked up the AutoSigning key for ring-VRF registration under the raw caller id. A request from a paired host whose calling product id is not a product id is now refused before anything is derived or the user is asked. The worker ledger keeps string ids: hosts match worker transitions against their own spelling, so normalizing it needs the hosts to change first.
…ed host sends them The previous commit parsed the calling product id at the signing host's SSO handlers, so a resource allocation, VRF signature or subtree request derived everything from the normalized id and refused an id that is not a product id. That is a behaviour change, and not one the rest of the system agrees with: host-spec C.7 calls product ids case-sensitive, the iOS and Android wallets answer the same requests using the id as sent, and host-papp and product-sdk's terminal send it verbatim, defaulting to the terminal's `appId`. The handlers go back to what main does. Statement-store, Bulletin and smart-contract allocations derive from the id as sent, so `allocate_*` take the requester's string again. An AutoSigning allocation parses the id for the product key and derives its ring-VRF domain entropy from the id as sent, through `derive_ring_vrf_domain_entropy_from_junction`. `sign_vrf_request` takes the raw caller id, and one that does not parse gets no grant and reaches the prompt. The two tests that pinned the normalized behaviour are removed, and the changeset no longer describes a behaviour change. Whether product ids are case-sensitive or normalized is left to a separate decision across host-spec, the wallets, host-papp and the core.
…untime `host_logic/dev_server.rs`, added on main by #1361, parses its product id with `ProductId::parse`, since this branch removes `normalize_product_identifier` and `is_localhost_product_identifier`.
Contributor
|
This pull request touches an app, which is not built by default. Add a label for each build you want:
Each starts as soon as it is added and follows the branch from then on. |
Contributor
Bundle size reportCompared with
WebAssembly modules
Changed files (1)
Commit: 9575799 |
Contributor
|
CI Status: 24 required jobs green, 21 passed and 3 skipped by path filter. All job results
Signing credentials: failure as of 2026-10-15, a release may fail Commit |
Shorter, plainer wording for the doc and test comments this branch wrote, and commas in place of the em dashes in the local_product_signing_key paragraph it touched.
ProductId::parse checked labels against a hand-written list of invisible characters, which let lookalike ids through: soft hyphens, fullwidth letters, Arabic letter marks and other Unicode the list did not name. dotNS itself registers only canonical lowercase ASCII DNS labels (isSingleLabel in the dotNS contracts): a-z, 0-9 and hyphens, no leading or trailing hyphen, 1 to 63 bytes, rejected rather than folded. So every label now has to follow that rule, after trimming and lowercasing. Unicode ids such as tést.dot could never resolve on dotNS, so the NFC step and the test claiming dotNS names are internationalized are gone.
…egister platform_bounds used `Tést.DOT`, which the dotNS label rule now refuses. A mixed-case ASCII id still checks that the key round-trips and the id is lowercased.
The product selection, the switch command, pairing and script runs and the per-product storage map hold a ProductId, so an id is parsed once where it enters the CLI. ProductContext::from_parts builds a context from an id that is already parsed.
Mikelle
marked this pull request as ready for review
October 15, 2026 19:50
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1385
Product ids are a
ProductIdnewtype instead of&str/String.ProductId::parseis the only constructor: it trims, lowercases and requires a name dotNS can register orlocalhost[:port], so holding one means the id is valid and normalized. It encodes as a string and decodes by parsing. Wire messages, review structs and stored records keep strings and are parsed where they enter the runtime.ProductIdis used in the permission and manifest paths, key derivation,ProductAuthorityand both host implementations, and the signing and pairing hosts' caches. The old string helpers are methods on it. Generated TypeScript, Swift and Kotlin bindings are unchanged.Breaking changes
normalize_product_identifier,is_product_identifier,is_localhost_product_identifier,has_trusted_remote_permissions(&str)andtruapi::manifest_cache_keyare removed in favour ofProductId::parseand its methods.ProductContext.product_id,ProductStorageKey::new,ProductAuthorityand thehost_logicderivation functions useProductId. The account gate methods aremay_use_product_account,holds_cross_product_scopeandholds_cross_product_scope_within, and returnbool.productNamemust be a full product id such aspeopl.paseo. A bare label such aspeoplstops startup before anything is stored.Behaviour
isSingleLabel: lowercase ASCII letters, digits and hyphens, no leading or trailing hyphen, at most 63 bytes), in any case.tést.dot,my_app.dot, fullwidth letters and invisible characters are refused, so no lookalike of a registered name passes.productName, soPeopl.Paseoseeds the grants forpeopl.paseo.The signing host's SSO handlers and the worker ledger keep ids as received, like the wallets and host-papp. Since dotNS names are lowercase, that matches the normalized form for every real product.
Testing
make fmt-check, clippy (pinned nightly,-D warnings), the wasm32 check andcargo testfortruapi,truapi-codegenandtruapi-host-cliall pass, apart from three localtruapi-host-cliscript-runner failures unrelated to this change. Codegen output is byte-identical before and after.