<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Darren Head</title>
    <description>The latest articles on DEV Community by Darren Head (@darrenhead).</description>
    <link>https://dev.to/darrenhead</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3983510%2F38c092ef-c16f-41a2-bbb8-d70a0d4e7e7c.png</url>
      <title>DEV Community: Darren Head</title>
      <link>https://dev.to/darrenhead</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/darrenhead"/>
    <language>en</language>
    <item>
      <title>Twinfolio: deploy your own AI portfolio that answers as you</title>
      <dc:creator>Darren Head</dc:creator>
      <pubDate>Tue, 16 Jun 2026 07:30:00 +0000</pubDate>
      <link>https://dev.to/darrenhead/twinfolio-deploy-your-own-ai-portfolio-that-answers-as-you-4ila</link>
      <guid>https://dev.to/darrenhead/twinfolio-deploy-your-own-ai-portfolio-that-answers-as-you-4ila</guid>
      <description>&lt;p&gt;My personal site, darrenhead.com, isn't a brochure, it's an AI twin you can chat with, and it answers from my real work, in my voice, with citations. People kept asking me the same thing after talking to it: &lt;em&gt;how do I build one for myself?&lt;/em&gt; The honest old answer, "clone my repo and delete all the parts about me" — was terrible. So I pulled the engine out into its own open-source project. It's called &lt;a href="https://twinfolio.app" rel="noopener noreferrer"&gt;&lt;strong&gt;Twinfolio&lt;/strong&gt;&lt;/a&gt;, the same stack running my site, generalised so it isn't about me, and this is the honest version of what it is, the engineering worth a look, and how you deploy your own in minutes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;strong&gt;Twinfolio launches on Product Hunt on June 16 as part of Vercel Day.&lt;/strong&gt; If you'd like a ping the moment it's live (and a chance to weigh in): &lt;strong&gt;&lt;a href="https://www.producthunt.com/products/twinfolio?launch=twinfolio" rel="noopener noreferrer"&gt;Notify me on Product Hunt →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2Fee7e9562-8a49-4ffd-855c-2c702afe2f43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2Fee7e9562-8a49-4ffd-855c-2c702afe2f43.png" title="The live twin answering a real question with inline citations" alt="The live twin answering a real question with inline citations" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F81c368be-1b8a-454b-a0ae-9376072af31c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F81c368be-1b8a-454b-a0ae-9376072af31c.png" title="The twin declining to invent an answer" alt="The twin declining to invent an answer" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What an AI portfolio actually is
&lt;/h2&gt;

&lt;p&gt;A normal portfolio is a brochure. You write it once, it goes stale, and the reader scans the headlines for thirty seconds. An AI portfolio is a conversation: the reader asks the thing they actually care about, "have you shipped anything with evals?", "what's your hardest project?" and gets a grounded answer, in your voice, with citations to the real material.&lt;/p&gt;

&lt;p&gt;That inverts who does the work. A brochure makes the reader guess which of your projects matters to them. A twin lets them ask, and answers from documents rather than from whatever a model imagines about your job title.&lt;/p&gt;

&lt;p&gt;It's polyglot, too — something I under-sold until I watched it happen. The interface ships in 40 languages, and because the brain is a model rather than a template, the twin follows the visitor's language: flip the site to French and Tesla answers in French, citations intact, even though every source document is in English. The corpus stays in whatever language you wrote it; retrieval doesn't care.&lt;/p&gt;

&lt;p&gt;One thing up front, because it's the whole pitch: Twinfolio is the engine, and &lt;strong&gt;darrenhead.com is its live reference twin&lt;/strong&gt; — the public demo, not a site that imports Twinfolio as a dependency. They're sibling Next.js codebases that share a lineage, and the extraction direction is site → engine. A clarification I owe you: the live darrenhead.com is a larger Supabase-backed app &lt;em&gt;with&lt;/em&gt; a database. The "no database" property below is Twinfolio-the-engine's, not that site's.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wedge: talk to it
&lt;/h2&gt;

