# Tx OS A bilingual (Arabic / English) internal "office OS" web platform. Single-tenant, self-hosted, designed to live behind a TLS-terminating reverse proxy on a private VPS or on-prem host. The repo is a pnpm monorepo containing: | Package | Purpose | | ------------------------------------ | -------------------------------------------------- | | `artifacts/api-server` | Express 5 + Socket.IO + Drizzle ORM API | | `artifacts/tx-os` | React 19 + Vite SPA (the user-facing app) | | `artifacts/mockup-sandbox` | Internal component preview server (dev-only) | | `lib/db` | Drizzle schema + migrations | | `lib/api-zod` + `lib/api-client-react` | OpenAPI-generated Zod schemas + React Query hooks | | `scripts` | DB seed + maintenance scripts | --- ## Features - **Glassmorphism OS UI** with animated gradient backgrounds and an app grid / dock. - **Bilingual** (RTL Arabic + LTR English) with per-user persisted locale. - **Session auth** (`express-session` + Postgres-backed `connect-pg-simple`, bcrypt password hashing) with full RBAC (admin / user roles + role-permission matrix + group-derived permissions). - **Real-time** chat / notifications / executive-meeting alerts via Socket.IO. - **Executive Meetings module** — scheduling, change requests, approvals, optimistic locking on postpones, audit log, and a Playwright-rendered HTML PDF export. - **S3-compatible object storage** with signed-URL uploads (production: MinIO; local dev: built-in filesystem driver — no extra services required). --- ## Quick start (Docker) The fastest path to a running stack on a Linux VPS with Docker installed. ```bash git clone cd tx-os cp .env.example .env # Edit .env — at minimum change SESSION_SECRET, POSTGRES_PASSWORD, # S3_SECRET_ACCESS_KEY, SEED_ADMIN_PASSWORD, SEED_USER_PASSWORD. $EDITOR .env docker compose build docker compose up -d db minio minio-init docker compose run --rm migrate # one-shot: pnpm run migrate (db push + seed) docker compose up -d api web ``` After this, the running stack exposes: | Service | Default host port | URL | | ---------------- | ----------------- | ------------------------------ | | SPA (`web`) | `3000` | `http://:3000/` | | API (`api`) | `8080` | `http://:8080/api/healthz` | | MinIO console | not exposed | (proxy to `:9001` if you need it) | | `mockup-sandbox` | `8081` | dev profile only — `docker compose --profile dev up -d mockup-sandbox` then `http://:8081/__mockup` | Front the SPA (port `WEB_PORT`, default `3000`) with Caddy / Nginx / Traefik for TLS and HTTP/2; bind the API port (`API_PORT`, default `8080`) to `127.0.0.1` in production so the edge proxy is the sole external entry. ### Default seeded accounts | Username | Password | Role | | -------- | ---------------------------------- | ----- | | `admin` | value of `SEED_ADMIN_PASSWORD` | admin | | `ahmed` | value of `SEED_USER_PASSWORD` | user | Both are seeded by `docker compose run --rm migrate` and **only** if they don't already exist (the seed is idempotent on conflict). ### Common compose commands ```bash docker compose logs -f api # tail API logs docker compose exec db psql -U tx tx_os # psql shell docker compose run --rm migrate # re-run migrations (safe to repeat) docker compose down # stop everything (data persists) docker compose down -v # stop AND wipe volumes (DANGER) ``` --- ## Local development (without Docker) You can run the stack natively if you have Node 24+, pnpm 10, and Postgres 16. ```bash pnpm install createdb tx_os export DATABASE_URL=postgres://localhost/tx_os export PORT=8080 BASE_PATH=/ ALLOWED_ORIGINS=http://localhost:25785 export SESSION_SECRET=$(openssl rand -hex 32) export PRIVATE_OBJECT_DIR=/local/private export PUBLIC_OBJECT_SEARCH_PATHS=/local/public # Note: with no S3_ENDPOINT set, the API falls back to the local-FS storage # driver and persists uploads under ./storage/. Safe for development only. pnpm --filter db run push # apply schema pnpm --filter scripts run seed # seed admin/ahmed accounts pnpm --filter @workspace/api-server dev # In another terminal: PORT=25785 BASE_PATH=/ pnpm --filter @workspace/tx-os dev ``` --- ## Configuration reference Every option is read from environment variables. See `.env.example` for the complete list with comments. Highlights: | Variable | Purpose | | --------------------------------- | ---------------------------------------------------------------------- | | `DATABASE_URL` | Postgres connection string. **Required.** | | `SESSION_SECRET` | HMAC key for session cookies + local-driver upload tokens. **Required in production.** | | `ALLOWED_ORIGINS` | Comma-separated CORS allow-list. Defaults to `*` (dev only). | | `STORAGE_DRIVER` | `s3` or `local`. Defaults to `s3` if `S3_ENDPOINT` set, else `local`. | | `S3_ENDPOINT` etc. | MinIO / S3 connection. Required when `STORAGE_DRIVER=s3`. | | `PRIVATE_OBJECT_DIR` | Path inside the bucket for private uploads, e.g. `/tx-private/private`.| | `PUBLIC_OBJECT_SEARCH_PATHS` | Comma-separated bucket paths searched by `GET /storage/public-objects/*`. | | `LOCAL_STORAGE_ROOT` | Filesystem root used by the local driver. Defaults to `./storage`. | | `SEED_ADMIN_PASSWORD` / `SEED_USER_PASSWORD` | Required by the seed script in production. | | `SMTP_*` | Optional outbound mail config for Executive Meetings notifications. | | `LOG_LEVEL` | pino log level. Defaults to `info`. | ### Storage drivers The API server has two object-storage backends, selected automatically: - **`s3`** (production): targets any S3-compatible endpoint (MinIO, AWS S3, R2, Backblaze B2, ...) via `@aws-sdk/client-s3` + presigned PUT URLs. - **`local`** (dev fallback, default when `S3_ENDPOINT` is unset): persists files under `LOCAL_STORAGE_ROOT` and issues HMAC-signed upload URLs that the API server validates and accepts on `PUT /api/storage/_local/upload`. Single-host only; not suitable for production. The route layer is unaware of which driver is active — both expose the same `StoredObject` interface in `lib/objectStorage.ts`. --- ## Tests ```bash pnpm --filter @workspace/api-server build pnpm --filter @workspace/api-server start & # boot API on 8080 pnpm --filter @workspace/api-server test # node --test suite pnpm --filter @workspace/tx-os test:e2e # Playwright UI tests ``` The `test` workflow chains all of the above. Tests use the same database specified in `DATABASE_URL` and clean up after themselves with `LIKE`-prefixed fixture rows. --- ## Production checklist Before fronting Tx OS with a public domain: 1. **Set every secret in `.env`.** No defaults in production. 2. **Run behind TLS.** Caddy / Nginx / Traefik should terminate HTTPS and forward to `web` on `${WEB_PORT}`. The API server trusts `X-Forwarded-For` from the first proxy hop (`app.set("trust proxy", 1)`). 3. **Restrict the API port.** The `api` service should never be exposed directly to the internet — only `web` is intended to be reachable. 4. **Back up the volumes.** `db_data` and `minio_data` are the only stateful surfaces. Snapshot them on a schedule. 5. **Rotate seeded passwords.** The first thing an admin should do is change the seeded `admin` and `ahmed` passwords from the user-management screen. 6. **Review `threat_model.md`.** Document-level threat model lives in the repo root and lists the trust boundaries this deployment relies on. --- ## Project conventions - **Package manager**: pnpm 10. The repo refuses to install with npm/yarn. - **Node**: 24 LTS. Older Nodes will not run the API bundle. - **TypeScript**: 5.9, strict mode, project-references build (`pnpm typecheck`). - **API contracts**: hand-written Zod schemas in `lib/api-zod` are the source of truth; the React-Query client in `lib/api-client-react` is generated from the same OpenAPI spec via Orval. - **Migrations**: Drizzle Kit. `pnpm --filter db run push-force` for dev, `pnpm --filter db run push` for production. --- ## License MIT.