The Broad Way is kinho.dev: my personal site and engineering blog, in English and Portuguese. The name comes from a Miyamoto Musashi line the site prints in the browser console: "When you know the way broadly you see it in all things."
I write about LLM agents, architecture and production work, in three sections: logs, rants and nonsense. The site also holds career and contact (/about), projects, consulting, the tools I use every day (/loadout) and my résumé as a D&D 5e character sheet (/sheet).
I started it on 20 November 2025 from create-next-app, and the site is live at kinho.dev.
>High-level design
It runs entirely on Vercel, on Next.js 16 with the App Router, React 19 and Tailwind v4. Every GitHub pull request gets a preview, and merging to main ships to production.
A request without a language goes through the middleware, which reads Accept-Language and redirects to /pt-BR or /en-US. From there it lands in one of two route groups with separate root layouts: (site), with the terminal navigation, or (sheet), the D&D sheet with its own HTML, fonts and styles.
There is no database: each post is a TypeScript module per language, and a single catalog hides drafts and feeds the pages, RSS and sitemap. The only path with external state is contact: the browser posts JSON to /api/contact, which validates, drops bots, rate-limits per IP and delivers in parallel to a GitHub issue and, when configured, an email through Resend.
Hover or focus a piece: it lights up with its links.
10 pieces9 links4 areas
Components
Locale middleware :: Redirects URLs without a language to /en-US or /pt-BR based on Accept-Language, with en-US as the default.
Post catalog :: One TypeScript module per post and language; a single function keeps drafts out of pages, RSS and the sitemap.
Feeds :: RSS at /en-US/rss.xml, /pt-BR/rss.xml and /rss.xml, and a sitemap built from the same catalog.
Character sheet (/sheet) :: A route group with its own root layout, four fonts of its own and a canvas-drawn d20 that respects reduced motion.
Contact API :: Validates the fields, drops bots with a honeypot, rate-limits per IP and delivers through an issue in a private repository plus optional email.
Tests :: Unit tests with node:test for the catalog, drafts, feeds, contact and sheet, plus Playwright browser suites.
>Trade-offs
The D&D sheet lives in its own route group, with an independent html and body.
Alternative rejected: Extending the framed site page, or hiding the frame with CSS.
Why: Separate route groups give an independent html and body and avoid rendering hidden, inaccessible site navigation.
A draft is a flag on the post, and a single catalog decides what shows.
Alternative rejected: Filtering drafts in each place: home, lists, post pages, RSS and sitemap.
Why: One filter covers all of them, and the tests check that a draft's title, URL and body never reach the RSS or the sitemap. The preview flag is not access control, and the README says so.
An explicit slug when a title gets corrected.
Alternative rejected: Always deriving the slug from the title.
Why: Fixing a title would change the published URL. Pinning the slug to the original keeps links already out there working.
Contact delivered through a GitHub issue as the main channel, with optional email. It counts as delivered if any channel accepts.
Alternative rejected: Relying on a single email send.
Why: Each message becomes an issue and arrives as a GitHub notification. If no channel accepts, the API returns 502 and the page shows the email address and LinkedIn, so a message never disappears silently.
>Highlights
>62 published posts per language (27 logs, 29 rants, 6 nonsense), from 20 Nov 2025 to 25 Sep 2026, plus 5 drafts kept offline.
>The /en-US RSS feed had 62 items on 22 Oct 2026.
>Unit tests with node:test, plus 4 Playwright browser test files.
>Contact with a honeypot, a limit of 5 messages per IP every 10 minutes, and messages up to 5,000 characters.
>The sheet's d20 is 385 lines of TypeScript on a 2D canvas: an icosahedron, quaternions and one shared animation loop for every die.
>Fun facts
*Open the browser console on kinho.dev: an ASCII katana shows up with the Musashi line that named the site.
*On the sheet's d20, the faces are numbered so opposite faces add up to 21, like a real die.