{"version":"https://jsonfeed.org/version/1.1","title":"Workshop | Philip I. Thomas","home_page_url":"https://www.philipithomas.com","feed_url":"https://www.philipithomas.com/feed/workshop/feed.json","description":"Journal about work in progress.","authors":[{"name":"Philip I. Thomas","url":"https://www.philipithomas.com"}],"items":[{"id":"https://www.philipithomas.com/stripe-projects-launch","url":"https://www.philipithomas.com/stripe-projects-launch","title":"Stripe Projects launches","content_html":"<p>Last month, I wrote <a href=\"https://www.philipithomas.com/agent-experience\">Agent experience</a> about the Stripe Projects beta and how developers must now treat AI as a first-class user of their products alongside humans. Today, <a href=\"https://projects.dev/\">Stripe Projects</a> launched during the <a href=\"https://stripesessions.com/\">Stripe Sessions</a> <a href=\"https://www.youtube.com/watch?v=e13-s0p1tfE&#x26;t=2770s\">keynote</a>, with Chroma as one of the launch integrations. You try it now at <a href=\"https://projects.dev/\">projects.dev</a>.</p>\n<p>For the launch, I made a video demo of <a href=\"https://www.philipithomas.com/chroma\">Chroma's</a> Stripe Projects integration - take a look below. It walks through provisioning a Chroma database as a coding agent, then builds an example application on Chroma that ingests local agent logs and turns them into a searchable knowledge base.</p>\n<p><a href=\"https://www.youtube.com/watch?v=mVPXjn4K76k\"><img src=\"https://i.ytimg.com/vi/mVPXjn4K76k/hqdefault.jpg\" alt=\"Chroma&#39;s Stripe Projects integration\" width=\"480\" height=\"360\"></a></p><p><a href=\"https://www.youtube.com/watch?v=mVPXjn4K76k\">Watch on YouTube: Chroma&#39;s Stripe Projects integration</a></p>\n<p>I will be speaking tomorrow at Stripe Sessions in the developer booth from 15:30 to 16:00 about agent experience at <a href=\"https://www.philipithomas.com/chroma\">Chroma</a>, covering both our Stripe Projects integration and other agent-focused efforts in the company. If you are at the conference, please stop by.</p>","summary":"Last month, I wrote Agent experience about the Stripe Projects beta and how developers must now treat AI as a first-class user of their products alongside humans. Today, Stripe Projects launched during the Stripe Sessions keynote, with Chroma as one of the launch integrations.","date_published":"2026-04-29T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/stripe-projects-launch-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/agent-experience","url":"https://www.philipithomas.com/agent-experience","title":"Agent experience","content_html":"<p>As <a href=\"https://www.philipithomas.com/software-in-the-ai-era\">coding becomes solved by AI</a>, developer tools need to be designed for agent users as much as human ones. \"Agent experience\" is a problem I have been focusing on at <a href=\"https://www.philipithomas.com/chroma\">Chroma</a>.</p>\n<p>The first step is legible docs. Any developer product benefits from models being <em>aware</em> of it, but must fight the stale training data of agents. To address this at Chroma, our team moved the docs to <a href=\"https://www.trychroma.com/customers/mintlify-case-study\">Mintlify</a>, which improved agent accessibility, such as with an <code>llms.txt</code> file. Mintlify also supports <a href=\"https://www.philipithomas.com/mcp-and-the-future-of-ai\">MCP</a>, giving agents persistent tool access with search. Next.js has taken this a step further in a way I think is prescient, bundling their docs in the package and <a href=\"https://nextjs.org/docs/app/guides/ai-agents\">setting agent directions</a> to read local documentation before writing any code.</p>\n<p>But docs tell an agent what is <em>possible</em>, not what is <em>recommended</em>. With Chroma Cloud, the recommended path means using our SDKs, using <a href=\"https://docs.trychroma.com/cloud/search-api/hybrid-search\">both dense and sparse vectors with hybrid search</a>, and probably using <a href=\"https://docs.trychroma.com/integrations/embedding-models/chroma-cloud-qwen#chroma-cloud-qwen\">Chroma's hosted embedding functions</a> for ease of integration. There are also nuanced directions for existing applications, such as how to model multi-tenant workloads and how to migrate existing data. To bridge that gap, I added a dedicated \"Prompt quickstart\" to Chroma Cloud, a block of text users can copy into their coding agent's context. Beyond sharing the docs as a resource, it gives opinionated directions for how to set up the product. Credential management is a concern here. You do not want to put API keys into the model context, so the quickstart includes a separate <code>.env</code> file download with credentials.</p>\n<p>As I showed prompt quickstart to some early users, one said, \"that's cool, but why do I as a human need to sign up? Can't the agent do that?\"</p>\n<p>That idea stayed in the back of my mind, until Stripe presented a potential solution.</p>\n<p>Today, <a href=\"https://projects.dev/\">Stripe Projects</a> is launching as a toolchain for agent-first commerce: agents can sign up for services, connect billing, provision resources, and manage secrets without a human in the loop. I have been collaborating with the Stripe team on the early protocol, and built Chroma as a launch integration. Try it out: <code>stripe projects add chroma/database</code>.</p>\n<p>Agent-first commerce is fundamentally a trust problem. When a human signs up for a service, they provide an email, enter a credit card, and agree to terms. An agent has none of those things. Stripe is uniquely positioned to solve this because they are already a trusted third party on both sides of the relationship: applications trust Stripe's identity verification and payment guarantees, and customers trust Stripe with their billing data. A new \"payment token\" protocol extends that trust layer so agents can provision paid services on behalf of their human operators. As an application developer, integrating with Stripe Projects felt like setting up a \"Google Sign-in\" flow, relying on Stripe as an identity provider backed by their existing KYC guarantees.</p>\n<p>Stripe Projects is early, but it points to a reality where developer tools maintain two experiences: one for humans, and one for agents.</p>","summary":"As coding becomes solved by AI, developer tools need to be designed for agent users as much as human ones. \"Agent experience\" is a problem I have been focusing on at Chroma. The first step is legible docs.","date_published":"2026-03-26T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/agent-experience-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/sync-phone-numbers-and-webhooks","url":"https://www.philipithomas.com/sync-phone-numbers-and-webhooks","title":"Sync, phone numbers, and webhooks","content_html":"<p>A couple of weeks have passed since I last posted. If this were an email, it would start with \"Sorry for the slow response.\"</p>\n<h2>Sync</h2>\n<p>A project I built just shipped at <a href=\"https://www.philipithomas.com/chroma?ref=contraption.co\">Chroma</a>. I added <a href=\"https://en.wikipedia.org/wiki/Amazon_S3\">Amazon S3</a> sync support to Chroma Cloud. This service lets people easily add arbitrary data from object storage to Chroma: PDFs, ebooks, images, and more.</p>\n<p>This was my first project in the <a href=\"https://rust-lang.org/\">Rust programming language</a>. I used lower-level features and data structures to tune the system for high-volume ingestions and <a href=\"https://docs.trychroma.com/cloud/sync/s3#auto-sync\">auto-syncing workloads</a>. The end result is a elegant product that conceals a lot of complexity.</p>\n<p>S3 Sync is live and is already being used at production scale. To explain it and the entire sync system, I recorded a video tutorial:</p>\n<p><a href=\"https://www.youtube.com/watch?v=Cliqy1W3yic\"><img src=\"https://i.ytimg.com/vi/Cliqy1W3yic/hqdefault.jpg\" alt=\"Sync, phone numbers, and webhooks\" width=\"480\" height=\"360\"></a></p><p><a href=\"https://www.youtube.com/watch?v=Cliqy1W3yic\">Watch on YouTube: Sync, phone numbers, and webhooks</a></p>\n<p>TJ, who runs Chroma's content, posted behind the scenes:</p>\n<blockquote>\n<p>My in house production setup for content at <a href=\"https://twitter.com/trychroma?ref_src=twsrc%5Etfw\">@trychroma</a> <a href=\"https://t.co/RGtUAMU7eM\">pic.twitter.com/RGtUAMU7eM</a></p>\n<p>— TJ (@TJkrusinski) <a href=\"https://twitter.com/TJkrusinski/status/2027184160528638031?ref_src=twsrc%5Etfw\">February 27, 2026</a></p>\n</blockquote>\n<p><em>(Sorry to</em> <a href=\"https://www.philipithomas.com/print\"><em>Print Edition</em></a> <em>subscribers - these embeds may not render well on paper, but the cover letter has a QR code you can scan to access this).</em></p>\n<h2>Phone calls</h2>\n<p>In college, I built a project called <a href=\"https://www.studlife.com/news/2012/11/15/you-are-unwanted-student-business-offers-humorous-text-rejections\">Text Reject</a> that was a \"<a href=\"https://en.wikipedia.org/wiki/Rejection_hotline\">rejection hotline</a>\" but over text messages. The project made the cover of the student newspaper, and I walked away with a deeper understanding and curiosity about telephony. Though Text Reject shut down, one vestige of that era is that I switched my personal cell phone number to end in my name (-PHIL).</p>\n<p><img src=\"https://www.philipithomas.com/images/posts/sync-phone-numbers-and-webhooks/12-11-15-1.jpg\" alt=\"Student Life front page featuring the Text Reject story\"></p>\n<p>At a poker game last week, the person next to me mentioned getting banned from a restaurant after having their <a href=\"https://www.philipithomas.com/openclaw-is-my-new-coworker?ref=contraption.co\">OpenClaw</a> try to place an order over the phone. And I thought: how fun! So, I set out to give my <a href=\"https://www.philipithomas.com/openclaw-is-my-new-coworker\">OpenClaw named Bell</a> a phone number.</p>\n<p>I bought an aftermarket 212 area code phone number, because <a href=\"https://en.wikipedia.org/wiki/Area_codes_212,_646,_and_332#history\">they are so rare and cool</a>. I connected it to <a href=\"https://www.philipithomas.com/openclaw-is-my-new-coworker\">Bell</a>. And, I had no calls to make.</p>\n<p>So, instead I built a voicemail system for the phone number. But I went a little overkill. I do not actually want to answer the phone, so based on the current date, time, and weather in New York City, an LLM dynamically generates an excuse for why nobody answered the phone, then uses <a href=\"https://developers.openai.com/api/docs/guides/text-to-speech/\">OpenAI Text to Speech</a> to speak it. Then, callers can leave a voicemail, which is transcribed and emailed to me.</p>\n<p>If you want to try it, call <a>+1 212 347 3190</a> for a timely greeting. And maybe I will call you back. Though, more likely Bell will.</p>\n<p>There are murmurs in the industry that AI is making UIs obsolete - that people want to buy APIs, then vibe-code their own custom interfaces. And that rang true with this voicemail mini-project. In the past, I would have used <a href=\"https://www.twilio.com/docs/studio\">Twilio's low-code builder</a> to make a simple voicemail. But with AI, I found it easier to custom code the integration I wanted (hosted in my <a href=\"https://www.philipithomas.com/app-of-ones-own?ref=contraption.co\">Junk Drawer</a>).</p>\n<h2>Webhooks</h2>\n<p>Webhooks are a way for one application to send information to another. And <a href=\"https://docs.openclaw.ai/automation/webhook#webhooks\">OpenClaw supports receiving them</a>, an overlooked feature that I think has a lot of potential. So, I set out to explore it.</p>\n<p>OpenClaw webhooks let you receive arbitrary data and set natural language rules for how to handle it. The first experiment I did was send <a href=\"https://booklet.group\">Booklet</a> posts to my OpenClaw, with the rule \"watch messages and alert me only of spam.\" Sending Booklet posts to this endpoint is pretty trivial, and enables <a href=\"https://zapier.com/\">Zapier</a>-like flows, and it caught some spam successfully while keeping communications calm.</p>\n<p>The next thing I did was start sending errors from my applications to OpenClaw. I instructed it to code a fix for any errors it received and open a pull request. And, the system helped me build my voicemail - quickly finding and fixing bugs. Sending errors through webhooks goes beyond \"if this, then that\" rules into a true feedback loop where my applications self-heal without human intervention.</p>\n<p>Feedback loops are the next horizon of applied AI.</p>\n<hr>\n<p>Building S3 sync took a lot of my focus. And now I intend to build some new projects. I plan to hack on a new project codenamed \"Manicule\" this weekend. And I will keep thinking about new applications for my 212 phone number.</p>","summary":"A couple of weeks have passed since I last posted. If this were an email, it would start with \"Sorry for the slow response.\" Sync A project I built just shipped at Chroma. I added Amazon S3 sync support to Chroma Cloud.","date_published":"2026-03-05T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/sync-phone-numbers-and-webhooks-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/fresh-coat-of-paint","url":"https://www.philipithomas.com/fresh-coat-of-paint","title":"A fresh coat of paint","content_html":"<p>Recently I have been watching documentaries by <a href=\"https://www.hustwit.com/films\">Gary Hustwit</a>. It started with <a href=\"https://www.amazon.com/gp/video/detail/B0GGBXDVX4?&#x26;linkCode=ll2&#x26;tag=contraption-20&#x26;linkId=0be2b7f1d033ebd3b3641f1d8f12141c&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">Rams</a>, about the life of Dieter Rams. Next, <a href=\"https://www.amazon.com/Urbanized-Norman-Foster/dp/B006O5RH2Y?&#x26;linkCode=ll2&#x26;tag=contraption-20&#x26;linkId=047346d4dcc4f075fc4af8d45d86e563&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">Urbanized</a> about city design. Then, <a href=\"https://www.amazon.com/s?url=search-alias%3Dinstant-video&#x26;field-keywords=helvetica&#x26;crid=2TJ6Z6E9QRDC&#x26;sprefix=helec%2Cinstant-video%2C589&#x26;linkCode=ll2&#x26;tag=contraption-20&#x26;linkId=3bceaf2f152a8e1b1ec792b76a354677&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">Helvetica</a> about the popular font.</p>\n<p>As I watched <em>Helvetica</em>, I confronted the fact that Contraption Company used the Helvetica font. I saw designers talking about the beauty of Helvetica: a revolutionary, canonical font. But also that it was generic, used for everything, becoming ambient and insipid. During the documentary, I decided it was time to stop using Helvetica and refresh the Contraption website.</p>\n<p>The recent Contraption site was influenced by modern, minimalist Norwegian websites, such as <a href=\"https://dekode.no/\">Dekode</a>, <a href=\"https://www.netlife.com/\">Netlife</a>, and <a href=\"https://www.snohetta.com/\">Snøhetta</a>. As a non-designer, I gravitate towards minimalism because limited palettes seem safe and hard to screw up. But I was ready for something more distinctive. I was inspired by sites like <a href=\"https://www.aimeleondore.com/\">Aimé Leon Dore</a>, <a href=\"https://www.sohohouse.com/\">Soho House</a>, and <a href=\"https://nomaprojects.com/\">Noma Projects</a> for a refresh that is still modern but brings in more character.</p>\n<p><img src=\"https://www.philipithomas.com/images/posts/fresh-coat-of-paint/localhost_2368_.png\" alt=\"Old contraption site in Helvetica\"></p>\n<p>I started with the fonts. I explored <a href=\"https://usgraphics.com/products/berkeley-mono\">Berkeley Mono</a>, which I use for coding. I liked <a href=\"https://en.wikipedia.org/wiki/FF_Meta\">FF Meta</a>. <a href=\"https://playtype.com/\">Playtype</a> in Copenhagen had some cool options. But I was ultimately enamored by the fonts from the <a href=\"https://klim.co.nz/fonts/\">Klim Type Foundry</a> in New Zealand, settling on <a href=\"https://klim.co.nz/collections/soehne/\">Söhne</a> paired with <a href=\"https://klim.co.nz/fonts/tiempos-text/\">Tiempos</a>.</p>\n<p>Over the long weekend, I <a href=\"https://github.com/contraptionco/contraption-ghost-theme/pull/6\">rewrote the entire theme</a> with the help of <a href=\"https://claude.ai/\">Claude Code</a>. At first glance, the site still looks minimal. But there is more color, more interactions, updated pages like <a href=\"https://contraption.co/projects/\">Projects</a>, and subtly different moods for different corners like <a href=\"https://www.philipithomas.com/workshop\">Workshop</a>. I wanted the website to feel like a maze: something you explore, with texture and surprises around each corner.</p>\n<p>One decision I made was to remove dark mode support. Somebody recently told me that dark mode frenzy was a sign that the technology industry was bored before the rise of AI. Supporting both light and dark mode makes sites converge to the same generic <a href=\"https://ui.shadcn.com/\">shadcn/ui</a> look. Generic can be good for software tools that people use all day, like <a href=\"https://notion.com\">Notion</a>. But for a more niche site, looking generic keeps you from being interesting or memorable. Without that constraint, I made the site visually interesting to explore, like the <a href=\"https://www.philipithomas.com/404\">entirely inverted 404 page</a>.</p>\n<p><a href=\"https://contraption.co\">Take a look</a>, explore some pages, and let me know what you think. Now, back to the documentary queue.</p>\n<p>P. S. - the <a href=\"https://www.philipithomas.com/print\">Print Edition</a> template has been updated, too.</p>\n<blockquote>\n<p>I did want [Stratechery] to be visually distinct. I did like a custom font, which back then was very rare. That was a new thing. I had the orange. There weren't very many orange sites back then. And I did a lot of these hand drawings, which were very visually distinct. [. . .] The reason for that is, what I was really thinking about was, oh, they follow an article, like, oh, that is a good article. A day later, a week later, a month later, they follow another link. They're like, wait, I've been on this site before, it's triggering my memory. That's really, I think, the key moment.<br>\n- <a href=\"https://www.acquired.fm/episodes/stratechery-with-ben-thompson\">Ben Thompson on Acquired</a></p>\n</blockquote>","summary":"Recently I have been watching documentaries by Gary Hustwit. It started with Rams, about the life of Dieter Rams. Next, Urbanized about city design. Then, Helvetica about the popular font.","date_published":"2026-02-19T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/fresh-coat-of-paint-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/three-ring-binder","url":"https://www.philipithomas.com/three-ring-binder","title":"Three-ring binder","content_html":"<p>When I was a kid, I would assemble binders with information about topics I was interested in. My favorite one was about airplanes and their specifications. I find that collecting information helps me organize it mentally. I think in a visual-spatial way, so when I recall a fact, I can picture the page it's on in a particular book and quickly flip to it.</p>\n<p>While writing more for Contraption over the past year, I craved a better way to organize my drafts and thoughts. I found my notes spread across <a href=\"https://ghost.org\">Ghost</a>, Apple Notes, and my to-do app. And, as I began using LLMs for editing, I found myself frequently copying and pasting text between windows. So, over the last month I built a new writing system. I moved my drafts to a folder on my computer called \"Three-ring binder\", and set up AI as a research and copyediting assistant within it.</p>\n<p>The idea for this system started when I saw a coworker using <a href=\"https://cursor.com\">Cursor</a> to organize notes from sales conversations. Cursor is an AI text editor intended for writing code. But it turns out that, as long as you're working with text files in local folders, then AI works just as well on prose as on code. AI coding tools are adept at searching, editing, and maintaining local files - and that's exactly what I needed to help organize my writing. So, I moved all of my writing and notes to local <a href=\"https://en.wikipedia.org/wiki/Markdown\">Markdown</a> text files in folders on my computer, and sync the folder to <a href=\"https://github.com\">GitHub</a> for backups and revision history. I decided to use <a href=\"https://obsidian.md/\">Obsidian</a> as the main text editor because it feels less like a coding tool. But, any text editor - including <a href=\"https://code.visualstudio.com/\">VS Code</a> or <a href=\"https://ia.net/writer\">iA Writer</a> - would work just fine for this setup.</p>\n<p>Once my writing lived in local text files, I could layer AI into my workflows. The simplest approach is to use something like <a href=\"https://docs.anthropic.com/en/docs/claude-code\">Claude Code</a> to work directly on the files. You can ask it to search your past writing, suggest edits following your style guide, or organize your notes. I went a step further and gave my <a href=\"https://www.philipithomas.com/openclaw-is-my-new-coworker\">OpenClaw assistant named Bell</a> access to the files. I can text Bell throughout the day to edit my notes. I will send things like \"Add 'hegemony' to my word list\" or \"Here is a quote I like from this book. Transcribe it and save it.\" Bell keeps things organized, such as ensuring that each word I find interesting also has an accompanying definition.</p>\n<p>Having my writing in local files also solved a problem I had with search. I often want to reference my past posts or look up things I have written. I found it hard to access that information scattered across the web. So I scraped and synced my old posts from multiple websites into Obsidian, including details like their publication date and URL. Now I can ask Bell ambiguous questions like \"What was that jazz album I listened to last year?\" and get an answer.</p>\n<p>I found local search so useful that I began storing posts from some bloggers I frequently read. Online writers tend to develop ideas over many disparate posts, so answering simple questions like \"How does Cal Newport do quarterly planning?\" turns out to be difficult. With their posts synced locally, I routinely use AI to search them.</p>\n<p>Beyond drafts and archived posts, I keep evergreen documents in a dedicated folder. I have a lot of lists in here that I build on over time: a style guide (\"never use the word 'very'\"), words I like, project ideas, people I admire, branding inspiration, and more. I use this for organizing my thoughts, but also as a reference for AI.</p>\n<p>My writing process now looks like this: I write drafts in Obsidian. When I want feedback, I ask Bell to email me a critique of the post. (I recommend asking AI to critique writing - it finds holes in logical arguments or sloppy parts needing revision.) After rewriting by hand a few times, I ask Bell for copy edits following my style guide. I review these carefully, rejecting some and accepting others. Then, after some final polishing, I copy and paste the finished post into Ghost to publish it.</p>\n<p>Here I sit in Obsidian, writing this post. The last several Contraption posts were written here in my digital three-ring binder. Using just local text files in folders seems old-school, but turns out to be the most effective way to leverage AI technology. As a kid, the beauty of my three-ring binders was that I could get lost in them, connect ideas, and do thinking. I have found that same feeling again, this time with an AI assistant that can search everything I have ever written. Altogether, I'm finding that this setup promotes immersive <a href=\"https://amzn.to/4rPfKWY\">flow</a> by keeping my entire process in one place.</p>","summary":"When I was a kid, I would assemble binders with information about topics I was interested in. My favorite one was about airplanes and their specifications. I find that collecting information helps me organize it mentally.","date_published":"2026-02-13T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/three-ring-binder-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/code-as-content-and-digital-proprioception","url":"https://www.philipithomas.com/code-as-content-and-digital-proprioception","title":"Code as content, digital proprioception, and what's next","content_html":"<p>I recently <a href=\"https://www.philipithomas.com/trivet-adds-google-sign-in-to-ghost\">launched Trivet</a>, and I've enjoyed seeing people install it on websites ranging from <a href=\"https://julirodriguez.pro/\">personal homepages</a> to <a href=\"https://blog.amassinsights.com/\">business blogs</a>.</p>\n<p>Before AI, Trivet would have taken me a few weeks to build. Now, <a href=\"https://openai.com/codex/\">Codex</a> wrote the first version in 40 minutes.</p>\n<p>In the past, building software was so difficult that launching something was impressive in and of itself. But as things get easier to build, it's harder to stand out. Code has become content - abundant, disposable, something you produce rather than labor over. We've transitioned from \"<a href=\"https://paulgraham.com/good.html\">make something people want</a>\" to \"make anything you want\".</p>\n<p>For example, <a href=\"https://github.com/contraptionco/contraption-ghost-theme\">I'll open-source the theme powering this site</a>. A few years ago, this would have represented a lot of work. There would have been utility for people making their own templates. Instead, today it's a footnote - a curiosity for those who want to \"view source\" on this website and see how it works.</p>\n<p>As a maker, AI enables me to build more and faster than I ever could have dreamed. But projects are no longer the investment they used to be. I find myself with decision fatigue - I ask \"what should I build next?\" every few days, instead of every few months. In the age of AI, it's harder to think of a \"big project\" to work on slowly in my free time.</p>\n<p>With code getting easier, I find myself thinking more about data. My old coworker Jason posted a <a href=\"https://www.youtube.com/watch?v=1ig8wHXSv0g\">video about using Claude Code to organize his notes</a>. There's so much useful knowledge spread out across many sources. People know Claude for writing code, but its superpower is searching and understanding thousands of files.</p>\n<p>When I went to Japan last year, I wanted to ask \"what is every Tokyo coffee shop and jazz kissa mentioned on <a href=\"https://craigmod.com\">craigmod.com</a>,\" but I couldn't. That desire led me to build <a href=\"https://www.philipithomas.com/mcp-and-the-future-of-ai\">Contraption MCP</a> - so I could have a research agent over my writing. The first time I used it and got a real answer synthesized from years of posts, it felt like a new kind of utility - a digital proprioception enabling me to fluidly navigate and explore my writing.</p>\n<p>Exploring information is still a hard problem in the age of AI. MCP is useful, but more of a technical protocol. \"RSS for AI,\" as <a href=\"https://www.linkedin.com/in/nolastan\">Stan</a> summarized to me.</p>\n<p>The next project I'm contemplating is codenamed \"Bell\" — a search agent for this blog, built on top of the Contraption MCP. I want it to be more accessible than raw MCP: something you can use in a browser, drawing on a variety of sources. It could answer questions like \"which craft-focused books are mentioned on this blog,\" but also \"how does subscriber export work on Postcard.\" Part of the motivation for Bell is <a href=\"https://www.philipithomas.com/app-of-ones-own\">building software for myself</a>. But, the other part is continuing to explore the future of digital tools through making projects.</p>\n<p>So, what should I build next? Two things feel true: we should default to building for ourselves, and understanding our own data is where some hard problems now live.</p>\n<blockquote>\n<p>I think the widespread impulse to take a photo of everything is in fact, at root, a creative one. It reflects a desire to not just receive life passively, but to intervene in it creatively: To frame the shot, to find the most compelling angle, to draw out the emotion, to honor the light… to participate.</p>\n<p>- <em><a href=\"https://www.blackbirdspyplane.com/p/this-life-gives-you-nothing\">This life gives you nothing</a></em>, Blackbird Spyplane</p>\n</blockquote>","summary":"I recently launched Trivet, and I've enjoyed seeing people install it on websites ranging from personal homepages to business blogs. Before AI, Trivet would have taken me a few weeks to build. Now, Codex wrote the first version in 40 minutes.","date_published":"2026-01-25T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/code-as-content-and-digital-proprioception-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/chroma-for-ruby","url":"https://www.philipithomas.com/chroma-for-ruby","title":"Chroma for Ruby","content_html":"<p>Last week, I shared a personal project I built over the holidays - <a href=\"https://www.philipithomas.com/trivet-adds-google-sign-in-to-ghost\">Trivet</a>, which adds Google sign-in to Ghost.</p>\n<p>Here, I'm sharing a work-focused holiday project I built: <a href=\"https://github.com/chroma-core/chroma/pull/6120\">an official Ruby client for Chroma</a>.</p>\n<p>Ruby is a programming language I enjoy and appreciate. I wrote <em><a href=\"https://www.philipithomas.com/rails-versus-nextjs\">Why Ruby on Rails still matters</a></em> about its elegance, and continue to maintain projects like <a href=\"https://booklet.group\">Booklet</a> and <a href=\"https://postcard.page\">Postcard</a> in Ruby.</p>\n<p>When I joined Chroma last year, I switched Booklet to use Chroma Cloud for search and <a href=\"https://www.philipithomas.com/chroma\">wrote about it here</a>. At the time, Chroma did not have an official Ruby client, so I used an unofficial community package. Unfortunately, that package has not been maintained and is no longer compatible with Chroma Cloud, leaving Booklet search broken.</p>\n<p>Over the holidays I spent some time with <a href=\"https://openai.com/codex/\">Codex</a> building a new Chroma library for Ruby. This new library supports all of Chroma's newest features, and will be part of the <a href=\"https://github.com/chroma-core/chroma\">core Chroma monorepo</a>. It is <a href=\"https://rubygems.org/gems/chromadb-experimental\">published to RubyGems as <code>chromadb-experimental</code></a><a href=\"#fn1\">[1]</a> - so you can use it right now.</p>\n<p>Chroma's search functions have gotten more powerful over the last year. Last month, Chroma released a <a href=\"https://www.trychroma.com/project/sparse-vector-search\">new Search API</a> with support for sparse vector searches and hybrid search. This overcomes many limitations of \"traditional\" vector search and turns Chroma Cloud into a powerful search product.</p>\n<p>Traditional dense vector search, like that built on <a href=\"https://platform.openai.com/docs/guides/embeddings\">OpenAI's vector embedding models</a>, is good at semantic search - searching \"marketing\" would match \"advertising\" in Booklet's search results. But this method does not handle keyword search well - searching for a person's name would not return their user profile as you would expect.</p>\n<p>I originally got around this by wrapping Booklet's search function with a full-text search layer:</p>\n<pre><code class=\"language-ruby\">@search = current_member.searches.create(query: query).embed\n\nmember_name_matches =\n  if @community.directory_enabled? || current_member.admin?\n    @community.members.active\n      .where(\"lower(members.name) LIKE ?\", \"%#{@search.query.downcase}%\")\n      .order(Arel.sql(\n        \"CASE WHEN lower(members.name) LIKE ? THEN 1 ELSE 2 END, \" \\\n        \"members.name\",\n        \"#{@search.query.downcase}%\"\n      ))\n  else\n    @community.members.active.where(id: 0)\n  end\n</code></pre>\n<p>This meant that if I searched \"Philip\", my profile would come to the top of the list, separate from the semantic search.</p>\n<p>With Chroma's new sparse vector support, I was able to remove this wrapper entirely. Sparse vector search is built to match on keywords: proper nouns, people's names, function calls. <a href=\"https://docs.trychroma.com/integrations/embedding-models/chroma-bm25#chroma-bm25\">BM25</a> is the best-known sparse algorithm, but has limitations with misspellings (\"Philip\" vs. \"Phillip\") and variations (\"Person\" vs. \"People\"). A more powerful implementation is <a href=\"https://docs.trychroma.com/integrations/embedding-models/chroma-cloud-splade\">SPLADE</a>, which handles fuzziness in keyword encoding better - it brings up my profile even if you search \"phillip\". Many Chroma applications power their entire search on SPLADE embeddings alone.</p>\n<p>Dense and sparse vectors each have their own benefits. Chroma now lets you combine them into a single <a href=\"https://www.trychroma.com/project/sparse-vector-search#hybrid-search\">hybrid search</a>, bringing the best of both into one API. This is what I implemented for Booklet's new search - combining SPLADE sparse vectors with <a href=\"https://docs.trychroma.com/integrations/embedding-models/chroma-cloud-qwen#chroma-cloud-qwen\">Qwen</a> dense vectors. The result matches keywords like member names, but also handles generic queries like \"marketing freelancing\".</p>\n<p>Some Booklet posts are long, so I <a href=\"https://research.trychroma.com/evaluating-chunking\">chunk</a> documents into smaller pieces for retrieval. This means that when running a search, the same post could show up multiple times in the results. To solve this, I implemented Chroma Cloud's <a href=\"https://www.trychroma.com/changelog/groupby\">new GroupBy functionality</a>, which de-duplicates results for a better experience.</p>\n<p>This post got technical. But the takeaway is simple: Chroma let me build a powerful, production-ready search system for a side project - with <a href=\"https://trychroma.com/pricing\">usage-based pricing</a>, no servers to manage, and cutting-edge retrieval techniques.</p>\n<p><a href=\"https://github.com/chroma-core/chroma/pull/6120\">Learn more about Chroma for Ruby here</a>, try it out, and let me know what you think.</p>\n<p>And, you can demo this new search functionality on <a href=\"https://frctnl.xyz\">FRCTNL</a> after logging in.</p>\n<hr>\n<ol>\n<li>Please consider the package name temporary and the API unstable until the <a href=\"https://github.com/chroma-core/chroma/pull/6120\">PR has been merged</a>, and feel free to provide feedback on that GitHub thread. <a href=\"#fnref1\">↩︎</a></li>\n</ol>","summary":"Last week, I shared a personal project I built over the holidays - Trivet, which adds Google sign-in to Ghost. Here, I'm sharing a work-focused holiday project I built: an official Ruby client for Chroma. Ruby is a programming language I enjoy and appreciate.","date_published":"2026-01-14T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/chroma-for-ruby-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/tacit-knowledge-in-software","url":"https://www.philipithomas.com/tacit-knowledge-in-software","title":"Outage, tacit knowledge, and a new project","content_html":"<p>Hello from Boulder, Colorado - where we've been dealing with power outages due to high wind. Alas, as I lost power in Boulder, my apartment in San Francisco also lost power. I knew because my <a href=\"https://www.philipithomas.com/a-mini-data-center\">Mac Mini home datacenter</a> went offline. This resulted in a few hours of downtime for this blog, <a href=\"https://postcard.page\">Postcard websites</a>, and <a href=\"https://booklet.group\">Booklet</a>. I apologize for the outage.</p>\n<p>I'm coming up on the one-year anniversary of moving all of my websites to run on a Mac Mini on my desk. This setup is not built for high uptime - there's no redundancy in power or internet. But I'm ending the year at 99.8% uptime, which seems reasonable. (While occasional downtime is expected, I take <a href=\"https://www.philipithomas.com/disaster-recovery\">off-site backups seriously</a> to prevent data loss.)</p>\n<h2>Tacit knowledge</h2>\n<p>I've been thinking a lot this week about tacit knowledge.</p>\n<p>It started while reading <a href=\"https://petermiller.com/products/new-nordic-cuisine-aesthetics-and-place-a-compendium-new-exhibit-arriving-in-seattle-november-15th\">New Nordic Cuisine, Aesthetics, and Place: A Compendium</a>, which had a section entitled \"Tacit knowledge\":</p>\n<blockquote>\n<p>Cooking in general can, to a large extent, be said to be about tacit knowledge – a certain kind of knowledge that can’t be described and taught with words (recipes) or through simple imitation and repetition. It must be experienced, through the act of making, with our hands and bodies and senses. Tacit knowledge could, therefore, be described as a form of embodied knowledge, reminiscent of the philosophies of Tim Ingold’s book Making from 2013, where he argues the importance of making material things rather than just learning about them from an external – intellectual – perspective. The interest in traditional cooking techniques, preservation methods, curing processes, and foraging amongst the new Nordic chefs is, at least in part, based on tacit knowledge.</p>\n</blockquote>\n<p>Tacit knowledge popped up again while I was re-reading part of <a href=\"https://www.amazon.com/Shop-Class-Soulcraft-Inquiry-Value/dp/0143117467?&#x26;linkCode=ll1&#x26;tag=contraption-20&#x26;linkId=d1ba3803813991ede5237ede71f08f70&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">Shop Class as Soulcraft</a>, which explores the erosion of tacit knowledge and intuition in the transition to knowledge work.</p>\n<p>Finally, Stripe Press announced a new video series called \"<a href=\"https://www.stripe.press/tacit\">Tacit</a>\":</p>\n<blockquote>\n<p>The mechanism for developing tacit knowledge is straightforward but slow: repeated practice that gradually moves skills from conscious effort to automatic execution. The mechanism for transmitting it is even slower: apprenticeship, where a learner works alongside someone experienced, observing and imitating until their own judgment develops. This is why tacit knowledge often concentrates in lineages, unbroken chains of practitioners passing expertise to the next generation.</p>\n</blockquote>\n<p>With so much discussion of tacit knowledge, I find myself reflecting on its role in software teams. At my job, I work with many people who are eight to ten years younger than me. I think often about what can be taught versus learned in software, and junior engineers are more current on theory than I am. But taste and experience are tough to replicate. An example that comes to mind happened a few months ago, when an on-call engineer was alerted about an outage and began poring through logs. I noticed three people crowded around them at the computer, trying to figure out why all of the servers had gone offline. I joined the group, glanced at the logs, then walked over to the computer and turned off webhooks from a third-party service - restoring the service.</p>\n<p>In the age of AI, building software is becoming easier. But knowing what to build, understanding systems, and running applications are becoming more important skills - and that kind of tacit knowledge isn't easy to teach.</p>\n<h2>A new project</h2>\n<p>I start new projects with a name. I keep a list of project name ideas in Apple Notes and continue to expand it. (\"Carnet\" was added this week.) I even <a href=\"https://www.philipithomas.com/chat-with-my-dog\">picked a dog name</a> long before adopting a dog.</p>\n<p>A name I've liked for a while has been \"Trivet\" - referring originally to the three-legged contraption for hanging a pot over a fire, but over time coming to mean a potholder. I like the name as being \"something that sits in-between.\" I went as far as to make a logo for it over a year ago.</p>\n<p>I explored some different project ideas for the name \"Trivet,\" including an email-based personal assistant I hacked on last holiday.</p>\n<p>But for the past month I've been ruminating on a new project, and the name \"Trivet\" has stuck.</p>\n<p>The current iteration of Trivet is a Google sign-in service for blogs hosted on <a href=\"https://ghost.org\">Ghost</a>, like this one. Any product manager will tell you that people prefer Google sign-in over passwords or magic links. But Ghost blogs do not have support for Google sign-in. While exploring Ghost's source code while building <a href=\"https://www.philipithomas.com/disaster-recovery\">backups of my posts</a>, I found some internal APIs that would make it possible to add middleware to enable Google sign-in—a \"trivet,\" if you will.</p>\n<p>I'm currently building it out as a holiday project, and hope to have it in a testable shape soon. Stay tuned, and happy holidays.</p>","summary":"Hello from Boulder, Colorado - where we've been dealing with power outages due to high wind. Alas, as I lost power in Boulder, my apartment in San Francisco also lost power. I knew because my Mac Mini home datacenter went offline.","date_published":"2025-12-24T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/tacit-knowledge-in-softwar-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/digital-gardening","url":"https://www.philipithomas.com/digital-gardening","title":"Digital gardening","content_html":"<p>I've been thinking about gardens this week.</p>\n<p>It started while reading the book <a href=\"https://www.amazon.com/Shopkeeping-Stories-Observations-Peter-Miller/dp/1797228765?&#x26;linkCode=ll1&#x26;tag=contraption-20&#x26;linkId=5410248cc1bbbc7b379f73398414f84d&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">Shopkeeping</a>, which had a section entitled \"Gardening\":</p>\n<blockquote>\n<p>A good shopkeeper will notice if a wall or section is in a slump or distress, as any gardener would. You will scan the displays and the cases with the wand of your mind, checking, in a way, for life and breath.</p>\n<p>A gardener would look for ground that has become too hard, or soil that needs enriching or shading or watering. So too, a shop owner must sense if the book or product is in the wrong place, or with the wrong things, or being shown incorrectly to make its presence best felt.</p>\n</blockquote>\n<p>Later, while showing me herbs he had grown, <a href=\"https://x.com/HammadTime\">Chroma's CTO</a> offhandedly mentioned, \"When I don’t know what to do, I prune my garden.\"</p>\n<p>There's a romantic idea of gardening - that you can walk in and find something to do. Water some plants, do some trimming, remove some weeds. Continuous improvement without a to-do list. Sure, gardeners can take on a <em>project</em> - but garden projects tend to come from a position of abundance or ambition, rather than obligation.</p>\n<p>Gardening seems quite the opposite of professional software work, where we often have very rigid structure. Tickets, tasks, sprints, OKRs, and standups are designed to keep people on task. In some teams, people are not allowed to contribute code except in response to a work order.</p>\n<p>When we use other people's products, we often contemplate what we would do to make them better - where a button might go, or what was unclear while using it -and think, \"I wish I could fix this for them.\" Yet, we rarely do the same with our own products. We make time for <a href=\"https://www.philipithomas.com/maintenance\">maintenance</a>, but it tends to be reactive to problems or known tech debt. We rarely approach our own projects with a present state of mind, unencumbered by past blemishes or future obligations, and see how they instinctively feel. <em>Gardening</em> our projects.</p>\n<p>Search “digital gardens” and you’ll quickly run into the world of <em>personal knowledge management</em> - note-taking systems inspired by books like <a href=\"https://www.amazon.com/How-Take-Smart-Notes-Technique/dp/B0DXR89LV8?crid=2US1EBWN0L7NJ&#x26;dib=eyJ2IjoiMSJ9.YBwST8cgh8_L-Bvv5mf6aFs1gODw2OQAxytHwksCroIi3v8sFm0ATXsA0gRw5jgZT44XxsNT9Xc31Q2Em6kvL1btWuNoiTb0cMCBw1EbAQ8-5onFKcU7giIPcoxVBEBJ_Lf9azTj_irUQqR2WuhJXusEIXw4XWToaoBReXTND1aAkL6V20x7uj3WhaMxQYU9.aSFkFZhiJkKFb6mS2xAedtdaz95f-I38mu32ooR3WT4&#x26;dib_tag=se&#x26;keywords=S%C3%B6nke+Ahrens&#x26;qid=1765899266&#x26;sprefix=s%C3%B6nke+ahrens%2Caps%2C165&#x26;sr=8-1&#x26;linkCode=ll1&#x26;tag=contraption-20&#x26;linkId=efdc4fa0b23000c880bc81a25701fba1&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">How to take smart notes</a>. In it, Sönke Ahrens argues that notes become valuable through linking: you revisit them over time, connect them to other ideas, and slowly grow a dense graph.</p>\n<p>When I experimented with using <a href=\"https://obsidian.md/\">Obsidian</a> to take notes in this <em>Zettelkasten</em> note-taking style years ago, I came away with an appreciation for perusing old notes. There's a <a href=\"https://www.amazon.com/dp/0918172020?&#x26;linkCode=ll1&#x26;tag=contraption-20&#x26;linkId=78498dbe6ba090f8ed4ba08e15c55bf1&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">beauty in the shadows</a> - the writing and pieces I had forgotten about. Reviewing old notes would lead to new insights - such as, \"I've highlighted three places I want to visit in Seattle across different books - perhaps I should plan a visit to that city.\" <a href=\"#fn1\">[1]</a></p>\n<p>As I maintain projects like <a href=\"https://postcard.page\">Postcard</a>, <a href=\"https://booklet.group\">Booklet</a>, and this website, I find myself doing a form of digital gardening. Sometimes I'll open the site, read through old pages, make some layout tweaks, and update the footer. It's little things that are probably inconsequential. But little scratches accumulate over time - broken links, old dates, rough copy. In aggregate, this low-stakes work keeps the site polished and cohesive.</p>\n<p>As we're entering the end of the year, many software teams are pausing non-essential deployments and slowing down. Consider taking this time to garden.</p>\n<p>Sign up for your own product, explore the website, and read the code - but without a goal in mind. Then, wander and improve things as they capture your attention. Create new connections, fix copy that doesn't sound right, and smooth the rough edges.</p>\n<p>Gardening is ultimately a fight against nature - an attempt to bring order to chaos. Disorder doesn't happen all at once; it happens progressively. Sure, a project can introduce some loose ends that need to be cleaned up. But often, the important work happens when you walk in with nothing to do and begin pruning.</p>\n<hr>\n<ol>\n<li>I no longer use Obsidian, but I apply some if its lessons to this website. <a href=\"#fnref1\">↩︎</a></li>\n</ol>","summary":"I've been thinking about gardens this week. It started while reading the book Shopkeeping, which had a section entitled \"Gardening\": A good shopkeeper will notice if a wall or section is in a slump or distress, as any gardener would.","date_published":"2025-12-16T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/digital-gardening-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/build-and-tinker","url":"https://www.philipithomas.com/build-and-tinker","title":"Building less, tinkering more, and walking away sooner","content_html":"<p>I'm back from my vacation to Japan, slowly adjusting to California time. It was a fun and inspiring trip – I'm thinking about it now while listening to the Eric Dolphy album <em>Out There</em> on vinyl, which I bought after hearing it at the <a href=\"https://www.google.com/maps/place/Eigakan+Jazz/@35.7220369,139.7523016,17z/data=!3m1!4b1!4m6!3m5!1s0x60188db601888faf:0xd45aed956dea453c!8m2!3d35.7220369!4d139.7523016!16s%2Fg%2F1vq9m8tq?entry=ttu&#x26;g_ep=EgoyMDI1MTIwMi4wIKXMDSoASAFQAw%3D%3D\">Eigakin</a> jazz kissa during the trip. (I used to play cello, so I appreciate its use in this album instead of a bass.)</p>\n<p>I first went to Japan last year for a <a href=\"https://www.philipithomas.com/almost-perfect\">residency at Almost Perfect</a>, with the goal of exploring <em>shokunin</em> culture – people who devote themselves to a craft. I kept following that thread on this trip, from tempura to coffee to clothing. All these craftspeople seemed to share a certain lightness and <a href=\"https://www.amazon.com/Flow-Psychology-Experience-Perennial-Classics/dp/0061339202\">flow</a> in their work—something I aspire to.</p>\n<p>In the back of my mind, I kept returning to the question of how to make Contraption posts available as a paper newsletter through the mail, something I originally explored in <a href=\"https://www.philipithomas.com/snail-mail\">Snail Mail</a>. Throughout the week I ruminated on an elaborate multi-part system for letter delivery. Reader information would live in the <a href=\"https://ghost.org\">Ghost</a> blog account system. I’d build a web app for managing shipping addresses. I would also build a unified single sign-on service, connecting Ghost to this portal and also to future apps.</p>\n<p>This multi-app setup seemed elegant – and with AI, it felt <em>possible</em>. I even had a coding agent build most of the login system while I read on the plane home.</p>\n<p>Back on my couch today, I realize how much I was overcomplicating the system. I think that's the danger of being away from building too long – I can romanticize systems, but then never find time to implement them. As my obligations to my past self began to mount, I stopped and asked: What would letter delivery look like if it were easy?</p>\n<p><a href=\"https://www.philipithomas.com/chroma\">Contraption Co. is not my job</a>, so <a href=\"https://www.philipithomas.com/the-next-iteration-of-contraption-company\">I want my work here to be fun</a>.</p>\n<p>Looking at Stripe's free no-code tools, I found a way to hack together something that syncs with my blogging system but also enables shipping address collection and management. Stripe payment links can collect addresses; the customer portal lets people update their info later. Ten minutes of tinkering saved me weeks of work.</p>\n<p>Now, the remaining work for letter delivery is straightforward: write a script that detects when a new post is published, look up subscribers in Stripe, and trigger letters through <a href=\"https://lob.com\">Lob</a>. That logic can live in my <a href=\"https://www.philipithomas.com/app-of-ones-own\">\"Junk drawer\"</a> instead of some fancy new app.</p>\n<p>The takeaway here isn’t “don’t think big.” It’s to be comfortable abandoning projects and walking away from them. It’s the same strategy I’ve learned with books: I used to force myself to finish boring ones, and then I’d just stop reading altogether. At some point I realized it’s better to follow my motivation than to cling to sunk costs. When I notice myself procrastinating, it's a sign that my current self isn't bought into the work I had previously committed to.</p>\n<p>Even though I won’t use the login system I started on the plane, I did stumble into a new AI coding pattern that I like. Building a login system required low-level knowledge of how Ghost works, plus some awareness of my theme for logos and branding. So I created a new <code>working/</code> folder on my computer, copied the Ghost source and my blog theme into subfolders, and added a blank <code>login/</code> folder.</p>\n<p>I told the coding agent to write all new code into <code>login/</code>, but to reference the Ghost and theme folders as needed – copying over components, styles, and patterns instead of reinventing them. This worked surprisingly well: it copied styling correctly and could analyze the <em>actual</em> Ghost and theme code, rather than relying on out-of-date documentation.</p>\n<p>If you're working on a project that needs to cross-reference multiple repos, I recommend trying this <strong>working directory</strong> pattern – put all the relevant source code side-by-side with the folder where the agent is generating new code.</p>\n<p>So: I’m not using the login system, but I <em>did</em> learn something by tinkering. That’s the balance I want: keep tinkering, but be willing to walk away from projects that begin to feel too heavy.</p>\n<p>This ended up being a bit of a meandering Workshop post – but <a href=\"https://www.philipithomas.com/introducing-workshop\">that’s what Workshop is for</a>.</p>\n<p>Stay tuned – paper newsletters will (hopefully) be available soon.</p>","summary":"I'm back from my vacation to Japan, slowly adjusting to California time. It was a fun and inspiring trip – I'm thinking about it now while listening to the Eric Dolphy album Out There on vinyl, which I bought after hearing it at the Eigakin jazz kissa during the trip.","date_published":"2025-12-05T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/build-and-tinker-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/disaster-recovery","url":"https://www.philipithomas.com/disaster-recovery","title":"Disaster recovery","content_html":"<p>I'm headed off on holiday later this week. As the trip approached, I found myself thinking about how to best back up my <a href=\"https://www.philipithomas.com/a-mini-data-center\">Mac Mini home server</a>.</p>\n<p>I've had an abnormal number of conversations about SOC 2 compliance this year, so “disaster recovery” has been a concept stuck in my mind. <a href=\"https://www.philipithomas.com/backup-plans\">Backup plans</a>, if you will.</p>\n<p>I've had a <a href=\"https://support.apple.com/en-us/104984\">Time Machine</a> external drive attached to the server, keeping a local backup - which would protect in case of disk failure. But many <a href=\"https://www.worldfinance.com/home/risk-encyclopaedia/correlated-risks\">correlated risks</a> exist - for instance, a fire would destroy the computer <em>and</em> its backup, permanently losing <a href=\"https://postcard.page\">Postcard</a> user accounts and blog subscribers<a href=\"#fn1\">[1]</a>.</p>\n<p>To feel confident, I wanted off-site backups of important data.</p>\n<p>I considered just paying for a service like <a href=\"https://www.backblaze.com/\">Backblaze</a> and calling it a day. But this has two issues. First, it goes against the spirit of my home data center, where I want to avoid recurring SaaS costs. Second, Backblaze isn't built to safely store databases - and I was afraid of data corruption issues.</p>\n<p>The core data I needed to back up were this blog and the <a href=\"https://en.wikipedia.org/wiki/PostgreSQL\">PostgreSQL</a> database that powers all my projects, including <a href=\"https://www.postcard.page\">Postcard</a>, <a href=\"https://booklet.group\">Booklet</a>, and <a href=\"https://www.philipithomas.com/app-of-ones-own\">\"Junk Drawer\"</a>.</p>\n<p>I was disappointed in how difficult it is to back up this self-hosted <a href=\"https://ghost.org\">Ghost</a> blog. Content, analytics, and subscribers have to each be seprately downloaded. Then, all of the post images on the website have to be copied manually from a folder on the server. Even then, there's no way to back up the site settings and blog comments.<a href=\"#fn2\">[2]</a> (Ghost sells a <a href=\"https://ghost.org/\">hosted product</a>, so I think they're not incentivized to help users who self-host their <a href=\"https://github.com/TryGhost/Ghost\">software</a>).</p>\n<p>I chose to export the blog to a private <a href=\"https://github.com/contraptionco\">GitHub</a> repo. Git isn't intended for storing websites, but at the scale of this project, it works fine. Part of the reason I chose GitHub was a bit macabre: I've been thinking about people's digital footprints after they die. Website domains are surprisingly ephemeral - <code>contraption.co</code> will stop working as soon as the first renewal payment is missed. But, a content archive on GitHub could last for a long time. So, I'm considering a “public archive” of Contraption posts on GitHub.<a href=\"#fn3\">[3]</a></p>\n<p>I edited my <a href=\"https://github.com/contraptionco/toolbox\">Toolbox configuration script</a> to incrementally export Ghost data to the private GitHub repo every hour. Alas, I got it working, and my Ghost blog is now securely backed up on GitHub every hour. In hindsight, Ghost's export features are a bit of a faff, and I should have directly exported the MySQL database.</p>\n<p>Next up was exporting the Postgres database powering all of my apps.</p>\n<p>As I dug in, I found that the databases were quite large due to dormant analytics data. Postcard sends a weekly email to accounts about how many visits their site has had that week, and Booklet aspires to do something similar. But years of page views were bloating the database, so I <a href=\"https://github.com/contraptionco/postcard/blob/main/app/jobs/cleanup_tracking_data_job.rb\">set up a 2-week retention period on analytics data</a>, which let me purge gigabytes from the databases before beginning backups<a href=\"#fn4\">[4]</a>.</p>\n<p>After that, I updated the same Toolbox script to export Postgres to <a href=\"https://aws.amazon.com/s3/\">Amazon S3</a> every single day. Its simple usage-based pricing matches Toolbox vibes better. If you're unfamiliar with Amazon S3, I consider it a modern wonder of the world. It's developer infrastructure for replicated storage, and it holds <a href=\"https://aws.amazon.com/blogs/aws/aws-pi-day-2024-use-your-data-to-power-generative-ai/\">350 trillion objects</a>. To put that in perspective - that's about 50,000 objects per human on Earth. (Does this also make you wonder about latent digital footprints for the deceased?)</p>\n<p>Finally, if there's one thing I've learned from SOC 2 discussions, it's that backups are only useful if you test them. So, to finish my whole backups adventure, I restored all of my applications on my laptop and logged into the personal accounts.</p>\n<p>With everything wired up and tested, I feel comfortable leaving the Mac Mini alone for a while. If my home data center were truly destroyed, recovery would still be a bit of a slog - probably a trip to the Apple Store and a few hours of reinstalling things. But at least I’ve already rehearsed the recovery process, and I know the important bits wouldn't go up in smoke with the hardware.</p>\n<p>This whole exercise also nudged me to zoom out. We collect an incredible amount of data, much of it sitting quietly in object stores and databases around the world. Some of it will outlive today's servers. Some of it will outlive us. As disaster recovery teams at companies work diligently to preserve our data, perhaps we can be more intentional about deciding which data we want to preserve ourselves.</p>\n<hr>\n<ol>\n<li>\n<p>Data loss sounds unlikely, but unfortunately <a href=\"https://www.reuters.com/world/asia-pacific/south-korea-restores-46-services-after-data-centre-fire-safety-minister-says-2025-09-29/\">it happens</a>. <a href=\"#fnref1\">↩︎</a></p>\n</li>\n<li>\n<p>I don't use Ghost's comments functionality, but this discourages me from using it. <a href=\"#fnref2\">↩︎</a></p>\n</li>\n<li>\n<p>My current backup includes members and drafts, which I wouldn't want in a public archive. <a href=\"#fnref3\">↩︎</a></p>\n</li>\n<li>\n<p>\"Wait, you decided to delete a ton of data <em>before</em> starting backups?\" <a href=\"#fnref4\">↩︎</a></p>\n</li>\n</ol>","summary":"I'm headed off on holiday later this week. As the trip approached, I found myself thinking about how to best back up my Mac Mini home server. I've had an abnormal number of conversations about SOC 2 compliance this year, so “disaster recovery” has been a concept stuck in my mind.","date_published":"2025-11-19T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/disaster-recovery-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/snail-mail","url":"https://www.philipithomas.com/snail-mail","title":"Snail mail","content_html":"<p>I’ve been getting back into analog media this year - reading paper books, listening to vinyl, subscribing to print magazines, even getting the Sunday <em>New York Times</em>.</p>\n<p>For some of the blogs I follow, such as <a href=\"https://craigmod.com/essays/popup_newsletters/\">Craig Mod pop-ups</a>, I’ve found myself craving physical copies - a stack of paper I can bring to a coffee shop. The trouble with email clients is that they’ve become our default to-do lists. When I open mine, my mind switches into frenetic bill-paying, package-tracking, and appointment-scheduling mode. So, when I’m in a reading mood - the last thing I want to open is my inbox.</p>\n<p>To get around that, I <a href=\"https://www.philipithomas.com/my-favorite-tool-for-making-email-less-stressful\">set up a separate email address (and app) just for newsletters</a>. But I still find myself craving paper - I don't like reading on my phone, and whipping out an iPad creates a coworking vibe.</p>\n<p>As an experiment, I tried sending a Contraption Company post to myself through the mail. I used the <a href=\"https://www.lob.com/\">Lob API</a>, which automates printing and postage. A few days later, I’d forgotten about it - until a letter from myself arrived. Every time I receive mail that isn’t a bill or junk, it sparks a small joy, and a bit of awe at the global postal system. <a href=\"#fn1\">[1]</a></p>\n<p>I’ve been off social media for over five years now. Every time I glimpse a feed, I think of <a href=\"https://en.wikipedia.org/wiki/Dead_Internet_theory\">Dead Internet Theory</a> - the idea that most content is generated by bots or optimized for algorithms. And, what human-generated content remains tends to be shallow and engagement-seeking. Amid the <a href=\"https://en.wikipedia.org/wiki/AI_slop\">slop</a>, personal blogs and newsletters feel like a last bastion of interesting short-form writing - spaces that can occupy a niche and stay there without bending toward a general audience.</p>\n<p>Reading on paper feels different. It’s a slower, more engaging medium. But I think it’s also better for writers — too much feedback creates blandness by enforcing an <a href=\"https://en.wikipedia.org/wiki/Overton_window\">Overton window</a>.</p>\n<p>Would you read a paper newsletter?</p>\n<blockquote>\n<p>“I kind of to some extent feel like it’s probably time to switch off the internet. I don’t think we’re allowed it. I don’t think it’s good for us… once we decided that everything should be algorithmically driven in terms of what we see, we ruined the internet for ourselves.”<br>\n- James Hoffmann, <a href=\"https://www.youtube.com/watch?v=sFA_iFeVhP4\"><em>The Coffee Show</em> with Kirk Pearson</a></p>\n</blockquote>\n<p>P. S. - I keep a list of blogs I like on my <a href=\"https://www.philipithomas.com/blogroll\">blogroll</a>.</p>\n<hr>\n<ol>\n<li>Thanks, Benjamin Franklin. <a href=\"#fnref1\">↩︎</a></li>\n</ol>","summary":"I’ve been getting back into analog media this year - reading paper books, listening to vinyl, subscribing to print magazines, even getting the Sunday New York Times.","date_published":"2025-11-11T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/snail-mail-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/maintenance","url":"https://www.philipithomas.com/maintenance","title":"Maintenance","content_html":"<p>I’m preparing to launch <a href=\"https://www.philipithomas.com/workshop\">Workshop</a> here as an independent newsletter within Contraption Company. I <a href=\"https://www.philipithomas.com/workshop-welcome\">started it as an experiment</a>, and I’m ready to graduate it from “experiment” to “project.”</p>\n<p>Before doing that, I wanted to clean up this website and a few projects that had fallen into disrepair.</p>\n<p>A few weeks ago, I launched the <a href=\"https://www.philipithomas.com/mcp-and-the-future-of-ai\">Contraption Company MCP</a>. Well, surprise - this project was secretly a test of the new <a href=\"https://docs.trychroma.com/cloud/schema/sparse-vector-search\">Chroma Cloud sparse vector support</a>, which we launched yesterday. I did some maintenance to upgrade it from test to production APIs. If you peek at the code, you’ll see it uses the new <a href=\"https://docs.trychroma.com/cloud/schema/overview\">Schema support</a>, <a href=\"https://github.com/chroma-core/chroma/blob/47287c40804fd6d6816d3dd188fe5de82fcdae48/chromadb/utils/embedding_functions/chroma_cloud_splade_embedding_function.py#L15\">hosted embedding functions</a>, the <a href=\"https://docs.trychroma.com/cloud/search-api/overview\">new Search API</a>, and <a href=\"https://docs.trychroma.com/cloud/search-api/hybrid-search\">RRF hybrid search</a>. Sparse vectors enable embedding functions like BM25 and SPLADE that add strong “keyword” matching to searches, significantly improving accuracy - especially when combined with dense vectors via in a hybrid search. Chroma Cloud is the only object-storage-backed database with generic sparse vector support, and this MCP server is a great use case of the technology.</p>\n<p>Back by popular demand—<a href=\"https://quesogpt.contraption.co\">QuesoGPT</a>! At a meetup earlier this year, I presented <a href=\"https://www.philipithomas.com/chat-with-my-dog\">QuesoGPT as a chatbot featuring my dog</a>. Many people enjoyed it, began mentioning to me that it was no longer online. As a quick hack, I hadn’t set up proper hosting - and it broke and went offline at some point. So I updated the code to be a bit more performant and set it up as a container on my <a href=\"https://www.philipithomas.com/a-mini-data-center\">Mac Mini home server</a> so it should stay online indefinitely.</p>\n<p>A few months ago, I <a href=\"https://www.philipithomas.com/postcard-open-source\">open-sourced Postcard</a>. As people keep signing up for the hosted service, they didn’t always realize Postcard is open source. I <a href=\"https://postcard.page\">updated the homepage</a> to make that clear. Please <a href=\"https://github.com/contraptionco/postcard/issues/new\">open issues on GitHub</a> with any feature requests - I’d like to make some improvements soon.</p>\n<p>I committed the first <a href=\"https://booklet.group\">Booklet</a> code in about eight months. I added the ability for admins to “quarantine” posts manually - excluding them from the newsletter without deleting them. <a href=\"https://www.philipithomas.com/introducing-frctnl\">FRCTNL</a> has now been running for over two years, and I needed better moderation tools for it. I enjoyed working on Booklet again—it’s a fun project, and I’d like to open-source it and keep tinkering.</p>\n<p>I updated the Contraption Company site. Posts in the Workshop newsletter (like this one!) now show the Workshop logo in the header. I refreshed the monospace font on the site. And I updated the <a href=\"https://www.philipithomas.com/projects\">Projects page</a> to show more of my work. This turned into a fun use case for the Contraption MCP server—I had my coding agent use it to research projects mentioned in Contraption Company posts and add them to the Projects page, which worked surprisingly well.</p>\n<p>Next up: introducing Workshop as a standalone newsletter - and some other tinkering.</p>","summary":"I’m preparing to launch Workshop here as an independent newsletter within Contraption Company.","date_published":"2025-10-30T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/maintenance-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/backup-plans","url":"https://www.philipithomas.com/backup-plans","title":"Backup plans","content_html":"<p>I’ve always been fascinated by planes. A few years ago, I took an introductory class toward a private pilot’s license - and it was less fun than I expected. The plane pretty much flies itself; the pilot’s real job is to anticipate and plan for emergencies.</p>\n<p>“If the engine fails on takeoff roll, we stop. Below 500 feet, we turn back. Over there, there’s a golf course we can land on. Down there, a park.”</p>\n<p>Flying, it turns out, is less about control, and more about preparedness. That felt more like work than leisure, so I didn’t continue flying.</p>\n<p>This morning, I woke up and my iPhone wouldn’t turn on. After a bit of debugging, I accepted it was dead. Then the dread set in: how would I get to work without my digital Clipper card? Could I call an Uber? Would I even be able to find the Apple Store without a map? What if PagerDuty goes off right now?</p>\n<p>For a moment, I felt useless.</p>\n<p>Then I remembered that I had a backup plan. When I was a digital nomad, I learned to take disaster recovery seriously. I had stashed an old phone in my suitcase, loaded with a data-only SIM and ready to go. I powered it on, ran some updates, and my digital life was back online within minutes.</p>\n<p>At the Apple Store, they told me the repair would take three days. Without that backup phone in hand, I probably would have impulse-bought a new phone and wasted money.</p>\n<p>As a user, you want your tools to never break. As an engineer, you just don’t want to be surprised when they do.</p>","summary":"I’ve always been fascinated by planes. A few years ago, I took an introductory class toward a private pilot’s license - and it was less fun than I expected. The plane pretty much flies itself; the pilot’s real job is to anticipate and plan for emergencies.","date_published":"2025-10-28T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/backup-plans-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/git","url":"https://www.philipithomas.com/git","title":"GitHub syncing","content_html":"<p>Something I worked on just launched: <a href=\"https://www.trychroma.com/changelog/introducing-chroma-sync\">Chroma GitHub Sync</a>.</p>\n<p>I love the mental model of businesses having a \"shadow business\" - a non-obvious skill they must master to achieve their main goal. My favorite example is the clothing rental company Rent the Runway, which also happens to be <a href=\"https://d3.harvard.edu/platform-rctom/submission/rent-the-runway-digitizes-high-fashion/?utm_source=chatgpt.com\">the largest dry cleaner in the country</a>. Other examples include payment processors, which must build strong fraud detection systems, and insurance companies, which must excel at <a href=\"https://finmasters.com/warren-buffett-insurance-float/\">float management</a>.</p>\n<p>One of the top uses of AI today is writing and fixing code. To do that effectively, AI needs context on the existing codebase - which is often large, complex, and difficult to traverse. Many people have been using <a href=\"https://trychroma.com\">Chroma</a> to index code so their agents can search it. It turns out that, for anyone building AI integrations with code today, the real challenge is not AI itself - it’s downloading code, making it searchable, and keeping it up to date. We heard from many coding agent startups that most of their engineering time wasn’t spent on AI, but on making their code searchable in the first place.</p>\n<p>At Chroma, we’ve now published code ingestion as a service. The core offering is a <a href=\"https://docs.trychroma.com/cloud/sync/overview#platform-sync\">platform</a> that connects to your custom GitHub app and lets you sync customer repositories. It follows best-in-class chunking and embedding logic, and uses <a href=\"https://www.trychroma.com/changelog/forking\">collection forking</a> to keep costs low and speed high.</p>\n<p>I didn’t work on the sync system directly - but I became the first “customer” of this syncing platform while building the Chroma Cloud dashboard. We wanted to create an “easy mode” of GitHub Sync for Chroma Cloud users, using the Chroma Cloud GitHub app. That way, customers could quickly sync their own code without setting up a GitHub app themselves. In effect, the Chroma Cloud dashboard acted like a normal user of Cloud Sync, letting us dogfood the product while shipping a great feature.</p>\n<p>Try it out - I think it’s a very cool product. Chroma built <a href=\"https://www.trychroma.com/package-search\">Package Search MCP</a> with this infrastructure, and now that the barrier to helping AI understand code is lower, there are many interesting use cases.</p>\n<p>For instance, I’m contemplating a “Support Q&#x26;A bot” for <a href=\"https://postcard.page\">Postcard</a>, which would have access to the raw source code through Chroma and could answer questions like “How do I reset my password?” or “Is my site optimized for mobile?”</p>\n<p>We’re in the <a href=\"https://avc.com/2015/02/the-carlota-perez-framework/\">installation phase of AI</a>: the core technology has matured, and now there’s so much work to do to make it useful.</p>","summary":"Something I worked on just launched: Chroma GitHub Sync. I love the mental model of businesses having a \"shadow business\" - a non-obvious skill they must master to achieve their main goal.","date_published":"2025-10-23T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/git-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/hacking-yc-again","url":"https://www.philipithomas.com/hacking-yc-again","title":"Hacking YC (again)","content_html":"<p>I like seeing how things work. Digitally, that often means keeping the network tab open to watch how websites talk to servers. Sometimes, I find mistakes.</p>\n<p>Last month, while browsing <a href=\"https://ycombinator.com\">Y Combinator</a>’s software, I noticed my browser was loading data it shouldn’t have. An API call exposed an investor-only feed with confidential information about YC startups. I checked and confirmed that others had access to it as well.</p>\n<p>I reported the issue to the YC security team, who quickly fixed the authorization bug.</p>\n<p>This was the third vulnerability I’ve found in YC’s software. The previous two are listed on <a href=\"https://ycombinator.com/security\">their security page</a>, though the new one isn’t credited there<a href=\"#fn1\">[1]</a>. But, for the first time, they sent me a bounty: $500.</p>\n<p>None of the three issues I reported were technically advanced. They only required curiosity and noticing when something looked out of place. This also highlights the importance (and difficulty) of building robust authorization logic into applications.</p>\n<hr>\n<ol>\n<li>I asked YC about credit on the security page, and they didn't respond. It's possible they no longer give public credit. <a href=\"#fnref1\">↩︎</a></li>\n</ol>","summary":"I like seeing how things work. Digitally, that often means keeping the network tab open to watch how websites talk to servers. Sometimes, I find mistakes. Last month, while browsing Y Combinator’s software, I noticed my browser was loading data it shouldn’t have.","date_published":"2025-10-01T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/hacking-yc-again-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/taste-skill-gaps","url":"https://www.philipithomas.com/taste-skill-gaps","title":"Taste-skill gaps","content_html":"<p>When I saw <a href=\"https://craigmod.com\">Craig Mod</a> speak in San Francisco this summer, he mentioned an concept that stuck with me. I don’t remember his exact context, only that he was new to something and knew his work wasn’t good yet, but didn’t know how to improve it - which he explained as a \"taste-skill gap\".</p>\n<p>I’ve since used that framework as a lens for work and life.</p>\n<p>I usually mean it as “taste exceeds skill.” A backend developer sees a page looks off, tries to fix it, and makes it worse. That’s a taste–skill gap. Closing it takes practice and feedback<a href=\"#fn1\">[1]</a>.</p>\n<p>The inverse happens, too: skill exceeds taste. With AI, a novice can ship a feature exactly as imagined, yet the choice may be in poor taste - for example, a blinking homepage banner.</p>\n<p>When work misses expectations, I ask: is this a skill gap or a taste gap? Skills respond to repetition and feedback. Taste improves by studying good work.</p>\n<hr>\n<ol>\n<li>IRL <a href=\"https://en.wikipedia.org/wiki/Reinforcement_learning_from_human_feedback\">RLHF</a> <a href=\"#fnref1\">↩︎</a></li>\n</ol>","summary":"When I saw Craig Mod speak in San Francisco this summer, he mentioned an concept that stuck with me.","date_published":"2025-08-29T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/taste-skill-gaps-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/joys-of-self-hosting","url":"https://www.philipithomas.com/joys-of-self-hosting","title":"The joys of self-hosting","content_html":"<p>When I checked my phone this morning, the first notification was that my <a href=\"https://www.philipithomas.com/a-mini-data-center\">home server \"Toolbox\"</a> was down. Everything on it was offline - <a href=\"https://postcard.page\">Postcard</a>, <a href=\"https://booklet.group\">Booklet</a>, this blog, and more.</p>\n<p>I pulled out my laptop, and began investigating the outage with some coffee <a href=\"#fn1\">[1]</a>. I could SSH in, so that meant the computer was online.</p>\n<p>It seemed unusual that every single app was down. That could typically indicate a database outage. But, this blog and my apps use separate databases - so I ruled that out.</p>\n<p>I checked the logs, and saw that none of the apps were receiving any traffic. I dove down a rabbit hole of debugging the <a href=\"https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/\">Cloudflare Tunnel</a> that routes traffic to the apps, because that seemed to make sense as the common point of failiure. But, after awhile I realized - the tunnel was working fine.</p>\n<p>Stuck, I looked at the stats on the machine. I thought that the <a href=\"https://www.philipithomas.com/adding-a-brain-to-my-toolbox\">AI I had installed might have gone haywire</a>. But, memory was fine. CPU was fine. Then, I found it. The disk was full.</p>\n<p>With the help of ChatGPT, I debugged the full disk. What had filled up a terabyte of space? I quickly narrowed in on Docker as the problem. <a href=\"https://en.wikipedia.org/wiki/Docker_(software)\">Docker</a> is a common tool for running apps in isolated containers. <a href=\"https://forum.gitlab.com/t/docker-images-taking-up-disk-space/41717/2\">Docker is known to hoard disk space</a>, so I began running some common commands to clean up old images. That got 50 gigabytes back, which restored functionality to the apps. But, 400 gigs were still being used. Ugh.</p>\n<p>Continuing my investigation, I realized my <a href=\"https://github.com/bbernhard/signal-cli-rest-api\">Signal API proxy</a> was the app hoarding space. I've used this service to send myself notifications on the Signal Messenger for months, but I began accessing it more frequently as I <a href=\"https://www.philipithomas.com/claude-launching-and-tools\">connected Signal to my local AI</a>. My AI code polls Signal every few seconds now to check for incoming messages. And, it turns out that the library I use has a bug that <a href=\"https://github.com/bbernhard/signal-cli-rest-api/issues/688\">writes to disk way too often</a>, quickly taking up 400 gigabytes of space.</p>\n<p>The short-term solution was simple: I deleted the container. It runs in a way where it re-boots with the correct configuration almost immediately. The Toolbox should be able to run smoothly for another week or two before the disk fills up again.</p>\n<p>Now, I need to figure out the long-term fix. As a junior coder, I would have been lazy - delete the container every day, automatically. As a mid-career coder, I would have rewritten the system to be more efficient to avoid filling up the disk. But, with my experience today, I will probably take the same approach as my earlycareer self. Over-engineering the solution creates no benefit for the end user (me), and could introduce new bugs. My time is better spent elsewhere. <a href=\"#fn2\">[2]</a></p>\n<p>Honestly, enjoyed fixing this outage this morning. It's fun to maintain a little server. Every day has the potential for amusing side quests.</p>\n<hr>\n<ol>\n<li>\n<p>A delicious Honduran Bourbon from <a href=\"https://timwendelboe.no/\">Tim Wendelboe</a>. <a href=\"#fnref1\">↩︎</a></p>\n</li>\n<li>\n<p>Nobody wants to admit it, but the internet is held together by duct tape. <a href=\"#fnref2\">↩︎</a></p>\n</li>\n</ol>","summary":"When I checked my phone this morning, the first notification was that my home server \"Toolbox\" was down. Everything on it was offline - Postcard, Booklet, this blog, and more. I pulled out my laptop, and began investigating the outage with some coffee [1].","date_published":"2025-08-27T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/joys-of-self-hosting-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/claude-launching-and-tools","url":"https://www.philipithomas.com/claude-launching-and-tools","title":"Claude, Launching, and Tools","content_html":"<p>For the last week, I’ve been leaning into Claude Code for development. I used to think of it as more of a hobbyist tool, rather than a professional one.</p>\n<p>Cursor had been the main way I used AI to write code professionally. It's an AI-augmented text editor- you see code and can be prescriptive about which files the AI should read and what it should do. Using it, I feel like I‘m an engineer. I know the code I want written, and I’m supervising Cursor to build the way I expect.</p>\n<p>Claude Code is a more autonomous - a chatbot rather than a text editor. It isn’t optimized for supervising the exact code that’s been written. When using Claude Code, I feel like a product manager - I inspect the results, instead of the methods. Does the page look right and behave how I want?</p>\n<p>Each tool has its place. I tend to use Cursor for more backend code, where security and implementation details matter, and Claude Code for UI, where work tends to be more constrained to one or two files.</p>\n<hr>\n<p>This week we launched <a href=\"https://trychroma.com/cloud\">Chroma Cloud</a>, the product I’ve been working on since I joined Chroma in January. It’s a search engine for AI applications.</p>\n<p>On Friday, I found out that there had been a miscommunication, and the landing page <a href=\"https://trychroma.com/cloud\">trychroma.com/cloud</a> had not been started yet. We had a design in Figma, but just one business day to build a complex, high-polish, and high-profile landing page.</p>\n<p>I took over building the page, and tried using Claude Code. I worked section by section, and passed screenshots to Claude to have it build the UI. I looked at the results in my web browser, and provided UI feedback instead of code-level feedback. There were some tricky interactions, mobile responsiveness concerns, and lots of text. I built the entire page in about four hours.</p>\n<p>Yes, this kind of work would have taken me a week prior to AI. But, even three months ago - I would have had to take a more low-level, prescriptive approach to building this page - probably using Cursor.</p>\n<p>AI models have gotten so good that I barely looked at the code. I could work at a higher level of abstraction.</p>\n<hr>\n<p>I'm currently exploring tools for LLMs.</p>\n<p>Tools are functions that LLMs can call. ChatGPT has tools - memory, web search, and code execution. Cursor and Claude Code have tools for viewing files, searching, editing, and running commands.</p>\n<p>I think we've only begun to scratch the surface of how to use tools with LLMs. My hypothesis is that we're building only generic tools today. But, each person or business may begin building custom tools for agents.</p>\n<p>In <a href=\"https://www.philipithomas.com/adding-a-brain-to-my-toolbox\"><em>Adding a brain to my toolbox</em></a>, I wrote about running a local LLM on my home server. Since then, I set up Signal, and hooked it into the LLM. So, now I can have an (encrypted) conversation with my home server LLM via chat.</p>\n<p><img src=\"https://www.philipithomas.com/images/posts/claude-launching-and-tools/Screenshot-2025-08-19-at-20.14.37.png\" alt=\"Signal chat with the home server LLM answering a greeting\"></p>\n<p>Yesterday, I began giving the LLM access to tools. As a proof of concept, I let it add items to my todo list (via the <a href=\"https://culturedcode.com/things/support/articles/2908262/\">Things</a>), and the ability to look up the weather.</p>\n<p><img src=\"https://www.philipithomas.com/images/posts/claude-launching-and-tools/Screenshot-2025-08-20-at-09.31.01.png\" alt=\"Chat where the LLM checks the weather and adds a todo\"></p>\n<p>The LLM can handle basic tasks pretty well. But, I still need to figure out how to do chained or compound tasks. For instance, I can say \"Add buying milk to my todo list\", but I can't say \"add 'bring an umbrella' to my todo list if it's going to rain tomorrow.\"</p>\n<p>The question I keep asking is, \"What would be a fun tool?\" Things that may be a little too dangerous for mainstream users. For instance, could I give a tool for it to send emails? Access to all of my past emails? Read code I've written? The ability to query <a href=\"https://postcard.page\">Postcard</a> or <a href=\"https://booklet.group\">Booklet</a> databases?</p>\n<p>Tools may also be dynamically available based on context. Perhaps customer-facing chatbots have access to limited tools, while employees may have more sensitive tool access.</p>\n<p>The hypothesis I have emerging is that people and companies may begin maintaining their own libraries of tools for personal LLMs. Generic tools, like web search, are everywhere. But, perhaps specialized tools are the future.</p>","summary":"For the last week, I’ve been leaning into Claude Code for development. I used to think of it as more of a hobbyist tool, rather than a professional one. Cursor had been the main way I used AI to write code professionally.","date_published":"2025-08-20T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/claude-launching-and-tools-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/adding-a-brain-to-my-toolbox","url":"https://www.philipithomas.com/adding-a-brain-to-my-toolbox","title":"Adding a brain to my toolbox","content_html":"<p>One of my favorite projects has been <a href=\"https://www.philipithomas.com/a-mini-data-center\">using my Mac Mini as a home data center</a>. It hosts this blog and all of my projects. After using cloud machines for decades, there's something tactile about maintaining this machine myself. Plus, it gives me agency - it's my computer, and I don't have to pay rent to somebody every month to keep using it.</p>\n<p>For the past few months, I have been contemplating running a local LLM in my home data center. OpenAI and Anthropic's AI is a service with usage-based pricing. That didn't quite fit the philosophy of my home data center. I didn't want to have to worry about what my OpenAI bill would be at the end of the month for my home projects.</p>\n<p>There's something constraining about having to bean-count every AI request. Programmers aren't used to it today - compute is abundant. Having to pay for every single AI request discourages developers from playing with AI. And, <a href=\"https://www.amazon.com/Feel-Good-Productivity-More-What-Matters/dp/1250865034?_encoding=UTF8&#x26;dib_tag=se&#x26;dib=eyJ2IjoiMSJ9.3zJv5Pn8Vi2RfyaIJCZtCMzLcx-IwigoeOT5cy9Fy_reCmZwEwAUk70Gy_Nvr6WldsODN_up74gmwp5KNmJkODUo0MYQe34F4zplBh_UqkTQTWsppKvx3KsjP1fTJ4WpbEEPEUQaalPibwp7mDYsMJ7ZasIGfV5aUgX5fyWw7LoIHTN2Jw6FVCfnWyl7IKNyrOV8gSKkATUdlQNw0sAzrLYAKFO6rhalw4IKTVScXQk.dbBYPgx5xbFZGTIkui9LzLv09-Xql3KEzDsP1XG2hN8&#x26;qid=1755275497&#x26;sr=8-1&#x26;linkCode=ll1&#x26;tag=contraption-20&#x26;linkId=14efed630a20cde07870acf21e497505&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">play is when good ideas happen</a>.</p>\n<p>As powerful and disruptive as AI has been, it's really incredible that some models are open, so you can run them on your own machines. The problem is that my home computer is too small to run most models. I considered getting an external GPU or a second, bigger computer - but that started to seem work like work instead of fun.</p>\n<p>When <a href=\"https://openai.com/index/introducing-gpt-oss/\">OpenAI released their open-source models</a> last week, inspiration struck. The smaller of their two models could run on my Mac Mini. So, I quickly installed it with <a href=\"https://ollama.com/\">Ollama</a> and switched my <a href=\"https://contraption.co/projects\">projects</a> to use the local AI. My Toolbox now has a brain, and it's pretty smart.</p>\n<p>I'm now entering a phase of experimenting with AI. I can put it everywhere, and not worry about a surprise bill. I can experiment with giving it tools - access to my <a href=\"https://postcard.page\">Postcard</a> database, or the ability to send an email. I can do dumb and irresponsible things - like have it go through every email I've sent over the last 20 years to apply labels or analyze questions.</p>\n<p>Having a local LLM has freed me to experiment. And, I'm looking forward to sharing some of my projects and learnings along the way.</p>","summary":"One of my favorite projects has been using my Mac Mini as a home data center. It hosts this blog and all of my projects. After using cloud machines for decades, there's something tactile about maintaining this machine myself.","date_published":"2025-08-15T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/adding-a-brain-to-my-toolbox-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/rethink-chat","url":"https://www.philipithomas.com/rethink-chat","title":"It's time to rethink chat","content_html":"<p>Today's chat tools feel dated. It's time for a startup to rethink them.</p>\n<p><a href=\"https://notion.so\">Notion</a> is an example of a team firing on all cylinders to become AI-native. But, no communication tools seem to have cracked AI.</p>\n<p>I believe there's an opportunity for a startup to take on chat. Something built from the ground-up with AI. Something that becomes \"ChatGPT for teams\", growing in knowledge as teams use it, and self-training AI agents for the entire team.</p>\n<p>Imagine opening the app, clicking the universal \"Compose\" button to type “the app seems down”, then the AI directing you to the <code>oncall</code> channel, explaining that there’s already a thread going, and drafting a recommended message for you to post <code>+1 - I'm also unable to access the site.</code></p>\n<p>I'm not going to build this. But, if I were going to - here would be my wishlist:</p>\n<ul>\n<li>A personal AI bot who can run searches for you and return results, like \"find and summarizes all ideas to improve our pricing page\"</li>\n<li>Live proofreading of messages</li>\n<li>AI image generation inline</li>\n<li>Smart \"to-do\" list, including being able to assign to-dos to other people</li>\n<li>Detecting when somebody is waiting on a response from you, and adding it to your to-do list</li>\n<li>AI titles and summaries of ongoing threads</li>\n<li>Detecting when discussions have reached a decision, posting the outcome in the channel, and closing the thread</li>\n<li>Allow users to make their own bots, including knowledge, tools, and prompt-level controls - no <a href=\"https://koomen.dev/essays/horseless-carriages/\">AI horseless carriage</a>\n<ul>\n<li>Marketing team can train their bot on guidelines, like \"use oxford commas\", and other teams can ask questions of it</li>\n<li>Engineering team can make a moderation bot, which auto-removes offtopic posts from their <code>oncall</code> channel</li>\n<li>Product team can have a bot that tags the relevant PM in every support disucssion - \"Sales team is talking about a feature you work on\"</li>\n<li>HR team can build a bot that auto-labels discussions in its channel, such as \"Question\" or \"Feedback\"</li>\n<li>Execs can have a bot that sends a daily summary of discussions across teams (\"Here were biggest customer support issues yesterday), but can escalate high-priority discussions in real-time</li>\n</ul>\n</li>\n<li>Prompt-level control of which notifications you want from which channel</li>\n<li>Full support for users without photos (showing initials instead of blob photos)</li>\n<li><a href=\"https://world.hey.com/jason/the-presence-prison-69608e0f/\">No presence indicator</a> because talking about work is not a discussion for doing work</li>\n<li>Shared channels across teams</li>\n</ul>","summary":"Today's chat tools feel dated. It's time for a startup to rethink them. Notion is an example of a team firing on all cylinders to become AI-native. But, no communication tools seem to have cracked AI. I believe there's an opportunity for a startup to take on chat.","date_published":"2025-08-14T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/rethink-chat-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/software-is-bread-not-burgers","url":"https://www.philipithomas.com/software-is-bread-not-burgers","title":"Software is bread, not burgers","content_html":"<p>I was reading the book <a href=\"https://www.amazon.com/Richard-Hart-Bread-Intuitive-Sourdough/dp/0593234294?crid=1QRLZCF5RH3QN&#x26;dib=eyJ2IjoiMSJ9.C6-HPik5CSK2AD06_URRtVfb7dpsY3cSkRh9ML6mpjfGjHj071QN20LucGBJIEps.36P0CJ8-PxyNGcyikCRhVEpHtLoKmzqKiCGfKKRL9WQ&#x26;dib_tag=se&#x26;keywords=bread+hart&#x26;qid=1754698502&#x26;sprefix=bread+har%2Caps%2C140&#x26;sr=8-1&#x26;linkCode=ll1&#x26;tag=contraption-20&#x26;linkId=f443f68a77ef647845032663b15079b4&#x26;language=en_US&#x26;ref_=as_li_ss_tl\">Richard Hart Bread</a> last night when I came across this paragraph:</p>\n<blockquote>\n<p>Absolute devotion to consistent methods and exact recipes is useful when you’re running a fast-food franchise or a fine dining kitchen, but when you’re baking bread, the most important thing is to understand your dough and be ready to make adjustments.</p>\n</blockquote>\n<p>It struck me as a good analogy for software.</p>\n<p>Some developers treat software work as factory job — assuming clear specifications, unambiguous instructions, and a clear “done” point on each task. This is not unlike making burgers at McDonald’s, where work is procedural and unambiguous.</p>\n<p>But software doesn't work that way. Designs are directional, not final. Implementation often reveals new complexities. Even after you think you’ve finished, there’s usually more to refine. Throughout the process, a developer must rely on intuition, and adapt dynamically.</p>\n<p>Software is bread, not burgers.</p>","summary":"I was reading the book Richard Hart Bread last night when I came across this paragraph: Absolute devotion to consistent methods and exact recipes is useful when you’re running a fast-food franchise or a fine dining kitchen, but when you’re baking bread, the most important thing...","date_published":"2025-08-09T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/software-is-bread-not-burgers-cover.jpg","tags":["workshop"]},{"id":"https://www.philipithomas.com/my-favorite-tool-for-making-email-less-stressful","url":"https://www.philipithomas.com/my-favorite-tool-for-making-email-less-stressful","title":"My favorite tool for making email less stressful","content_html":"<p>Email has become a to-do list—bills, questions, scheduling requests, expense reports, and more. Opening it can induce stress.</p>\n<p>Amid these tasks were email newsletters—long-form pieces I wanted to read. In that context, the newsletters felt like tasks to work through. I felt compelled to clear them quickly to maintain inbox zero, and the reading experience became stressful rather than fun.</p>\n<p><a href=\"https://calnewport.com/contact/\">Inspired by Cal Newport</a>, I set up a separate address for newsletters: <strong><a href=\"mailto:interesting@philipithomas.com\">interesting@philipithomas.com</a></strong>. I used my iCloud address—the free one Apple includes with your storage that few people use. I access this inbox with a separate app: Superhuman for transactional email, Apple Mail for newsletters. I keep Apple Mail only on an iPad by a cozy chair and on my personal laptop. It’s not on my phone or work computer.</p>\n<p>Six months later, this change has made newsletters enjoyable again. I read them in my free time, with no tasks mixed in. There’s no pressure to achieve inbox zero—the inbox probably has a thousand items, and that’s fine.</p>\n<p>If you face email overload, try a newsletter-only inbox that you access with a separate device.</p>\n<p>P. S. - I also recommend setting up filters in Gmail to auto-archive receipts. Do you really need to read every email from \"Uber Receipts\"? Have those skip the inbox and save yourself some context shifting.</p>","summary":"Email has become a to-do list—bills, questions, scheduling requests, expense reports, and more. Opening it can induce stress. Amid these tasks were email newsletters—long-form pieces I wanted to read. In that context, the newsletters felt like tasks to work through.","date_published":"2025-08-05T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/my-favorite-tool-for-making-email-less-stressful-cover.JPG","tags":["workshop"]},{"id":"https://www.philipithomas.com/workshop-welcome","url":"https://www.philipithomas.com/workshop-welcome","title":"Welcome to Workshop","content_html":"<p>Welcome to a new project: a members-only newsletter experiment on Contraption Company called <a href=\"https://www.philipithomas.com/workshop\">Workshop</a>.</p>\n<p>So far this year, 98k people have read essays on this site<a href=\"#fn1\">[1]</a>, and more than 4k subscribe to its email updates. Posts such as <a href=\"https://www.philipithomas.com/postcard-open-source\">Postcard is now open source</a> <em>(48k readers)</em>, <a href=\"https://www.philipithomas.com/rails-versus-nextjs\">Why Ruby on Rails still matters</a> <em>(39k readers)</em>, and <a href=\"https://www.philipithomas.com/a-mini-data-center\">A mini data center</a> <em>(11k readers)</em> drive most of that traction. Each focuses on a digital tool I had just finished.</p>\n<p>I’m proud of these anchor pieces, but they raise the bar for what I feel comfortable publishing. I want to write shorter posts that dive into the Postcard codebase, sketch future projects, or document a tool while I build it. My main newsletter doesn’t feel like the right channel for that, and I have been hesitant to ramble to so many people.</p>\n<p>That’s where Workshop comes in. Here I’ll bloviate publish about the process of crafting digital tools. Expect more frequent updates on what I’m building. I hope the conversation around building these projects leads to more tools, which I can showcase in the main newsletter.</p>\n<p>So, here goes nothing.</p>\n<p>Some other updates:</p>\n<ul>\n<li>With some help from ChatGPT, I <a href=\"https://github.com/contraptionco/toolbox\">added a logo to Toolbox</a></li>\n<li>I updated the [Projects] page on this site to show Github stars for open-source tools. (Postcard passed 500 stars!)</li>\n<li>I made a <a href=\"https://contraption.co/workshop/\">Workshop logo and page</a>, which uses a font I discovered in a piece about the branding of <a href=\"https://gretelny.com/hart-bageri\">Hart Bageri</a></li>\n</ul>\n<hr>\n<ol>\n<li>My analytics are open - check them out: <a href=\"https://contraption.co/live-analytics/\">https://contraption.co/live-analytics/</a> <a href=\"#fnref1\">↩︎</a></li>\n</ol>","summary":"Welcome to a new project: a members-only newsletter experiment on Contraption Company called Workshop. So far this year, 98k people have read essays on this site[1], and more than 4k subscribe to its email updates.","date_published":"2025-08-02T00:00:00.000Z","image":"https://www.philipithomas.com/images/covers/workshop-welcome-cover.jpg","tags":["workshop"]}]}