Commit Graph

39 Commits

Author SHA1 Message Date
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
Jeff Emmett 1db8341fb2 feat: email forwarding via Mailcow aliases + private space access gate
Email forwarding (EncryptID):
- New mailcow.ts API client for alias CRUD via Mailcow REST API
- Schema: email_forward_enabled + email_forward_mailcow_id columns
- API endpoints: GET/POST email-forward status, enable, disable
- Profile email change hook updates/disables alias automatically
- Docker: rmail-mailcow network + MAILCOW_API_URL/KEY env vars

Private spaces:
- Access gate overlay blocks members_only spaces for unauthenticated users
- Space visibility injected into HTML via middleware
- Auto-provision creates spaces as members_only by default
- Personalized "Create {username}'s Space" CTA in space switcher
- Removed unused /notifications endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 21:54:07 -08:00
Jeff Emmett a2f0752fed feat: add user profile and encrypted address API endpoints
Server-side support for user profile management and zero-knowledge
postal address storage:

Schema:
- ALTER users table: add bio, avatar_url, profile_email,
  profile_email_is_recovery, wallet_address, updated_at columns
- CREATE encrypted_addresses table with composite PK (id, user_id),
  label CHECK constraint, and cleartext metadata for UI listing

DB layer:
- getUserProfile, updateUserProfile (dynamic column updates)
- getUserAddresses, getAddressById, saveUserAddress (upsert),
  deleteUserAddress
- Default-address logic: unsets all others when isDefault=true

API routes:
- GET/PUT /api/user/profile — bio validation (500 chars), email format
- GET/POST /api/user/addresses — max 10 addresses, label validation
- PUT/DELETE /api/user/addresses/:id — 404 if not found

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 13:13:32 -08:00
Jeff Emmett 1ff0f69218 feat: wire up account settings endpoints (email, device, guardians)
Server (src/encryptid/server.ts):
- POST /api/account/email/start — send 6-digit verification code via SMTP
- POST /api/account/email/verify — verify code and set email on account
- POST /api/account/device/start — WebAuthn creation options for same-device
  passkey registration (authenticated, reuses existing userId)
- POST /api/account/device/complete — store additional credential under
  existing account

DB (src/encryptid/db.ts):
- Add 'device_registration' to StoredChallenge.type union
- Add 'email_verification' to StoredRecoveryToken.type union

Client (shared/components/rstack-identity.ts):
- Rewrite social recovery modal to use existing guardian API:
  GET /api/guardians, POST /api/guardians, DELETE /api/guardians/:id
- Loads existing guardians on open, adds/removes in real-time
- Shows guardian status (accepted/pending), invite emails sent on add
- Two name+email inputs (max 3 guardians, server-enforced)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:57:32 -08:00
Jeff Emmett 6b1a8ade9c feat(encryptid): guardian recovery, device linking, enhanced profile
Add 2-of-3 guardian recovery system:
- Guardian invite via email or shareable link
- One-click approval page for recovery requests
- Social recovery initiation (anti-enumeration)
- 7-day recovery request expiry

Add second device linking:
- QR code + link for cross-device passkey registration
- 10-minute link expiry, one-time use

Enhanced profile page:
- Account security checklist (email, device, guardians)
- Guardian management (add/remove, max 3)
- Device linking with QR code display
- Recovery initiation form for lost devices

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:07:40 +00:00
Jeff Emmett 7103366047 Dynamic RP ID: use caller's domain for WebAuthn passkeys
Instead of hardcoding rpId to "rspace.online" (which requires Related
Origins support), derive the RP ID from the request's Origin header.
Each r* app (rmaps.online, rnotes.online, etc.) now gets its own RP ID
matching its domain, so passkeys work natively without browser support
for Related Origin Requests.

- Added resolveRpId() helper that maps Origin → hostname for allowed origins
- Registration creates passkeys with the caller's domain as RP ID
- Authentication uses the caller's domain as RP ID
- Added rp_id column to credentials table for per-credential RP ID tracking
- rspace.online subdomains still use rspace.online as shared RP ID

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:46:15 +00:00
Jeff Emmett 08985d774e feat: add membership endpoints and bidirectional shape sync
Adds space_members table and CRUD endpoints to EncryptID server for
centralized membership management. Extends Automerge CommunityDoc with
members map and PATCH endpoint for module→canvas shape updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 14:31:48 -07:00
Jeff Emmett 163ab3c288 fix: remove hardcoded secret fallbacks from EncryptID (GitGuardian alert)
Require DATABASE_URL and JWT_SECRET via env vars instead of falling back
to hardcoded defaults. Removes insecure fallback passwords from compose
file as well. Production was already using strong .env secrets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:33:02 -07:00
Jeff Emmett 89fba95e40 feat: add email recovery with Mailcow SMTP and recovery page
- Add email column to users table, recovery_tokens table
- Add recovery endpoints (set/request/verify email)
- Integrate nodemailer with Mailcow SMTP (mx.jeffemmett.com)
- Add branded HTML recovery email template
- Add /recover landing page with passkey registration
- Add SMTP env vars to docker-compose

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 09:35:53 -07:00
Jeff Emmett e5af01119b feat: upgrade EncryptID server to PostgreSQL
Replace in-memory Maps with persistent PostgreSQL storage:
- Add db.ts with typed query functions for users, credentials, challenges
- Add schema.sql with users/credentials/challenges tables
- Update server.ts to use async DB queries
- Add postgres service to docker-compose
- Health endpoint now reports database connectivity
- Auto-cleanup of expired challenges every 10 minutes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:34:13 -07:00