A desktop-launchable chat UI that lets you ask questions as if you're talking to Garry Tan, grounded in the gstack ethos. Each reply is produced by a fresh, isolated Claude Code instance spawned via the Claude Agent SDK — fully independent from your own Claude Code sessions.
- Frontend: single-page chat (dark, amber accent, Satoshi / DM Sans / JetBrains Mono) using gstack's own design system.
- Backend: Node HTTP server streaming SSE. Every
POST /api/chatspawns an independentclaudesubprocess via@anthropic-ai/claude-agent-sdk. - Persona: a system prompt synthesized from the public gstack repo — ETHOS.md (Boil the Lake, Search Before Building, User Sovereignty), README.md, DESIGN.md, and the full slash-command index.
- One-command desktop launch:
./launch.shhandles first-run install, starts the server, and opens your browser.
- Node.js 20+
- Claude Code CLI (
claude) — the Agent SDK spawns it under the hood. Install from https://docs.anthropic.com/en/docs/claude-code. - Anthropic API key (
sk-ant-...)
cp .env.example .env
# open .env and paste your ANTHROPIC_API_KEY
./launch.shThe launcher installs deps on first run, starts the server on http://localhost:5173, and opens it in your default browser.
npm install
ANTHROPIC_API_KEY=sk-ant-... node server.mjsTwo ways to set an API key / base URL — the in-app settings panel (gear icon, top-right) takes precedence over .env.
In-app settings (stored in browser localStorage):
- Provider:
Official AnthropicorCustom base URL - Base URL: any Anthropic-compatible endpoint (LiteLLM, OpenRouter, your own gateway)
- API Key: required — either an
sk-ant-...key or your proxy's key - Model: optional override; leave blank to use the server default
.env fallbacks (used when the UI leaves a field blank):
| Var | Default | Notes |
|---|---|---|
ANTHROPIC_API_KEY |
— | optional — UI can supply one instead |
ANTHROPIC_BASE_URL |
— | optional — e.g. https://openrouter.ai/api/v1 |
MODEL |
claude-sonnet-4-5 |
any Claude model id works — e.g. claude-opus-4-7 |
PORT |
5173 |
local port |
HOST |
127.0.0.1 |
loopback-only by default since the UI handles API keys |
Anonymous visitors get 5 questions/day, 1000 words per question. The counter lives in a signed cookie (HMAC-SHA256, no database). Signing in with the unlock account removes both caps. A pill in the header shows usage and opens the login modal.
Required env vars (set in Vercel for the deployed copy, in .env for local):
| Var | Notes |
|---|---|
SESSION_SECRET |
random 48-byte string. node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))" |
UNLOCK_EMAIL |
e.g. admin@example.com. Leave blank to disable login. |
UNLOCK_PASSWORD |
the password |
DAILY_QUESTION_LIMIT |
optional, default 5 |
WORDS_PER_QUESTION_LIMIT |
optional, default 1000 |
public/ static chat UI (no bundler, no framework)
server.mjs Node HTTP server + SSE streaming
persona.mjs Garry-Tan-flavored system prompt from gstack docs
rag.mjs zero-dep BM25 retriever over YC directory + news
fetch-yc.mjs pulls all YC companies from yc-oss.github.io/api
fetch-news.mjs pulls recent Garry Tan / YC news
data/ corpus (yc-companies.json, garry-news.txt — gitignored)
gstack-ref/ clone of garrytan/gstack (grounding material, gitignored)
launch.sh desktop launcher
The assistant can cite specific YC companies with links. Refresh the corpus with:
npm run fetch-yc # ~5900 companies from the yc-oss mirror (daily updates)
npm run fetch-news # recent Garry Tan / YC newsBoth write to data/ and are re-read on next query (mtime-based cache). launch.sh runs fetch-yc on first launch if the corpus is missing.
On each user message, server.mjs calls the SDK's query() with:
systemPrompt= the personasettingSources: []— no CLAUDE.md or user settings leak inallowedTools: []— pure conversation, no filesystem or web toolsmaxTurns: 1— one reply per requestincludePartialMessages: true— token-level streaming to the browser
That instance is isolated: no shared session state, no access to your machine, no memory between requests. Short conversation history is re-sent by the browser each turn so context is preserved without persistence.
The system prompt draws directly from the gstack repo and refuses to invent facts. Key frames:
- The Golden Age — a single person with AI builds what used to take a team of twenty.
- Boil the Lake — the complete implementation costs minutes more than the shortcut, so do it.
- Search Before Building — three layers of knowledge; prize first-principles observations.
- User Sovereignty — AI recommends, users decide. Always.
- Build for Yourself — specificity of a real problem beats generality of a hypothetical one.
If the user asks whether it's really Garry, it says no and offers to keep helping.
Y Combinator or Garry Tan. This is a fan-built persona grounded in public writing. Trademarks belong to their owners.