Commit Graph

133 Commits

Author SHA1 Message Date
Jeff Emmett cede2232b5 fix(encryptid): unified device list with names, confirmation, and rename/delete
- Add detectDeviceName() JS helper to all 6 registration pages (parses
  UA → "Chrome on Windows", "Safari on iPhone", etc.)
- Accept deviceName in /api/register/complete, /api/account/device/complete,
  and /api/device-link/:token/complete; store as credential label at creation
- Add optional label param to storeCredential() in db.ts
- Replace separate "Your Passkeys" section with unified device list in
  "Linked Devices" showing name, status, created/last-used dates, and
  inline rename (PATCH) and delete (DELETE) actions
- Make checklist "Second device" confirmation-aware: only marks done when
  a second device has actually been used to sign in (has lastUsed set)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:23:05 -04:00
Jeff Emmett 97bf0504cb fix(encryptid): detect missing WebAuthn in QR scanner WebViews
When scanning a device-link QR code, many phone apps open the URL in
an embedded WebView that lacks PublicKeyCredential support, causing
"user agent does not support public key credentials". Now the /link
page checks for WebAuthn early and shows a helpful fallback with a
Copy Link button so the user can open it in Safari/Chrome instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 10:59:36 -04:00
Jeff Emmett f06852dd3b fix(oidc): handle literal \n in PEM key from .env files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 11:54:24 -04:00
Jeff Emmett 0ba9ea272e feat(oidc): switch from HS256 to RS256 token signing
- Generate or load RSA keypair for OIDC token signing (OIDC_RSA_PRIVATE_KEY env)
- Add /oidc/jwks endpoint exposing public key in JWK format
- Update discovery document with jwks_uri and RS256 algorithm
- Sign ID tokens and access tokens with RS256 private key
- Verify access tokens with RS256 public key in userinfo
- Fix OIDC_ISSUER default to auth.rspace.online (was auth.ridentity.online)
- Add POST handler for /oidc/userinfo (RFC compliance)
- Add error logging to userinfo endpoint for debugging