&lt;p&gt;Here's the part that's genuinely different. Every other "chat with a personal RAG" project assumes you arrive with a clean corpus to upload. Most people don't have one. Twinfolio flips that: the twin interviews &lt;strong&gt;you&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You open a short self-interview — by voice or by text — and it asks about your work like a sharp friend who wants the real story: roughly eight to twelve warm, one-at-a-time questions across a few themes, favouring follow-ups over yes/no. You can paste a link and a &lt;code&gt;read_url&lt;/code&gt; tool fetches and summarises the page. When you're done, a pipeline turns the conversation into your knowledge base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;capture → normalize → proper-noun pass → distill → write → re-index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;distillTranscript&lt;/code&gt; rewrites your answers into first-person markdown (a faithful reorganisation, not a creative rewrite), a proper-noun pass fixes a glossary, and &lt;code&gt;mergeIntoProfile&lt;/code&gt; folds it all into a single canonical &lt;code&gt;content/profile.md&lt;/code&gt; where &lt;strong&gt;newer answers win on conflict&lt;/strong&gt;. Role and bio edits aren't auto-applied, the twin &lt;em&gt;proposes&lt;/em&gt; them and you confirm. The promise in the README is that you can have written nothing and still end up with a citeable AI twin in about ten minutes.&lt;/p&gt;

&lt;p&gt;The voice path runs on Gemini Live, an ephemeral, single-use token is minted server-side so your real key never reaches the browser, with 16 kHz PCM up / 24 kHz down, barge-in, and session resumption so a dropped connection reconnects transparently. The text path runs on the same model brain as the chat. Either way, the corpus is something you &lt;em&gt;talk into existence&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2Fd00be05c-ba82-49b2-aaed-72e3c4a33849.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2Fd00be05c-ba82-49b2-aaed-72e3c4a33849.png" title="Voice self-interview start screen" alt="Voice self-interview start screen" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F6984e7f5-6d03-4a12-8df3-029f9c97562b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F6984e7f5-6d03-4a12-8df3-029f9c97562b.png" title="Interview review screen with live cost" alt="Interview review screen with live cost" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  No database: "git is the database"
&lt;/h2&gt;

&lt;p&gt;This is Twinfolio's technical soul, and the cleanest engineering story in it. A deployed personal site with admin editing, a CMS, and RAG — and zero database to provision.&lt;/p&gt;

&lt;p&gt;Three plain files are the source of truth:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;twinfolio.json&lt;/code&gt;&lt;/strong&gt; — persona, site, models, usage caps, projects (and even an inline base64 avatar; on the chika-head deploy the headshot is literally a string in the tracked JSON).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;app/theme.css&lt;/code&gt;&lt;/strong&gt; — only the &lt;code&gt;:root&lt;/code&gt; and &lt;code&gt;.dark&lt;/code&gt; oklch token blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;content/&lt;/code&gt;&lt;/strong&gt; — the RAG corpus: markdown, PDFs, text, images, plus the distilled &lt;code&gt;profile.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F0071e3fe-9c66-4a0c-86d7-17235a7728c9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F0071e3fe-9c66-4a0c-86d7-17235a7728c9.png" title="The corpus map in /admin" alt="The corpus map in /admin" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/admin&lt;/code&gt; Studio edits these through a swappable &lt;code&gt;ConfigStore&lt;/code&gt; seam. In local dev, &lt;code&gt;FileConfigStore&lt;/code&gt; writes the files directly. Deployed, &lt;code&gt;GitHubConfigStore&lt;/code&gt; commits &lt;code&gt;twinfolio.json&lt;/code&gt; / &lt;code&gt;theme.css&lt;/code&gt; straight to your repo via the Octokit Contents API — which triggers a Vercel redeploy. That's the whole "no database" trick.&lt;/p&gt;

&lt;p&gt;The hard part is that a serverless filesystem is read-only at runtime, so a live site can't just write its own files. Twinfolio's corpus &lt;strong&gt;write ladder&lt;/strong&gt; (&lt;code&gt;lib/corpus/persist-file.ts&lt;/code&gt;) tries, in order, and never throws:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Vercel Blob&lt;/strong&gt; — runtime-writable, re-indexes live with no redeploy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git commit&lt;/strong&gt; — redeploys, lands in about a minute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dev filesystem.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Nothing writable → hand you the markdown to commit by hand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's a subtler bug I'm glad I caught: because each save commits the whole file, &lt;code&gt;GitHubConfigStore&lt;/code&gt; reads config from the &lt;strong&gt;repo tip&lt;/strong&gt; on every editor read, not from the build-time bundle — otherwise editing from a stale snapshot would silently erase commits that landed since this deploy, including your own previous save still redeploying. The corpus read is tip-aware the same way. The &lt;code&gt;HELP.md&lt;/code&gt; documents the "my admin edit disappeared after a deploy" symptom honestly, because it's a real failure mode if you get this wrong.&lt;/p&gt;

&lt;p&gt;Visitor chat history? Browser &lt;code&gt;localStorage&lt;/code&gt; — explicitly the no-database replacement for a Supabase-backed persistence layer. Usage caps default to in-memory per-instance windows, optionally backed by Upstash Redis for exact cross-instance counts.&lt;/p&gt;

&lt;h2&gt;
  
  
  One OpenRouter key = a complete twin
&lt;/h2&gt;

&lt;p&gt;Setup cost collapses to almost nothing because a single key does double duty. OpenRouter is the brain: &lt;code&gt;lib/ai/openrouter.ts&lt;/code&gt; builds the provider with &lt;code&gt;HTTP-Referer&lt;/code&gt; / &lt;code&gt;X-Title&lt;/code&gt; from your config so traffic is attributed to your deployment, and &lt;code&gt;getChatModel()&lt;/code&gt; resolves whatever model id you set (free models are explicitly endorsed). The &lt;strong&gt;same key&lt;/strong&gt; embeds the default RAG.&lt;/p&gt;

&lt;p&gt;And the default RAG is local vectors, not a managed service. &lt;code&gt;npm run embed&lt;/code&gt; chunks your markdown, text-extracts PDFs via &lt;code&gt;unpdf&lt;/code&gt;, and — since OpenRouter can't embed images — captions images with a vision model and embeds the &lt;em&gt;caption&lt;/em&gt;. Everything lands in one JSON file, &lt;code&gt;content/.vectors.json&lt;/code&gt; (1536-dim, &lt;code&gt;openai/text-embedding-3-small&lt;/code&gt;). At query time the backend embeds the query once and runs in-memory cosine top-K — no LLM in the retrieval path, no vector DB to babysit, so retrieval stays fast by design. Gemini File Search exists as an &lt;em&gt;optional&lt;/em&gt; managed backend for Office docs and large corpora, but local vectors are the default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F0e9acfa1-d499-4043-8184-29ca502e2030.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F0e9acfa1-d499-4043-8184-29ca502e2030.png" title="npm run embed building the local vector index" alt="npm run embed building the local vector index" width="799" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F4b39fe77-c5d8-40ee-be9d-08f3a7e2935d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F4b39fe77-c5d8-40ee-be9d-08f3a7e2935d.png" title="Sources panel showing the matched passages" alt="Sources panel showing the matched passages" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The billing safety is its own load-bearing module — &lt;code&gt;lib/ai/model-chain.ts&lt;/code&gt;, commented "this is the money path." &lt;code&gt;allow_fallbacks&lt;/code&gt; defaults &lt;strong&gt;off&lt;/strong&gt;, so a free or cheap default can never silently bill through a paid fallback; fallbacks are tier-matched, and every call is output-token-capped so a provider can't pre-authorise a huge budget and fail on a low-balance or BYOK account. That one line — a free default never silently routing to a paid model — is the difference between an open-source toy and something you'd point at the public internet.&lt;/p&gt;

