The 4-constraint set (applied across the full portfolio — verified consistent across all 11 portfolio repos):
- Zero credit card — no paid API / cloud service required for the default path. A reviewer can clone, install, and run with $0 spend and no payment method on file.
- Local LLM (default) — when an LLM is involved, the default path is local (Ollama / similar) or deterministic mock. Paid cloud LLM is opt-in via env var, never default.
- Free / OSS only — every runtime dependency is permissively-licensed open source (MIT / Apache-2.0 / BSD-3); no proprietary SDK at build time.
- Security defense-in-depth — secrets-scan CI +
.gitignorehardening, encrypted-at-rest where PII is involved, append-only audit logging where applicable, dep-vuln gating (pip-audit/pnpm audit), paid-API constructor gate where applicable.This repo specifically demonstrates: full-stack monorepo (Boardly realtime collab + Knowlex multi-tenant RAG) running on free-tier infrastructure with ADR-0046 zero-cost-by-construction
EMERGENCY_STOPkill-switch (proven under the 2026-04-29 Google AI Studio free-tier revocation incident, see ADR-0067). BYOK runbook (5-step, ~5 min) is the user-facing path for tier 3 (production swap). Security headers A rating (securityheaders.com) + branch protection ruleset live-enforced + drift-detect CI (ADR-0057) collectively cover constraints 4 (security defense-in-depth).
Full-stack portfolio monorepo — Boardly (realtime collaborative kanban with drag-and-drop, multi-tenant workspaces) + Knowlex (multi-tenant RAG on pgvector HNSW + Postgres FTS hybrid via RRF + Gemini; full ADR sequence in docs/adr/ — 68 records covering everything from monorepo choice to incident response).
Two production-grade SaaS applications designed and built from schema to deploy, end to end, to demonstrate full-stack × from-scratch engineering capability.
Built with AI pair-programming (Claude Code). 68 of the 225 commits carry
Co-Authored-By: Claude Opus 4.7 (1M context)co-authorship marking — explicit evidence of AI-leveraged development at industry-baseline-exceeding engineering quality (68 ADRs, 276 Vitest + 24 Playwright, OpenSSF Scorecard, branch protection ruleset enforced live, real production incident handled per ADR-0067). The portfolio is itself the live demo of "ship Mid → Senior tier full-stack with AI pair-programming, while preserving honest engineering discipline".
5 closed graduation cycles in 5 ships — T-01 (ADR-0060, v0.5.11) → I-01 (ADR-0061, v0.5.12) → ADR-0049 § 8th arc (ADR-0062, v0.5.13) → ADR-0011 deferred (ADR-0063, v0.5.14) → ADR-0064 architectural-gap (ADR-0065, v0.5.15). Each disclose carries a TTL + named accelerator triggers + closure ADR; the ratchet log itself is the brand. Pattern documented in
KL-build_ci-202604-graduation-cycle.
Production incident response record (2026-04-29). Google AI Studio silently revoked Free tier access at the account level for the Knowlex deploy — diagnosis + containment via ADR-0046
EMERGENCY_STOPkill-switch (shipped 6 months prior, defense-in-depth working under stress) + scope pivot to BYOK landing recorded end-to-end in ADR-0067. Senior-level signal: real production incident handled with structural discipline, not panic.
Framework is structurally enforced, not declared. Branch protection ruleset on
main(id15652440, ADR-0058) rejected the author's owngit push origin mainattempts during ratchets S266 + S267 — see PR #53 and #54 commit history. The framework foundation axiom (a framework that asserts X must be structurally enforced) operating live, not just on paper.
Two narrated demos, one per app. Japanese narration via VOICEVOX (speaker: ずんだもん, free tier); pipeline reproducible end-to-end — see scripts/demo/ + scripts/demo-knowlex/.
▶️ Boardly — 45 s walkthrough — DnD, labels, assignees, @mentions + notifications bell, invitations with the acceptUrl flow, activity feed, and the architectural decisions summary.▶️ Knowlex — 33 s RAG walkthrough —/kbingest (chunk → 768-dim embed),/ask (pgvector HNSW cosine kNN → streamed Gemini 2.5 Flash with numbered citations),/api/kb/statslive index-type probe,/docs/apihand-written OpenAPI 3.1.
flowchart LR
subgraph client["Browser"]
B_UI["Boardly UI<br/>(Next.js 16 client)"]
K_UI["Knowlex UI<br/>(Next.js 16 client)"]
end
subgraph vercel["Vercel Hobby ($0)"]
B_APP["craftstack-collab<br/>Node + Edge runtime"]
K_APP["craftstack-knowledge<br/>Node runtime"]
end
subgraph neon["Neon Singapore (Free)"]
B_DB[("boardly-db<br/>Postgres 16")]
K_DB[("knowlex-db<br/>Postgres 16 + pgvector<br/>HNSW cosine index")]
end
subgraph ext["External (all free tier, $0)"]
UPSTASH[("Upstash Redis<br/>Tokyo")]
PUSHER["Pusher Channels"]
RESEND["Resend email"]
GEMINI["Google AI Studio<br/>gemini-embedding-001<br/>gemini-2.5-flash"]
end
subgraph ghci["GitHub Actions (free)"]
CI["ci.yml<br/>lint + typecheck + test<br/>+ knowledge-integration<br/>(pgvector service)"]
SMOKE["smoke.yml<br/>live smoke every 6 h"]
CODEQL["codeql.yml<br/>security scan"]
SBOM["sbom.yml<br/>CycloneDX on tag"]
end
B_UI -->|"auth · boards · cards"| B_APP
K_UI -->|"ingest · ask"| K_APP
B_APP --> B_DB
B_APP -.->|"rate-limited<br/>$0 cap"| GEMINI
K_APP --> K_DB
K_APP -->|"768-dim embeddings<br/>+ streamed answer"| GEMINI
B_APP -.-> UPSTASH
B_APP -.-> PUSHER
B_APP -.-> RESEND
SMOKE -.->|"E2E_BASE_URL"| K_APP
CI -.->|"pgvector service container"| K_DB
All production services are free-tier, no credit-card-on-file. Step-by-step signup guide in docs/FREE_TIER_ONBOARDING.md; cost-attack threat model in COST_SAFETY.md.
Boardly: https://craftstack-collab.vercel.app
Knowlex (grounded RAG, own Vercel deploy): https://craftstack-knowledge.vercel.app — 🛑 temporarily disabled via EMERGENCY_STOP=1 (ADR-0046 kill-switch); see ADR-0067 for the 2026-04-29 Gemini Free tier account-level revocation incident report. → Run locally with your own API key (5-step BYOK runbook, ~5 min). The implementation is complete (chunking, 768-dim gemini-embedding-001, pgvector HNSW cosine kNN per ADR-0041, hybrid Postgres FTS + RRF fusion per ADR-0063, CI Credentials provider per ADR-0065, streamed Gemini answers with numbered citations). The attestation endpoint at /api/attestation remains live and surfaces the current ADR count, schema state, and last-eval-run baseline.
A Gemini-compatible API key (or a 768-dim free-tier alternative such as Cloudflare Workers AI's bge-base-en-v1.5) plus Docker Desktop and Node 20+ is the full setup. 5-step BYOK runbook (~5 min total: ~30 s Postgres start + ~30 s migrator role + ~2 min pnpm install cold + ~30 s prisma migrate deploy + ~30 s edit .env + start dev server).:
# 1. Postgres + pgvector (Docker Desktop must be running):
docker run -d --name knowlex-pg \
-e POSTGRES_DB=knowlex -e POSTGRES_USER=app -e POSTGRES_PASSWORD=app \
-p 5432:5432 pgvector/pgvector:pg16
docker exec -e PGPASSWORD=app knowlex-pg psql -U app -d knowlex \
-c "CREATE USER migrator WITH SUPERUSER PASSWORD 'migrator';"
# 2. Install + apply schema:
pnpm install
pnpm --filter knowledge exec prisma migrate deploy
# 3. Paste your key into apps/knowledge/.env:
# GEMINI_API_KEY="AIza..." ← from https://aistudio.google.com/app/apikey
# AUTH_SECRET="<openssl rand -base64 32 output>"
# 4. Start the dev server:
pnpm --filter knowledge dev # → http://localhost:3001
# 5. (optional) Re-run the calibration eval:
EVAL_JUDGE=1 E2E_SHARED_SECRET=<openssl rand -base64 32 output> \
pnpm --filter knowledge eval
# → docs/eval/reports/<date>.json with passRate / p50 / p95 / judgeMeanCleanup: docker stop knowlex-pg && docker rm knowlex-pg.
The CI Credentials provider that gates the calibration eval's session-attached ingest path is documented in ADR-0065; the calibration architectural-gap that closure path resolves is in ADR-0064.
Knowlex Playground (bring-your-own-context): https://craftstack-collab.vercel.app/playground — same Gemini pipeline but you paste the context inline instead of ingesting.
Sign in to reach the authenticated dashboard. Workspace + board creation flows are wired end-to-end against Neon Postgres (Singapore) and Upstash Redis (Tokyo).
Reviewers: Continue with GitHub is the recommended button — it works for any GitHub account out of the box. The Google OAuth app is still in Google's "Testing" status, so Google sign-in will only succeed for email addresses already registered as test users inside the Google Cloud consent screen. Publishing the Google app requires verification review and is deferred until the app is feature-complete.
Invitations (token-hashed, email-bound, three-layer rate-limited per ADR-0026 / ADR-0027) and the Knowlex RAG experience (pgvector HNSW + streamed Gemini 2.5 Flash with numbered citations per ADR-0039 / ADR-0041) are both shipped and live. Attachments (Cloudflare R2 per ADR-0008) are schema-ready at the Prisma layer; UI wiring is a follow-up.
| App | Description | Tech highlights | Status |
|---|---|---|---|
| Boardly | Collaborative kanban with drag-and-drop and realtime fanout | Next.js 16 · Auth.js v5 · Prisma 7 · PostgreSQL · LexoRank · Optimistic lock · @dnd-kit · Pusher Channels |
v0.1.0 — live deploy |
| Knowlex | Multi-tenant RAG — pgvector HNSW kNN + Postgres FTS hybrid via RRF + streamed Gemini 2.5 Flash with numbered citations + LLM-as-judge --judge flag. Workspace schema partitioning shipped per ADR-0047 (v0.5.0); access control via Auth.js + Membership demo allow-list per ADR-0061 (v0.5.12); hybrid retrieval per ADR-0063 (v0.5.14); CI Credentials provider for calibration per ADR-0065 (v0.5.15). Live demo currently EMERGENCY_STOPPED per ADR-0067; BYOK runbook in this README. |
Next.js 16 · pgvector (HNSW) + tsvector GIN · Gemini Embeddings + 2.5 Flash + 2.5 Pro · Auth.js v5 · Prisma 7 | Live (EMERGENCY_STOP) |
craftstack/
├── apps/
│ ├── collab/ # Boardly
│ └── knowledge/ # Knowlex
├── packages/
│ ├── ui/ # shadcn/ui based shared components
│ ├── auth/ # Auth.js v5 wrapper
│ ├── db/ # Prisma client + withTenant() helper
│ ├── logger/ # pino + Sentry
│ ├── config/ # ESLint / TSConfig / Prettier presets
│ └── api-client/ # OpenAPI-generated types
├── infra/
│ └── docker/ # docker-compose + init scripts
├── docs/
│ ├── design/ # 13-part design bible (see docs/design/README.md)
│ ├── adr/ # Architecture Decision Records (68 entries)
│ ├── api/ # OpenAPI specs
│ ├── architecture/ # System diagrams
│ ├── compliance/ # Data retention policy
│ ├── eval/ # RAG evaluation (golden QA + reports)
│ ├── hiring/ # Interview Q&A + portfolio LP + demo storyboards
│ ├── ops/ # Runbook
│ └── security/ # STRIDE threat model
└── .github/workflows/ # CI / deploy / eval
- Frontend: Next.js 16 (App Router, Turbopack) · TypeScript 5 · TailwindCSS 4
- Backend: Next.js Route Handlers on Node runtime · Edge Runtime proxy
- Database: PostgreSQL 16 on Neon (Singapore) · Prisma 7 with
@prisma/adapter-pg - Auth: Auth.js v5 with JWT session strategy · Google + GitHub OAuth · PrismaAdapter
- Deploy: Vercel Hobby · GitHub Actions CI (lint / typecheck / test / build)
- Security headers — scored A on securityheaders.com. Layers: Content-Security-Policy with explicit Vercel-platform allowlists +
'unsafe-inline'+'unsafe-eval'(W3C-spec rollback from the earlier A+ nonce +'strict-dynamic'stance — platform-injected scripts couldn't carry our per-request nonce and hydration broke.'unsafe-eval'retained because Vercel Speed Insights useseval()at runtime; see ADR-0040 § Decision + § Consequences. Both directives' presence is now structurally pinned byscripts/check-csp-coherence.mjsper ADR-0068 § Finding C closure.), HSTS 2y preload, X-Frame-Options DENY, Cross-Origin-Opener-Policy same-origin, Cross-Origin-Resource-Policy same-origin, Permissions-Policy denying every unused sensor / media / power API, and Referrer-Policy strict-origin-when-cross-origin - Testing: Vitest (276 unit cases across both apps — 174 collab + 102 knowledge) · Playwright (24 scenarios — smoke, authed E2E (board/dashboard/rate-limits/workspace), a11y + authed-a11y, signin, run with
pnpm --filter collab test:e2e/pnpm --filter knowledge test:e2e) · Knowlex retrieve integration test against a realpgvectorservice container viadocker compose(pnpm --filter knowledge test:integration) · k6 scenario - Drag & drop:
@dnd-kitsortable cards with LexoRank positions + optimistic UI +VERSION_MISMATCHrollback - Realtime: Pusher Channels (free tier) —
board-<id>fanout for card/list mutations; no-op locally when unconfigured - Invitations: Token-hashed invitation flow (ADMIN+ creates, accept page binds membership). Resend-backed email delivery with graceful fallback to console log when
RESEND_API_KEYis unset - Abuse defence: Three-layer rate limits on invitation creation (global 1000/mo, per-workspace 50/day, per-user 20/day) — all env-override-able, 429 with specific error code on trip
- Card comments: thread per card with author + ADMIN-moderator deletion, soft-delete, 4000-char cap, Pusher fanout on create/delete
- Activity log: audit feed per workspace (card/list/comment create/update/move/delete) with cursor pagination, human-readable summaries, best-effort logging (log insert failure never aborts the business mutation)
- Labels: workspace-scoped color-coded labels (ADMIN-curated palette), full-replace attach API with cross-workspace guard, dots on board cards + inline picker on the card modal
- @mentions + Notifications bell: comment body is scanned for
@handletokens (email-local-part or display-name match against workspace members), Mention rows + per-user Notification rows are written, header bell polls/api/notificationsevery 30s and shows an unread badge with a deep-link dropdown - Card assignees: full-replace PUT with membership guard (cross-workspace assigns rejected), avatar stack on board cards with +N overflow, modal picker listing workspace members, newly-added assignees get an ASSIGNED notification (self-assigns silent)
- Board label filter: URL-driven (
?labels=id1,id2) chip bar above the board — shareable, survives refresh, union semantics (card shown if it has any active label) - WIP limits per list (ADMIN+): inline
⚙editor on list headers, amber header at-limit, red border + ring when over, back-end validates positive integer or null - Command palette (⌘K / Ctrl-K): global overlay with dark glassmorphism, fuzzy cross-workspace search of workspaces / boards / cards (all membership-scoped server-side via
/api/search), plus a>-prefix action mode for "New workspace" / "New board" / "Sign out". Mounted on every authenticated header; empty query also renders recent workspaces + boards so it doubles as a jump-to navigator - Knowlex playground at
/playground(public, no signup, lives on the collab deploy): paste any passage + ask a question → streamed Gemini 2.5 Flash answer grounded only in the pasted context. Env-guarded with a deterministic demo-mode fallback so the page works end-to-end withoutGEMINI_API_KEY; per-IP + global budget caps - Knowlex MVP (live, end-to-end RAG) — the full RAG app in
apps/knowledge: paste text at/kb→ chunked (paragraph-aware, ~512 chars with 80-char overlap) → embedded withgemini-embedding-001at 768 dimensions (viaoutputDimensionality) → stored in pgvector behind an HNSW cosine index → cosine-kNN retrieval at query time → streamed Gemini 2.5 Flash answer with numbered citations. Running on its own Vercel project against a dedicated Neonknowlex-db(Singapore) per ADR-0018. MVP scope in ADR-0039; the ivfflat → HNSW swap forced by a silent-zero-rows pathology at corpus=2 is documented in ADR-0041 - Keyboard shortcuts (
?opens a modal reference):⌘/Ctrl+Kor/opens the command palette from anywhere,>switches it into action mode,Esccloses any modal. The help modal enumerates everything and is mounted on every authenticated header - Accessibility — axe-core runs against every public and authenticated page in Playwright, now as a PR-blocking gate (previously only the 6-hourly
smoke.ymlcron caught regressions post-merge).ci.ymlspins up pgvector + the Knowlex dev server to gate/,/kb,/docs/api;e2e.ymlpiggybacks on the already-running Boardly server to gate/,/signin,/playground. Public pages gate on zeroserious+criticalWCAG 2.1 AA violations; authenticated pages (/dashboard,/w/...) gate on zerocriticalonly —seriouswarnings are logged but non-blocking pending a color-contrast polish sweep on dense secondary metadata. See ADR-0034 and ADR-0046 (PR-gate rationale) - Authenticated E2E — a dedicated CI workflow (
e2e.yml) boots a Postgres service container, seeds the DB, signs in via a CI-only Credentials provider (triple-gated:NODE_ENV !== "production",E2E_ENABLED=1,E2E_SHARED_SECRETconstant-time compare against a 3-email allowlist) and runs a Playwright suite covering dashboard, workspace, board, rate-limits, and authed a11y. See ADR-0038 - Bundle analyzer via
@next/bundle-analyzer—pnpm --filter collab analyzespits out a client / server / edge bundle report atapps/collab/.next/analyze/for at-a-glance chunk-size regressions - OpenAPI 3.1 contract at
apps/collab/src/openapi.ts. Browsable in-app at https://craftstack-collab.vercel.app/docs/api (server-rendered, inside the strict CSP, zero external CDN), served as raw JSON at https://craftstack-collab.vercel.app/api/openapi.json. Hand-written so the spec is the contract (ADR-0035).pnpm --filter collab generate:api-typesemits a fully-typedpathsinterface intosrc/openapi-types.tsviaopenapi-typescript - Release hygiene — human-readable CHANGELOG.md per Keep-a-Changelog, signed tags, GitHub Releases, and a CycloneDX 1.5 SBOM auto-generated and attached to every
v*release (see.github/workflows/sbom.yml) for supply-chain inspection - Undo / redo on card moves —
Ctrl-Z/⌘-Zreverses the last drag,Ctrl-Shift-Z/⌘-Shift-Zre-applies it. Bounded 25-entry LIFO stack, replays against the existing optimistic-lock-protected/api/cards/:id/moveendpoint so concurrent-edit rejection behaves exactly like a fresh drag. Pure state-machine module (6 Vitest cases) in ADR-0036 - Cost safety by construction — every service the project touches (Vercel, Neon, Gemini via AI Studio, Pusher, Resend, GitHub Actions, Upstash, Sentry) is on a free tier that caps out to zero cost rather than auto-scaling to the attacker's credit card. In-code defense-in-depth: per-IP + global daily/monthly budget on
/api/kb/askand/api/kb/ingest(Knowlex parity, see ADR-0043), per-user rate limits on authenticated reads, three-layer cap on invitation emails. The guarantee is enforced, not declared:scripts/check-free-tier-compliance.mjsruns as a PR-blockingfree-tier-compliancegate inci.ymlthat fails merges introducing paid-planvercel.json, billable SDKs, or leaked secret patterns. A single-flag kill switchEMERGENCY_STOP=1short-circuits every write + AI endpoint on the next request (runbook §9); its counterpart observability endpoint/api/kb/budgetmirrors/api/kb/statsto expose the current used/cap state (disabled-by-default in production per ADR-0046 § Trade-offs; returns a structured 404{"code":"DISABLED"}untilENABLE_OBSERVABILITY_API=1is set on the server). STRIDE threat model covers this attack shape explicitly asC-01..C-06. Decision record: ADR-0046. Credit-card-free signup walk-through indocs/FREE_TIER_ONBOARDING.md; cost-attack threat model inCOST_SAFETY.md - Error-capture pipeline with demo mode — both apps boot
@sentry/nextjsvia Next'sinstrumentation.ts+instrumentation-client.tshooks; server and browser errors, unhandled rejections, and everyerror.tsxboundary flow through a unifiedlib/observability.tsseam. WhenSENTRY_DSNis configured the captures ship upstream; when it's not, they land in an in-memory ring buffer surfaced at/api/observability/captures, so a reviewer can prove the pipeline works end-to-end without signing up for Sentry. Rationale in ADR-0044 (wiring) and ADR-0045 (demo-mode dual-backend) - Knowlex RAG regression stack —
retrieve.integration.test.tsexercises the real pgvector kNN path against apgvector/pgvector:pg16service container in CI, asserting "returns every row whenk ≥ corpus size" — the exact regression a misconfigured ivfflat(lists, probes) silently produces (ADR-0041 documents the production diagnosis).scripts/bench-retrieve.tsreports min / p50 / p95 / p99 / max latency over N=1000 / M=100 probes.scripts/eval.tsseeds a self-contained 10-doc / 30-question golden set v4 (21 OR-mode + 6 AND-mode + 3 adversarial) and scores substring-faithfulness + citation-coverage + refusal correctness against the live deploy — full measurement methodology in ADR-0042 / ADR-0043 / ADR-0049 § 7th arc (substring-OR scoring + 12 expanded refusal markers) anddocs/eval/README.md - Live deploy smoke, scheduled —
.github/workflows/smoke.ymlruns Playwright against both production URLs every 6 h (plus onworkflow_dispatchand on main pushes after a 90-second Vercel-settle sleep). Knowlex smoke assertsindexType === "hnsw"so an accidental ivfflat rollback trips the workflow, not production users - Demo video pipeline (
pnpm demo:tts && pnpm demo:compose): capture a silent screen recording once, and an ffmpeg+TTS toolchain overlays a fully synthesized Japanese narration. Pluggable providers — VOICEVOX (local, $0) or Azure Neural TTS (500k chars/mo free). Script lives as JSON; editing the story is two commands away from a new mp4. See scripts/demo/README.md
Planned (see Roadmap)
- Storage: Vercel Blob (free tier)
- Observability: Sentry webpack-plugin source-map upload (SDK + capture already shipped) · Better Stack · UptimeRobot · pino · Web Vitals
- AI (Knowlex): pgvector HNSW + streamed Gemini 2.5 Flash with citations already shipped in v0.4.0; planned extensions — Cohere Rerank · HyDE · BM25 hybrid · LLM-as-judge faithfulness scoring in
scripts/eval.ts - Load: k6 (200 VU)
All production services are targeted to run within free-tier quotas ($0/month).
This codebase is AI-assisted. Claude (Anthropic's Claude Code) was used as a pair-programmer for scaffolding, boilerplate, and tests; every architectural decision below was author-specified and author-reviewed before being committed. The author can whiteboard any of these patterns from scratch in an interview.
Non-obvious decisions made in this repo, with rationales:
- Four-tier RBAC (OWNER > ADMIN > EDITOR > VIEWER) with a single
roleAtLeastcomparator driving every server check. Chosen over boolean flags so the model scales to per-feature gates (labels ADMIN+, comments EDITOR+, activity VIEWER+) without schema churn. - Optimistic locking via
versioncolumn on Card.updateManyfilters byid + version, 0 rows affected → 409VERSION_MISMATCH. The client bumps its local version on success so rapid drags don't stale-conflict with themselves. Chosen over pessimistic locking because multiple editors on the same board is the norm. - LexoRank positions for List + Card ordering. Reordering touches one row (
between(prev, next)), not N. Using thelexoranknpm package for Jira-compatible semantics. - Token-hashed invitations. Plaintext token exists only in the email / UI; only
SHA-256(token)is persisted. Accept requires the signed-in email to match the invitation's email — defeats token phishing and accidental link sharing. - Three-layer invitation rate limit (global 1000/mo, per-workspace 50/day, per-user 20/day), counts include revoked+accepted rows so an attacker can't reset quota by revoking. Trip returns a specific error code so the UI explains which quota fired.
- Full-replace set semantics for labels and assignees (
PUT /api/cards/:id/labelswith the desiredlabelIds[]). Simpler to reason about than two endpoints; the server diffs against current state and emits the right notifications for adds only (no spam on removes). - Cross-workspace guards on both
setCardLabelsandsetCardAssignees. A card in workspace A cannot be tagged with a label from workspace B, cannot be assigned to a user who isn't a member. Defense in depth against tenant leaks from a malicious or buggy client. - Best-effort side effects. Activity log inserts, Pusher broadcasts, Resend emails, and notification rows are all wrapped so a failure cannot abort the originating business write. Every one catches + console.warns and returns — the user's card save is the transactional piece; fanout is cosmetic.
- URL as source of truth for board filters (
?labels=…,?q=…). Shareable, refresh-survives, composable. Chosen over a local React store so a user can paste a filtered-board URL into Slack. - @mention resolution: email local-part OR display-name slug. The regex is tuned to not match email addresses in running text (
contact me at [email protected]doesn't fire). - Env-guarded integrations (Pusher, Resend). Missing credentials = silent no-op with a fallback (console log of accept URL, cross-tab refresh skipped). Means the app runs end-to-end locally without any external signup.
Each of the ten items above is also captured as a one-page Architectural Decision Record with alternatives and trade-offs: see docs/adr/ — ADR-0023 through ADR-0032 are the implementation-phase records mapping 1-to-1 to this list.
See also the per-module doc comments in apps/collab/src/server/*.ts — each exported function has a short rationale for the specific design choice.
- Node.js 20 LTS (
.nvmrcpinned) - pnpm 9+ (
packageManagerfield pinned) - Docker Desktop
git clone https://github.com/leagames0221-sys/craftstack.git
cd craftstack
cp .env.example .env
docker compose up -d # Postgres + Redis
pnpm install
pnpm dev:collab # Boardly on http://localhost:3000
pnpm dev:knowledge # Knowlex on http://localhost:3001| Area | Entry point |
|---|---|
| Architecture overview | docs/architecture/system-overview.md |
| Audit attestation | live single-curl: https://craftstack-knowledge.vercel.app/api/attestation (ADR-0056) |
| Decision records (68) | docs/adr/ |
| API specs (OpenAPI) | collab · knowledge |
| Rate limits | docs/api/rate-limits.md |
| STRIDE threat model | docs/security/threat-model.md |
| Incident runbook | docs/ops/runbook.md |
| Data retention policy | docs/compliance/data-retention.md |
| RAG prompt registry | apps/knowledge/src/server/ai/prompts/ |
| RAG evaluation | docs/eval/ |
| Interview Q&A (30) | docs/hiring/interview-qa.md |
| Portfolio landing copy | docs/hiring/portfolio-lp.md |
| Demo storyboard | docs/hiring/demo-storyboard.md |
| Design bible (13 parts) | docs/design/README.md |
| Contribution guide | CONTRIBUTING.md |
- ✅ Week 1–2 — Monorepo scaffolding, CI, Docker Compose
- ✅ Week 3 — Prisma schema (17 models), Auth.js v5 OAuth (Google+GitHub), 4-tier RBAC, initial Vitest suite (40 cases at the time, now 276)
- ✅ Boardly v0.1.0 — Deployed to Vercel + Neon + Upstash; authenticated dashboard, workspace & board CRUD
- ✅ Week 4 — Resend-backed workspace invitations with token-hashed accept flow (7-day expiry, revocable, email-matching enforcement)
- ✅ Week 5 — Card/List CRUD with optimistic lock, editor modal,
@dnd-kitdrag-and-drop - ✅ Week 6 — Pusher Channels realtime fanout (card/list mutations broadcast to peers on the same board)
- ✅ Week 7–9 — Search (⌘K command palette + label filter, membership-scoped server-side), notifications (mention bell + Notification rows + unread badge polling)
- ✅ Demo videos — Boardly 45 s + Knowlex 33 s narrated walkthroughs (VOICEVOX, free tier, fully reproducible pipeline)
- ✅ Knowlex MVP through v0.5.4 — full RAG live: ingestion (paragraph-aware 512-char chunking → 768-dim
gemini-embedding-001→ pgvector HNSW cosine kNN), streamed Gemini 2.5 Flash with numbered citations, nightly eval cron + golden v4 OR-mode scoring (ADR-0049 § 7th arc) with green-run report auto-commit + measured-eval README badge (v0.5.3, ADR-0049 § 7th arc Tier C-#2), workspace schema partitioning (ADR-0047 partial), schema-vs-prod drift fix +vercel-buildmigration regime (ADR-0051), drift-detect-v2 viapg_catalogassertion gating PRs, runtime schema canary at/api/health/schemaclosing the runtime side of ADR-0051 (v0.5.4, ADR-0053) - ✅ Drift-audit framework v1.0 through v0.5.10 — 13-axis framework, 10 structurally enforced via PR-time gates (doc-drift-detect / runtime schema canary / ADR-claim cross-check / ADR-ref resolution / external-artefact smoke probes / cron health hint), 3 honestly disclosed in the threat-model as T-07/T-08/T-09 with explicit re-evaluation dates; framework foundation closed via repository ruleset on
mainper ADR-0058; industry-standard hygiene baseline via OpenSSF Scorecard workflow weekly + on push (Branch-Protection / Pinned-Dependencies / Token-Permissions / Security-Policy / Code-Review / Dangerous-Workflows / CII-Best-Practices); axis 6 cron stale enforcement (smoke.ymlfails whendaysSinceLastGreenRun > 7); axis 7 PR-time block forcing new ADRs to update_claims.jsonor carry an explicitno-claim-neededmarker; framework frozen at v1.0 per ADR-0059 — future ratchets require external trigger (real incident / reviewer feedback / 2026-Q3 re-audit)
- 🚧 Presence indicators / cursor sharing — Pusher presence channels, follow-up to Week 6
- ⏳ Attachments (Cloudflare R2) — schema-ready at the Prisma layer per ADR-0008; UI wiring follow-up
- ⏳ Multi-language support and k6 load-test scenario execution (k6 script exists, measured run pending)
- ⏳ Knowlex retrieval extensions — hybrid search (BM25 + vector via RRF), HyDE, Cohere Rerank, all named in ADR-0011 / ADR-0014
- ⏳ LLM-as-judge
--judgeflag inscripts/eval.ts(gemini-2.5-pro rubric, optional env-toggled CI job so the default eval stays $0) - ⏳ Auth.js on Knowlex +
WorkspaceMemberaccess-control (v0.5.4 arc, the access-control half of ADR-0047 § Status) - ⏳ HNSW tuning at 10 k-chunk corpus — measured p95 ×
ef_search×mgrid indocs/eval/HNSW_TUNING.md, hourly background ingest under the 1500 RPD AI Studio cap (v0.6.0)
Two AI-engineering portfolio repos share craftstack's zero credit card + drift-CI enforced thesis, in different domains (browser automation + LLM long-context measurement) on the same consumer-laptop tier:
| Repo | Domain | Honest evidence |
|---|---|---|
| browser-agent-demo | Local-only browser RPA (Ollama + Qwen2.5-7B + browser-use) | 5-layer defense-in-depth journey, 5 honest failures with JSON evidence: rogue navigation → fabrication → training-data attractor → architectural intervention → frontier free-tier token cap. $0/run × 5 runs, drift-CI green throughout, ADR-006 Qwen-Alibaba e-commerce attractor hypothesis literally indirect-validated by the frontier comparison cell. |
| longctx-bench-honest | Long-context LLM measurement (Qwen2.5-7B-1M local + GitHub Models cloud) | The literal map of consumer-laptop × zero-CC × long-context: local 6GB VRAM ceiling = 4k tokens (rescued only by Windows shared-memory PCIe spillover, ADR-007), WSL2 + vllm cannot even fit weights + activations on the same hardware (ADR-009, counterintuitive negative result), cloud free-tier accessibility matrix shows GPT-5 unavailable + Claude absent from GitHub Models + DeepSeek/gpt-5 capped at 4000 tokens (ADR-008). 11 JSON evidence cells, drift-CI enforces every numeric claim. |
Cross-repo unifying thesis: constraint-optimized engineering — under (zero credit card, consumer laptop, public source / OSS only, drift-CI enforced), what is the literal best buildable in 2026? craftstack answers for full-stack web; the two siblings answer for AI engineering. Each repo discloses both the working zone AND the literal constraint boundary as JSON / log evidence — not as claims.
MIT — see LICENSE.