Commit Graph

85 Commits

Author SHA1 Message Date
Jeff Emmett 79448a230a 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:07 -07:00
Jeff Emmett fab970e439 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:09:45 -07:00
Jeff Emmett 0c3d8728a0 fix: graph 3d-force-graph resolution, DID/username display, space role checks
- Switch 3d-force-graph CDN from jsdelivr to esm.sh with bundle-deps
  to resolve missing "three-forcegraph" bare specifier error
- Fix storeCredential() to pass displayName and DID to createUser()
  (prevents NULL did column for credential-first user creation)
- Fix invite acceptance to use claims.did instead of claims.sub for
  space_members.user_did (DID format consistency)
- Fix session refresh to look up username from DB when missing from
  old token (prevents empty username after token refresh)
- Fix resolveCallerRole() in spaces.ts to check both claims.sub and
  claims.did against ownerDID and member keys (auto-provisioned spaces
  store ownerDID as did🔑, API-created as raw userId)
- Refactor CRM route to use URL subpath tabs with renderCrm helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 01:31:24 -07:00
Jeff Emmett e207b18adf fix(encryptid): deterministic key derivation and server-backed guardian recovery
- Key derivation: replace random crypto.subtle.generateKey with deterministic
  P-256 via @noble/curves/p256 and real Ed25519 did:key generation via
  @noble/curves/ed25519 with multicodec prefix + base58btc encoding
- Guardian recovery: wire RecoveryManager to server API (GET/POST/DELETE
  /api/guardians) instead of localStorage-only persistence. Server handles
  invite emails, client syncs guardian list on load and merges with local
  type metadata. verifyGuardian checks actual server acceptance status.
- Notifications dispatch CustomEvents on document for UI integration
- GuardianSetupElement awaits server sync before first render

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 23:26:52 -07:00
Jeff Emmett 8efe18280c feat: consolidate domains, install deps, fix EncryptID types
- TASK-24: Install h3-js, @xterm/xterm, @xterm/addon-fit
- TASK-51.3: Remove app switcher external link arrows, update
  ridentity.online UI links to /rids paths
- TASK-51.4: Prune allowedOrigins (~30 → 16), simplify JWT aud
  to 'rspace.online', remove standalone domains from webauthn,
  update EncryptID HTML template links. Keep ridentity.online as
  canonical EncryptID/OIDC domain.
- Fix EncryptIDClaims type: add username, did fields; update aud
  type to string | string[] — resolves pre-existing TS error
- TASK-12: Update backlog status (80% code-complete, blocked on
  security audit)
- Backlog task updates for TASK-25/37/40/44, new TASK-110

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 21:55:39 -07:00
Jeff Emmett a2d01ed3ad fix(encryptid): drop CHECK constraint before migrating authority values
The old constraint rejected new values during UPDATE. Must drop first,
migrate data, then add new constraint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:54:23 -07:00
Jeff Emmett 4c44eb9941 feat(rnetwork): enhanced trust flow viz + rename authorities to gov/fin/dev-ops
- Compute delegatedWeight per node from delegation edges (both directions)
- Animated directional particles on delegation edges (count/size ~ weight)
- Wider delegation edges (1+weight*8) with 0.15 curvature
- "All" authority overlay mode with per-authority colored edges
- Rename 5 authorities (voting/moderation/curation/treasury/membership)
  to 3 verticals: gov-ops, fin-ops, dev-ops
- DB migration: update CHECK constraint + migrate existing data
- Update all frontend components + backend defaults

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:51:30 -07:00
Jeff Emmett 2f47835699 feat(encryptid): CryptID → EncryptID backward compatibility bridge (Phase 1)
Identity linking with P-256 signature verification for legacy CryptID users.
Adds migration/challenge, migration/verify (fresh + historical proof modes),
migration/auth (fallback JWT), and migration/status endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:17:10 -07:00
Jeff Emmett 5d019566f3 fix(rnetwork): trust data renders for spaces without CRM token
- Restructure graph API so trust enrichment runs regardless of whether
  Twenty CRM token is configured (demo space has no CRM token)
- Add missing listActiveDelegations import in encryptid server

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:07:56 -07:00
Jeff Emmett 913f9aa4d0 fix(rnetwork): trust data rendering in graph and delegation views
- Fix graph cache keying: include trust/authority params so cached
  non-trust responses don't shadow trust-enriched requests
- Add /api/delegations/space endpoint to EncryptID for space-level
  delegation listing (no auth required, for graph/sankey)
- Fetch and include delegates_to edges in graph API response
- Pass auth-url attribute to delegation manager and sankey components
- Rewrite sankey loadData to use space-level delegation endpoint
  instead of per-user endpoints (shows all flows, not just current user)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:03:23 -07:00
Jeff Emmett 5a33293a23 feat(rnetwork): implement delegative trust flows for liquid democracy
Person-to-person delegation within spaces across 5 authority verticals
(voting, moderation, curation, treasury, membership). Trust engine
recomputes scores every 5 min with time decay, transitive BFS, and
50% per-hop discount. Graph viewer shows trust-weighted node sizing
with authority selector. New Delegations tab in CRM with management
UI and Sankey flow visualization.

Schema: delegations, trust_events, trust_scores tables
API: delegation CRUD, trust scores, events, user directory
Frontend: folk-delegation-manager, folk-trust-sankey, graph trust mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 19:16:06 -07:00
Jeff Emmett 4212a651e1 fix(auth): exchange WebAuthn credential for server-signed JWT + rNetwork CRM cleanup
- Session manager now calls EncryptID /api/auth/start + /api/auth/complete
  to get a properly signed JWT instead of creating unsigned local tokens.
  This fixes 401 errors on /api/payments, /api/notifications, and other
  authenticated endpoints that verify tokens via EncryptID server.
- Token refresh calls /api/session/refresh instead of extending unsigned tokens
- Server generateSessionToken now includes authTime, jti, recoveryConfigured
- rNetwork: /crm route renders folk-crm-view instead of iframe
- rNetwork: ?view=app redirects 301 to /crm (backward compat)
- rNetwork: graph viewer always uses API (removed hardcoded demo data)
- docker-compose: pass through TWENTY_API_TOKEN from Infisical
- rcart: add catalog product images

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:13:19 -07:00
Jeff Emmett 31b088543e feat: add ViewHistory for in-app back navigation, rename rWork to rTasks
Add shared ViewHistory<V> utility class that provides a proper navigation
stack for rApps with hierarchical views. Replaces hardcoded data-back
targets with stack-based back navigation across 10 rApps: rtrips, rmaps,
rtasks, rforum, rphotos, rvote, rnotes, rinbox, rschedule, rcart.

Rename rWork module to rTasks — directory, component (folk-tasks-board),
CSS, exports, domains, and all cross-module references updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:04:13 -07:00
Jeff Emmett a7063d24f5 feat: PWA support — installable app, Web Push notifications, app badge
Phase 1: Installable PWA
- Add web app manifest with multi-size icons (192, 512, maskable, apple-touch)
- Add PWA meta tags to all entry points (shell.ts, canvas.html, index.html, create-space.html)
- Register service worker on all pages (previously only canvas.html)
- Add manifest.json to precache core list
- Capture beforeinstallprompt for custom install UX

Phase 2: Web Push Notifications
- Add web-push dependency + push_subscriptions DB table
- VAPID key endpoint, subscribe/unsubscribe routes in notification-routes.ts
- Web Push delivery in notify() with auto-cleanup of expired subscriptions
- SW push + notificationclick event handlers
- Client push subscription flow in notification bell component

Phase 3: Install UX Polish
- App badge (setAppBadge/clearAppBadge) on unread count changes
- "Enable push" button in notification panel header

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 17:35:30 -07:00
Jeff Emmett 2131d79f38 chore(encryptid): remove temporary OIDC debug logging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:40:31 -07:00
Jeff Emmett 6d0ebf2958 debug(encryptid): add OIDC token exchange debug logging
Temporary logging to diagnose invalid_grant errors on token exchange.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:19:32 -07:00
Jeff Emmett 61c735cabd chore(encryptid): brand verification email as rIdentity + footer links
- From address: rIdentity <noreply@ridentity.online>
- Callback URL uses auth.ridentity.online
- Footer links to rIdentity, rStack, rSpace

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:03:00 -07:00
Jeff Emmett cf7fab25f3 feat(encryptid): OIDC email verification flow for access_denied
When passkey auth succeeds but user's email doesn't match the OIDC
client's allowedEmails, show an inline email verification form instead
of a dead-end error. Sends a branded verification email with a single-use
30-minute token, then updates users.email on callback and lets the user
retry sign-in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:33:20 -07:00
Jeff Emmett 49f55dffc8 fix: resolve 500s on notifications, cache errors in SW, and [object Object] on-ramp alert
- Notification routes: wrap GET / and GET /count in try-catch, return
  graceful fallbacks instead of 500s when DB table is missing/unavailable
