joelclaw-web
joelclaw.com Web App
Next.js app at apps/web/ in the joelclaw monorepo (~/Code/joelhooks/joelclaw/).
Deployed to Vercel on push to main. Dark theme, minimal, system-y aesthetic.
OPSEC Rules
Never expose real infrastructure details on public pages.
- Node/host names: Use Stephen King universe aliases, never real tailnet hostnames
- Ports: Do not publish port numbers for any service
- Usernames: Strip
com.joel.or similar prefixes from service identifiers - IPs/subnets: Never show real IP addresses or CIDR ranges
- Brands/models of network gear: Generalize (e.g. "NAS" not "Synology DS1821+")
- Tailscale: OK to mention as the mesh VPN product, but no tailnet name or node IPs
Current King universe mapping (network page):
| Role | Alias | Source |
|---|---|---|
| Mac Mini (control plane) | Overlook | The Shining |
| NAS (archive) | Derry | IT |
| Laptop (dev machine) | Flagg | The Dark Tower |
| Linux server | Blaine | The Dark Tower |
| Router (exit node) | Todash | The Dark Tower |
Content Model (Convex-first)
Canonical runtime content lives in Convex contentResources:
article:<slug>(type = article)adr:<slug>(type = adr)discovery:<slug>(type = discovery)
Filesystem content under apps/web/content/ is seed/backfill material, not runtime source.
Runtime read policy:
apps/web/lib/posts.ts→ Convex-first articles (article:*)apps/web/lib/adrs.ts→ Convex-first ADRs (adr:*)apps/web/lib/discoveries.ts→ Convex-first discoveries (discovery:*)- Optional local escape hatches (non-production only):
JOELCLAW_ALLOW_FILESYSTEM_POSTS_FALLBACK=1(articles)JOELCLAW_ALLOW_FILESYSTEM_CONTENT_FALLBACK=1(ADRs/discoveries)
Article fields still mirror MDX frontmatter shape:
---
title: "Post Title"
type: "article" | "essay" | "note" | "tutorial"
date: "2026-02-19T11:00:00" # ISO datetime, NOT just date
updated: "2026-02-19T14:30:00" # optional, bumps sort position
description: "One-liner for cards and meta"
tags: ["tag1", "tag2"]
draft: true # optional, hides from prod
source: "https://..." # optional, for video-notes
channel: "Channel Name" # optional, for video-notes
duration: "00:42:02" # optional, for video-notes
---
Sorting: Posts sort by updated ?? date descending. Use full ISO datetimes (not bare dates) for deterministic ordering. Setting updated bumps a post to the top without changing its original publish date.
Slugs: Derived from fields.slug in Convex (resourceId = article:<slug>).
Hard Trigger Workflow
write article about X
- Generate slug from title/topic.
- Upsert
contentResourceswithresourceId = article:<slug>,type = "article", full MDX body infields.content, andfields.draft = true. - Set
fields.dateto current ISO timestamp. - Return slug + draft preview link (
/<slug>if draft-visible in dev).
publish article <slug>
- Read
article:<slug>from Convex. - Patch/upsert with
fields.draft = falseandfields.updated = now. - Revalidate all affected surfaces via
POST /api/revalidatewith:- tags:
post:<slug>,article:<slug>,articles - paths:
/,/<slug>,/<slug>.md,/<slug>/md,/feed.xml,/sitemap.md
- tags:
- Verify
/,/<slug>,/<slug>.md, and/feed.xmlinclude the published post.
Media Embeds
Always embed YouTube videos in /cool discoveries and articles when they add context. Use the <YouTube id="VIDEO_ID" /> MDX component (available in both .mdx articles and .md discoveries). Extract the video ID from the URL (youtube.com/watch?v=VIDEO_ID). Place embeds near the top of the relevant section, before the prose discussion.
Writing Voice
Use the canonical joel-writing-style skill for prose. Key traits: direct, first-person when the claim is actually Joel's, strategic profanity, short paragraphs, bold emphasis, conversational but technical. Never corporate-speak and never fabricate Joel's beliefs or philosophy.
Design System
- Theme: Dark (
bg-[#0a0a0a]), neutral grays,--color-claw: #ff1493(hot pink accent) - Fonts: Geist Sans (body), Geist Mono (code/data), Dank Mono (code blocks with ligatures)
- Content width:
max-w-2xl(672px) — intentionally narrow for reading - Header: Single row — claw icon + "JoelClaw" left, nav links + search right. No tagline in header.
- Nav items: Writing (
/), Cool (/cool), ADRs (/adrs), Network (/network) - Active nav: White text vs neutral-500 for inactive, detected via
usePathname() - Search: ⌘K dialog using pagefind, type-based icons/badges
- Mobile: Full-screen overlay nav via
MobileNavcomponent - Code blocks: Catppuccin Macchiato theme, rehype-pretty-code
- Sidenotes: Tufte-style CSS sidenotes (pure CSS, no JS)
Key Files
| File | Purpose |
|---|---|
app/layout.tsx |
Root layout, fonts, metadata, footer |
app/page.tsx |
Home page (post list) |
app/[slug]/page.tsx |
Post detail pages |
app/adrs/page.tsx |
ADR list |
app/adrs/[slug]/page.tsx |
ADR detail (strips H1 to avoid duplicate title) |
app/cool/page.tsx |
Cool/discoveries list |
app/network/page.tsx |
Infrastructure status page |
components/site-header.tsx |
Header with active nav (client component) |
components/mobile-nav.tsx |
Mobile overlay nav |
components/search-dialog.tsx |
⌘K search |
lib/posts.ts |
Article loading from Convex (article:*) |
lib/adrs.ts |
ADR loading from Convex (adr:*) |
lib/discoveries.ts |
Discovery loading from Convex (discovery:*) |
lib/constants.ts |
Site name, URL, tagline |
lib/claw.ts |
SVG path for claw icon |
ADR Display Rules
- ADR runtime source is Convex (
contentResourceswithresourceId = adr:<slug>) - Vault sync still updates repo snapshots under
apps/web/content/adrs/ - Project snapshots into Convex with:
bun scripts/seed-adrs-discoveries.ts - The detail page (
app/adrs/[slug]/page.tsx) strips the H1 from markdown content because the page already renders the title with ADR number prefix - Regex:
content.replace(/^#\s+(?:ADR-\d+:\s*)?.*$/m, "").trim()
Adding a New Post
- Draft in Convex (
contentResources.upsert,resourceId = article:<slug>,draft: true). - Use ISO datetime in
datefield. - Add images to
apps/web/public/images/<slug>/if needed and reference/images/<slug>/...in MDX. - Publish by setting
draft: false, then revalidate tags + paths (post:<slug>,article:<slug>,articles,/,/<slug>,/<slug>.md,/<slug>/md,/feed.xml,/sitemap.md). - Verify route + markdown twin + homepage + feed consistency.
Backfill scripts:
scripts/seed-articles.tsfor article resourcesscripts/seed-adrs-discoveries.tsfor ADR + discovery resources
Network Page
The network page (app/network/page.tsx) shows real infrastructure with aliased names. When updating:
- Check actual system state (
kubectl get pods,tailscale status,launchctl print, etc.) - Apply OPSEC rules — alias all hostnames, strip ports/IPs/usernames
- Keep data arrays at top of file for easy updates
- Status dots: green (Online) with ping animation, yellow (Idle), gray (Offline)
More from joelhooks/joelclaw
cli-design
Design and build agent-first CLIs with HATEOAS JSON responses, context-protecting output, and self-documenting command trees. Use when creating new CLI tools, adding commands to existing CLIs (joelclaw, slog), or reviewing CLI design for agent-friendliness. Triggers on 'build a CLI', 'add a command', 'CLI design', 'agent-friendly output', or any task involving command-line tool creation.
129k8s
>-
88docker-sandbox
Create, manage, and execute agent tools (claude, codex) inside Docker sandboxes for isolated code execution. Use when running agent loops, spawning tool subprocesses, or any task requiring process isolation. Triggers on "sandbox", "isolated execution", "docker sandbox", "safe agent execution", or when working on agent loop infrastructure.
86joel-writing-style
Joel's writing voice and style guide for joelclaw.com content. Use when writing, editing, or reviewing any blog post, essay, book chapter, or prose content for joelclaw.com. Also use when asked to 'write like Joel,' 'match Joel's voice,' 'draft a post,' 'write content for the blog,' or 'review this for voice.' This skill captures Joel's specific writing patterns derived from ~90,000 words of published content spanning 2012–2026. Cross-reference with copy-editing and copywriting skills for marketing-specific copy.
81task-management
Manage Joel's task system in Todoist. Triggers on: 'add a task', 'create a todo', 'what's on my list', 'today's tasks', 'what do I need to do', 'remind me to', 'inbox', 'complete', 'mark done', 'weekly review', 'groom tasks', 'what's next', or when actionable items emerge from other work. Also triggers when Joel mentions something he needs to do in passing — capture it.
54skill-review
Audit and maintain the joelclaw skill inventory. Use when checking skill health, fixing broken symlinks, finding stale skills, or running the skill garden. Triggers: 'skill audit', 'check skills', 'stale skills', 'skill health', 'skill garden', 'broken skill', 'skill review', 'fix skills', 'garden skills', or any task involving skill inventory maintenance.
49