Skip to content

Support subdomains and linked domains in autofill matching #91

@Loule95450

Description

@Loule95450

Context

Keyfount derives passwords deterministically: the registrable domain (extracted via tldts in src/shared/domain.ts:26) is the Argon2id salt, so every subdomain of a site shares one password and an account cannot be reused across domains. AccountEntry (src/shared/types.ts:76) is keyed on (domain, username), which is also the sync identity. Full cross-repo design: docs/superpowers/specs/2026-05-28-subdomains-and-linked-domains-design.md (in the desktop repo).

Problem

  • A subdomain cannot have its own password (e.g. x.y.com vs w.y.com).
  • An account saved for x.com cannot be offered on a second site y.com.
  • Matching is registrable-only at four call sites: src/popup/vault.ts:37, src/content/Badge.tsx:476, src/entrypoints/content.ts:73 and :128, src/background/context-menus.ts:41.

Proposed approach

  • Add optional linkedDomains?: string[] to AccountEntry (src/shared/types.ts); it is match-only and never affects derivation. Identity stays (domain, username).
  • Add fullHost(), domainMatches(m, host) and matchAccounts(url, accounts) to src/shared/domain.ts implementing the rule: a registrable match domain matches every subdomain (broad, current behaviour); a full-host match domain matches the exact host only (narrow). Match set per account = {domain} ∪ linkedDomains; rank exact-host > registrable, then lastUsedAt.
  • Replace the four registrable-only call sites with matchAccounts.
  • src/background/accounts.ts: filter via the match rule (not e.domain === domain); add link/unlink helpers; recordAccount takes the chosen canonical domain.
  • Save flow: per-account granularity toggle on a subdomain page — registrable by default, opt-in to full host.
  • Account editor (src/popup/components/AccountDetailScreen.tsx): linked-domains add/remove + "use an existing account here" on an unmatched site.
  • Carry linkedDomains through sync ops/snapshots (src/background/sync/*); tombstones unchanged.

Acceptance criteria

  • matchAccounts unit-tested table-driven: broad, narrow, linked, precedence, localhost/chrome:// → no match.
  • Registrable derivation byte-identical to today (golden vector); new full-host golden vector added.
  • Sync round-trip preserves linkedDomains; tombstone identity unchanged.
  • Playwright e2e: offered on a linked domain and on a subdomain (broad); a narrow full-host entry is not offered on the registrable root.
  • npm run lint, npm run typecheck, npm test and the coverage gate pass.

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priorityenhancementNew feature or requestsecuritySecurity-related issue

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions