MacLustr Research Infrastructure — maclustr.spboucher.ai
Bilingual (English/French) research site documenting MacLustr, a personally owned Apple Silicon cluster. It is a technical showcase and research log, not a commercial service.
Stack
Next.js 16 (App Router, static generation), React 19, TypeScript strict, Tailwind CSS 4, Zod 4, sharp, Vitest, Playwright + axe.
Content model (all version-controlled, validated by Zod at build time)
| File | Content |
|---|---|
data/nodes.json |
One record per machine (specs, role, lifecycle, placement, verifiedAt, source, publicVisibility) |
data/photos.source.json → data/photos.json |
Captions and alt text → generated dimensions and blur placeholders |
data/research.json |
Research log entries (completed / ongoing / proposed) with references |
data/benchmarks.json |
Benchmark observations; empty until measured |
data/timeline.json, data/planned.json, data/site.json |
History, planned hardware, contact and dates |
src/lib/dict/{en,fr}.ts |
All interface and editorial text |
Totals shown on the site are computed by src/lib/inventory.ts; planned, retired and hidden records never count.
A total is withheld when one counted record lacks the value.
Commands
pnpm install
pnpm images # assets/*.jpeg -> public/images (metadata stripped) + data/photos.json
pnpm dev
pnpm check # validate data, lint, type-check, unit tests, build, privacy scan
pnpm test:e2e # browser tests (starts `next start` on port 3210)Put the original photographs in assets/ (git-ignored: they carry location metadata). Only pnpm images output is published.
Updating the inventory
Edit data/nodes.json (or re-run scripts/import-discovery.py on fresh discovery output), set verifiedAt and source,
update data/site.json dates, then pnpm check. Unknown values stay null and render as "Not yet documented".
Environment variables
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SITE_URL |
Extra allowed origin for the form endpoint (the canonical URL is in data/site.json) |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS |
Optional. Enables server-side delivery of access requests |
ACCESS_MAIL_TO, ACCESS_MAIL_FROM |
Recipient and sender for those requests |
Without SMTP_HOST and ACCESS_MAIL_TO at build time, the access form prepares a message in the visitor's own mail
application and says so; it never displays a "sent" state. No secret belongs in the repository.
Deployment (owner's infrastructure)
The site runs as one Node process (next start) behind the public gateway's reverse proxy, which terminates HTTPS.
- DNS:
A maclustr.spboucher.ai→ public gateway. - Manifest on the gateway node:
~/dispatch/apps/maclustr-research.json(port,health_path: /api/health, tunnel domain, build hookpnpm install --frozen-lockfile && next build). - First deployment from the laptop:
mld stage <this folder> maclustr-research && mld deploy maclustr-research. - Afterwards the deployed folder is the reference clone: edit on the node,
pnpm build && pm2 restart maclustr-research, commit and push from the node.
- Health check:
GET /api/health→{"status":"ok","records":N}. - Logs:
pm2 logs maclustr-researchon the node. - Backups: the site is stateless; the git repository is the backup.
- Rollback:
git checkout <previous commit> && pnpm build && pm2 restart maclustr-research; the orchestrator also rolls back a failed deploy.
Security and privacy
- Security headers and CSP in
src/lib/security.ts; the browser never contacts a private node. - Form endpoint: same-origin JSON only, Zod validation, honeypot, rate limit, nothing persisted, contents never logged.
pnpm privacyfails if a private address, internal domain, credential pattern or image metadata is found in what ships.
Documents
docs/INVENTORY-AUDIT.md (facts, unknowns, items for owner verification), docs/DESIGN.md, docs/screenshots/, CHANGELOG.md.