Project

bxThreads

A public BoxLang port of DismalThreads — a Reddit-style forum app — built on boxlang-express instead of ColdBox, with no cbwire. Plain server-rendered HTML, fetch()-driven actions, and a STOMP-based realtime layer for comments, votes, and notifications.

Realtime over STOMP

One shared broker (ws/Stomp.bx) drives live comment, vote, and notification broadcasts plus per-forum presence — no polling.

Achievements

Tiered, data-driven achievement tracks evaluated inline after posting, commenting, and voting — thresholds live entirely in an AchievementTiers table, not code.

Link previews

Pasting a link into the post editor scrapes OG/Twitter tags, oEmbed, and JSON-LD for a title and preview card; a server-side image proxy resizes the preview image so the browser never downloads a full-resolution original.

Two auth models

Session-cookie auth for pages/actions, a constant-time-compared X-Api-Key header for the admin REST API — CSRF, secure cookies, and per-IP rate limiting on login/signup on top.

Setup

Requires BoxLang + CommandBox, and MySQL for the dismal datasource. The fastest path installs dependencies, prompts for every .env value, and creates the database plus an admin/admin login if one doesn't exist yet:

bashboxlang setup.bxs

Re-running it is safe — it reuses whatever's already in .env as the new defaults and never touches a database that already exists. To do it by hand instead:

bashbox install
cp .env.example .env       # fill in real values
mysql -u root -p < db/schema.sql
boxlang --bx-config ./boxlang.json app.bxs

Serves on http://localhost:$APP_PORT (default 3000).

Project layout

PathContents
lib/Framework-ish infrastructure: config, DI container (AppContext), sessions, view rendering, SQL helper, request context
models/beansPlain data beans (Post, Comment, User, Forum, Vote, ...)
models/daoDismalDAO — raw data access
models/servicesBusiness logic: users, activity, achievements, sitemap, spider/link-preview detection, image proxying
routes/AppRouter (pages), ActionsRouter (session-authenticated mutations), ApiRouter (X-Api-Key admin REST API)
ws/STOMP broker (Stomp.bx), broadcast (Broadcast.bx), and forum presence (Presence.bx)
views/.bxm templates, composed through lib/View.bx — boxlang-express has no built-in layout support

Architecture notes

  • DI: lib/AppContext.bx is a simple registry — services/DAOs/beans are registered once in app.bxs and scanned from models/beans, models/dao, models/services.
  • Sessions: cookie name dt_sid, 30-day maxAge, backed by boxlang-express's in-memory session middleware — shared with the WebSocket layer since app.ws() routes sit outside the HTTP middleware chain.
  • Realtime: one shared STOMP broker drives comment/vote/notification broadcasts and forum presence over /stomp.
  • Moderation: a moderator-only user profile view with grant/revoke for a super-admin role.
  • Error handling: full error messages only in APP_ENV=development; production returns a generic 500.

Security

A dedicated hardening pass covered:

  • CSRF protection on every session-authenticated page/action route (hidden field for form POSTs, header for fetch()-driven JS) — /api/* stays X-Api-Key only
  • Full session destruction on logout, secure cookies outside development
  • A pinned JVM DNS cache TTL closing a rebinding TOCTOU window between the link-preview/image-proxy SSRF safety check and the actual fetch's own DNS re-resolve
  • Constant-time comparison for the API key check
  • Per-IP rate limiting on login/signup
  • Post-login/signup redirects allowlisted to same-origin paths

Testing

bashbox run-script test

Runs the TestBox suite (tests/specs/unit, tests/specs/integration) via tests/runner.bxs.