- getUnreadCount: add null safety (row?.count ?? 0) and catch DB errors
- Service worker: add .catch(() => {}) to all cache.put() calls to
  suppress NetworkError on quota-exceeded or corrupted cache entries
- On-ramp error display: coerce err.error to string so alerts show the
  actual message instead of [object Object]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 12:31:14 -07:00
Jeff Emmett bc810d34e4 fix(security): low-severity hardening and security headers
- L-1: Remove internal error details from SIWE verify response
- L-2: Stop forwarding raw Safe API error bodies to clients (log server-side)
- L-3: Evict stale keys from nonce rate limiter to prevent memory leak
- L-4: Add input length/type guards on wallet-link verify body fields
- L-5: Sanitize and cap limit query param on Safe transfers route (max 200)
- L-6: Server recomputes addressHash from SIWE address instead of trusting
  client-supplied value for dedup
- L-7: Reset LinkedWalletStore singleton on logout to clear cached keys
- I-1: Add X-Content-Type-Options, X-Frame-Options, Referrer-Policy headers
- I-9: Build EIP712Domain type array dynamically from domain fields in
  ExternalSigner.signTypedData (was hardcoded to empty, dropping fields)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:06:07 -07:00
Jeff Emmett 92fde65d60 fix(security): rate limit, sender verification, icon sanitization, SSRF prevention
- H-3: Rate limit wallet-link nonce to 5 per user per 5 minutes (429)
- H-4: Verify sender address matches JWT walletAddress in add-owner-proposal;
  also include walletAddress in JWT eid claims
- M-1: Sanitize EIP-6963 provider icons — only allow https: and safe
  data:image/(png|jpeg|gif|webp), block SVG and javascript: URIs
- M-2: Validate threshold is a positive integer ≤ newOwnerCount, fetch
  actual Safe owner list for bounds checking
- M-3: Add VALID_ETH_ADDR regex validation to all 9 routes that accept
  address params (Safe proxy, EOA proxy, propose, confirm, execute,
  add-owner-proposal) to prevent SSRF via path traversal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:51:18 -07:00
Jeff Emmett 45f5cea095 fix(security): AES-256-GCM encryption at rest, XSS escape, salted hashes
- C-1: Replace Base64 fake encryption with real AES-256-GCM server-side
  encryption for linked wallet data (HKDF-derived key from JWT_SECRET)
- H-1: Escape token name/symbol in balance table to prevent XSS
- H-2: Salt address hash with user ID to prevent cross-user correlation
- M-4: Remove cleartext sessionStorage cache for linked wallets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:42:09 -07:00
Jeff Emmett 8723aae3f6 fix(encryptid): show success page instead of auto-OIDC redirect
The auto-redirect through OIDC authorize fails because the client app
(Postiz) didn't initiate the OAuth flow and has no matching state.
Instead, show a branded success page with a link to the app. The user
signs in with their passkey when they visit the app normally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:31:32 -07:00
Jeff Emmett d861c0ad99 fix(encryptid): harden wallet link flow + add device_registration type
- Atomic nonce consumption prevents TOCTOU races
- SIWE domain validation against allowlist
- Unique constraint on linked_wallets(user_id, address_hash)
- Add device_registration to challenge type enum

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:17:50 -07:00
Jeff Emmett c789481d91 feat(rwallet): link external wallets via EIP-6963 + SIWE
Users can now connect browser wallets (MetaMask, Rainbow, etc.) to their
EncryptID identity via SIWE ownership proof, and view linked wallet
balances in the unified rWallet viewer.

New files:
- eip6963.ts: EIP-6963 multi-provider discovery
- external-signer.ts: EIP-1193 provider wrapper for tx signing
- linked-wallets.ts: encrypted client-side store (same AES-256-GCM pattern)

