Skip to content
ahmadrrrtxPublic

About

SoulSpace πŸ’š A free, anonymous peer support platform for random 1-on-1 chats.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

SoulSpace

A quiet place to talk, steady yourself, and reflect. Free, anonymous peer support: real 1-on-1 matching, small topic rooms, calm tools, and a private journal β€” no account, no tracking, and honest language about all of it.

SoulSpace is peer support. It is not therapy, not a crisis service, and not an AI therapist. Crisis resources are localized (Pakistan first, plus international directories) and always one tap away.

Live: https://soul-space-alpha.vercel.app


What changed in v2 (from the v1 MVP)

The v1 MVP simulated its core promise: "matching" was a setTimeout, chat channels were random UUIDs nobody else could join, room counts were hardcoded, and two unauthenticated API routes allowed anyone to eavesdrop on any private channel or send messages as anyone. v2 is a security-first rebuild around the same product soul:

v1 v2
Matching Fake timer Real server-side queue (Turso) with topic compatibility pairing
Channel access Anyone could auth anything HMAC capability tokens, verified per-channel, fail-closed
Messaging Open relay (spoof/spam/eavesdrop) Server-authoritative sends with membership + rate limits
Crisis handling Blocked common words, redirected mid-chat Tiered detection: gentle nudges, never silent censorship, localized resources
Privacy claims "100% secure & anonymous" Precise, architecture-matched disclosures
Social proof Fabricated counts everywhere Removed; presence is real (Pusher presence channels)
Tests / CI None 38 vitest tests incl. attack paths + GitHub Actions

Architecture

Next.js 16 (App Router, React 19, Turbopack)
β”œβ”€β”€ src/app            β€” routes (server components by default; chat/tools are client islands)
β”œβ”€β”€ src/app/api
β”‚   β”œβ”€β”€ match/*        β€” join / status / leave  (queue + pairing + capability tokens)
β”‚   β”œβ”€β”€ chat/*         β€” send / leave           (server-authoritative, membership-checked)
β”‚   β”œβ”€β”€ room/*         β€” join / send            (fixed room allowlist, server-assigned handles)
β”‚   β”œβ”€β”€ report         β€” durable abuse reports
β”‚   β”œβ”€β”€ pusher/auth    β€” channel authorization  (capability-token gated)
β”‚   └── health         β€” honest provider configuration status
β”œβ”€β”€ src/lib
β”‚   β”œβ”€β”€ capability.ts  β€” HMAC-signed, expiring, resource-bound tokens
β”‚   β”œβ”€β”€ matching.ts    β€” pairing logic (FIFO + topic compatibility)
β”‚   β”œβ”€β”€ store.ts       β€” Turso adapter w/ in-memory fallback + TTL cleanup
β”‚   β”œβ”€β”€ crisis.ts      β€” tiered distress detection + localized helplines
β”‚   β”œβ”€β”€ ratelimit.ts   β€” token-bucket limiter (per-instance on serverless)
β”‚   └── validate.ts    β€” strict input validation for every endpoint
└── tests/             β€” vitest: matching lifecycle, capability forgeries, API attack paths

Identity model (no accounts)

  • No accounts, no cookies, no analytics. The browser generates a random UUID per conversation (sessionStorage, never persisted server-side).
  • To join a chat or room the client must present a capability token the server issued to a specific participant. Tokens are HMAC-SHA256 signed (APP_SECRET, falling back to PUSHER_SECRET), bound to one exact channel, and expire (2–3 h).
  • /api/pusher/auth re-verifies the token and live membership before authorizing any subscription. Ending a chat deletes its row β€” access is revoked immediately for both parties.
  • Messages are relayed via Pusher and never written to the database. The sender identity (role/handle) is stamped server-side; clients cannot impersonate each other or publish to channels they don't hold tokens for.

Data storage summary

Data Where Lifetime
Chat messages Relayed through Pusher, never stored by SoulSpace Gone on delivery
Queue / pairing rows Turso (or in-memory fallback) Deleted on match/leave, TTL 10 min
Active chat rows Turso (or in-memory fallback) Deleted on leave, TTL 2 h
Check-ins / journal / safe kit Browser localStorage only Until user exports or erases
Abuse reports Turso (or in-memory fallback) Reviewed manually by the maintainer

Environment variables

Copy .env.example β†’ .env.local for development. In production (Vercel) set the same keys. Without Turso configured, matching still works but only pairs users served by the same warm instance β€” /api/health reports which mode is live (matching: "turso" | "memory-fallback").

Variable Required Purpose
NEXT_PUBLIC_PUSHER_KEY yes Realtime client (public by design)
NEXT_PUBLIC_PUSHER_CLUSTER yes Realtime client
PUSHER_APP_ID yes Server-side Pusher
PUSHER_SECRET yes Server-side Pusher; also signs capability tokens if APP_SECRET unset
APP_SECRET recommended Dedicated capability-token signing key (openssl rand -hex 32)
TURSO_DATABASE_URL recommended Durable matching queue + reports
TURSO_AUTH_TOKEN recommended Turso auth

Local development

npm install
cp .env.example .env.local   # fill in real values
npm run dev                  # http://localhost:3000
npm test                     # vitest suite (38 tests)
npm run lint
npm run build                # production build check

The database schema is created on demand (CREATE TABLE IF NOT EXISTS in src/lib/store.ts) β€” no migration step needed.

Deployment

Vercel auto-deploys the main branch. Pre-deploy checklist:

  1. npm run build && npm test locally
  2. Vercel env vars present (see table above)
  3. After deploy: GET /api/health must show realtime: "configured" and (ideally) matching: "turso"

Security model & assumptions

  • What we defend: channel eavesdropping, message spoofing, cross-channel injection, unauthenticated relaying, flooding (burst + sustained buckets per session/IP), oversized/malformed payloads, cross-origin browser abuse, arbitrary room creation.
  • Honest limitations: in-memory rate limiting is per serverless instance (approximate across instances); moderation is one human, not real-time; TLS transport only (no E2E encryption β€” disclosed in the privacy page, not hidden).
  • Not provided: emergency intervention, medical advice, law-enforcement escalation. Crisis resources link to trained services (Umang/Rozan/Taskeen in Pakistan; findahelpline.com / IASP internationally).

Known limitations & roadmap

Highest-value next steps, in order:

  1. Turso on production (if not yet configured) so matching spans serverless instances.
  2. Nonce-based CSP (replacing unsafe-inline for scripts) and a load-tested presence model for rooms.
  3. Moderation tooling: a private review view for stored reports; rate limits on a shared store (e.g. Upstash) for cross-instance accuracy.
  4. Listener mode: structured "just listening" vs "want to talk" intent during matching.
  5. Voice notes, hardened (size/duration caps + server-side crisis scan) β€” existed in v1, deliberately deferred.

License / credits

Built as an independent, open-source product. Illustrations are original; fonts are Google Fonts (Inter, Fraunces). Not affiliated with any crisis service it links to.

About

SoulSpace πŸ’š A free, anonymous peer support platform for random 1-on-1 chats.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages