rnotes-online/backlog/tasks/task-15 - EncryptID-persona...

4.3 KiB

id title status assignee created_date updated_date labels dependencies priority
TASK-15 EncryptID personal subdomains: <user>.r*.online with local-first data Done
2026-02-25 03:01 2026-02-25 04:48
architecture
auth
encryptid
infrastructure
cross-app
high

Description

When a user logs in via EncryptID, they should operate out of <encryptID>.r*.online (e.g., alice.rnotes.online, alice.rspace.online), with all their data saved securely to their local space.

Scope

This is a cross-cutting feature affecting all rStack apps. Key areas:

1. DNS & Routing

  • Wildcard DNS for each r*.online domain (*.rnotes.online, *.rspace.online, etc.)
  • Cloudflare wildcard CNAME records pointing to the tunnel
  • Traefik wildcard Host rules to route *.r*.online to the correct app container

2. Auth / EncryptID Integration

  • On login, redirect to <encryptID>.r*.online
  • Middleware to extract subdomain, validate it matches the authenticated EncryptID
  • Reject requests where subdomain doesn't match session identity

3. Data Isolation & Local-First Storage

  • Each user's data is scoped to their EncryptID
  • Explore local-first / CRDTs (e.g., Yjs, Automerge) for offline-capable storage
  • Sync strategy: local device ↔ user's personal encrypted space on server
  • Encryption at rest using keys derived from EncryptID

4. Multi-App Consistency

  • Shared auth session across *.r*.online subdomains (cross-subdomain cookies or token-based)
  • AppSwitcher links should resolve to <user>.rspace.online, <user>.rnotes.online, etc. when logged in
  • Consistent UX: user always sees their subdomain in the URL bar

5. Migration

  • Existing data needs a migration path to the new per-user scoped storage
  • Backward compat for users accessing the root domain (redirect to subdomain after login)

Open Questions

  • What is the EncryptID identifier format? (alphanumeric, length constraints, case sensitivity)
  • Should unauthenticated users see public content at the root domain?
  • How does this interact with rSpace's existing space/room model?
  • Storage backend: SQLite per user? Postgres row-level security? Encrypted blob store?

Acceptance Criteria

  • #1 Wildcard DNS configured for all r*.online domains
  • #2 Middleware extracts and validates EncryptID from subdomain
  • #3 User data is scoped and encrypted per EncryptID
  • #4 AppSwitcher links resolve to user's personal subdomain when logged in
  • #5 Cross-subdomain auth session works across rStack apps
  • #6 Migration path defined for existing data
  • #7 Local-first storage strategy documented and prototyped

Implementation Notes

Research complete - EncryptID uses DID format (did🔑z, 50+ chars). DIDs too long for DNS labels (63 char limit). Username-based subdomains recommended but usernames are currently optional.

Final Summary

Implemented username-based personal subdomains (<username>.rnotes.online) across 8 phases:

Phase 1 - Infrastructure: DNS wildcard and Traefik routing already configured. Cloudflared needs manual *.rnotes.online entry.

Phase 2 - EncryptID: Server already enforces username UNIQUE NOT NULL + includes in JWT. SDK updated: made username required in EncryptIDClaims type, updated createSession().

Phase 3 - Schema: Added workspaceSlug field to Notebook model with index + migration SQL.

Phase 4 - Middleware: Sets x-workspace-slug header from subdomain extraction. New workspace.ts helper.

Phase 5 - API Filtering: All GET endpoints filter by workspace on subdomains (notebooks, notes, search, notebook detail, notebook notes).

Phase 6 - AppSwitcher: Fetches /api/me for username, generates <username>.r*.online links when logged in.

Phase 7 - Sessions: SubdomainSession component syncs localStorage ↔ .rnotes.online domain-wide cookie. authFetch falls back to domain cookie.

Phase 8 - Migration: Auto-assigns unscoped notebooks to user's workspace on auth.

Manual steps remaining: Remove stale container on netcup, run migration, add cloudflared wildcard entry.