Fixes Cloudflare Access OIDC integration which requires asymmetric
token signing via JWKS for ID token verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 11:49:36 -04:00
Jeff Emmett 44cc47ecf1 fix(auth): same-origin passkey proxy + PRF fallback for Safari
- Add /api/auth/*, /api/register/*, /api/account/* proxy routes to
  rspace-online server, forwarding to encryptid container internally.
  This eliminates cross-origin requests that Safari blocks via ITP or
  Cloudflare security challenges.
- Change client auth URLs from https://auth.rspace.online to same-origin
  in rstack-identity, rspace-header, login-button, and session modules.
- Add PRF extension try/catch fallback in webauthn.ts — Safari throws
  TypeError on the unsupported PRF extension, now retries without it.
- Bump SW cache version v7→v8 to bust stale cached bundles.

Fixes passkey login for Safari/macOS users (e.g. christina) who were
getting "Network error when attempting to reach resource".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:31:59 +00:00
Jeff Emmett 4f8cddaaf7 feat(holons): add Holon Explorer canvas shape with hex hierarchy + appreciation
New folk-holon-explorer shape unifying H3 geospatial holons and nested
rSpace spaces into a zoomable circular navigator with appreciation weight
normalization and MetatronGrid sacred geometry background. Endorsements
logged to trust engine via new POST /api/trust/endorse endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 12:41:35 -04:00
Jeff Emmett cdb62e2ee8 feat(encryptid): social recovery guardian UX enhancements
- Red pulsing alert dot on avatar when social recovery not configured
- SVG puzzle piece visualization for guardian slots (empty/pending/accepted)
- Key assembly animation when 2+ guardians accepted
- Recovery drill system: test the full guardian approval flow without actual recovery
  - POST /api/recovery/drill/initiate, GET .../status, POST .../complete
  - Drill-specific emails with "TEST ONLY" branding
  - Live polling UI with puzzle pieces filling in as guardians approve
  - Drill timestamp tracking (last_drill_at on users table)
- Solo walkthrough modal: 5-step animated preview of how recovery works
- Approval page detects drill flag, shows DRILL badge
- Account status now returns acceptedGuardianCount and lastDrillAt
- Recovery section shows emergency override messaging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 10:48:55 -04:00
Jeff Emmett 76df746e15 feat: cross-browser/cross-device compatibility sweep
Browser compat gate (WASM/ESM check + structuredClone polyfill),
structured WebAuthn error handling with user-facing messages,
email-only login mode when passkeys unavailable, Firefox passphrase
fallback for document encryption (salt storage, modal UI, key
derivation bridge), CSS flex gap fallbacks for Safari <14.1,
MapLibre CDN load error handling, and server-side auth error
improvements with proper HTTP status codes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 19:57:59 -04:00
Jeff Emmett 885f0baeb1 feat: add Linea chain support and WalletAdapter abstraction
TASK-120 Phases 1-2: Add Linea mainnet (59144) and Linea Sepolia (59141)
to all chain maps (CHAIN_MAP, RPC, env names, native tokens, popular tokens,
CoinGecko, Zerion, chain colors/names, Safe prefixes). New WalletAdapter
class provides chain-parameterized abstraction over Safe/EOA/UP wallets
with immutable withUniversalProfile() and fromSafe/fromEOA/fromUP factories.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 19:14:31 -04:00
Jeff Emmett a711af055a fix(encryptid): remove all remaining authenticatorAttachment: 'platform' hardcodes
Three client-side registration flows still had authenticatorAttachment: 'platform'
hardcoded, blocking Samsung Passkey and Linux users:
- lib/rspace-header.ts (main site header registration)
- shared/components/rstack-identity.ts (2 occurrences)

Also added server-side validation for missing userId in register/complete
to return 400 instead of crashing with TypeError.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 22:27:16 -04:00
Jeff Emmett b2d443421e fix(encryptid): allow cross-platform authenticators on Linux
Registration was hardcoded to authenticatorAttachment: 'platform',
which rejects devices without a platform authenticator (common on
Linux desktops). Now only forces platform when available, otherwise
lets browser offer cross-platform options (security keys, phone as
authenticator). Also relaxed isEncryptIDAvailable() to only require
WebAuthn support, not platform auth specifically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 19:30:41 -04:00
Jeff Emmett ca45eb43d2 fix(encryptid): handle non-JSON error responses in auth flow
When EncryptID server returns plain text errors (e.g. "Internal Server
Error"), the client's .json() calls threw SyntaxError which surfaced
as an ugly parse error to users. Add .catch() to all unsafe .json()
calls in session.ts, login-button.ts, and recovery.ts so auth
gracefully falls back to unsigned tokens instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 19:25:51 -04:00
Jeff Emmett 988b10fd65 fix(encryptid): use port 25 without auth for internal Mailcow SMTP
SMTP auth (port 587) credentials are stale, causing 535 auth failures
on startup. Detect internal mailcow/postfix hosts and connect on port
25 without auth, matching the pattern already used in server/spaces.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 23:01:07 -04:00
Jeff Emmett 7e07170304 fix: ranking drag-drop, spider slider drag, video gen timeout & progress
- Ranking: replace broken :hover drop-target with getBoundingClientRect hit testing
- Spider: add #isSliding guard to prevent slider destruction during drag
- Video gen: bump timeout to 10min, show real fal.ai queue position/status
- Fix NotificationCategory type to include 'payment' in db.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 16:03:58 -04:00
Jeff Emmett 0e2eebf890 feat(encryptid): add wallet lookup + payment notification APIs
Add internal endpoints for payment infrastructure integration:
- GET /api/internal/user-by-wallet — resolve wallet to email/username
- POST /api/internal/notify — trigger in-app notifications by wallet/DID
- Add 'payment' notification category and payment_sent/received event types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 01:28:33 +00:00
Jeff Emmett c4b85a82e6 feat(rnetwork): graph visualization polish, delegation sync, layers persistence
Phase 1 — UX Polish: loading spinner, node hover glow (THREE.RingGeometry),
empty state, live slider labels, keyboard shortcuts (Esc/F/T/L), toast
notifications, touch-friendly sizing (@media <=768px).

Phase 2 — Live Data Integration: edit delegation flow (POST vs PATCH),
cross-component sync via CustomEvent("delegations-updated"), dynamic
getAuthUrl() fallback chain, sparkline weight tracking via trust events,
revocation-aware time slider, new GET /api/trust/events endpoint,
include_revoked + revokedAt support on /api/delegations/space.

Phase 3 — New Visualizations: BFS delegation path tracing (max depth 3),
node/edge opacity dimming for non-path elements, transitive chain indicators
(TorusGeometry when >30% received weight), network metrics sidebar (Gini
concentration index, top 5 influencers with click-to-focus), log-scale
flow thickness (1.5 + log10(1 + w*9) * 3).

Phase 4 — Layers Persistence: LayerConfig + CrossLayerFlowConfig schema
types, CRDT doc version bump to 2 with migration, saveLayerConfig/
getLayerConfig in local-first-client, auto-persist on rebuildLayerGraph,
restore-on-connect, onEngineTick sine-wave pulse animation for compatible
feed targets during wiring, wiring progress indicator banner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 14:46:18 -07:00
Jeff Emmett 45372c6681 Fix sidebar showing all modules: hide Manage panel when none disabled
- App switcher: only show "Manage rApps" when there are actually
  disabled modules or active restrictions. Move "Available to Add"
  above "Remove" to prioritize adding. Eliminates duplicate module
  listing when all modules are enabled.
- Shell: update app switcher on modules-changed event (was only
  updating tab bar and folk-rapp, not the sidebar itself).
- SMTP: use space-agent@rspace.online as From for invite/approval
  emails with proper envelope sender for DKIM alignment.
- Shell CSS: fix banner z-index, smooth header transition on banner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 12:32:53 -07:00
Jeff Emmett d2fa533519 Improve rTasks drag-drop UX + sync space members on invite claim
CI/CD / deploy (push) Failing after 9s Details
rTasks: port backlog-md ordinal algorithm (bisection + rebalance),
fix column detection via bounding-box hit test, add empty-column
drop zones, source column dimming, no-op detection, and optimistic
DOM updates (no flash). New bulk-sort-order rebalance endpoint.

EncryptID: sync claimed invite members to Automerge doc immediately,
redirect to space subdomain after identity claim.

Server: add /api/internal/sync-space-member endpoint, fallback
member check in WebSocket auth for not-yet-synced invites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 12:19:37 -07:00
Jeff Emmett bbbe14246c feat(rinbox): space agent mailbox system — per-space MI email identity
Each space gets {space}-agent@rspace.online as a real Mailcow mailbox
(auto-provisioned with generated password). Inbound emails are IMAP-polled
and processed by MI (Gemini Flash) for auto-reply. All outbound emails
(approvals, notifications) set reply-to to the agent address so replies
route back through MI.

- mailcow.ts: createMailbox/deleteMailbox/mailboxExists API
- schema.sql + db.ts: agent_mailboxes table for per-space IMAP creds
- space-alias-service.ts: provisionAgentMailbox/deprovisionAgentMailbox
- server.ts: internal routes for agent mailbox CRUD + member-emails
- rinbox/mod.ts: initAgentMailbox, per-space IMAP sync, processAgentMI
- rinbox/agent-notify.ts: sendSpaceNotification (BCC members)
- rcal/rtasks/rvote: notification hooks on create

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:59:52 -07:00
Jeff Emmett 676e29902e feat(encryptid): device management — labeled passkey list + nudge fix
Add label column to credentials, PATCH/DELETE endpoints for rename/remove,
device list UI in account modal with rename/remove actions, and clear stale
nudge dismiss timestamp after device registration so multiDevice API check
takes over permanently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 18:04:20 -07:00
Jeff Emmett aa23108f5f fix(invites): redirect to invited space, improve invite emails
- Fix invite accept fetch URL in shell.ts (was missing /api/spaces prefix)
- After accepting invite, redirect to the invited space instead of reloading
- Notification actionUrls now point to the space subdomain (https://slug.rspace.online)
- Direct-add email includes inviter name, role, and space description
- Identity invite email includes space name/role context when inviting to a space

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 15:39:58 -07:00
Jeff Emmett 858457c056 fix(invites): show email invites in space settings pending list
listSpaceInvites now queries both space_invites and identity_invites
tables, merging results so email-based invites (via /invite endpoint)
appear in the Pending Invites section. revokeSpaceInvite also falls
through to identity_invites if not found in space_invites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 15:24:26 -07:00
Jeff Emmett 8071b620e1 fix(auth): throttle session validation, typed auth-change events, misc fixes
- rstack-identity.ts: throttle server session validation to every 5min,
  add reason detail to all auth-change events (signin/signout/revoked/
  refresh/persona-switch), remove redundant location.reload on signout
- shell.ts: skip UI side-effects on token refresh, only redirect home
  on genuine signout/revocation
- server.ts: add PUT to CORS allowMethods
- folk-inbox-client.ts: pass auth token on mailbox API fetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 12:21:26 -07:00
Jeff Emmett b3fb51c39a feat(encryptid): known accounts picker + passkey-first for both UIs
- login-button.ts: no-known-accounts state shows passkey-first button
  (unscoped WebAuthn) with email magic link fallback, auto-revealed on
  NotAllowedError. Fix stale usernameInput ref.
- server.ts (auth.rspace.online): add localStorage known accounts system.
  Returning users see their stored usernames as clickable buttons.
  handleAuth() accepts optional username for scoped auth. Saves account
  after successful login. renderSigninAccounts() called on page init.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 11:29:44 -07:00
Jeff Emmett 75ad3f8194 feat(rinbox): per-space email forwarding via Mailcow aliases
Provision {space}@rspace.online forwarding aliases that route to
opted-in members' personal emails. Admins/mods opted in by default;
regular members can opt in via PUT /api/spaces/:slug/email-forwarding/me.

New: space-alias-service.ts, schema tables, 8 DB functions, 6 API routes.
Hooks: rinbox onSpaceCreate/Delete, spaces.ts member lifecycle, startup migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:40:29 -07:00
Jeff Emmett af446938be fix(auth): show username input on first login before passkey prompt
When no known accounts exist in localStorage, show a username/email
input field instead of immediately triggering the unscoped passkey
picker. User types their username, then gets a scoped passkey prompt
for only that account's credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:41:29 -07:00
Jeff Emmett 3ead9b4ca0 fix(auth): keep known accounts on logout, pass transports in scoped auth
Logout no longer removes the account from the picker — users see
"Sign in as [username]" on next visit. fetchScopedCredentials now
returns full PublicKeyCredentialDescriptor with transports so the
browser can locate the right authenticator without showing a picker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:26:33 -07:00
Jeff Emmett b625913eba feat(auth): username-first passkey login with account picker
Scoped passkey prompts via /api/auth/start so the browser only shows
matching credentials for the selected account. Known accounts stored
in localStorage and surfaced as a picker (1 account = named button,
multiple = list). "Use a different account" falls back to unscoped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 11:00:36 -07:00
Jeff Emmett 4722aca065 fix(auth): wire cross-session logout in rstack-identity + encryptid profile
rstack-identity is the actual sign-out component used in production.
clearSession() now calls /api/session/logout, and connectedCallback
validates the session with the server to detect revocation. Also
updated the auth.rspace.online profile page handleLogout().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:46:44 -07:00
Jeff Emmett e0e76446db feat(encryptid): cross-session logout propagation
When a user logs out in one browser, all other sessions are now revoked
on their next page load or token refresh. Adds logged_out_at column to
users table, server-side revocation checks on verify/refresh endpoints,
and a new /api/session/logout endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 10:32:52 -07:00
Jeff Emmett 9695e9577a feat(encryptid): encrypt all PII at rest in database
AES-256-GCM encryption for 18 PII fields across 6 tables (users,
guardians, identity_invites, space_invites, notifications, fund_claims).
HMAC-SHA256 hash indexes for email/UP address lookups. Keys derived from
JWT_SECRET via HKDF with dedicated salts. Dual-write to both plaintext
and _enc columns during transition; row mappers decrypt with plaintext
fallback. Includes idempotent backfill migration script.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:50:21 -07:00
Jeff Emmett a9ff1cf94b feat(encryptid): complete social recovery end-to-end flow
Add /recover/social page for users to finalize account recovery after
guardian approvals, fix status filter so approved requests remain
findable, return requestId from initiation API with tracking link on
login page, and add actionUrl to recovery notifications.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 14:32:38 -07:00
Jeff Emmett cb784b8102 fix(notifications): proxy to encryptid instead of direct DB access
The rspace container cannot resolve encryptid-db hostname, causing
/api/notifications/count to 524 timeout on every 30s poll. Rewrites
notification-routes.ts as an HTTP proxy to encryptid (which has DB
access), adds notification API endpoints to encryptid server, and
wraps BroadcastChannel.postMessage in try/catch to prevent uncaught
errors during navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:30:58 -07:00
Jeff Emmett 7618433498 refactor(auth): replace @encryptid/sdk imports with local auth module
Consolidates token verification into server/auth.ts, removing the
external SDK dependency. All modules now import from the local module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:41:59 -07:00
Jeff Emmett 29c4f48634 feat(identity): store verified email on profile + show in account modal
Email verification wrote to the `email` column but account status read
from `profile_email` — now setUserEmail writes both. Account modal email
section displays the verified address when collapsed. Tour finale step
triggers identity setup on completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 15:49:09 -07:00
Jeff Emmett 6491681e3e fix(auth): force platform authenticator + redirect disabled modules
Force authenticatorAttachment: 'platform' across all WebAuthn registration
flows to prevent USB security key prompts. Redirect browser navigations to
space root when accessing disabled modules instead of returning JSON error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 22:31:13 -07:00
Jeff Emmett cc504d4a86 fix(spaces): make DID resolve endpoint public (no auth needed for profile data)
The resolve-dids endpoint was returning 401 because unsigned fallback tokens
fail HS256 verification. Since username/displayName is public profile data,
remove auth requirement from the endpoint and client call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 18:06:36 -07:00
Jeff Emmett acafe15c4b feat(spaces): resolve member DIDs to usernames in space settings
Add POST /api/users/resolve-dids batch endpoint in EncryptID, proxy
/api/users/* through rspace server, and batch-resolve missing displayNames
in the space settings panel so owners and members show usernames not DIDs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:39:48 -07:00
Jeff Emmett b679fc9f1f feat(spaces): bridge email invites with EncryptID identity system
New users get sent to /join for passkey registration + auto-space-join.
Existing users are directly added with in-app + email notification.
Add-by-username now also sends email notification if email is on file.

- Add id to /api/users/lookup response
- Enhance /api/internal/user-email/:userId with recovery + profile email
- Add GET /api/internal/user-by-email for email→DID resolution
- Rewrite POST /:slug/invite to use identity invite flow
- Add email notification to POST /:slug/members/add
- Add success/error feedback to space settings invite UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:04:22 -07:00
Jeff Emmett d31e8fdca4 feat(spaces): blank canvas init + team inbox provisioning
New spaces start with an empty canvas instead of 25+ template shapes.
Each space gets a {slug}@rspace.online team inbox (multi-sig ready)
via the rinbox onSpaceCreate hook. Fix EncryptID auto-provision passing
raw string instead of SpaceLifecycleContext to module hooks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 13:59:35 -07:00
Jeff Emmett 8bd8348529 feat(encryptid): fix DID consistency, add PRF key derivation, stepper signup, and magic link login
- Fix DID mismatch: server now stores and reads proper did🔑z6Mk... DIDs
  from database instead of deriving truncated did🔑${slice(0,32)}
- Add PRF extension to WebAuthn create/get flows for client-side key derivation
- Derive DID, signing keys, encryption keys, and EOA wallet from passkey PRF
- Auto-upgrade truncated DIDs to proper format on sign-in
- Add POST /api/account/upgrade-did endpoint for DID migration
- Add 5-step educational registration wizard (identity, passkey, DID, wallet, security)
- Add email/username field to sign-in for scoped passkey selection
- Add magic link email login for external devices without passkeys
- Add POST /api/auth/magic-link and GET /magic-login verification page
- Add mintWelcomeBalance() for 5 fUSDC to new users
- Store EOA wallet address during registration when PRF available

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 11:29:20 -07:00
Jeff Emmett 73ad020812 fix(spaces): fix space creation routing and use /rspace URLs
Space creation was broken because the canvas module has id "rspace" but
all navigation URLs used "/canvas". On production subdomain routing this
resulted in 404s after creating a space.

- Switch create-space form from deprecated /api/communities to /api/spaces
- Replace all /canvas navigation URLs with /rspace to match module ID
- Fix DID matching in space listing to check both sub and did:key formats
- Add proper client DID support in EncryptID registration flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 11:07:01 -07:00
Jeff Emmett 1f97a2ceba fix(smtp): use noreply@rmail.online as sender across all modules
Mailcow rejects noreply@rspace.online because the authenticated user
is noreply@rmail.online. Updated all SMTP_FROM and SMTP_USER defaults
to use rmail.online consistently: spaces invites, rSplat notifications,
EncryptID auth emails, and rCart payment receipts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 21:00:41 -07:00
Jeff Emmett e7f0181f70 fix: stop cross-tab active tab fighting + add per-user tab persistence
activeLayerId was being written to the shared Automerge CRDT on every tab
switch, causing all open windows/devices to follow. Now active tab is
local-only. Adds REST API + server-side storage so authenticated users'
tab lists persist across sessions and devices.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 16:40:50 -07:00
Jeff Emmett 9bf1aee921 Merge origin/dev + add Universal Profile support
- schema.sql: UP columns (up_address, up_key_manager_address, up_chain_id, up_deployed_at)
- db.ts: getUserUPAddress, setUserUPAddress, getUserByUPAddress
- server.ts: GET/POST /api/profile/:id/up endpoints, UP info in JWT claims

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 02:29:02 +00:00
Jeff Emmett fe128f832e feat: add Universal Profile support — schema, DB ops, server endpoints, JWT claims
- Schema: up_address, up_key_manager_address, up_chain_id, up_deployed_at columns
- DB: getUserUPAddress(), setUserUPAddress(), getUserByUPAddress()
- Server: GET/POST /api/profile/:id/up endpoints
- JWT: eid.up object in session tokens, eid.authTime fix, wallet capability from UP
- Backlog: task-72 for UP × EncryptID integration tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 02:24:04 +00:00
Jeff Emmett ccca8318a3 feat: payment email notifications, GLB viewer, and EncryptID email lookup
- Add EncryptID internal endpoint for email lookup by userId
- rcart: send "Payment Sent" to payer and "Payment Received" to recipient
- rcart: resolve emails via EncryptID when not provided in request
- rsplat: add GLB/GLTF 3D viewer using Three.js GLTFLoader
- rsplat: enable publicWrite for photo uploads without space membership
- docker-compose: add SITE_URL and SPLAT_NOTIFY_EMAIL env vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 04:02:07 +00:00
Jeff Emmett 82123937b3 fix(encryptid): persist verified email and fix OIDC re-prompt bug
Normalize emails to lowercase at all setUserEmail() call sites so
case mismatches no longer break the OIDC allowedEmails check. Split
the authorize error into email_required (shows verification form) vs
access_denied (shows error message) so users with a verified email
are never re-prompted unnecessarily.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:28:27 -07:00
Jeff Emmett fc1776aedf feat(encryptid): sync wallet associations across devices via encrypted vault
Wallets stored in local WalletStore are now bidirectionally synced with
the encrypted AccountVault on the server. On login, vault wallets are
restored to the local store; on wallet changes, local state is pushed
back to the vault. The server user profile wallet_address is also set
on login so mobile devices (without PRF) get the address via JWT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:55:10 +00:00
Jeff Emmett 9a1afe9be9 fix(encryptid): use CONFIG.smtp.from for OIDC verification emails
The sendVerificationEmail function was hardcoding noreply@ridentity.online
as the sender, but SMTP authenticates as noreply@rspace.online. Mailcow
rejected the mismatch with 553 "Sender address rejected: not owned by user".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:41:29 -07:00