Thanks to visit codestin.com
Credit goes to github.com

Skip to content

leagames0221-sys/craftstack

craftstack

CI Security Headers: A Tests: 276 Vitest + 24 Playwright Knowlex eval (measured) Infra: $0/mo Free-tier CI-enforced License: MIT Node pnpm Next.js TypeScript Constraint: zero credit card Constraint: local LLM (default) Constraint: free / OSS only Constraint: security defense-in-depth

Selected under

The 4-constraint set (applied across the full portfolio — verified consistent across all 11 portfolio repos):

  1. 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.
  2. 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.
  3. Free / OSS only — every runtime dependency is permissively-licensed open source (MIT / Apache-2.0 / BSD-3); no proprietary SDK at build time.
  4. Security defense-in-depth — secrets-scan CI + .gitignore hardening, 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_STOP kill-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_STOP kill-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 (id 15652440, ADR-0058) rejected the author's own git push origin main attempts 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.

🎬 Walkthroughs

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/kb ingest (chunk → 768-dim embed), / ask (pgvector HNSW cosine kNN → streamed Gemini 2.5 Flash with numbered citations), /api/kb/stats live index-type probe, /docs/api hand-written OpenAPI 3.1.

🗺️ Architecture

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
Loading

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.

🌐 Live demo

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.

Run Knowlex locally with your own API key (BYOK)

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 / judgeMean

Cleanup: 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.

Apps

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)

Monorepo layout

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

Tech stack

Shipped in v0.1.0

  • 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 uses eval() at runtime; see ADR-0040 § Decision + § Consequences. Both directives' presence is now structurally pinned by scripts/check-csp-coherence.mjs per 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 real pgvector service container via docker compose (pnpm --filter knowledge test:integration) · k6 scenario
  • Drag & drop: @dnd-kit sortable cards with LexoRank positions + optimistic UI + VERSION_MISMATCH rollback
  • 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_KEY is 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 @handle tokens (email-local-part or display-name match against workspace members), Mention rows + per-user Notification rows are written, header bell polls /api/notifications every 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 without GEMINI_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 with gemini-embedding-001 at 768 dimensions (via outputDimensionality) → 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 Neon knowlex-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+K or / opens the command palette from anywhere, > switches it into action mode, Esc closes 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.yml cron caught regressions post-merge). ci.yml spins up pgvector + the Knowlex dev server to gate /, /kb, /docs/api; e2e.yml piggybacks on the already-running Boardly server to gate /, /signin, /playground. Public pages gate on zero serious+critical WCAG 2.1 AA violations; authenticated pages (/dashboard, /w/...) gate on zero critical only — serious warnings 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_SECRET constant-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-analyzerpnpm --filter collab analyze spits out a client / server / edge bundle report at apps/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-types emits a fully-typed paths interface into src/openapi-types.ts via openapi-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 movesCtrl-Z / ⌘-Z reverses the last drag, Ctrl-Shift-Z / ⌘-Shift-Z re-applies it. Bounded 25-entry LIFO stack, replays against the existing optimistic-lock-protected /api/cards/:id/move endpoint 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/ask and /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.mjs runs as a PR-blocking free-tier-compliance gate in ci.yml that fails merges introducing paid-plan vercel.json, billable SDKs, or leaked secret patterns. A single-flag kill switch EMERGENCY_STOP=1 short-circuits every write + AI endpoint on the next request (runbook §9); its counterpart observability endpoint /api/kb/budget mirrors /api/kb/stats to expose the current used/cap state (disabled-by-default in production per ADR-0046 § Trade-offs; returns a structured 404 {"code":"DISABLED"} until ENABLE_OBSERVABILITY_API=1 is set on the server). STRIDE threat model covers this attack shape explicitly as C-01..C-06. Decision record: ADR-0046. Credit-card-free signup walk-through in docs/FREE_TIER_ONBOARDING.md; cost-attack threat model in COST_SAFETY.md
  • Error-capture pipeline with demo mode — both apps boot @sentry/nextjs via Next's instrumentation.ts + instrumentation-client.ts hooks; server and browser errors, unhandled rejections, and every error.tsx boundary flow through a unified lib/observability.ts seam. When SENTRY_DSN is 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 stackretrieve.integration.test.ts exercises the real pgvector kNN path against a pgvector/pgvector:pg16 service container in CI, asserting "returns every row when k ≥ corpus size" — the exact regression a misconfigured ivfflat(lists, probes) silently produces (ADR-0041 documents the production diagnosis). scripts/bench-retrieve.ts reports min / p50 / p95 / p99 / max latency over N=1000 / M=100 probes. scripts/eval.ts seeds 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) and docs/eval/README.md
  • Live deploy smoke, scheduled.github/workflows/smoke.yml runs Playwright against both production URLs every 6 h (plus on workflow_dispatch and on main pushes after a 90-second Vercel-settle sleep). Knowlex smoke asserts indexType === "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).

How this was built

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 roleAtLeast comparator 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 version column on Card. updateMany filters by id + version, 0 rows affected → 409 VERSION_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 the lexorank npm 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/labels with the desired labelIds[]). 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 setCardLabels and setCardAssignees. 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.

Local development

Prerequisites

  • Node.js 20 LTS (.nvmrc pinned)
  • pnpm 9+ (packageManager field pinned)
  • Docker Desktop

Boot

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

Documentation map

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

Roadmap

Shipped

  • 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-kit drag-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-build migration regime (ADR-0051), drift-detect-v2 via pg_catalog assertion gating PRs, runtime schema canary at /api/health/schema closing 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 main per 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.yml fails when daysSinceLastGreenRun > 7); axis 7 PR-time block forcing new ADRs to update _claims.json or carry an explicit no-claim-needed marker; framework frozen at v1.0 per ADR-0059 — future ratchets require external trigger (real incident / reviewer feedback / 2026-Q3 re-audit)

Planned

  • 🚧 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 --judge flag in scripts/eval.ts (gemini-2.5-pro rubric, optional env-toggled CI job so the default eval stays $0)
  • Auth.js on Knowlex + WorkspaceMember access-control (v0.5.4 arc, the access-control half of ADR-0047 § Status)
  • HNSW tuning at 10 k-chunk corpus — measured p95 × ef_search × m grid in docs/eval/HNSW_TUNING.md, hourly background ingest under the 1500 RPD AI Studio cap (v0.6.0)

Related portfolio work — constraint-optimized AI engineering siblings

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.

License

MIT — see LICENSE.

About

Full-stack portfolio monorepo: Boardly (realtime collaborative kanban via Pusher) + Knowlex (single-tenant RAG demo; workspace schema partitioning shipped per ADR-0047, auth-gated access control deferred to v0.5.4). Next.js 16 · TypeScript · Prisma · pgvector · Gemini · Vercel

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors