Skip to content

refactor(identity): establish canonical ID foundation#258

Draft
chhoumann wants to merge 3 commits into
feat/secret-storagefrom
refactor/canonical-identifiers
Draft

refactor(identity): establish canonical ID foundation#258
chhoumann wants to merge 3 commits into
feat/secret-storagefrom
refactor/canonical-identifiers

Conversation

@chhoumann

Copy link
Copy Markdown
Owner

Stack

This draft is stacked on #257 (feat/secret-storage), which is stacked on #256. Review this PR as the canonical identity foundation only. It must not merge before its base.

Summary

  • Add fixed-length, domain-separated SHA-256 feed and episode IDs using the browser/mobile-compatible @noble/hashes package.
  • Derive feed IDs only from the first-observed normalized subscription URL. A channel podcast:guid is retained as evidence but never used to merge feeds.
  • Parse the complete feed before assigning episode IDs, exclude GUID, enclosure, and item-link candidates that are ambiguous across distinct items, and use a deterministic source-tuple fallback only as the current ID.
  • Preserve the actual direct RSS item link separately from the existing url fallback to the feed URL.
  • Add one-to-one prior-observation reconciliation with bounded, validated identity history. Split, merge, forged, sparse, truncated, oversized, and hostile evidence all fail closed.
  • Add optional identity fields to PodcastFeed and Episode without changing existing public fields.

Deliberate non-goals

This PR does not bump the data schema, re-key any persisted map, change getEpisodeKey, alter queue/progress/download behavior, bump the feed cache, or change URI/API methods. The next stacked PR will perform the schema-v3 and consumer cutover atomically so no mixed title/ID key model can ship.

Identity and security invariants

  • IDs use pnf1_ or pne1_ plus the full SHA-256 base64url digest and are always 48 characters.
  • Identity inputs are domain separated and bounded to 16 KiB per component, 64 KiB per preimage, and 32 historical aliases.
  • Strong URLs use WHATWG normalization and remove only the fragment. Query order, credentials, and authentication parameters remain identity-bearing.
  • GUIDs are scoped to the feed. Duplicate GUID, enclosure, or direct item-link candidates are removed after whole-feed analysis.
  • Prior identities survive only a degree-1 to degree-1 intersection of trusted aliases. Source-tuple fallback IDs are never used as cross-observation evidence.
  • Imported or malformed runtime values cannot mint trusted IDs or throw through the identity boundary.

Verification

All verification ran under Node 22.20.0.

  • npm ci
  • npm run lint
  • npm run format:check
  • npm run typecheck
  • npm run build
  • npm test - 75 files, 1,021 tests, zero Svelte warnings/errors
  • uv run --with-requirements docs/requirements.txt mkdocs build -f docs/mkdocs.yml -d site
  • npm audit --audit-level=low - zero vulnerabilities
  • isolated real-Obsidian E2E - 13/13 tests passed
  • clean captured PodNotes reload - plugin active, no console errors, no runtime errors
  • independent adversarial security review - clean

Production bundle impact against the exact base: +10,147 bytes raw and +4,115 bytes gzip.

Risk and migration

There is no migration in this PR. New fields are additive and existing schema/map/URI behavior stays intact. The main review surface is identity determinism, ambiguity handling, hostile-input validation, and reconciliation correctness.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying podnotes with  Cloudflare Pages  Cloudflare Pages

Latest commit: e8f7848
Status: ✅  Deploy successful!
Preview URL: https://7496ea8e.podnotes.pages.dev
Branch Preview URL: https://refactor-canonical-identifie.podnotes.pages.dev

View logs

@chhoumann chhoumann force-pushed the feat/secret-storage branch from a5472f5 to f299477 Compare July 10, 2026 09:39
@chhoumann chhoumann force-pushed the refactor/canonical-identifiers branch from ee2f13b to e8f7848 Compare July 10, 2026 09:40
@chhoumann chhoumann force-pushed the feat/secret-storage branch from f299477 to d7deb9d Compare July 10, 2026 12:31
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.

1 participant