Server: wallet-link nonce/verify/list/delete routes, linked_wallets table,
Safe add-owner-proposal endpoint, new session permissions.

UI: "My Wallets" section with provider picker, SIWE linking flow,
wallet type badges, and click-to-view for linked wallets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:10:12 -07:00
Jeff Emmett b6bc1a756a fix: prioritize ridentity.online in WebAuthn Related Origins
Browsers enforce a 5 eTLD+1 limit on Related Origins. The previous
config dumped all 29 r*.online domains, causing ridentity.online to
be ignored (position 15). Now only lists the 5 domains that actually
need passkey auth: ridentity, rsocials, crypto-commons, p2pfoundation,
rwallet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 16:37:47 -07:00
Jeff Emmett 9c74bff465 feat: add OIDC admin page for managing clients and email allowlists
Admin UI at /admin/oidc with passkey login, gated by ADMIN_DIDS.
Supports viewing/creating/deleting clients, adding/removing allowed
emails per client, revealing/rotating secrets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 16:32:19 -07:00
Jeff Emmett 4665d14633 fix: seed OIDC clients after DB init completes
Move OIDC client seeding into the database init IIFE to prevent
race condition where seeding runs before tables are created.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 16:03:46 -07:00
Jeff Emmett f19a5b9904 feat: add OIDC provider + identity invite flow to EncryptID
Add full OIDC Authorization Code flow (discovery, authorize, token,
userinfo) so external apps like Postiz can authenticate via EncryptID
passkeys through auth.ridentity.online.

Add "Claim your rSpace" identity invite system — authenticated users
can invite friends by email, who register a passkey and optionally
auto-join a space.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:53:37 -07:00
Jeff Emmett f5388ecc2c fix(rwallet): migrate to new Safe Global API (api.safe.global)
Safe Global deprecated per-chain subdomains (safe-transaction-*.safe.global)
in favour of api.safe.global/tx-service/{shortcode}. The old URLs now 308
redirect, and the /balances/usd/ endpoint no longer exists.

- Update CHAIN_MAP prefixes to new shortcodes (eth, oeth, gno, etc.)
- Switch all Safe API calls to new base URL
- Use /balances/ instead of /balances/usd/ (fiat data no longer available)
- Normalize balance response with native token info and placeholder fiat fields
- Update client to show tokens with non-zero balance even without fiat data
- Update encryptid/server.ts Safe verify endpoint to new API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:57:09 -07:00
Jeff Emmett a62a33b4dc feat: add wallet and spaces sections to EncryptID account page
- My Wallet: shows linked wallet address with rWallet link, plus any
  pending fund claims with "Claim Now" button
- My Spaces: lists communities the user belongs to with role and
  treasury wallet links
- New APIs: GET /api/user/spaces, GET /api/user/claims
- New DB function: listSpacesForUser()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:57:55 -08:00
Jeff Emmett f662881170 fix: cumulative fund claims — deposits accumulate until claimed
Instead of expiring old claims, new deposits add to the existing
pending claim's fiat_amount and extend the expiry. The claim email
shows the updated total.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:42:44 -08:00
Jeff Emmett 6c807afeb0 fix: one wallet per EncryptID user, deduplicate fund claims
- OpenfortProvider.findOrCreateWallet() searches by player name before
  creating, ensuring the same email always maps to the same wallet
- Fund claims endpoint expires old pending claims before creating new ones
- Added expireFundClaim() to db layer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:33:45 -08:00
Jeff Emmett c2f9a07389 feat(encryptid): link claimed funds to rWallet with wallet address
After claiming funds, the success button now links to
{username}.rspace.online/rwallet?address={walletAddress} instead of
rflows.online, so users can visually analyze their wallet flows.

The accept endpoint now returns the username for URL construction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:10:53 -08:00
Jeff Emmett 8a4fc3051e fix(encryptid): fix JS syntax errors breaking entire landing page
Template literal escape sequences (\' and \\') were consumed by the
template engine, producing broken JavaScript that prevented the entire
<script> block from parsing — no click handlers worked at all.

- removeGuardian onclick: replaced inline onclick with data-guardian-id
  attribute and event delegation (avoids quote escaping entirely)
- device link page: replaced \\' with String.fromCharCode(39)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:55:21 -08:00
Jeff Emmett 57a72e53d4 fix(encryptid): fix claim page login flow
- Claim page now checks both encryptid_token and encryptid_session keys
- Login/register links point to /?redirect=... instead of /auth/login
- Landing page redirects to claim page after successful login
- Support ?tab=register to auto-switch to registration form

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:50:14 -08:00
Jeff Emmett bc73e6e1be fix: claim page syntax error and rflows delete-in-editor bug
- claim page: \'ll in template literal had backslash consumed, breaking
  browser JS parser (Unexpected identifier 'll'). Changed to "will"
- rflows: use composedPath()[0] to pierce Shadow DOM event retargeting
  so Delete/Backspace in editor inputs doesn't delete canvas nodes
- rflows: block Delete/Backspace at document level when editor is open
- rflows: add stopPropagation on editor panel inputs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:44:02 -08:00
Jeff Emmett 10a801e045 feat(encryptid): add fund claim flow for on-ramp transactions
After payment via Coinbase/Transak, users receive a claim email to link
their funded wallet to their EncryptID account — no keys or seed phrases
needed. Adds fund_claims table, CRUD layer, internal service-to-service
API, public claim page, and post-payment UX showing claim instructions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:53:57 -08:00
Jeff Emmett cdfe8c5b78 feat: add unified notification system with real-time WS delivery
Persistent, PostgreSQL-backed notification system replacing the in-memory
access request polling. Notifications are created via notify(), persisted
to DB, and delivered in real-time over WebSocket with a 30s polling fallback.

Infrastructure:
- notifications + notification_preferences tables in EncryptID schema
- 10 CRUD functions in db.ts (create, list, count, read, dismiss, etc.)
- notification-service.ts: core notify(), WS registry, notifySpaceAdmins()
- notification-routes.ts: REST API at /api/notifications
- rstack-notification-bell.ts: bell icon component with dropdown panel

Module integration (11 hooks):
- spaces.ts: access_request, access_approved, access_denied, member_joined,
  member_left, role_changed
- index.ts WS handler: ping_user (24h expiry)
- encryptid/server.ts: guardian_accepted, recovery_initiated (owner + guardians),
  recovery_approved

Legacy cleanup:
- Removed access request polling, badge, and approve/deny UI from
  rstack-identity.ts (now handled by notification bell)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 18:59:09 -08:00
Jeff Emmett 06f7d67cd3 chore: slash-command refinements, server import fixes, misc cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:21:04 -08:00
Jeff Emmett 35a5a5f29a feat: workflow template, choice components, space settings, EncryptID vault, UI polish
- Pre-populated 4-node workflow template (trigger→action→condition→output) with blue arrows
- Add folk-choice-vote, folk-choice-rank, folk-choice-spider component libraries
- New rstack-space-settings component
- EncryptID encrypted vault schema and server endpoints
- Space management and community store enhancements
- Shell, landing, and module CSS refinements
- Tab bar, app switcher, identity, and MI component updates
- rNotes app improvements
- rFunds diagram adjustments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 13:15:13 -08:00
Jeff Emmett e2e12afc96 feat: add encrypted server-side account vault for EncryptID
Zero-knowledge vault stores account data (profile, emails, devices,
wallets, preferences) as AES-256-GCM encrypted blob via backup API.
Key derived from WebAuthn PRF — server never sees plaintext. Dashboard
UI with save/restore buttons triggers passkey re-auth for encryption.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:13:41 -08:00
Jeff Emmett cb828fc605 fix: add retry logic to EncryptID database init for Docker networking delays
Docker container networking can take a moment to stabilize even after
depends_on health checks pass. This adds 5 retries with exponential
backoff (2s, 4s, 6s, 8s, 10s) to survive transient CONNECT_TIMEOUT errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:23:26 -08:00
Jeff Emmett 94c5346eda fix: account completion indicators, email verify token type, SW cache
- Add /api/account/status endpoint returning email, multi-device,
  social recovery completion state
- Show red/green status dots on Account modal section headers for
  incomplete vs complete steps (email, device, recovery, data storage)
- Highlight Data Storage section with red warning when using local-only
  storage so users know they're responsible for their own data
- Fix email verification 500 error: change token type from
  'email_verification' to 'email_verify' to match DB check constraint
- Fix service worker: skip non-http(s) schemes to prevent
  chrome-extension:// cache put errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:28:13 -08:00
Jeff Emmett 88118cd157 fix: use server-initiated WebAuthn flow for guardian acceptance and login
The guardian page and auth.rspace.online login page were using the
client-side authenticatePasskey()/registerPasskey() SDK functions which
generate their own challenge and return AuthenticationResult — but then
tried to send result.challenge and result.credential (both undefined)
to the server. This caused postgres to throw "UNDEFINED_VALUE" resulting
in a 500 "Internal Server Error" that the client couldn't parse as JSON.

Fix: use the proper server-initiated flow matching rstack-identity.ts:
1. POST /api/auth/start (or /register/start) to get server challenge
2. navigator.credentials.get/create with that challenge
3. POST /api/auth/complete (or /register/complete) with challenge + credential

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:34:43 -08:00
Jeff Emmett 22db2f439f feat: client-side encryption wiring + space scoping UI (Phase 5+6)
Phase 5 — EncryptID → DocCrypto bridge:
- Add EncryptedDocBridge connecting WebAuthn PRF to document encryption
- Add per-doc relay mode to SyncServer (encrypted spaces bypass participant mode)
- Wire encryption toggle to syncServer.setRelayOnly() on PATCH /:slug/encryption
- Restore relay mode for encrypted spaces on server startup
- Initialize DocBridge from PRF on login, clear on sign-out (both login-button + identity)
- Use bridge helpers for encrypted backup toggle in My Account

Phase 6 — Space scoping UI:
- Add "Modules" tab to Edit Space modal (enable/disable modules, scope toggles, encryption)
- Auto-filter app switcher by space's enabledModules via renderShell()
- Show "G" badge on global-scoped modules in app switcher
- Show lock icon in header for encrypted spaces
- Add getSpaceShellMeta() helper for auto-populating shell options

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:50:16 -08:00
Jeff Emmett 33156cc249 fix: resolve all pre-existing tsc --noEmit errors (10 errors across 5 files)
- folk-map-viewer.ts: remove explicit return type, let TS infer
- test-x402.ts: cast account as any (test script, readContract unused)
- key-derivation.ts: cast Uint8Array at WebCrypto boundaries (BufferSource/ArrayBuffer)
- wallet-store.ts: cast .buffer as ArrayBuffer
- webauthn.ts: cast PRF output as ArrayBuffer | undefined

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:48:37 -08:00
Jeff Emmett be271de7fb feat: add Gnosis Safe + EncryptID passkey wallet abstraction
Derive a deterministic secp256k1 EOA from the passkey's PRF output via
HKDF-SHA256, enabling hardware-backed signing for x402 micropayments and
Safe treasury proposals without storing private keys.

Key changes:
- EOA key derivation with domain-separated HKDF (eoa-derivation.ts)
- Key manager integration with PRF-only EOA path (key-derivation.ts)
- Encrypted client-side wallet store for Safe associations (wallet-store.ts)
- Passkey-backed x402 signer replacing EVM_PRIVATE_KEY (passkey-signer.ts)
- Safe propose/confirm/execute proxy routes in rwallet (mod.ts)
- Wallet capability flag in JWT via users.wallet_address (server.ts)
- Payment operation permissions: x402, safe-propose, safe-execute (session.ts)

Privacy: Safe wallet associations stored client-side only (AES-256-GCM
encrypted localStorage). Server only knows user has wallet capability.

108 tests passing across 5 test suites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:18:34 -08:00
Jeff Emmett 2b58068a1a feat: persistent sessions with 30-day JWT and auto-refresh on page load
Sessions now last 30 days instead of 15 minutes. Both the rstack-identity
component and legacy header auto-refresh the token when < 7 days remain,
so users who visit at least once every ~23 days stay logged in indefinitely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:33:31 -08:00
Jeff Emmett b12cc52892 feat: admin dashboard with user management and delete capabilities
- Add tabbed admin UI (Spaces | Users) with auth gate
- Add admin API endpoints on EncryptID: list users, delete user, clean space members
- Add admin force-delete space endpoint on rSpace (bypasses owner check)
- Protect all admin endpoints with ADMIN_DIDS env var
- Add ADMIN_DIDS to both Docker Compose configs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 22:30:21 -08:00