No description
  • TypeScript 95.8%
  • CSS 2.1%
  • Dockerfile 1%
  • Shell 0.6%
  • PLpgSQL 0.4%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
snoopy ae22b78e89
Some checks failed
CI / Unit & integration tests (push) Has been cancelled
CI / Docker build (push) Has been cancelled
CI / E2E tests (push) Has been cancelled
build: switch runtime image from node:22-alpine to node:22-slim
Alpine's musl libc has cost real debugging time on this project twice
(the linux-musl-openssl-3.0.x binary target requirement, and a separate
arm64-Mac hardcoded engine path bug). Debian slim is Prisma's
best-tested target, at the cost of a larger image (264MB -> 337MB,
measured).

Swaps apk for apt-get in all three Dockerfile stages that install
openssl, updates schema.prisma's binaryTargets to debian-openssl-3.0.x,
and adds wget to the runner stage since Debian slim doesn't ship it by
default and docker-compose.prod.yml's healthcheck depends on it.

Verified: full test suite green with the new Prisma engine, and a full
docker compose build + boot + /health + migration check against real
Postgres.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzoQCndx1wdcQxY7uHLvMF
2026-08-04 19:49:08 +00:00
.gitea/workflows feat: Phase 1E — CI pipeline, error path tests, push adapter contract tests, Playwright E2E 2026-06-11 20:46:33 +00:00
deploy feat: Goal 1 phases 1A-1D — security hardening, deployment readiness, and user management 2026-06-11 20:17:34 +00:00
e2e feat: Phase 1E — CI pipeline, error path tests, push adapter contract tests, Playwright E2E 2026-06-11 20:46:33 +00:00
packages feat: Goal 1 phases 1A-1D — security hardening, deployment readiness, and user management 2026-06-11 20:17:34 +00:00
prisma build: switch runtime image from node:22-alpine to node:22-slim 2026-08-04 19:49:08 +00:00
scripts feat: Docker image, seed data, UX fixes, planned severity, admin publish UI 2026-06-07 10:06:08 +00:00
src fix: clear OIDC state/nonce from session, surface discovery failures clearly 2026-08-04 19:48:10 +00:00
test fix: clear OIDC state/nonce from session, surface discovery failures clearly 2026-08-04 19:48:10 +00:00
.dockerignore feat: Docker image, seed data, UX fixes, planned severity, admin publish UI 2026-06-07 10:06:08 +00:00
.env.example docs: add ntfy.sh as push delivery destination for Slice 3 2026-06-04 20:03:24 +00:00
.gitignore feat: deliver Slice 1 — channels, messages, and RSS feed 2026-06-04 18:29:11 +00:00
AI-NOTES.md build: switch runtime image from node:22-alpine to node:22-slim 2026-08-04 19:49:08 +00:00
ARCHITECTURE.md fix: clear OIDC state/nonce from session, surface discovery failures clearly 2026-08-04 19:48:10 +00:00
docker-compose.prod.yml feat: Goal 1 phases 1A-1D — security hardening, deployment readiness, and user management 2026-06-11 20:17:34 +00:00
docker-compose.yml feat: Docker image, seed data, UX fixes, planned severity, admin publish UI 2026-06-07 10:06:08 +00:00
Dockerfile build: switch runtime image from node:22-alpine to node:22-slim 2026-08-04 19:49:08 +00:00
entrypoint.sh fix: remove hardcoded Prisma schema engine path for arm64 Mac compatibility 2026-06-15 20:56:59 +00:00
package-lock.json feat: Phase 1E — CI pipeline, error path tests, push adapter contract tests, Playwright E2E 2026-06-11 20:46:33 +00:00
package.json feat: Phase 1E — CI pipeline, error path tests, push adapter contract tests, Playwright E2E 2026-06-11 20:46:33 +00:00
PLAN.md docs: reconcile PLAN.md checkboxes with what's actually shipped 2026-08-04 19:48:49 +00:00
playwright.config.ts feat: Phase 1E — CI pipeline, error path tests, push adapter contract tests, Playwright E2E 2026-06-11 20:46:33 +00:00
README.md docs: update all documentation and add user guide 2026-06-07 10:34:49 +00:00
TESTING.md docs: add weekend test plan (local auth + Entra SSO) 2026-06-12 05:46:53 +00:00
tsconfig.json feat: deliver Slice 1 — channels, messages, and RSS feed 2026-06-04 18:29:11 +00:00
USER-GUIDE.md docs: update all documentation and add user guide 2026-06-07 10:34:49 +00:00
vitest.config.ts feat: Phase 1E — CI pipeline, error path tests, push adapter contract tests, Playwright E2E 2026-06-11 20:46:33 +00:00

