# slackwsh

Slack-parity collaboration platform. See [`ARCHITECTURE.md`](ARCHITECTURE.md) and
[`DECISIONS.md`](DECISIONS.md) for the full design and ADR history — this file
is only the local dev quickstart.

## Repo layout

```
apps/
  api/         NestJS on Fastify — HTTP
  gateway/     NestJS + Socket.IO — realtime
  worker/      NestJS + pg-boss — background jobs
  web/         Next.js static export — the product (web, PWA, desktop bundle)
  marketing/   Next.js SSR — public site
  desktop/     Tauri v2 shell (Windows, macOS)
libs/
  contracts/   Zod schemas -> OpenAPI + TS types (single source of truth)
  core/        Policy engine, mention parsing, blocks serialisation
  data/        Drizzle schema, RLS tenant helper, migrations
  messaging/   Channels + messages service layer, shared by api and gateway
  sync/        Client sync engine — MessageStore, Outbox, ChannelSync
  realtime/    Socket.IO client wrapper (not yet built)
  ui/          Design system (not yet built)
  editor/      ProseMirror + Yjs (Phase 1 spike OD-2, not yet built)
infra/terraform/  Provider-agnostic baseline, pending OD-1
```

## Prerequisites

- Node 20+, pnpm 9 (`corepack enable`)
- Docker (for local Postgres/Redis/Typesense/MinIO) — **or**, if you don't
  have Docker (e.g. Windows without WSL): Postgres via any native installer,
  and Redis via `scoop install redis` (native Windows port, no admin/service
  required — `redis-server --port 6379` to run it). This is what this repo
  was actually developed against.
- Rust + the Tauri v2 prerequisites for `apps/desktop` (see the [Tauri docs](https://v2.tauri.app/start/prerequisites/))

## Local dev

```bash
cp .env.example .env
docker compose up -d   # or point at Postgres/Redis you already run — see below
pnpm install
```

File uploads go to **Amazon S3** with short-lived signed PUT/GET URLs. Set
`S3_BUCKET`, `S3_REGION`, `S3_ACCESS_KEY`, and `S3_SECRET_KEY` in `.env` and
leave `S3_ENDPOINT` unset. The API applies bucket CORS from `S3_CORS_ORIGINS`
and can create the bucket on first boot when `S3_AUTO_CREATE_BUCKET=true`.
The bucket stays private (no public ACLs). `S3_ENDPOINT` is only for a local
MinIO / S3-compatible stand-in.

**Database setup — read this, it's not optional.** Migrations need a
privileged connection (they run DDL); the app's `DATABASE_URL` must be a
**non-superuser, non-BYPASSRLS** role or row-level security silently does
nothing (Postgres superusers bypass RLS unconditionally — `FORCE ROW LEVEL
SECURITY` does not override that). This was caught by actually running
`libs/data/src/rls.integration.test.ts` against a real database, not by
inspection.

```bash
# 1. Run migrations + RLS policies as your admin/superuser role:
DATABASE_URL="postgres://<admin>:<pw>@localhost:5432/slackwsh" \
  pnpm --filter @slackwsh/data run db:migrate

# 2. Create the app's own non-superuser role (edit the password first —
#    pass it to -v with NO inner quotes; the .sql file's own :'app_password'
#    syntax does the SQL-escaping):
psql "postgres://<admin>:<pw>@localhost:5432/slackwsh" \
  -v app_password=change-me -f libs/data/sql/app-role.sql

# 3. Point .env's DATABASE_URL at that role (slackwsh_app), not the admin one.
```

If you're using `docker-compose.yml` as-is, the `slackwsh`/`slackwsh`
superuser it creates is fine for step 1 but must not be what `.env` uses for
step 3 — always create and use `slackwsh_app` for the running services.

```bash
redis-server --port 6379 &   # if not already running — see Prerequisites
pnpm dev:api        # :3001
pnpm dev:gateway    # :3002 — needs Redis; skip if you're only testing auth/workspaces
pnpm dev:worker
pnpm dev:web        # :3000
pnpm dev:marketing  # :3010
```

Open `http://localhost:3000` — it redirects to `/login`. Sign up, then check
the `api` process's console for the dev-mode "verification email" (a logged
link — no real mail sending until Phase 4's Postmark/SES integration), mark
verified, log in, create a workspace, create a channel, send messages. With
Redis and `apps/gateway` running, open the same channel in a second tab and
confirm messages appear live with no refresh. If `apps/gateway`/Redis aren't
running the channel page still works over HTTP alone — it just won't update
live in a second tab until you refresh.

**Calls (Phase 6).** `docker compose up -d livekit` then copy the `LIVEKIT_*`
block from `.env.example`. The web client mints a room token from the API and
connects via the LiveKit JS SDK; if LiveKit is down it falls back to the
existing mesh. Connect (the persistent channel audio room) is the **Connect**
button in a room header.

## Status

- **Phase 0 (Foundation)**: scaffolded — monorepo, three NestJS apps,
  Next.js web/marketing, Tauri shell, Zod→OpenAPI contracts, docker-compose
  infra, CI with a Tauri Windows+macOS matrix, Terraform baseline (pending
  OD-1). Not done: real cloud provisioning, code-signing certs, the
  two-node staging round trip.
- **Phase 1 (Identity, Workspaces & Spikes)**: done and manually verified
  end-to-end against a real Postgres — auth, sessions, workspaces, invites,
  RLS live. The three spikes (OD-2/OD-3/OD-6) were not run; see
  [`SPIKES.md`](SPIKES.md).
- **Phase 2 (Messaging Core & Sync Engine)**: channels, messages, the
  client sync engine (`libs/sync`), and realtime fanout through
  `apps/gateway` + Redis are all built and manually verified end-to-end —
  including live cross-tab delivery and reload-triggered catch-up.
  Presence, typing, file upload, the rich composer, search, and the full
  property/fault-injection/TLA+ verification suite are not built. See
  [`PHASE2_STATUS.md`](PHASE2_STATUS.md).