&lt;p&gt;The voice itself is grounded but honest. The system prompt forces first person, injects a numbered &lt;code&gt;CONTEXT&lt;/code&gt; block, and hard-rules the model: ground every claim in the retrieved context, never invent employers, dates, or numbers, and when the context doesn't cover the question, say so and offer to connect rather than guess. Citations stream &lt;strong&gt;first&lt;/strong&gt;, as &lt;code&gt;data-citation&lt;/code&gt; parts, so the source strip renders before the text does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo-mode-first, then make it yours
&lt;/h2&gt;

&lt;p&gt;Friction at "step zero" is where open-source projects lose people, so the default path is "it runs": a fresh clone runs end-to-end with &lt;strong&gt;zero keys&lt;/strong&gt;. With no key, the chat streams a friendly canned reply through the &lt;em&gt;real&lt;/em&gt; AI SDK stream — so the UI is genuinely functional — and retrieval degrades to persona-only answers rather than throwing. That's a hard requirement in the code, not a nicety.&lt;/p&gt;

&lt;p&gt;There are two onboarding doors. A GUI wizard at &lt;code&gt;/welcome&lt;/code&gt; (Identity → Branding → Models &amp;amp; keys → Knowledge → Go live), and bundled Claude Code agent skills — &lt;code&gt;/setup-twin&lt;/code&gt;, &lt;code&gt;/add-to-corpus&lt;/code&gt;, &lt;code&gt;/retheme&lt;/code&gt;, &lt;code&gt;/deploy&lt;/code&gt;, &lt;code&gt;/add-vector-db&lt;/code&gt; — that any agent can run via &lt;code&gt;AGENTS.md&lt;/code&gt;. Skills are just prompts; opening the repo runs nothing until you act.&lt;/p&gt;

&lt;p&gt;A detail I'm quietly proud of: every shipped sample file carries an HTML-comment sentinel, &lt;code&gt;{/* twinfolio:sample */}&lt;/code&gt;, and the index builder excludes sentinel-marked docs. So the example persona's bio can never bleed into a real twin's retrieval, even in the onboarding preview, even before you clear it. Your first real save deletes the samples.&lt;/p&gt;

&lt;p&gt;Here's the whole loop end to end — zero to a live, cited Nikola Tesla twin:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/DeL_jVLoHlk"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The adapter spine and the Theme Studio
&lt;/h2&gt;

&lt;p&gt;The architecture that makes "no database," "pick your RAG," and "make it look like you" all true at once is what &lt;code&gt;CLAUDE.md&lt;/code&gt; calls the adapter spine: one Studio over two swappable seams. &lt;code&gt;ConfigStore&lt;/code&gt; decides &lt;em&gt;where config persists&lt;/em&gt; (File or GitHub); &lt;code&gt;RagBackend&lt;/code&gt; decides &lt;em&gt;how retrieval happens&lt;/em&gt; (local vectors, Gemini File Search, or none). &lt;code&gt;getConfigStore()&lt;/code&gt; and &lt;code&gt;getRagBackend()&lt;/code&gt; are the factories — nothing else in the app assumes where the vectors live. &lt;code&gt;RagBackend&lt;/code&gt; is a tiny two-method interface, which is why the &lt;code&gt;/add-vector-db&lt;/code&gt; skill can scaffold a pgvector or Pinecone adapter behind the same seam in a fork without touching core.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F00d9ccb9-8f83-4748-9fa4-a6bbde1f756a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2F00d9ccb9-8f83-4748-9fa4-a6bbde1f756a.png" title="Twinfolio architecture diagram" alt="Twinfolio architecture diagram" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most demoable surface is the Theme Studio — a no-code live oklch token editor lifted from darrenhead.com's own design system. About 32 colour tokens per mode, plus radius, fonts, and a six-step shadow scale, all driven from &lt;code&gt;app/theme.css&lt;/code&gt;. A WCAG-AA contrast engine (using &lt;code&gt;culori&lt;/code&gt;) audits roughly 16 token pairs per mode, composites translucent foregrounds before scoring, and an auto-fix nudges only the oklch lightness channel until each pair clears AA. A "Copy globals.css" button lifts the theme into any shadcn / Tailwind-v4 project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2Ff863f180-04e7-4659-895d-cb92957fcfc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fqgfuwsyiohnoahxiudsd.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Fcontent%2Ff863f180-04e7-4659-895d-cb92957fcfc1.png" title="Theme Studio A11y tab scoring every token pair" alt="Theme Studio A11y tab scoring every token pair" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is not — and the licence
&lt;/h2&gt;

&lt;p&gt;I'll be straight about scope. Twinfolio is not a CRM or lead-capture tool. It's not for interviewing &lt;em&gt;other&lt;/em&gt; people — it interviews you, to build your twin. It's not a clone, companion, or deepfake; it's always legibly an AI version of me, trained on my work. It's not an ATS screener, and it's not a hosted SaaS. It's a free thing you deploy yourself. Provided as-is, no support, no guarantees — fork freely.&lt;/p&gt;

&lt;p&gt;Twinfolio is &lt;strong&gt;AGPL-3.0&lt;/strong&gt;. The &lt;code&gt;LICENSE&lt;/code&gt; is the GNU Affero GPL v3 and &lt;code&gt;package.json&lt;/code&gt; declares &lt;code&gt;AGPL-3.0-or-later&lt;/code&gt;. The practical meaning: if you run a modified version as a network service, you offer your source under the same licence. For a project whose whole premise is grounding claims in reality, shipping the engine open — and being honest about its licence — is the consistent move.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your own
&lt;/h2&gt;

&lt;p&gt;The deploy is the easy part. The corpus is the real work, and now you don't even have to write it from scratch — you can just talk to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/darrenhead/twinfolio.git twinfolio
&lt;span class="nb"&gt;cd &lt;/span&gt;twinfolio
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev          &lt;span class="c"&gt;# http://localhost:3000, /welcome wizard, ZERO keys needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it yours through the wizard (or &lt;code&gt;/setup-twin&lt;/code&gt;), then go live: the wizard publishes to &lt;strong&gt;your own new GitHub repo&lt;/strong&gt; and keeps Twinfolio as &lt;code&gt;upstream&lt;/code&gt;, so engine updates pull cleanly. Import at &lt;code&gt;vercel.com/new&lt;/code&gt;, set &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt; (real chat + the build-time embed — needed before the first build) and &lt;code&gt;ADMIN_TOKEN&lt;/code&gt; (&lt;code&gt;openssl rand -hex 32&lt;/code&gt;; gates &lt;code&gt;/admin&lt;/code&gt;, fail-closed in prod). Then in the live &lt;code&gt;/admin → Deploy&lt;/code&gt;, click &lt;strong&gt;Connect GitHub&lt;/strong&gt; — a device flow, no PAT, no callback URL — so Studio saves commit and redeploy.&lt;/p&gt;

&lt;p&gt;If you've ever wanted a portfolio that answers for you, that's the whole loop: clone it, talk to it, ship it. The engine is open at &lt;a href="https://github.com/darrenhead/twinfolio" rel="noopener noreferrer"&gt;&lt;strong&gt;github.com/darrenhead/twinfolio&lt;/strong&gt;&lt;/a&gt; — feed it your story.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;⭐ If this resonated: Twinfolio is live on **Product Hunt&lt;/em&gt;* today for &lt;strong&gt;Vercel Day&lt;/strong&gt; — I'd genuinely love your take 👉 &lt;strong&gt;&lt;a href="https://www.producthunt.com/products/twinfolio?launch=twinfolio" rel="noopener noreferrer"&gt;Notify me on Product Hunt →&lt;/a&gt;&lt;/strong&gt;. And if you deploy a twin, send me the link — I want to see what yours says about you.*&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>nextjs</category>
      <category>showdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
