A 4chan-style anonymous imageboard, restricted to the NSUT (Netaji Subhas University of Technology) campus network. No accounts, no names, no tracking — just post. Built with Node.js, Express, SQLite, and Sharp.
Classic imageboard, faithfully rebuilt — then some:
- Boards — 9 NSUT-themed boards (
/b/,/nsut/,/cs/,/g/,/exam/,/plac/,/soc/,/mess/,/meme/), easily edited insrc/boards.js, plus an overboard (/all/) showing the most active threads everywhere. - Anonymous posting — every post is "Anonymous" by default. Optional
tripcodes (
Name#secretinsecure,Name##secretsecure) for persistent identity, and per-thread poster IDs with deterministic colors. - Threads & replies — OP requires an image (4chan-style); replies may be text-only or include an image. Thumbnails generated automatically; spoilered images are blurred until expanded.
- Imageboard markup —
>greentext,>>123quote-links (with dead-link detection and hover previews),>>>/board/123cross-board links,[spoiler]…[/spoiler]text,[code]…[/code]blocks, and URL autolinking. All user text is HTML-escaped first (XSS-safe). - Backlinks & (You) — every post shows which posts quote it ("Replies:"), computed server-side; quotes pointing at your own posts get a (You) marker client-side.
- Live threads — thread pages auto-update in place: new replies are polled, rendered server-side, and appended without a reload, with a countdown, manual Update, unread-count in the tab title, and smart backoff (idle threads poll less; hidden tabs pause).
- Full-text search — SQLite FTS5 across subjects, names, and comments,
per-board or site-wide, with highlighted snippets (
/search). - Themes — Yotsuba (default), Yotsuba B, Tomorrow (dark), and Photon, switchable from the nav bar and remembered per browser with no flash of the wrong theme.
- Bumping — threads bump on reply;
sagereplies without bumping; configurable bump limit and image limit. - Real-time web archival — nothing is ever silently lost:
- Threads that fall off the last page are archived, not deleted: they
become read-only, browsable at
/:board/archive, still searchable, and viewable at their original URLs with an "archived" banner. - Every thread is continuously captured as a standalone single-file HTML snapshot (own styling, thumbnails inlined as base64, quote-links localized) — written to disk in real time as posts arrive (debounced), finalized the moment a thread is archived, and downloadable from any thread via the Download link. Snapshots need no server, database, or network to read.
- Optional retention (
ARCHIVE_RETENTION_DAYS) frees database rows and images for old archived threads while the HTML snapshots remain on disk as the permanent record. Default is keep-forever. - Mods can archive/un-archive any thread manually.
- Threads that fall off the last page are archived, not deleted: they
become read-only, browsable at
- Views — paginated board index, catalog (with client-side filter), thread view, inline image expansion.
- Moderation — password-gated
/moddashboard: delete posts/threads, delete images, sticky, lock, a report queue with one-click ban actions, and a full ban system (board-scoped or global, timed or permanent, optional wipe of all the poster's posts). - Post deletion — posters can delete their own posts/images with their password (auto-generated and remembered by the browser).
- Anti-abuse — per-IP rate limiting on threads and replies, a honeypot field, duplicate-comment flood detection, and duplicate-image rejection (same file in a thread, same OP image on a board).
- JSON API — read-only, loosely 4chan-API-shaped (
/api/...), plus the live-update endpoint that returns new posts as rendered HTML fragments. - Hardening — Content-Security-Policy and friends on every response,
gzip compression,
noindex, hashed IPs only (raw addresses are never stored).
Access is gated by a CIDR allowlist (src/middleware/network.js +
src/lib/cidr.js, with full IPv4/IPv6 support). Requests from outside the
allowed ranges get a friendly "Access Restricted" page instead of the board.
Configure it with two environment variables:
RESTRICT_NETWORK=true
ALLOWED_CIDRS=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,203.0.113.0/24Set ALLOWED_CIDRS to NSUT's real campus subnets (ask the network admins
for the ranges the campus Wi-Fi / lab machines / VPN use). The defaults allow
localhost and private LAN ranges so the app runs out of the box on a campus
LAN. If you deploy behind nginx or a campus proxy, set TRUST_PROXY to the
number of proxy hops so the real client IP is read from X-Forwarded-For.
/healthz is intentionally exempt so orchestrators can probe it from anywhere.
npm install
cp .env.example .env # edit secrets + campus CIDRs
npm run seed # optional: a few sample threads
npm start # http://localhost:3000For local development without the network gate:
RESTRICT_NETWORK=false npm run devAll settings live in src/config.js and are overridable via environment
variables — see .env.example for the full list. Highlights:
| Variable | Default | Meaning |
|---|---|---|
PORT / HOST |
3000 / 0.0.0.0 |
Listen address |
RESTRICT_NETWORK |
true |
Enforce the campus-network gate |
ALLOWED_CIDRS |
localhost + RFC1918 | Permitted client networks |
TRUST_PROXY |
1 |
Proxy hops to trust for client IP |
MOD_PASSWORD |
changeme |
Moderator panel password |
TRIP_SALT |
(dev default) | Secret for tripcodes/IDs/passwords |
MAX_FILE_SIZE |
4194304 (4 MB) |
Max upload size |
BUMP_LIMIT / IMAGE_LIMIT |
300 / 150 |
Per-thread limits |
THREAD_COOLDOWN / REPLY_COOLDOWN |
30 / 10 s |
Anti-flood |
Change
TRIP_SALTandMOD_PASSWORDbefore deploying. The salt protects secure tripcodes, poster IDs, and deletion passwords.
src/
server.js Express app wiring
config.js Central config (env-overridable)
boards.js Board definitions
db.js SQLite schema, FTS5 index, migrations, counters
middleware/
network.js Campus-network CIDR gate
security.js CSP + security headers
lib/
cidr.js IPv4/IPv6 CIDR matching (no deps)
format.js Escaping, greentext, quotes, spoiler/code, tripcodes
upload.js Multer + Sharp: validation & thumbnails
posts.js Threads, posts, backlinks, bans, search, dedup
util.js Hashing, timestamps, sizes, poster IDs/colors
routes/
boards.js Pages: home, overboard, search, index, catalog, thread
actions.js Posting, deleting, reporting (ban-gated)
mod.js Moderation panel + ban management
api.js JSON API + live-update endpoint
views/ EJS templates (themeable classic look)
public/ CSS (4 themes), client JS, favicon
test/ Unit + integration tests (node --test)
- Storage: SQLite via
better-sqlite3(WAL mode) atdata/nsutchan.db, with an FTS5 full-text index kept in sync by triggers. Uploaded images live inuploads/. Both are git-ignored — nothing user-generated is committed. - Images: validated and thumbnailed with
sharp; animated GIF/WebP kept intact. Only JPG/PNG/GIF/WebP accepted. - Privacy: IPs are salted-hashed before storage; the raw address never touches the database.
- Tests:
npm test— 31 tests covering CIDR matching, markup rendering, and full post/thread/ban/search lifecycles against a scratch database.
- Put it behind nginx (TLS) on a campus-reachable host.
- Set
ALLOWED_CIDRSto the NSUT subnets andTRUST_PROXY=1. - Set a strong
TRIP_SALTandMOD_PASSWORD. - Run under a process manager (systemd/pm2). Back up
data/anduploads/.
NSUTchan is an unofficial, student-run project and is not affiliated with or endorsed by NSUT. Operators are responsible for moderating content in line with university policy and applicable law.
MIT