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
| Path | Contents |
|---|---|
lib/ | Framework-ish infrastructure: config, DI container (AppContext), sessions, view rendering, SQL helper, request context |
models/beans | Plain data beans (Post, Comment, User, Forum, Vote, ...) |
models/dao | DismalDAO — raw data access |
models/services | Business 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.bxis a simple registry — services/DAOs/beans are registered once inapp.bxsand scanned frommodels/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 sinceapp.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.