betterComms

An org-wide communications hub that gives engineers control over what they hear and when.

Admins publish structured messages to named channels. Engineers subscribe to the channels and tags they care about and pull a personalised RSS feed. High-severity messages (incidents, warnings) are also pushed via Teams webhook, email, ntfy.sh, and Viva Engage.


The problem

Engineering teams suffer from communication overload: too many Slack channels, too many email lists, no opt-in point, and no relevance filtering. Important incident notices get lost in the noise; routine announcements interrupt focus work.

betterComms separates signal from noise by making subscriptions explicit and feed-reader-native.


How it works

  1. An admin creates a channel (e.g. platform-incidents, releases-payments). Subscribers can request new channels; admins approve or reject requests.
  2. Admins or CI/CD pipelines publish messages to a channel via REST API or the web UI. Each message has a title, Markdown body, severity (info, planned, release_note, warning, incident), and optional tags.
  3. Engineers subscribe to the channels they care about, optionally filtering by tags.
  4. Each engineer gets a private RSS/Atom feed URL they add to their feed reader. The feed shows only messages from their subscribed channels and tags.
  5. For warning and incident severity messages, the service also pushes to configured channels: Teams webhook, SMTP email, ntfy.sh, and/or Viva Engage.

See USER-GUIDE.md for step-by-step instructions for subscribers and admins.


Stack

Layer Technology
Runtime Node.js 22, TypeScript
HTTP server Fastify 4.x with fastify-type-provider-zod
ORM Prisma 5.x
Database PostgreSQL 16
Frontend React 18 + Vite 5 (served by Fastify in production)
Testing Vitest (68 integration tests against real Postgres)
Container Docker (multi-stage Alpine image)
Local dev docker-compose
Auth Session cookie + optional OIDC SSO

Project layout

service-betterComms/
├── src/
│   ├── index.ts              # entry point
│   ├── app.ts                # Fastify app + plugin registration
│   ├── routes/               # route handlers by domain
│   │   ├── auth.ts           # login, logout, register, /auth/me, OIDC
│   │   ├── channels.ts       # channel CRUD + approval flow
│   │   ├── messages.ts       # publish, list, edit, delete messages
│   │   ├── subscriptions.ts  # subscribe / unsubscribe / list
│   │   ├── feeds.ts          # RSS/Atom feed generation
│   │   └── me.ts             # feed token, personalised feed JSON
│   ├── lib/                  # prisma client, OIDC client
│   └── workers/              # push delivery polling worker
├── packages/
│   └── web/                  # React + Vite SPA
│       └── src/pages/
│           ├── Login.tsx
│           ├── ChannelList.tsx
│           ├── ChannelDetail.tsx       # subscriber view
│           ├── AdminChannelDetail.tsx  # admin CRUD view
│           └── MyFeed.tsx
├── prisma/
│   ├── schema.prisma
│   └── migrations/
├── scripts/
│   └── seed.ts               # dev seed with Harness CI/CD sample data
├── test/                     # Vitest integration tests
├── Dockerfile                # multi-stage Alpine build
├── entrypoint.sh             # runs prisma migrate deploy then starts server
├── docker-compose.yml
├── PLAN.md                   # delivery slices and task checklist
├── ARCHITECTURE.md           # design decisions and rationale
└── USER-GUIDE.md             # end-user and admin documentation

# Build image and start Postgres + app
docker compose up -d --build

# App is available at http://localhost:3001
# Database migrations run automatically on container start

To seed the database with sample data (Harness CI/CD scenario):

npm run db:seed

Running in development

# Start Postgres (port 5434) and test-Postgres (port 5433)
docker compose up -d db test-db

# Install dependencies
npm install

# Apply migrations to dev database
npm run db:migrate

# Start API server with hot reload (port 3000)
npm run dev

# In a separate terminal: start Vite SPA dev server (port 5173, proxies API to :3000)
cd packages/web && npm run dev

Running tests

# Requires test-db to be running (docker compose up -d test-db)
npm test

Environment variables

Required

Variable Description
DATABASE_URL Postgres connection string — postgresql://user:pass@host:5432/db
SESSION_SECRET Secret for session signing — minimum 32 characters, random

