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