High priority fixes: - T0: Profile page now filters comments by namespace (was leaking cross-site) - T1: URI hostnames and namespace names normalized to lowercase (was causing duplicate threads and "stock comments" bug). Includes merge script. - T2: Thread detail API now paginated with SQL-side filtering (was 502 on 267+ reply threads) Features: - T3: GDPR account deletion (tombstone user with scrubbed PII) and data export - T4: Customizable button text and comment labels per namespace - T5: Self-service namespace deletion for owners - T6: @mention notifications with profile links - T7: Webmention receiving endpoint with h-card extraction - T8: Configurable max nesting depth and collapse depth per namespace - T9: AJAX thread title search to prevent duplicates - T10: Browser push notification support (VAPID/service worker) Docs and housekeeping: - T11: Documented thread_uri behavior when moving embeds - T12/T13: Drafted community replies for resolved feature requests - Collapse depth defaults to infinite (load-more disabled unless configured) 364 tests pass, 4 skipped.
2.8 KiB
T1: Namespace/URI case-sensitivity causes "stock comments" bug
Status: resolved
Priority: high
Source: FAQ 7eb0baec-2da1-11ef-b0c7-1f90b6841245, 6b21e360-ce62-11ef-b298-29ab4fb285a0
Filed: 2026-02-01
Problem
Users embed Remarkbox on their site and see pre-existing comments that don't belong to them ("stock comments"). Two separate FAQ threads report this for sparklingcyber.com and acrosstheborder.blog.
Root Cause
Same class of bug as the duplicate email accounts issue (see docs/postmortem-2026-01-29-duplicate-email-accounts.md). URIs and namespace names are compared case-sensitively:
get_uri_by_uri()inremarkbox/models/uri.py:82—Uri.data == unicode(external_uri)is case-sensitiveget_namespace_by_name()inremarkbox/models/namespace.py:363—Namespace.name == unicode(name)is case-sensitive
When a user visits https://Example.com/page vs https://example.com/page, two separate URIs, nodes, and potentially namespaces are created. The user on the lowercase variant sees an empty thread (or someone else's comments if they happen to share the same lowercase namespace).
Example scenario
Time 1: User A embeds on https://Example.com/blog
→ Uri "https://Example.com/blog" created
→ Namespace "Example.com" created
→ Comments posted here
Time 2: User B visits https://example.com/blog
→ Uri lookup for "https://example.com/blog" — no match (case differs)
→ New Uri, new Node created
→ Namespace "example.com" — no match, new namespace created (empty)
→ User B sees no comments or wrong comments
Proposed Fix
Follow the same pattern as the email fix:
- Normalize URIs to lowercase hostname in
get_or_create_uri()before lookup/storage - Normalize namespace names to lowercase in
get_or_create_namespace()before lookup/storage - Use
func.lower()for comparisons in lookup functions - Create a migration/merge script for existing case-variant duplicates (similar to
merge_duplicate_email_users.py) - Add regression tests
Files
remarkbox/models/uri.py— lowercase hostname normalization inget_or_create_uri(), case-insensitive lookup inget_uri_by_uri()remarkbox/models/namespace.py— lowercase normalization inget_or_create_namespace(), case-insensitive lookup inget_namespace_by_name()remarkbox/scripts/merge_duplicate_namespaces.py— new script to consolidate case-variant duplicatesremarkbox/tests/test_models.py— regression tests for case-insensitive URI and namespace matching
Acceptance Criteria
https://Example.com/pageandhttps://example.com/pageresolve to the same threadExample.comandexample.comresolve to the same namespace- Existing duplicate namespaces/URIs can be merged with a script
- Regression tests prevent reintroduction