Auth

Variable Default Description
ALLOW_LOCAL_REGISTRATION false Set true to enable POST /auth/register (dev/early setup)
COOKIE_SECURE false Set true when serving over HTTPS — required for Secure cookie flag
API_KEY Plain-text publisher API key for CI/CD pipelines (stored unhashed at runtime)
OIDC_ISSUER_URL OIDC provider discovery URL (e.g. https://accounts.google.com)
OIDC_CLIENT_ID OIDC client ID
OIDC_CLIENT_SECRET OIDC client secret
OIDC_REDIRECT_URI Callback URL registered with the OIDC provider (e.g. https://bettercomms.example.com/auth/oidc/callback)

Push delivery

Push delivery is disabled by default. Set PUSH_DELIVERY_ENABLED=true to activate. Each channel is independently optional.

Variable Description
PUSH_DELIVERY_ENABLED Set true to start the push delivery worker
PUSH_POLL_INTERVAL_MS Polling interval in ms (default 30000)
TEAMS_WEBHOOK_URL Microsoft Teams incoming webhook URL
SMTP_HOST SMTP server hostname
SMTP_PORT SMTP port (default 587)
SMTP_USER SMTP username (omit for unauthenticated relay)
SMTP_PASS SMTP password
SMTP_FROM From address for outgoing email
SMTP_TO Destination address(es) for push email
NTFY_TOPIC_URL ntfy.sh topic URL (e.g. https://ntfy.sh/your-org-alerts)
VIVA_ENGAGE_TOKEN Viva Engage (Yammer) bearer token
VIVA_ENGAGE_GROUP_ID Viva Engage group ID to post into

Key API endpoints

Auth

Method Path Auth Description
POST /auth/register Register (requires ALLOW_LOCAL_REGISTRATION=true)
POST /auth/login Login with email + password
POST /auth/logout Session Log out
GET /auth/me Session Return current user
GET /auth/oidc/login Redirect to OIDC provider
GET /auth/oidc/callback OIDC callback — creates/updates user and sets session

Channels

Method Path Auth Description
GET /channels Session List channels (admins see all; subscribers see active only)
GET /channels/:slug Session Get a single channel
POST /channels Session Create channel (admin → active; subscriber → pending approval)
GET /channels/pending Admin List channels awaiting approval
PATCH /channels/:slug/status Admin Approve or reject a pending channel

Messages

Method Path Auth Description
GET /channels/:slug/messages Session List messages for a channel
POST /channels/:slug/messages Publisher Publish a new message
PATCH /channels/:slug/messages/:id Admin Edit a message
DELETE /channels/:slug/messages/:id Admin Delete a message

Subscriptions

Method Path Auth Description
GET /subscriptions Session List my subscriptions
POST /subscriptions Session Subscribe to a channel (with optional tag filters)
DELETE /subscriptions/:channelSlug Session Unsubscribe

My feed

Method Path Auth Description
GET /me/feed Session Personalised feed as JSON
GET /me/feed-token Session Get my RSS feed token
POST /me/feed-token/regenerate Session Rotate feed token (old URL stops working)

Feeds (public / token-authenticated)

Method Path Auth Description
GET /feeds/channel/:slug/rss.xml None Public RSS feed for a channel
GET /feeds/user/:feedToken/rss.xml Token in URL Personalised RSS feed

Feed token URLs must be kept private — anyone with the URL can read the feed. Rotate via POST /me/feed-token/regenerate if compromised.


Publishing via API key (CI/CD)

curl -X POST https://bettercomms.example.com/channels/my-channel/messages \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: your-api-key" \
  -d '{
    "title": "Deployment v2.14.0 complete",
    "body": "## What changed\n\n- Feature X shipped\n- Bug Y fixed",
    "severity": "release_note",
    "tags": ["payments", "v2.14"]
  }'

Valid severity values: info, planned, release_note, warning, incident


Delivery slices

Slice Status Summary
1 Complete Core scaffold: channels, messages, RSS feed
2 Complete Web UI, subscriptions, personalised feed
3 Complete Push delivery: Teams, email, ntfy.sh, Viva Engage
4 Complete Channel approval flow, OIDC SSO, Backstage plugin
5 In progress Single deployable image (Docker build done; scaling TBD)
6 Planned Security testing & QE hardening
7 Planned Settings page (themes, fonts, delivery preferences)

See PLAN.md for the full task checklist and ARCHITECTURE.md for design decisions.