feat(extension): Web Store packaging, per-site permissions, and a wal… - #119
Merged
Conversation
…let UI Prepares the extension for a Chrome Web Store submission and closes the two permission problems that would have dominated its review, then rebuilds the UI around what the wallet actually does. Packaging - `manifest.json` becomes a template: the version comes from package.json (three copies previously disagreed, and the Store rejects a non-increasing version), and `key` is injected for local installs only — a new-item upload is rejected outright if the manifest carries one. - `npm run package` produces the upload zip; CI builds and validates it every run, so a submission is never the first exercise of that path. Permissions - `host_permissions: <all_urls>` becomes `optional_host_permissions`, granted one origin at a time from a user gesture. Safe because DID resolution needs no grant (the webvh host serves `Access-Control-Allow-Origin: *`) while the VTA does, since vta-service applies an origin allowlist. - No static `content_scripts`. The page provider is registered at runtime for granted origins only, so a site the user has not approved cannot see the wallet. CI asserts the packaged manifest declares none. - Cookie injection is scoped and enforced, not merely intended: https-only bar loopback, RFC 6265 domain-matching against the bound origin, refusals surfaced rather than swallowed, and user-initiated instead of automatic. The previous check would have let a session bound to `evilexample.com` claim `Domain=example.com`. - docs/web-store-review.md carries the single-purpose statement and the per-permission justifications, including why `cookies` exists only for legacy relying parties. Agent names - Implemented against the specification rather than inferred: a name is an `alsoKnownAs` claim, never derived from DID structure. Canonicalisation is ported verbatim from the Rust reference, because implementations that normalise differently disagree about whether a name verifies. Resolution is three-stage with a mandatory `alsoKnownAs` check and no permissive fallback. - Onboarding accepts either an agent name or a DID in one field. UI - One token set across popup, options and consent; ~50 accumulated hex literals removed. Options becomes an app shell: Setup, Network, Vault, Sites, Advanced. - Onboarding moves to a full tab, where Chrome cannot tear it down mid-flow when a native dialog takes focus (crbug 40721470). - New Sites view merges consent trust with host grants — two halves of "what can this site do?" that previously lived in two places, or nowhere. - New Network view: a live trust graph plus sequence diagrams for SIOPv2 sign-in and delegated trust-task execution. - Consent prompt: DID rendering that never elides the host. The previous head-and-tail truncation could hide it entirely for a path-qualified DID, on the one screen whose whole job is verifying it. Adds 140 tests. Lint clean; MV3 single-bundle invariant holds. Signed-off-by: Glenn Gore <glenn.g@affinidi.com>
stormer78
force-pushed
the
feat/store-packaging-permissions-ui
branch
from
August 17, 2026 10:05
15730dc to
04ca660
Compare
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.
Prepares the extension for a Chrome Web Store submission, closes the two permission problems that would have dominated its review, and rebuilds the UI around what the wallet actually does.
Adds 140 tests (215 → 355 workspace-wide). Lint clean; the MV3 single-bundle invariant holds.
Packaging
manifest.jsonis now a template. The version comes frompackage.json— three copies previously disagreed (0.1.0 / 0.1.2 / 0.2.0), and the Store rejects a version that is not strictly greater than the last accepted upload.keyis injected for local installs only. A "+ New item" upload is rejected outright if the manifest carries one, while unpacked installs need it to hold the extension ID still — and that ID is load-bearing:src/holder.tsuseschrome.runtime.idas the WebAuthn PRF rpId, so a moving ID orphans every passkey-wrapped secret.npm run packageproduces the upload zip. CI builds and validates it on every run, so a submission is never the first time that path executes.Permissions
host_permissions: <all_urls>→optional_host_permissions. Granted one origin at a time, from a user gesture. This is only safe because DID resolution needs no grant — the webvh hosting service servesAccess-Control-Allow-Origin: *— while the VTA does, since vta-service applies an origin allowlist. Had resolution needed a grant, this would have silently degradedverifyDid, which backs the consent prompt.content_scripts. The page provider is registered at runtime for granted origins only (src/content-registration.ts), so a site the user has not approved cannot see the wallet at all. CI asserts the packaged manifest declares none.c.domain !== baseUrl.host) would have let a session bound toevilexample.comclaimDomain=example.com; Chrome would have refused it, but inside a swallowingcatchwhere it was indistinguishable from a transient failure.docs/web-store-review.mdcarries the single-purpose statement and per-permission justifications, including whycookiesexists only for legacy relying parties.Agent names
Implemented against
design-docs/agent-names-design-guide.htmland the Rust reference, not inferred.alsoKnownAsclaim, never derived from DID structure. An earlier iteration of this branch derived a handle from the DID's trailing path segment; the reference implementation deliberately ships no such helper, because it "would manufacture unverified guesses behind an authoritative-looking API".alsoKnownAscheck and no permissive fallback: a redirect alone proves nothing, since whoever controls a domain can point a name at somebody else's DID.UI
Cross-repo notes
packages/core:verifyDidnow carries the resolved document'salsoKnownAs. Additive; no behaviour change for existing callers.doOnboardConnectthrew, making a baredid:peeragent impossible to onboard.Pre-merge checklist
reqwest::Client::new()/ barefetch(); all clients have finite timeouts (R1.2) — the agent-name redirect fetch carriesAbortSignal.timeout(15s);proxyFetchunchanged at 20schrome.storage.sessionis a UI resume hint that nothing reads for correctnessdeny_unknown_fieldswhere security-relevant, schema registered, all consumers (incl. JS) updated (R3.*) — new types are extension-internal bridge messages, not VTA wire typesNot verified
Every UI change here was exercised in a browser by the author during review, but the automated tests cover logic only — there is no DOM or browser-integration test in this repo. The agent-name resolution network path is covered with stubs, not against a live host.