Voidex

VOIDEX — Step 1 architecture

VOIDEX is a personal digital space that looks and behaves like its own operating system but ships as an application (phone app stores, desktop app, web). Step 1 builds the foundation every later app will stand on: one VOIDEX Account, one server-side source of truth, an OS shell with a window system and an app registry, and two real system apps — Settings and Mail.


1. Audit (starting point)

Area Finding
This repository Empty — no commits. Greenfield.
Existing product: VOIDEX messenger (Voyzen) Next.js 16 + React 19 + Tailwind v4 + motion + Remix Icons. Front-end only (static export to GitHub Pages); accounts, auth and data live in localStorage (mock).
Reused Visual language: grey canvas #ececed, white surfaces, violet accent #6c5cff, soft radii; the same UI libraries (React 19, Tailwind v4, motion, Remix Icons) so both products feel like one ecosystem.
Not reused The messenger’s auth and data layer — it is decorative (client-side only) and can’t provide security, sync or ownership checks.
Missing (built in Step 1) Backend, database, real authentication, sessions/devices, SMS verification, legal consents, OS shell, app registry, Settings, internal Mail.

Decision: a new pnpm monorepo. Next.js was not carried over: VOIDEX is an app shell that will be wrapped by native containers (Capacitor for iOS/Android, Tauri/Electron for desktop), which want a static SPA plus a separate API — so the client is Vite + React and the server is Fastify + PostgreSQL.

2. Repository layout

apps/server      Fastify API, Drizzle ORM, PostgreSQL, SSE — the source of truth
  src/db           schema.ts (all tables), migrations in ../drizzle
  src/services     accounts, sessions, verification (OTP), challenges, mail, vibex, apps, legal, events, sms/
  src/routes       auth, account/security/apps/events, mail, vibex, system
  legal/           versioned legal documents (<key>/<version>.<lang>.md)
  test/            integration tests against a real PostgreSQL
apps/web         Vite + React 19 + Tailwind v4 client (the OS shell and apps)
  src/os           workspace, window manager, app registry, system menu, events
  src/apps         settings/, mail/, vibex/ — code-split system apps
  src/auth         welcome, sign-up, sign-in, new-device challenge, recovery
  src/ui           design-system components; src/styles tokens
  e2e/             Playwright end-to-end tests (phone + desktop layouts)
packages/shared  validation rules, request schemas, DTOs, app registry, legal registry
                 used by BOTH server (authoritative) and client (instant feedback)

3. Identity, sessions and security

VOIDEX ACCOUNT (users row)
        │
  VOIDEX BACKEND  ── PostgreSQL (source of truth)
        │  REST + Server-Sent Events
  ┌─────┴──────┐
iPhone        PC        … every device reads/writes the same account
  └────SYNC────┘

SMS — production dependency

SmsProvider interface with ConsoleSmsProvider (development), TwilioSmsProvider and SmsRuProvider. In development the code is logged and returned to the client flagged as development (shown in a yellow notice). The server refuses to start in production with the console provider — it never pretends to send SMS. Credentials: TWILIO_* or SMSRU_API_ID (see apps/server/.env.example).

4. Data model

users · user_avatars · user_preferences (synced settings) · consents (document key + version + time + IP) · devices · sessions · phone_verifications · auth_challenges · security_events · installed_apps · mail_accounts · mail_threads · mail_messages · mail_recipients · mail_entries · mail_attachments (schema reserved).

Mail stores a message once; every mailbox that can see it has its own mail_entries row with folder, read and star state. Threads group replies; visibility is never granted by the thread — only by entries (a person added later to a conversation doesn’t see earlier messages). transport columns keep room for an external (SMTP) transport later; today only internal exists and external recipients are refused.

5. OS shell

Adding an app: manifest in shared/apps.ts → folder web/src/apps/<id> → entry in os/app-registry.tsx → server endpoints under /api/<id> guarded by app.authenticate and scoped to req.auth.

6. Cross-device sync

The server is the only source of truth. Each signed-in device keeps one SSE stream (GET /api/events, fetched with the Authorization header). Events: mail.received, mail.changed, account.updated, preferences.updated, sessions.updated, approval.requested/resolved, session.revoked. Clients invalidate the matching React Query caches; on reconnect everything is refetched, so nothing missed while offline stays stale. EventHub is in-process; for several server instances back it with Redis/NATS pub-sub (same interface).

7. Internationalisation & regions

LANGUAGES (en, ru) in shared + one dictionary file per language in web/src/lib/i18n. The account’s language syncs to every device. Country names come from Intl.DisplayNames. regionPolicy(country) is the single place where a country affects behaviour (default language, minimum age, feature flags).

8. Native packaging (next step)

The web build is a static SPA talking to /api. For stores: wrap with Capacitor (iOS/Android) and Tauri or Electron (Windows/macOS), set VITE_API_BASE, add the shell origin to ALLOWED_ORIGINS, and store tokens via the native path (X-Voidex-Client: native). Push notifications (APNs/FCM) will be a new notifications transport next to SSE.

9. Not in Step 1 (by design)

App Market, Browser, Messenger integration, Files, Photos, Music, cloud drive, external e-mail, payments, AI, 2FA apps/passkeys/biometrics. Attachments have a reserved schema but no UI — nothing is shown that doesn’t work.