# Matt Palmer - Personal Website Matt Palmer is a Developer Relations professional focused on AI devtools, developer education, and making technical concepts accessible. ## Content /posts # Custom desktop DSLR mount September 20, 2026 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) [Image: The camera and the monitor sharing one arm, seen from the window side] (/img/HBAknVjOUR-640.jpeg) [Image: Sony A7 IV in a cage on a cheese plate, mounted to an Ergotron monitor arm above the desk, with the dummy battery adapter hanging below and lenses on the desk] (/img/EVY2Tn_YVa-640.jpeg) I've shot content on a Sony Alpha 7 IV (https://amzn.to/3GEJvaS) for years, but I'd yet to find a good solution for mounting the camera - until today. 95% of my content is talking-head, so I wanted something mounted to the desk. This is necessary because of space constraints, but it's also nice with a standing desk - I can get the same shot seated or standing. Unfortunately, most desk mounts are cheap and underbuilt (https://amzn.to/4y18Ilj). These mounts are not only flimsy but rigid - any movement of the desk will cause the camera to shake. But it turns out we've already gotten quite good at mounting certain things to desks - monitors. Here's how I built a rock-solid DSLR mount with a VESA monitor mount. ## The setup (#the-setup) Monitor arms are one of those things it's worth spending a few extra bucks on: I'm willing to pay a bit extra so the arm is worth 1/10th of the thing it's holding instead of 1/100th. I use Ergotron (https://amzn.to/4hcF7y7) monitor arms, but any arm will work. Parts: - Monitor arm (https://amzn.to/4hcF7y7) - L-shape cheese plate (https://amzn.to/4jfTqEJ) (inedible) - Ball mount (https://amzn.to/4hf7tb5) with quick-release head - Screw kit (https://amzn.to/4Apk5VN) - M4 nuts (https://amzn.to/46B8Gog) The cheese plate mounts to the VESA and acts as a secure platform for my camera. This allows me to mount both my camera (via a ball mount) and monitor (https://amzn.to/4rGA665). Because the Ergotron is a premium mount, rated for a heavier weight, the camera is completely secure. With a gas piston, the camera movement is smooth and precise. I can even rotate the arm, which provides more precision and a greater range of motion than alternate solutions. Finally, because the arm has a piston for adjustment, it absorbs more force and tiny movements from the desk, resulting in fewer vibrations while shooting. It's funny how the perfect solution can sit unnoticed for years - I found one today! You can read more about my setup here (/setup). /posts # 52 weeks of changelogs December 27, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) For my entire career, there have been two types of work: boring and interesting. Boring tasks have fallen into two categories: automatable and unautomatable. [Image: Chart showing the evolution of work automation constraints over time] (/img/posts/2025/12/claude-agent-changelog/chart-light.svg) For the last half-decade, I've been automating the boring parts of my work, recognizing that many automations have been limited by my free time, skill, or technology. There has been a relatively small overlap between boring and automatable work. [Image: Work distribution before 2026] (/img/posts/2025/12/claude-agent-changelog/before-2026-light.svg) But these constraints no longer apply. In the last 6 months, I've had to challenge myself to rethink what's possible. I'm continually trying to increase the amount of time I spend in an _enthusiastic_ state, with the hypothesis that we produce our highest and best work when we're creating from a place of passion and love. The great news is: AI is allowing us to do just that. A great example is writing changelogs. This has been a largely unautomatable, recurring task with a majority of undifferentiated work and a sprinkle of skill. Changelogs have been a thorn in my side since I became a DevRel, and I'd be lying if I said I didn't dread writing them. In 2024/2025 we shipped 52 consecutive weekly changelogs (https://docs.replit.com/updates?utm_source=Matt&utm_medium=matts-blog&utm_campaign=blog). This is how we did it with the Claude Agent SDK. 1 (#user-content-fn-1) 2 (#user-content-fn-2) ## Mindset (#mindset) These changelogs aren't meant to ship without review. The AI drafts with **me** as the audience, not our users. It includes images, links to Slack threads, and internal context. Then I do the editorial work: crafting story and tone, mostly by cutting what's internal or irrelevant. Early iterations _were_ written for our users, but I found myself delegating critical judgment calls to an agent: what to omit, what to include, what to emphasize. By framing myself as the audience, I'm first learning what we built, then communicating that to users as clearly as possible. That's the differentiated skill I bring as a professional... and the entire point of using an agent: isolate undifferentiated work and delegate it. Changelogs _are_ important and they _do_ require skill, it's just that much of the work is boring: our goal is to isolate that which is not. ## Coding agents (#coding-agents) Perhaps unsurprisingly, _coding_ agents turned out to be the best fit for the task. Marketing-specific (and even workflow-building) tools silo automation from your existing infrastructure. Coding agents compose naturally with developer tools: MCP servers, GitHub, CI pipelines. As it turns out, content is just code (https://leerob.com/agents). I found the Claude Agent SDK to be a simple, declarative way to build multi-agent workflows. Combined with Skills and MCP servers, it provided a consistent, extensible foundation. It was also shockingly fast to build & iterate on. The experience makes me believe that there will be a convergence between content and coding agents in 2026 - either everyone will become an engineer or generalist engineers will become content creators. ## Context and tools (#context-and-tools) I used MCP servers (https://docs.claude.com/en/docs/agents-and-tools/mcp) to perform actions and Claude Skills (https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) for relevant context. The system uses five MCP servers: two custom and three official. **Custom MCP servers** I wrote two custom MCP servers for deterministic operations: - Slack server: Fetches and structures messages from our feature announcement channel, including all responses, threads, and images - GitHub changelog server: Handles PR creation and frontmatter formatting These are really just tools wrapped in the MCP interface. The SDK's create_sdk_mcp_server makes this easy and gives me deterministic control over what data gets extracted. **Official MCP servers** Three official MCP servers work without custom configuration. - GitHub: Has 90+ tools organized into selectable toolsets via headers (I use pull_requests and repos ). - Documentation servers (Mintlify and Replit): Agents get context about Replit features and Mintlify components. They can reference docs and use correct syntax without me embedding it in prompts. MCP servers are a universal standard: any MCP server works with any LLM that supports the protocol. We can reuse these strategies across workflows, repositories, and agents. 3 (#user-content-fn-3) **Skills** Skills came to prominence _while_ I was building with the SDK. They provide domain expertise through progressive disclosure: load content on-demand rather than embedding it in prompts. Skills in our workflow - Brand guidelines: Our brand guidelines, including tone, voice, and style - Changelog formatting: How to format a changelog by our standards - Documentation quality: Verbatim this guide (https://cookbook.openai.com/articles/what_makes_documentation_good) - Media insertion: How to properly insert images and videos into a changelog Skills solved one of the biggest issues I was having with the MVP: supplying the right context at the right time. Interestingly, the final SDK output runs through the command line, so the agent is executed as one long bash command. This has implications for cross-platform development. 4 (#user-content-fn-4) ## From Matt to multi-agent (#from-matt-to-multi-agent) The agent architecture mirrors my human workflow: - Changelog Writer: Fetches Slack updates and drafts content - Template Formatter: Reformats to match the template structure - Review & Feedback: Reviews tone, quality, and accuracy - PR Writer: Creates a GitHub PR with formatted content Mermaid diagram %%{init: {'theme':'neutral'}}%% flowchart TD subgraph " " direction TB subgraph MCP[MCP Servers] direction TB S1[(Slack)] S2[(Replit Docs)] S3[(Mintlify Docs)] S4[(GitHub)] end SK[/Skills/] end O[Orchestrator] --> CW[Changelog Writer] CW --> TF[Template Formatter] TF --> RF[Review & Feedback] RF --> PW[PR Writer] CW -.-> S1 CW -.-> S2 CW -.-> SK TF -.-> SK RF -.-> SK RF -.-> S3 PW -.-> S4 Introspecting your own workflows is a necessary prerequisite for building agents. The same way a product thinker must understand their own product, _agent builders_ should understand their own workflows. An early MVP chained prompts in sequence. No context sharing, no revision, no tools. The multi-agent rewrite fixed that. Agents share context, revise based on feedback, and compose with MCP servers and Skills. ## 150 lines of Python (#150-lines-of-python) The entire multi-agent orchestration fits in one file: about 150 lines total. The rest is skills, docs, & servers. The structure breaks down into five parts: - Configure MCP servers. Map server names to their configurations (https://docs.claude.com/en/docs/agent-sdk/mcp) - Define granular permissions. Each permission is a string that controls what actions an agent can take. This includes file operations ( Read, Write, Edit ) and MCP server tools ( mcp__slack_updates__fetch_messages_from_channel ). - Group permissions by agent. Each agent gets only the permissions it needs for its specific task. The changelog_writer can fetch from Slack and write docs. The template_formatter can only edit today's changelog file. - Define each agent. Each AgentDefinition includes a description (for routing), a prompt, a model choice, and its permission group. Anthropic's model lineup works well for this: Opus, Sonnet, and Haiku all have a role. - Run the orchestrator. The ClaudeSDKClient coordinates everything: routing between agents, enforcing permissions, and connecting to MCP servers. Code example # 1. Configure MCP servers MCP_SERVERS = { "github": McpHttpServerConfig( type = "http", url = "https://api.githubcopilot.com/mcp/", headers = { "X-MCP-Toolsets": "pull_requests,repos", "Authorization": f "Bearer {GITHUB_TOKEN} ", }, ), "slack_updates": create_sdk_mcp_server( name = "slack_updates", tools = [fetch_messages_from_channel], ), } # 2. Define permissions permissions = { "read_docs": "Read(./docs/**/*.md)", "fetch_messages": "mcp__slack_updates__...", } # 3. Group by agent permission_groups = { "changelog_writer": [ permissions[ "fetch_messages" ], permissions[ "read_docs" ], ], "template_formatter": [ permissions[ "edit_changelog" ], ], } # 4. Define agents options = ClaudeAgentOptions( agents = { "changelog_writer": AgentDefinition( description = "Fetch and summarize", prompt = "You are a changelog writer...", model = "sonnet", # Use Sonnet for complex tasks tools = permission_groups[ "changelog_writer" ], ), "template_formatter": AgentDefinition( description = "Format to template", prompt = "Format the changelog...", model = "haiku", # Use Haiku for simple formatting tools = permission_groups[ "template_formatter" ], ), }, mcp_servers = MCP_SERVERS, ) # 5. Run orchestrator (also uses Sonnet) async with ClaudeSDKClient(options) as client: await client.query( prompt = USER_PROMPT ) The Agent SDK abstracts away the complexity of multi-agent coordination, permission enforcement, and tool routing. You define what each agent does and what it can access. ## Media handling (#media-handling) One of the most boring parts of changelogs is handling media: images, videos, GIFs, etc. The custom Slack MCP server recursively crawls every message and thread, extracting all images and maintaining context about which feature they document. On average, each changelog pulls 15-20 images across 8-12 threads. The system over-fetches initially, then CI handles cleanup: lint fails on unused images, compression reduces file sizes by 40-60%, and GIFs must stay under 2MB. Mintlify is especially nice because it handles image hosting directly from our GitHub repo and auto-deploys branches to a preview URL. Each changelog is a self-contained, previewable document that's ready for review immediately after it's generated. ## Results (#results) After 52 weeks in production, each changelog went from 2 hours to 10 minutes. More importantly, our users stayed informed. That consistency compounded into trust. [Image: Docs view in Mintlify] (/img/posts/2025/12/claude-agent-changelog/docs-light.png) (https://docs.replit.com/updates?utm_source=Matt&utm_medium=matts-blog&utm_campaign=blog) The system runs entirely on Replit with no infrastructure maintenance. 5 (#user-content-fn-5) Running on the Claude API costs under $1 per changelog, totaling ~$52 annually for a system that saved $15,000+ in labor. Generating changelogs allowed me to build flows for _monthly_ updates, which I send via email and a 2025 Replit Roundup (forthcoming). The pattern extends beyond changelogs. Any recurring content task with structured inputs (release notes, status updates, weekly digests) can follow this architecture. Want to pull from Linear instead of Slack? Add an MCP server. Need agents to understand your design system? Add a Skill (https://support.claude.com/en/articles/12512198-creating-custom-skills). The architecture doesn't change. You just extend the tooling. The differentiated work is editorial judgment. Everything else is delegatable. Automating it means more time creating from passion. [Image: Work distribution after 2026] (/img/posts/2025/12/claude-agent-changelog/after-2026-light.svg) And increasingly, the tool doing the delegating is a coding agent, even when the task has nothing to do with code. ## Footnotes (#footnote-label) - I am aware this SDK is not 52 weeks old at the time of this writing. An earlier, much more crude version ran on several chained prompts. ↩ (#user-content-fnref-1) - The source code is available on GitHub (https://github.com/mattppal/shipping-szn). ↩ (#user-content-fnref-2) - If you'd like to deploy your own, I've found Replit to be the best option (https://replit.com/@matt/MCP-on-Replit-TS?utm_source=Matt&utm_medium=matts-blog&utm_campaign=mintlify-changelog). ↩ (#user-content-fnref-3) - Linux's ARG_MAX is half of macOS (~131k vs ~262k bytes). The app worked on my Mac but failed silently on remote Linux when prompts exceeded the limit. Skills solved this by loading content on-demand. If you're deploying on Linux, test there early. Thanks to Thariq (https://x.com/trq212) for the debugging assist. ↩ (#user-content-fnref-4) - You can import and deploy your own version by visiting https://replit.new/github.com/mattppal/shipping-szn (https://replit.new/github.com/mattppal/shipping-szn) ↩ (#user-content-fnref-5) /posts # 8 rules for better docs October 18, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) Most companies don't succeed because of good docs, they succeed _despite_ bad docs: bad docs are the norm. But bad docs are also unacceptable. They slow down users, increase support load, and make your product hard to discover. Today, I'll share how we write docs at Replit. We overhauled our docs (https://docs.replit.com/?utm_source=Matt&utm_medium=matts-blog&utm_campaign=mintlify-changelog) in 3 months, 10x'ing internal contributors and 2x'ing monthly views. But the real win was getting our docs integrated directly into the product. Replit Agent now uses our docs to advise users. Soon, more agents than humans will use your tool. How will they find you? How will they be successful? This post covers 8 rules for writing docs **with** agents and **for** agents. 1 (#user-content-fn-1) - Write for humans, optimize for agents (#write-for-humans-optimize-for-agents) - Start with a funnel (#start-with-a-funnel) - Use Diataxis to scaffold (#use-diataxis-to-scaffold) - Write with AI, build for agents (#write-with-ai-build-for-agents) - Offload to background agents (#offload-to-background-agents) - Automate quality with CI (#automate-quality-with-ci) - Automate scaffolding and routine (#automate-scaffolding-and-routine) - Make contribution easy and visible (#make-contribution-easy-and-visible) ## Write for humans, optimize for agents (#write-for-humans-optimize-for-agents) Many have said "the future of the web is agents," but what does that mean for docs? Perhaps unsurprisingly: agents need great docs the same way humans do. Our first rule: write clear docs for humans and agents benefit automatically. Best practices work for both: - Use logical structure with clear connections 2 (#user-content-fn-2) - Don't lock critical information in images, write it out - Remove outdated content (even hidden pages show up in search and llms.txt ) - Use clear headings, examples, and context - Write well: sentences, paragraphs, and sections should be clear and easy to understand. From there, you can start to optimize for agents: - Add llms.txt and llms-full.txt to make your docs discoverable - Create an MCP server 3 (#user-content-fn-3) for your docs to help agents understand your product - Build agents into your onboarding and maybe even an install command (https://posthog.com/blog/envoy-wizard-llm-agent) ## Start with a funnel (#start-with-a-funnel) Design the path from landing to "aha," that means treating docs like a product funnel. Ask "How does a new user see your docs? Can they find what they need? Is it _obvious_ how to get started?" Think about what developers want: why are they here? What should they see first? The docs funnel: - What is your thing, and why should I care? - How do I get started quickly? - How do I learn more? This starts with a great landing page, a rapid quickstart, and guided next steps. Landing page Answer "what" and "why" as directly as possible. Users visit your docs for one thing: how does this tool help me achieve my goal? Use brief descriptions and be sure to list common use cases. Avoid marketing speak: if users are already reading your docs, the goal is to help them achieve their goal. Quickstart Can you get someone started with one command? Developer tooling has improved alongside (and thanks to) AI. We have better sandboxes, resources, and infra. There's no excuse for not having a rapid quickstart where users can get started immediately or simplifying the install process as much as possible 4 (#user-content-fn-4) Next steps Where should users go next? What is the next step in learning your product, and why should they continue? Once you have a clear path, you can start to use Diataxis to scaffold your docs. ## Use Diataxis to scaffold (#use-diataxis-to-scaffold) Diataxis (https://diataxis.fr/) is a way of thinking about docs built on four content types: _Tutorials, How-tos, Reference, Explanations._ - **Tutorials:** How do I build X from scratch? - **How-tos:** How do I do Y right now? - **Reference:** What does Z support? - **Explanations:** Why does this work this way? Diataxis is a tool, not a rulebook. Your docs depend on your product and audience—always ask yourself who you're writing for and what you're writing about. You will find exceptions to the framework. That's a good sign—it means you're writing for your product and audience, not because someone told you to. If your product is complex, break it into primitives and build from the user's view. For example, take Replit. Replit lets you build Apps using Replit Agent, which lives in the Workspace. The primitives are Replit, Agent, Workspace, and Apps. These become sections in your reference material. Then you add subsections and details. Structure, organization, and planning matter for you, your team, and AI. Docs overhaul If you've gotten this far, you might be thinking about a docs overhaul. An overhaul needs up-front work. Balance structure and writing, but do both in parallel—don't let an overhaul stop you from shipping. A good way of doing this is assigning priorities and actions to each page. What's p0? What's p1? What's p2? Will we delete this page? Will we rewrite it? Will we move it? [Image: Documentation audit board highlighting p0/p1/p2 priorities] (/img/ZmipGlE5_d-400.jpeg) Ask yourself: - What's the state of the docs? - What's the goal? Take our situation: - Docs debt from a pivot, wrong audience - Rewrite consumer docs, fix tone and voice Once you know your goals, pick a pattern and start writing! ## Write with AI, build for agents (#write-with-ai-build-for-agents) You should use AI to write docs. I repeat, you **should** use AI to write docs......but it requires up-front work. At Replit, we codify brand and structure in templates. Our style guide is available across all AI tools: local editors, Replit Agent, background agents, & PR review agents. [Image: Mintlify components view illustrating reusable docs templates] (/img/GIFWkeWpSb-400.webp) The pattern: - Ground AI with brand guidelines and style guides - Use AI for repetitive work - Add human touch for the final 25-50% Our templates:. ├── templates │ ├── features.mdx │ ├── overview.mdx │ ├── replit-brand-guidelines.md │ └── replit-docs-style-guide.md Docs for your docs Writing docs with an agent is like writing code. You need "docs for your docs.". └── rules ├── adding-docs.md ├── brand-guidelines.md ├── docs-dev.md └── vale-rules.md └── README.md EVERYTHING about your developer setup and writing guidelines needs to be written down. Even better: add these to your coding agent's rules with explicit instructions. Be clever with context In addition to "docs for your docs," you should be clever with context. That might mean using a custom MCP server to help agents understand your product. Since we use Mintlify, we get docs MCP servers for free. We use servers _not only_ for the very docs we're writing (improves agents' ability to link to the right pages), but for Mintlify's docs. That makes it easier for agents to know the framework they're building with. { "mcpServers": { "mintlify": { "url": "https://mintlify.com/docs/mcp" }, "replit": { "url": "https://replit.com/docs/mcp?utm_source=Matt&utm_medium=matts-blog&utm_campaign=mintlify-changelog" } } } Once you have these rules in place, draft with an agent, review with a human, & publish with checks. Implementing them all takes time, but the effects are compounding. ## Offload to background agents (#offload-to-background-agents) Where do you spend most of your time? Is it deep or shallow work? For me, it used to be small fixes: - Checking out new branches - Fixing formatting errors - Responding to internal requests These tasks pile up very quickly. Background agents can clear them while you do real work. They shine at: - Fixing broken links and images - Normalizing headings, frontmatter, and tables - Adding missing examples or "See also" links - Migrating pages to new templates To make them work: - Write precise tasks with success criteria - Keep tasks scoped to one directory or type - Make all checks scriptable - Prefer many small PRs over one large change Sometimes I queue three to five agents for housekeeping. Thirty minutes later, I have clean PRs ready to review. As we'll discuss later, background agents have a nice side effect of democratizing contribution: they're friendly for non-technical users. You can even automate the review process itself—more on that next. ## Automate quality with CI (#automate-quality-with-ci) The only way to enforce a rule is if a check fails when someone breaks it. That's it. Anything short of requiring green CI will have exceptions and eventually fall by the wayside.. ├── workflows │ ├── check-unused-images.yml │ ├── claude-code-review.yml │ ├── mintlify-broken-links.yml │ ├── optimize-images.yml │ └── vale-style-check.yml └── pull_request_template.md On every PR, CI runs style checks, link checks, and image hygiene. We use the Claude Code Action (https://github.com/anthropics/claude-code-action) to review PRs for style, content, and structure. Why? So far, it has the best MCP support of any review agent. Now you might say "ok Matt, we get it you're an MCP maximalist," but there are some crazy useful MCPs. Specifically, the GitHub (misunderstood (https://x.com/mattppal/status/1971333902724915509)) MCP server, which allows you to add inline comments to a PR (the GitHub CLI does not) Our Claude Code config name: Claude Auto Review on: pull_request: types: [ opened, ready_for_review ] jobs: review: runs-on: ubuntu-latest permissions: contents: read pull-requests: write issues: read id-token: write steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Run Claude Code id: claude uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} track_progress: true allowed_bots: "*" # Tools for comprehensive PR review claude_args: | --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" --mcp-config '{"mcpServers": {"mintlify": {"type": "http", "url": "https://mintlify.com/docs/mcp"}}}' prompt: | Context: - Repo: ${{ github.repository }} - PR Number: ${{ github.event.pull_request.number }} - PR Head SHA: ${{ github.event.pull_request.head.sha }} - PR Base SHA: ${{ github.event.pull_request.base.sha }} This is a code review of the Replit documentation repository. Please review this documentation using the following criteria and reference these essential files: Essential Reference Files: - Readme: README.md - Brand guidelines: templates/replit-brand-guidelines.md - Style guide: templates/replit-docs-style-guide.md - Cursor rules:.cursor/rules/adding-docs.mdc,.cursor/rules/brand-guidelines.mdc,.cursor/rules/docs-dev.mdc - Templates: templates/overview.mdx, templates/features.mdx Provide detailed feedback using inline comments for specific issues. ## Checklist - [ ] New features or overview pages adhere to templates - [ ] Language matches our brand guidelines - [ ] Images or videos are - [ ] Links are valid and functional - [ ] Mintlify components are used appropriately - [ ] Navigation is updated in docs/docs.json - [ ] Style and formatting adhere to the style guide - [ ] Brand voice and messaging is consistent - [ ] Content adheres to the diataxis framework Use `gh pr comment` for top-level feedback. Use `mcp__github_inline_comment__create_inline_comment` to highlight specific code issues. Suggest changes with the inline comment tool using the github suggestion syntax for a line of code. Only post GitHub comments - don't submit review text as messages. Use the Mintlify MCP server to help you research info on Mintlify, our docs framework With this configuration, you can take action directly from GitHub. [Image: Claude Code inline comments] (/img/wMAEOBXchb-400.jpeg) The key here is tight feedback loops: devs can make changes directly from GitHub, without having to checkout the branch. With Mintlify's preview deploys (https://www.mintlify.com/docs/deploy/preview-deployments), we can implement the suggested changes and see them live in minutes. Couple that with background agents and you have a powerful system: - Fire off docs from Slack - Merge feedback on GitHub - Deploy with a button press You can do all of these from your phone. ## Automate scaffolding and routine (#automate-scaffolding-and-routine) Writing requires thought and attention. That's our differentiated skill. Everything else—branches, navigation, PR setup, running checks—is undifferentiated work. It's not the point of docs. The point of docs is to inform and educate users, not to worry about creating PRs. Branches, navigation, PR setup, running checks... That's 15-30 minutes before you write anything. Do that five to ten times a week and you're spending more time on scaffolding than writing. The amazing thing about agents is that they eliminate scaffolding. Agents can: - Create and check out branches - Update docs.json or navigation files - Run lints and pre-commit hooks - Push changes and write PR descriptions - Update related files (changelogs, navigation, metadata) - Fix formatting across files That means we need to build the repo for agents. If we have that, we don't need to do _any_ of that. [Image: Agents handle routine work] (/img/Fi2KjvOjMm-400.jpeg) Again, this tightens feedback loops and improves the developer experience: contributors spend time writing, not wrestling with tooling. Let Agents handle routine work At Replit, changelogs are ~90% automated. We draft them from internal updates with an AI workflow. The agent has access to our brand voice, docs guidelines, MCP servers, and repo config. Every Friday I wake up to a PR ready for review. I open the Mintlify preview, make edits, and ship. We've shipped a changelog and monthly product updates for 11 months straight. Lack of automation is another common way docs fall by the wayside. The more we can lever AI productively, the more we can focus on writing and keeping our docs up to date. ## Make contribution easy and visible (#make-contribution-easy-and-visible) Democratization is just as much about habit-building as it is about tools. For months, I was the only one maintaining our docs. To get the team contributing, we made the process easy and visible. Then, I relentlessly nagged everyone. 😅 Show your work. Share Looms of your process. Train the team on workflows. Celebrate small PRs when they land. When someone asks you to make a change, make it using background agents, then tag them in the agent's Slack thread in a not-so-passive-aggressive way. Habits and tools that help - Record Loom walkthroughs for new contributors - Have an _excellent_ README.md for contributors - Make your docs the _best_ developer experience in your company - Build in public: show just how easy it is to contribute - Sit down with new contributors and show them the ropes - Host an educational session for the team - Run background agents in public and tag relevant people - (Tastefully) nag your team - Be pedantic. This is one of the only times you get to nag people and be annoying. Do it. Share your progress with the team, celebrate first-time contributors, and make the default path easy to follow. ## What good docs look like (#what-good-docs-look-like) Good docs help users succeed. At Replit, we went from "docs as an afterthought" to "docs as a product feature." Docs are a growth lever. They're developer experience. They're the difference between users who succeed and users who churn. Build great docs, build a great product. ## Footnotes (#footnote-label) - This article is a collection of rules based on my work at Replit (https://replit.com?utm_source=Matt&utm_medium=matts-blog&utm_campaign=mintlify-changelog). For the backstory and outcomes, see Mintlify's case study and deep dive: Replit customer story (https://www.mintlify.com/customers/replit) and Behind Replit's Documentation Transformation (https://www.mintlify.com/blog/behind-replits-documentation-transformation). ↩ (#user-content-fnref-1) - Both needle in a haystack (https://github.com/gkamradt/LLMTest_NeedleInAHaystack) and context rot (https://research.trychroma.com/context-rot) support direct connections reducing hallucination in LLMs. ↩ (#user-content-fnref-2) - Mastra does this well with their MCP server (https://mastra.ai/blog/introducing-mastra-mcp). ↩ (#user-content-fnref-3) - For a good example, see PostHog (https://posthog.com/blog/envoy-wizard-llm-agent). ↩ (#user-content-fnref-4) /posts # One of one October 4, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) The odds of winning the California Mega Millions jackpot are 1 in 290,472,336 "Success" can feel like winning the lottery. That is, until you realize the "arena" is much smaller than you thought. There might be 10,000 people in the world doing what you do. That's a lot. There are only 1,000 people doing it at a high level. There are only 100 people doing it in San Francisco. There are only 10 people doing it _well_. There's only 1 of _you_. Life doesn't work like a lottery. Consistency, execution, and _pure enthusiasm_ can turn 1 of 10,000 into 1 of 1. If you are willing to go where others will not, you will be where others will not be. Controlling variables is a choice. Competitive advantages are a choice. Being yourself is a choice. You can choose to be 1 of 1. /posts # Content is king September 3, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) We're witnessing the rise of a new kind of founder: the content creator. Founders have always been storytellers, but now the "story" is directly tied to brand & distribution. This shift comes from: - falling inference costs - AI that can build software - a fast-moving devtools market that now faces consumers - an increasing velocity of information As AI and devtools get better, software becomes a commodity. As software costs deflate, the value of brand & distribution increases. If software is no longer a moat, brand is. If _everyone_ is shipping software, choice is paradoxical (https://en.wikipedia.org/wiki/The_Paradox_of_Choice). You'll buy from the people you know and trust to avoid the noise. You already subscribe to your favorite YouTubers. You will "subscribe" to your favorite founders. Investors know this and they're worried. They're worried because they've been betting on stable (or rising) engineering costs for decades. Now, they're hedging their bets. That's why they backed Cluely (https://x.com/cluely) and Arcarae (https://x.com/NicoleSHsing/status/1961505968782774778). It's why a16z is ramping up their media (https://x.com/eriktorenberg/status/1927401419205669190) division. It's how TBPN (https://x.com/tbpn) became a household name in just a few months. Elon _bought_ the distribution when he acquired Twitter. At Replit, many of our biggest sales days are "Amjad (https://x.com/@amasad) led growth."" If zero to $100M is the new normal, story and speed win. Beyond adoption, companies endure when customers _trust_ the founder and _believe_ in the narrative. Today, a compelling narrative has become reason enough to raise. Hell, a meme is now reason enough to raise. In five years, you'll buy from voices you trust, hire people you know, and invest in what matters to you. We default to the familiar in a noisy world: it's already difficult to tell what's real and what's not. This is already true in the labor market. There's so much AI noise that the best roles are being hired offline, based on reputation. This will be true in other markets as well. People don't care about companies, they care about stories. These stories are often tied to a person or a movement (brand). It turns out there's no replacement for being human, even as we push toward the artificial. The best thing we can do is be ourselves and tell our stories in a genuine, authentic way. /posts # Signal, noise, and storytelling September 29, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) Developer education makes financial sense again As capital inflows to AI (devtools) increase, so does competition for attention. This is reflected in high CAC (/posts/2025/09/content-is-king/) for performance marketing and a firehose of developer content. But the economic effects of content differ from traditional supply and demand. Traditional supply and demand suggests: More content → incremental content is less valuable Higher supply = lower demand. But what we're seeing is: More content → higher barrier to effectiveness → premium for quality → content is more valuable Higher supply = higher demand. As content options increase, the value of curation and differentiation rises. More creators _increase_ total market demand for distinguishing services, so more distinguishing services are created—and now there are endless options; this is the paradox of choice (https://en.wikipedia.org/wiki/The_Paradox_of_Choice). Another variable is at play: the _audience size_ is increasing, too. More audience demand → more content supply → higher production value required to capture that demand If _nothing_ is happening, anything stands out. When _everything_ is happening, nothing stands out. Well, only the best stand out. Content that stands out is now more valuable. Signal-to-noise ratio is the scarce resource. Economically, communication and storytelling are now more viable than two years ago. What does that mean? Well, if paid marketing is more expensive and organic content is more expensive, then: Good content is now more valuable Companies are willing to put a premium on quality content, but more than just content—the **people** who create it. Those people are effective communicators. Those people are DevRel. More funding, more devtools companies, and more content increases the demand for **effective communicators**. We'll see companies increasingly spend (https://x.com/craigsdennis/status/1959994720597504494) on high-production-value (https://x.com/a16z/status/1969091780772839705) organic content. This will be especially true in AI, but it won't be limited to AI: - We'll see more sponsorships for organic content and influencer marketing - We'll see more courses and lavish consulting gigs - Those directing capital (VCs) will increasingly invest in organic content alongside labs and startups - We might even see the first VC-hired DevRel soon (portfolio advocates?) Now, this isn't doom, gloom, and more "course guys." It's a great opportunity for storytellers. If there's an idea you have, whether for a course or something _you know_ would help people, it's much more likely that you'll be able to make it happen. Either through sponsorship, employment, raising money, or entrepreneurship. Times of economic flourishing, which can edge into bubble (https://press.stripe.com/boom) territory, drive progress and innovation. This storytelling "boom" is going to make us all better storytellers. We're raising the floor for everyone. Under normal economic circumstances, there's a normal demand for storytelling. Under _our_ economic circumstances, the demand has never been higher. /posts # A web-enabled coding agent in 200 lines August 6, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) ## Introduction (#introduction) There are many coding agents, but how do they work? Surely, they must be complicated... As it would turn out, all you need for an agent is: an LLM, a loop, and some tools. This blog post demonstrates how to build a coding agent with web search & code execution in ~200 lines—the only external dependency being anthropic. Our agent will be able to: - View and edit files - Search the web - Execute bash commands You may find the full source on Github (https://github.com/mattppal/claude-codes/blob/main/simple_agent.py). %%{init: {'theme':'neutral'}}%% flowchart TD Start([Start]) --> UserInput[Get User Input] UserInput --> Claude[Send to Claude] Claude --> NeedsTools{Needs Tools?} NeedsTools -->|No| ShowResponse[Show Response] NeedsTools -->|Yes| ExecuteTools[Execute Tools] ExecuteTools --> SendResults[Send Results to Claude] SendResults --> Claude ShowResponse --> UserInput ExecuteTools -.-> Tools ## Jumping in (#jumping-in) Normally, tools are defined using a JSON schema, for example a web search implementation might look like this. We can use tools built-in to the Anthropic API, which don't require JSON schema definitions, but do have different characteristics. def web_search (topic): print ( f "pretending to search the web for { topic } " ) web_search_tool = { "name": "web_search", "description": "A tool to retrieve up to date information on a given topic by searching the web", "input_schema": { "type": "object", "properties": { "topic": { "type": "string", "description": "The topic to search the web for" }, }, "required": [ "topic" ] } } ## Init (#init) Claude comes with predefined tools that require shorter definitions: text editor, web search, and bash. These are the only tools we need for this demonstration. We start with imports and tool definitions. Setting web search max_uses to 5 prevents research loops. load_dotenv() ANTHROPIC_MODEL = "claude-sonnet-4-0" ANTHROPIC_API_KEY = os.getenv( "ANTHROPIC_API_KEY" ) if not ANTHROPIC_API_KEY: raise ValueError ( "ANTHROPIC_API_KEY must be set" ) ANTHROPIC_TOOLS = [ { "type": "text_editor_20250728", "name": "str_replace_based_edit_tool" }, { "type": "web_search_20250305", "name": "web_search", "max_uses": 5 }, { "type": "bash_20250124", "name": "bash" }, ] ## Prompting (#prompting) Now, we'll take a look at our prompt—it's recommended that the system prompt _only_ contain the model's role. We split the prompt in our code and load only the role tag, the rest is in the first user message. Using best practices for prompts helps with tool execution and reasoning. ### XML (#xml) We define prompt blocks in XML tags for structure and interpretability by the model. This is a best practice that I've found useful in my own projects. A nice side effect is that prompts are more human-readable, too. ### Context & role (#context--role) We build context around the task and clearly define the role of the agent. We include the role tag in the system prompt and pass the rest of the prompt as the first user message. ### Thinking (#thinking) Using the block, we encourage the model to think through each problem. This is also known as "chain of thought". ### Instructions (#instructions) We use explicit, declarative instructions on exactly how the model should perform a given task. This includes the steps the model should take on each turn. ### Tool use (#tool-use) We define tool use best practices to ensure parallel tool calls and proper work checking. By default, parallel tool use is enabled, but explicit prompts can maximize parallel use. role > You are an expert software engineering assistant specializing in code analysis, debugging, and implementation. You excel at understanding codebases, identifying issues, and implementing clean, maintainable solutions that follow best practices. You are working in the current directory. When referencing files, use relative paths from the current working directory unless you specifically need an absolute path. role > thinking_process > Before taking any action, think through the problem step by step: 1. **Analyze**: What is the specific request or error? What context do I need? 2. **Plan**: What tools and steps are needed to address this effectively? 3. **Execute**: Implement the solution methodically 4. **Verify**: Ensure the solution addresses the original problem Always reason through your approach before acting. thinking_process > instructions > When working with code: 1. **Understanding First**: Always examine existing files to understand the current state, structure, and patterns 2. **Targeted Changes**: Use precise `str_replace` operations that maintain code quality and consistency 3. **File Creation**: When creating new files, first understand the project structure and follow existing conventions 4. **Testing**: Always use `uv run` instead of `Python` for execution (e.g., `uv run test.py`) 5. **Error Handling**: Provide clear, helpful error messages when operations fail For each task: - Start by thinking through what you need to understand - Gather necessary information through file inspection - Plan your approach before making changes - Execute changes systematically - Verify results by executing any file you create or edit Please be concise and direct in your responses. instructions > tool_usage_best_practices > - Use parallel tool calls when performing multiple independent operations - Always check if files exist before attempting to modify them - Provide detailed, helpful feedback about what actions were taken - Verify results by executing any file you create or edit tool_usage_best_practices > code_quality_principles > - Write clean, readable, and maintainable code - Follow existing project conventions and patterns - Include appropriate error handling - Make minimal, focused changes that solve the specific problem - Ensure changes don't break existing functionality code_quality_principles > ## Handling Tools (#handling-tools) "Tool use" can be a confusing term—while we're equipping the model with the knowledge to execute tools, we still need to provide the tools themselves. We have one server tool (web search), but other tools need local execution. This function defines a group of tool actions that we'll give access to our model to execute. ### execute_tool (#execute_tool) We accept a tool_name and tool_input, then route tool requests to the appropriate operation. This provides a nice way to implement error and retry logic close to the tool implementations. Some best practices when executing tools: - Adding an is_error property to the response, which we can then pass to Claude - Using proper try / except logic with detailed logging for the agent For this implementation, we only log errors. You could add retry logic as needed. ### View (#view) Be sure to handle both directories and files ### String replace (#string-replace) Best practice: replace _only_ the content that needs to be altered, rather than the entire file. ### Bash (#bash) Best practice: ensure reasonable timeouts for our bash tool and return both stdout & stderr to our agent def execute_tool (tool_name: str, tool_input: dict ) -> dict: """Execute a tool and return structured result with error handling.""" try: # string replace tools if tool_name == "view": path = Path( str (tool_input.get( "path" ))) if path.is_file(): content = path.read_text() return { "content": content, "is_error": False } elif path.is_dir(): content = " \n ".join( sorted ([f.name for f in path.iterdir()])) return { "content": content, "is_error": False } else: return { "content": f "Error: { path } does not exist", "is_error": True } elif tool_name == "create": path = Path( str (tool_input.get( "path" ))) content = str (tool_input.get( "file_text" )) if not content: return { "content": "Error: No content provided in file_text", "is_error": True, } path.parent.mkdir( parents = True, exist_ok = True ) path.write_text(content) return { "content": f "File { path } written successfully", "is_error": False } elif tool_name == "str_replace": path = Path( str (tool_input.get( "path" ))) old_str = str (tool_input.get( "old_str" )) new_str = str (tool_input.get( "new_str" )) if not path.exists(): return { "content": f "Error: File { path } does not exist", "is_error": True, } content = path.read_text() if old_str not in content: return { "content": f "Error: String ' { old_str } ' not found in { path } ", "is_error": True, } new_content = content.replace(old_str, new_str, 1 ) path.write_text(new_content) return { "content": f "Replaced ' { old_str } ' with ' { new_str } ' in { path } ", "is_error": False, } # bash tools elif tool_name == "bash": command = tool_input.get( "command" ) print (command) if not command: return { "content": "Error: No input in command", "is_error": True, } result = subprocess.run( command, shell = True, capture_output = True, text = True, timeout = 30, # Add timeout for safety ) # Return both stdout and stderr, mark as error if non-zero exit code output = f "stdout: { result.stdout }\n stderr: { result.stderr } " return { "content": output, "is_error": result.returncode!= 0 } else: return { "content": f "Error: Unknown tool ' { tool_name } '", "is_error": True, } except Exception as e: return { "content": f "Error executing { tool_name }: {str (e) } ", "is_error": True, } ## Building an agent (#building-an-agent) Now, we'll take a look at our agent: The agent uses a while loop that handles different cases. First, we initialize the Anthropic client and pass in our tools. The temperature is set to a low value to encourage concise responses. ### Caching (#caching) Prompt caching caches the _full prefix_ up to the cache point in the following order: **tools**, **system**, **messages**. That means our cache point caches the system prompt, tools, and first message. Since this is sent with every message, we get cost savings during the cache window (5 minutes by default). ### Stop reasons (#stop-reasons) Next, we handle stop_reasons, which is the client's way of communicating why the chat ended. A best practice is to implement robust stop reason handling. We keep handling short for our demo, but we recommend handling _all_ possible reasons. ### Responses (#responses) Now, we loop through responses and check for events: text, tool_use and citations (from our web results). These are surfaced to the user _and_ our agent. ### Tools (#tools) If tools were requested, we make conditional calls to our executor function by extracting relevant details. You might notice our web search handling differs from the string replace & bash tools. That's because web search is a **server** tool with a different output structure. ### Final response (#final-response) We return the results to the client and raise an error if one occurred. This while loop makes another good structure for top-level retries or error handling logic. We simply raise an exception, but you could imagine some number of more complex iterations. Messages and tool results are tracked through message blocks, returned to the client. ### Thoughts (#thoughts) And thats... it. 200 lines, a few while loops, and some tool calls. There's something magical about understanding the tools we use everyday—Claude Code, Cursor, and the like can feel like magic, but they're just tools. An "agent" is just a loop that handles different cases using AI... and that doesn't _have_ to be complicated. Now, a production agent is another story, but this is a good starting point for anyone whose curious about how agents work. if __name__ == "__main__": # Load and parse prompt prompt_content = Path( "./public/instructions.md" ).read_text() system_prompt = prompt_content[ prompt_content.find( " " ) + 6: prompt_content.find( " " ) ].strip() instructions_content = prompt_content[ prompt_content.find( " " ): ].strip() client = anthropic.Anthropic( api_key = ANTHROPIC_API_KEY ) while True: user_input = input ( "💬 User: " ) # Cache everything up to first user message messages = [ { "role": "user", "content": [ { "type": "text", "text": instructions_content, "cache_control": { "type": "ephemeral" }, } ], }, { "role": "user", "content": user_input}, ] while True: # TODO: delegate—haiku for simple tasks, opus for complex response = client.messages.create( model = ANTHROPIC_MODEL, system = [{ "type": "text", "text": system_prompt}], max_tokens = 4096, temperature = 0.2, messages = messages, # type: ignore tools = ANTHROPIC_TOOLS, # type: ignore ) # TODO: stop reason handling if response.stop_reason in [ "tool_use" ]: tool_results = [] tool_calls = [] # First pass: collect all tool calls and display text for block in response.content: if hasattr (block, "text" ): print (block.text) # web search tool if block.type == "server_tool_use": print ( f "Searched for: { block.input.get( 'query' ) } " ) if hasattr (block, "citations" ) and block.citations: print ( f "Cited sources: {len (block.citations) } " ) if block.type == "tool_use": if block.name == "bash": tool_name = block.name elif block.name == "str_replace_based_edit_tool": tool_name = block.input.get( "command", None ) tool_calls.append( { "tool_name": tool_name, "tool_use_id": block.id, "tool_input": block.input, } ) # Second pass: execute all tools if tool_calls: print ( f "Executing {len (tool_calls) } tool(s)..." ) for tool_call in tool_calls: tool_name, tool_use_id, tool_input = ( tool_call[ "tool_name" ], # type: ignore tool_call[ "tool_use_id" ], # type: ignore tool_call[ "tool_input" ], # type: ignore ) print ( f "Executing tool: { tool_name } " ) result = execute_tool(tool_name, tool_input) print (result[ "content" ]) # Handle structured error results tool_result = { "type": "tool_result", "tool_use_id": tool_use_id, "content": result[ "content" ], } if result[ "is_error" ]: tool_result[ "is_error" ] = True tool_results.append(tool_result) messages.append( { "role": "assistant", "content": [block for block in response.content], } ) if tool_results: messages.append({ "role": "user", "content": tool_results}) continue else: # Handle non-tool responses for block in response.content: if hasattr (block, "text" ): print (block.text) if response.stop_reason in [ "end_turn" ]: break # Break out of inner loop to restart conversation ## Next steps (#next-steps) - Implement more robust stop reason handling, retry logic, and try / except blocks - Implement streaming for more responsive messages - Turn our simple agent into a Multi-agent Architecture (https://www.anthropic.com/engineering/built-multi-agent-research-system) with Opus as an orchestrator and Haiku for lightweight tasks - Play with remote code execution (https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/code-execution-tool) for a sandboxed approach - Reduce latency (https://docs.anthropic.com/en/docs/test-and-evaluate/strengthen-guardrails/reduce-latency) in our responses - Add guardrails to bash tool execution A big thank you to Thorsten Ball (https://ampcode.com/how-to-build-an-agent) of Amp for the inspiration on this project. ## Resources (#resources) - Anthropic documentation (https://docs.anthropic.com) - Anthropic Cookbook (https://github.com/anthropics/anthropic-cookbook) /posts # On demand learning August 13, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) Software engineering has traditionally assumed a high cost of knowledge and a long process of understanding. The traditional educational model has worked well enough: there are experts and instructors who help students understand complex topics that instructors are uniquely qualified to teach. Want to learn to code? Most people won't make it through Learning Python (https://cfm.ehu.es/ricardo/docs/python/Learning_Python.pdf) on their own, better take a course. 1 (#user-content-fn-1) This is because there have always been a large number of primitives that served as prerequisites to _any_ project. To build a _Hello World_ app, you need to know about syntax, variables, and functions. You actually need to know Python. Anything more complex and you need to know about virtual environments and dependency management. Want a database? Time to install Postgres on your laptop. You need to know about SQL, and how to use a terminal. This is how it's always been. The internet made things easier, but it was still a _slow_ process to put in the work and understand the concepts. But two parallel forces are disrupting the traditional education model: - A falling cost and latency to learning - Rapidly improving devtools ## On-demand learning (#on-demand-learning) The internet brought the cost and latency of learning down, but _understanding_ still took time. You had to find, vet, and reconcile conflicting answers. Stack Overflow helped by surfacing exact fixes. Now, AI makes learning near-instant and free. With high-quality OSS models, the marginal cost per token is effectively zero. [Image: OSS model costs trend toward zero at the edge] (/img/cPrutKIwIQ-400.jpeg) GPT model costs are falling while models continue to improve. Moreover, you can get an answer tailored to your exact question and learning style. At a broad level, **you can just do things**. It's possible for a reasonably clever person to ship a web app in a few hours with _vibe coding_, serverless tools, and modern scaffolding. No CS degree, no fundamentals, just logic & agency (https://giansegato.com/essays/agency-is-eating-the-world). What I'm seeing is that if you ask some of these new learners about _how things work_ they have no clue, which doomers will tell you is totally bad, evil, and the end of software engineering as we know it. But I'm here to say it's totally fine. They're learning. I'm not going to poke fun of someone at the gym, even if they're doing the silliest thing I've ever seen. Maybe it's their first day. Maybe they just got on the path. Maybe this is a new evidence-based approach and _I'm_ wrong (unlikely). Maybe they're trying to get a bit better every day, like me. There's a human inclination to talk down to those who are early in their journey. Some engineers valorize deep systems knowledge and dismiss newer workflows outright. That attitude often extends to vibe coders. ## What's the goal? (#whats-the-goal) The goal of engineering is not to know more fun facts about TypeScript. The goal of engineering is to build amazing things that improve lives. Learning is now on-demand. I don't _need_ to know Python to build a _Hello World_ app. I can build the app, then understand how it works. I don't _need_ to know DevOps to have a serverless production database. I can connect it to my app and learn along the way. Vibe coding, ephemeral chats, and curiosity can get you _very_ far with today's tools. Arguably too far. The problem is that many new learners do not understand the difference between a toy and a project. I might spend a morning spinning up a prototype to validate an idea. I have no idea how the code works and I guarantee it's the most hideous thing I've ever seen. But I _recognize_ that. I recognize that if I want to use this thing, I'll need to tear it apart, understand it, then rebuild it from scratch. Yes, rebuild it with AI, but rebuild it file by file. The divide between building irresponsibly and responsibly is understanding. Exploration without deep understanding is fine; shipping to users is not. Many new learners are so amazed that they created something that actually works... that they'll run with it. They'll run with it even if they don't understand it. And that's because _creation_ has been gated for so long. The same was true of the first photos on Instagram—creating digital images was limited to professional photographers, so when people _realized_ they could create, they ran with it. The result was millions of blurry, horribly filtered images. Now, a dozen years later, new content creators are being born because of a different type of democratization: Personal software. And my prediction is that we'll see the same thing, albeit on a compressed timeline, that we did with media. But putting your name on something you don't understand is a dangerous game, one that doesn't apply to photos, but does apply to executable code. ## The contract (#the-contract) If I gave you a contract and you skimmed the first paragraph and made some edits, would you sign your name? Software is a contract. Creating is a contract. When I create something, I'm putting my name on it, just like I'm putting my name on this post. And if you have integrity, you'll only do that if you care deeply about what you're creating. Now, it seems increasingly likely that a future exists where none of this matters—someday we'll be able to build whatever, whenever and not worry about code. That is not today. On-demand learning for data security doesn't _really work_ if my app is already live and storing user data. Learning that lesson the hard way _not only_ hurts me, but all the users of my app. _Creating software_ is something special... and a bit scary. Code is not like a meme or an Instagram Reel. ### Our contract (#our-contract) As "vibe coders," we're building the future. We're building the future of work. We're building the future of learning. We need to understand when we don't understand. It's ok to not understand! It's actually important to not understand, because recognizing is the first step to understanding. But there comes a time (we all know that time) when you need to sit down and do the hard work. AI can tempt us to hand-wave and bullshit over the parts we don't get. The contract we have to make with ourselves is that we will not put our name on something we don't understand. The contract is not about being "above" anyone. It's about being a professional. Professionals deliver excellent work. Professionals do not compromise on user safety, data security, or truthful representation of competence. And if you want to make the claim that you're building a $50k MRR SaaS or that you're writing 10k lines of code everyday, you are claiming to be a professional. My challenge is to act like it. The contract I'm willing to make is that I will do everything in my power to make understanding these concepts as _simple_, _relatable_, and _straightforward_ as possible. But we (vibe coders) have to make a similar contract with ourselves—to be professionals. ## Where next? (#where-next) I'm not saying everyone needs to be an expert (I am not one). I'm not saying we should stifle new learners. I'm not saying we should be pessimistic. We are barreling toward a future where we can build anything, anytime and we _are not_ in control of where that future leads. 2 (#user-content-fn-2) Me yapping on this blog isn't going to stop a vibe coder from leaking their API keys. But we can do everything in our power to make sure that we're building the future in a way that is safe, secure, and responsible. I believe that happens through education and understanding. We need to continue encouraging understanding. Last week, I wrote a post on building agents (/posts/2025/08/coding-agent-in-200-lines/) and I was a bit embarrassed. It took a LONG time to shake off the rust... almost everything I've built in the past few months has been vibe coded. But it continues to be useful as I formulate ways to educate and improve. And so I have two asks: - If you're already a software engineer, don't feel threatened by new learners. They have access to the same tools you do, but much less nuance and pattern mapping. Try to help them understand, learn, and grow. - If you're a new learner, ABSOLUTELY use every tool at your disposal, but start from the premise of improving your craft and growing. That means being a professional, that means putting in the work, and that means understanding. If you vibe code something quickly, anyone with taste can spot it from a mile away. I'm incredibly excited to see where the future leads. Now is truly the greatest time in human history to be a builder (and a learner). ## Footnotes (#footnote-label) - I learned Python by reading _Learning Python_ every night after work... I hope no one else ever has to. ↩ (#user-content-fnref-1) - This is similar to Anthropic's take on safety (https://www.anthropic.com/news/core-views-on-ai-safety). ↩ (#user-content-fnref-2) /posts # What is developer marketing? July 4, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) _This is a living post. It's largely influenced by my experience at Replit (https://replit.com/?utm_source=Matt&utm_medium=matts-blog&utm_campaign=mintlify-changelog) leading DevRel and technical marketing on our run from $2M to $250M+ ARR (https://x.com/paulg/status/1953991503581470977)._ Marketing to developers is no different than marketing to any other audience. The three things that matter are: what, who, how. ### What you're building Be hyper-opinionated about what you're building ### Who you're building for Be hyper-opinionated about who you're building for ### How you communicate Be hyper-opinionated about how you communicate Marketing starts with product. Product requires being hyper-opinionated about two things: - What you are building - Who you're building it for This is surprisingly hard to do well. As a marketer, your number one job is to understand the product and the user. This is one of the reasons I'll argue that developer marketers need to be technical: if you plan to communicate with devs, it will be much easier if you _are_ a dev. How can you _really_ understand a product you don't use? The easiest way to understand the user is to _be_ the user. Marketing adds one more layer of opinion: - How you communicate That's it. Marketing is product + communication. Now, this is hard for technical products. If you haven't noticed, most engineers communicate through code, not copy. Moreover, most folks interested in technical concepts want to nerd out. We start with features. We explain how things work. We write technical explanations. It's very rare to find someone who seeks to understand _and_ simplify. Why? ## If you're confused, you're winning (#if-youre-confused-youre-winning) If you find software confusing, you're already ahead of 90% of developer marketers. That moment when you're reading docs and thinking "what the hell does this even mean?" Your users are thinking the exact same thing. Most people who build technical products live so deep in the solution that they've forgotten what the problem feels like. But you remember. That is your superpower. When something finally clicks for you, pay attention. That's the exact moment you need to recreate for your users. The best developer marketers aren't the smartest engineers. They're the ones who stayed curious about why things are hard. ## The translation gap (#the-translation-gap) Most technical marketing sucks because it's written by people who think in features, not problems. They write: "Our platform provides comprehensive AI-assisted development capabilities with enterprise-grade security and scalability." But developers aren't thinking about "comprehensive AI-assisted development capabilities." They're thinking: "I'm tired of writing the same damn boilerplate code for the 50th time this month." The magic happens when you translate from solution-speak back to human-speak: ❌ Robot "Our platform provides comprehensive AI-assisted development capabilities with enterprise-grade security and scalability." ✅ Human "Build apps in minutes. No coding required." ❌ Robot "Leverage our advanced machine learning algorithms to optimize your development workflow." ✅ Human "Stop writing boilerplate. Start building cool stuff." ❌ Robot "Our solution addresses the pain points experienced by modern development teams." ✅ Human "Remember when coding was fun? It can be again." I didn't dumb anything down. I just stopped talking like a robot. ## How to actually do this (#how-to-actually-do-this) This isn't magic. It's a process: **Step 1: Start with the user's perspective** Understand their world before selling your solution. What keeps them up at night? Where do they hang out? What do they complain about? **Step 2: Focus on outcomes, not features** Lead with what they'll achieve, not how it works. Deploy with confidence, build faster, sleep better at night. **Step 3: Keep it simple** 4th-grade reading level. Short sentences. Clear language. No jargon, one idea per sentence. **Step 4: Lead with emotion, back with logic** Hook them with feelings, then justify with facts. Feel confident first, then show monitoring. Emotion first, specs second. **Step 5: Be confidently opinionated** Have strong opinions about everything you do. We are the best, this is the way, no apologizing. That's it. Five steps. Nothing revolutionary, but most people skip at least three of them. Start with users, focus on outcomes, keep it simple, lead with emotion, be confident. Do all five consistently and you're well on your way to being a great developer marketer. ## Building the movement (#building-the-movement) The best developer marketing doesn't create customers, it creates champions. But what do people actually evangelize? It's not your product. It's their success. Getting a promotion because they shipped that impossible project on time. Starting a business of their own. Getting to go home early and spending more time with family. Accomplishing something they never thought possible. People don't tell their friends about your features. They tell their friends about how they won. **Share your failures.** Don't just show the polished demo. Show how you struggled with the same problems they face. Your failures make their successes feel achievable. **Ask for help.** Your community wants to contribute. When they help improve your product, they become invested in everyone else's success too. **Celebrate their wins.** When someone builds something amazing, make them famous. Not your product. Their success story becomes your most powerful marketing. **Show up consistently.** Be there when they succeed. Be there when they struggle. Success feels better when someone notices. The goal isn't to trick people into using your product. It's to become essential to their success stories by **making them successful**. ## What good looks like (#what-good-looks-like) Here's what happens when you nail this: Someone tries your product because you actually understood their problem. Not some made-up persona problem—their real, daily frustration. They use it. It works. They ship something faster than expected. They look good in the standup. They go home on time for once. Now they have a story. "Dude, you have to try this thing. I just shipped that feature in like two hours." Their friend tries it. Same result. More stories. Before you know it, you're not marketing anymore. Your users are. And they're way more convincing than you ever were. That's how things actually grow. Through people who genuinely can't shut up about how much better their life got. ## The only things that matter (#the-only-things-that-matter) Developer marketing comes down to three things: - **Build something worth talking about** - **Talk about it like a human being** - **Help others talk about it too** Everything else is just noise. The foundation is product. The execution is communication. But the secret is making your users successful. When they win, you win. When they achieve something meaningful with your tool, they don't just become customers—they become living proof of what you built. Most people wouldn't call that marketing, but I do. /posts # Hats off to Replit June 23, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) [Image: Replit] (/img/zLjc5xfvIi-400.jpeg) The past year has been the most challenging of my life. It's also been the most rewarding. Rewarding because I've worked with the brightest, most driven people in the world. Rewarding because I've learned things about myself I never knew. Rewarding because I've helped tens of thousands realize they can CREATE and BUILD. Being mission-driven is about more than money. It's about impact on real-life human beings. We are on the precipice of the COMPLETE AND TOTAL democratization of creativity. I learned Python with a textbook. That is now a thing of the past. Anyone can learn by building a working application in 5 minutes. This is the MOST EXCITING TIME in humanity to be an educator. My job is simple: show people what they can do. And they can do infinitely more now than 9 months ago. I've watched people succeed and fail my entire life: school, sports, fitness, career. The ones who succeed weren't chasing dollars or stats. What I'm about to say contradicts 'the algorithm' and what you see on the internet, but it is reality: There are no dollars at 5am or 10pm. There are no dollars when it's you and a screen. Or when you need to do some very unsexy work. No dollars when you're alone somewhere new. No dollars when you're trying and repeatedly failing. No dollars when things are unfair, life sucks, or you feel like a victim. But dollars aren't the point. Creating, motivating, and growing is the point. And if you can't find it in yourself to grind when no one is telling you to... If you can't pick yourself up and get 1% better SIMPLY BECAUSE YOU CARE... It's unlikely you'll make it to the part where there are dollars. I work with exceptional builders. I might seem busy because I'm always yapping, but they actually ship product. I just spread the word. Hats off to the outstanding engineers, designers, operators, & leaders who built Replit. Hats off to those grinding when no one was looking—few see all the work. Hats off to everyone who got us where we are today. Most importantly, thank you to our users and supporters. The product wouldn't be the same without you. You make my job fun. The mission is to improve lives through software. It's been inspiring to watch it come to life. I can't wait to see where it goes next. 🧡 /posts # Precision, salience, and billing June 10, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) "42% battery remaining." I drove an EV for the first time this weekend and I noticed one thing: how fast the charge decreased. In fact, I found myself constantly drawn to the charge. I watched 42% become 41%. Then 40%. Then, well, you get it. It was constantly on my mind: finding chargers, driving less, making sure I could keep the car charged. Then, wondering why chargers were different, what the hell ChargePoint was, downloading apps to find EV chargers, and finally, just trying to figure out how much I was spending on charging. The answer? Less Now, renting an EV in San Antonio, Texas probably wasn't the smartest move. Better yet, with gas under $3 a gallon in Texas, I should have just got a gas car and saved myself all this trouble. But, I on a walk I tried to figure out why this bothered me. The thing is, I _rarely_ think about the range on my gas vehicle. And I drive a truck that gets 16 mpg. Two things stuck out to me: **precision** and **salience**. The precision of battery levels and the salience of the car's charge put me in a scarcity mindset. ## Gauges and levels (#gauges-and-levels) With a gas car, I never stress about the numbers. The fuel gauge is vague, and gas stations are everywhere, even if gas is $6 a gallon in California. How fast does my gas tank empty in a day? No idea. How much charge did I lose driving from my hotel to the Alamo? 8%. A similar analogy is enabling the "battery number" on iPhone. I instantly go from being "somewhat aware" of how much battery I have to fixated on how fast my phone is losing charge, then subconsciously filled with conspiracy theories about planned obsolescence. All I really need is "yellow" then "red." Then, I'll turn on low power mode and conserve, but only if I REALLY have to. [Image: iPhone battery] (/img/xRJWTjPY8O-400.jpeg) ## Billing and AI (#billing-and-ai) The obvious parallel here is billing in AI. Now, there's a reason why I have no idea what I'm spending on Google Cloud until I get an invoice in my email: they know I'll spend less if I'm hyper-aware of my usage. Yet, for AI products, we've set the precedent that we're going to show users **OVERLY precise** pricing. We're going to show users EVERYTHING; sessions, chats, even tokens. [Image: Billing from a certain AI code editor] (/img/4Qp3fNnA2I-400.jpeg) Scarcity is the enemy of creativity. There's no better way to make someone less creative than to make them anxious and concerned. We should never deceive users, BUT we should remove the burden of precision. We should allow users to be **creative** without worrying about spending $5.38 in a session. I have a friend who likes to point out the Law of Triviality (https://en.wikipedia.org/wiki/Law_of_triviality) in pricing issues, but hyper-precise pricing creates anxiety and worsens the user experience, so it's anything but trivial. ## A better way forward (#a-better-way-forward) Usage based billing needs to move from a "charge" model to a "gauge" model. From a battery percentage to an simple indicator with a warning light. AI pricing should allow for creativity and surface usage intelligently. We're incredibly early in the days of AI billing and there's a wide margin to improve. I, for one, would like a little less precision in my life. /posts # CVE-2025-48757 May 29, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) ## Overview (#overview) A vulnerability exists in deployed Lovable-generated projects due to default insufficient Row Level Security (RLS) policies on client-controlled direct database requests. You can learn more about RLS in Postgres projects here (https://www.postgresql.org/docs/current/ddl-rowsecurity.html). This can result in Lovable projects being deployed with RLS policies that don't match the business logic, potentially exposing sensitive data (including Personally Identifiable Information—PII—and credentials) to unauthenticated remote attackers via direct client access to database endpoints despite using the non-sensitive and unprivileged anon (https://supabase.com/docs/guides/api/api-keys#the-anon-key) key. Furthermore, attackers may issue unauthorized writes or injections of malicious data to some of these projects with minimal additional effort. ## CVE Information (#cve-information) - **CVE ID**: CVE-2025-48757 (https://www.cve.org/CVERecord?id=CVE-2025-48757) - **Published**: May 29th, 2025 ## CVSS Score (#cvss-score) **CVSS v3.1**: - AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N/E:F/RL:W/RC:R (https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N/E:F/RL:W/RC:R&version=3.1) - **Base Score**: 8.26 - **Temporal Score**: 7.58 ## Affected Products (#affected-products) - **Product**: Lovable - **Vendor**: Lovable (https://lovable.dev/ (https://lovable.dev/)) - **Vulnerable Versions**: All versions - **Fixed Versions**: None - **Components**: Public endpoints of generated code ## Vulnerability Details (#vulnerability-details) ### Description (#description) The vulnerability is an Incorrect Authorization (https://cwe.mitre.org/data/definitions/863.html) issue stemming from missing or insufficient Row Level Security (RLS) policies on databases used by Lovable projects. This affects any Lovable project that uses a database, created on or before April 15th, 2025, but may also affect projects created after that date. The root cause lies in the failure to enforce or maintain secure default RLS configurations for user projects. This can lead to projects being deployed with RLS that don't match security requirements for certain tables. Exploitation requires the target Lovable project to be using an external database as its backend, with specific tables lacking appropriate RLS policies to restrict data access. Attackers can leverage this by sending crafted HTTP requests to public Postgres (https://docs.postgrest.org/en/v13/) endpoints associated with vulnerable Lovable projects. These requests can be modified to bypass intended data access restrictions: for example, by attempting to select all records from a table, thereby exposing sensitive information. The requests may also be modified to insert, update, or delete data, potentially allowing attackers to inject malicious content or manipulate existing records. ### Exploitation (#exploitation) - **Attack Vector**: Network - **Attack Complexity**: Low - **Privileges Required**: None - **User Interaction**: None An attacker first identifies public endpoints associated with a Lovable project (e.g., by inspecting network traffic of the application). Lovable creates frontend applications that make direct REST API calls against the database from the client, using a public and unprivileged anon key, while relying exclusively on RLS to ensure the privacy and integrity of the contents of the database. If an application interacts with a table that has missing or insufficient RLS policies, the attacker can modify the REST request (e.g., query parameters or request body) to query for data they should not have access to, such as all rows in a table (e.g.?select=* ). For security reasons, we have chosen not to provide specific examples of exploitation scripts, as this could enable malicious actors to target vulnerable sites. However, the vulnerability can be exploited through standard HTTP request manipulation techniques. [ { "id": "65cfe5b1-8d1b-4524-82f4-e1b4431165bc", "email": "user1@example.com", "linkedin_url": "https://www.linkedin.com/in/user-profile-1/", "generation_id": "00b580fe-83a4-44fa-bb1e-c2f1d65006f9", "payment_status": "pending", "website_url": null, "created_at": "2025-04-01T10:03:33.87533+00:00", "updated_at": "2025-04-01T10:03:33.87533+00:00", "created_by": null }, { "id": "6420adfa-0952-4d19-acd4-1dcccb139817", "email": "user1@example.com", "linkedin_url": "https://www.linkedin.com/in/user-profile-1/", "generation_id": "39dd80de-0614-4cef-ad2f-a13489d20e91", "payment_status": "pending", "website_url": null, "created_at": "2025-04-01T10:03:42.112132+00:00", "updated_at": "2025-04-01T10:03:42.112132+00:00", "created_by": null } ] Impact - **Confidentiality Impact**: High - **Integrity Impact**: LowHigh - **Availability Impact**: None - **Scope**: Unchanged Successful exploitation of this vulnerability can lead to significant data breaches and unauthorized data modification. Attackers can gain unauthorized access to and in some cases even modify a wide range of sensitive information, including: - Personally Identifiable Information (PII) from users tables (e.g., names, email addresses,). - API keys and access tokens for third-party services (e.g., Google Maps, Gemini API, eBay tokens), allowing further unauthorized actions on those platforms acting on behalf of the compromised application or its users. - Financial data such as transactions and subscriptions details, including the ability to modify payment status. ## Resolution (#resolution) ### Patches and Updates (#patches-and-updates) None available ### Workarounds (#workarounds) Users of Lovable projects utilizing external services are strongly urged to take immediate action to mitigate this vulnerability. As an example, we provide links to Supabase, one database vendor that has good documentation on the topic: - **Implement and Enforce Row Level Security (RLS) Policies**: This is the primary mitigation. Review all database tables to ensure RLS is enabled. Lovable's Publish feature will help ensure that RLS policies are enabled in all tables and notify if they aren't (but doesn't necessarily indicate if they are sufficient). Note that RLS is enabled by default on tables created with the Table Editor in the dashboard. If you created the tables via the SQL editor or via another way, enable RLS manually: ALTER TABLE schema_name. table_name ENABLE ROW LEVEL SECURITY; Define granular RLS policies for all tables, particularly those containing sensitive data, adhering to the principle of least privilege (https://csrc.nist.gov/glossary/term/least_privilege): This means only allowing users access to the specific rows they are authorized to view or modify. For publicly accessible data, create explicit policies that permit public read access only to non-sensitive columns. For authenticated access, ensure policies correctly use auth.uid() or similar session variables to restrict data access to the logged-in user or their specific roles and permissions. Crucially, avoid overly permissive policies, such as USING (true) for select operations on sensitive tables without additional restrictive conditions. Example of a secure policy: CREATE POLICY "Users can view their own todos" ON todos FOR SELECT TO authenticated USING ( ( SELECT auth. uid ()) = user_id ); - **Audit Endpoints and Data Storage**: Carefully review all data stored in external services and tables. Identify and assess any Personally Identifiable Information (PII), credentials (like API keys or tokens), financial information, or other sensitive data. Determine if this sensitive data absolutely needs to be stored. If it does, evaluate whether it can be stored more securely (e.g., encrypted secrets—though RLS remains the primary defense against this specific CVE). Remove any unnecessary sensitive data from the database. You can rely on server-side technologies (like Supabase Edge Functions (https://supabase.com/docs/guides/functions)) to limit the client-side exposure of data. - **Secure API Keys and Credentials**: Avoid storing API keys, access tokens, or other credentials directly in database tables if they are intended to be client-accessible: If service-to-service keys are necessary, consider using secure server-side secrets storage with environment variables for storing secrets (as an example, Supabase Secrets (https://supabase.com/docs/guides/functions/secrets), which are compatible with Edge Functions). Ensure these functions incorporate appropriate authorization checks if they return sensitive information. This follows the principle of defense-in-depth (https://csrc.nist.gov/glossary/term/defense_in_depth). - **Regularly Review RLS Configuration**: Periodically review RLS policies and data schemas, even if the security scanner doesn't show any issues. This ensures they remain effective and align with the application's security requirements, especially following updates to the application or changes in data handling practices. - **Separate tables with sensitive and non-sensitive data**: Since PostgreSQL Row-Level Security can only operate on full rows, if a row has sensitive (e.g. payment information) and non-sensitive (e.g. username) information, even with RLS enabled, attackers will still be able to access sensitive information if direct client-side access to the database is enabled. For cases like these, separate tables should be used, with independent RLS policies to prevent access to the sensitive data by any users. Alternatively, an architectural change like relying on server-side technologies could make the implementation of custom access control easier. Alternatively, PostgreSQL privileges (https://www.postgresql.org/docs/current/ddl-priv.html) can also be used as an advanced feature to implement column-level security. You can find comprehensive information on how to configure RLS at: https://supabase.com/docs/guides/database/postgres/row-level-security (https://supabase.com/docs/guides/database/postgres/row-level-security) ### Detection (#detection) Methods for detecting this vulnerability differ for project owners and external parties. ### For Project Owners/Administrators (#for-project-ownersadministrators) Project owners and administrators should begin by reviewing their RLS policies in their database dashboard. For each table, particularly those storing sensitive information (e.g., users, transactions, API keys), verify that RLS is enabled. Any table created through the Supabase Dashboard will have RLS enabled by default, but tables created via SQL editor will need manual enablement. Examine the defined policies to ensure they are not overly permissive—for example, a simple public read access without conditions on a sensitive table is a red flag. Policies should restrict access based on user roles or ownership (e.g., using conditions like auth.uid() = user_id if the tables don't hold any sensitive information), or restrict client-side access altogether if there are columns with sensitive information. Next, it's crucial to inspect table grants: Check the privileges granted on tables to ensure that the anon and authenticated roles do not have overly broad SELECT, INSERT, UPDATE, or DELETE permissions that could bypass RLS or are otherwise unintended. Finally, manually test the application's API endpoints that interact with the database. This involves attempting to access data for which permission should not be granted (e.g., as an unauthenticated user, or as one user trying to access another user's private data). The dashboards of some vendors allows you to impersonate users and test queries to validate your RLS policies. As an additional resource, you can follow Supabase's production checklist (https://supabase.com/docs/guides/deployment/going-into-prod#security) for more best practices. ## Disclosure Timeline (#disclosure-timeline) - **Discovery Date**: March 20th, 2025 - **Vendor Notification**: March 21st, 2025 - **Vendor Acknowledgement:** March 24th, 2025 - **Vendor Disclosure**: April 15th, 2025 - **Vendor Response**: April 15th, 2025 - **Patch Development**: N/A - **Patch Release**: April 24th, 2025 - **Public Disclosure**: May 29th, 2025 ## Credits (#credits) - **Discovered by**: Matt Palmer - **Additional Contributors**: Kody Low - **Independently reported by**: https://x.com/danialasaria/status/1911862269996118272 (https://x.com/danialasaria/status/1911862269996118272) - **Organization**: Replit, Inc. ## Contact Information (#contact-information) For questions regarding this disclosure: - **Security Contact**: security@replit.com (mailto:security@replit.com) _This disclosure follows responsible disclosure practices and coordinated vulnerability disclosure guidelines._ /posts # Statement on CVE-2025-48757 May 29, 2025 https://x.com/mattyp ### Matt Palmer DevRel & Product @mattyp (https://x.com/mattyp) On March 20th, 2025, we discovered a critical vulnerability in Lovable's implementation of Row Level Security (RLS) policies. Applications developed using its platform often lack secure RLS configurations, allowing unauthorized actors to access sensitive user data and inject malicious data. Lovable applications, being primarily client-driven, rely on external services for backend operations like authentication and data storage. This architecture shifts the security burden to the implementor of the application (https://supabase.com/docs/guides/deployment/shared-responsibility-model). However, misaligned RLS policies between the client-side logic and backend enforcement frequently result in vulnerabilities, where attackers can bypass frontend controls to directly access or modify data. Lovable later introduced a "security scanner," but it merely checks for the existence of any RLS policy, not its correctness or alignment with application logic. This provides a false sense of security, failing to detect the misconfigurations that expose data. This document details the vulnerability, its discovery, and subsequent findings, for which a formal CVE disclosure has been published here (https://www.cve.org/CVERecord?id=CVE-2025-48757). ### Initial Discovery & Scope Assessment (#initial-discovery--scope-assessment) The vulnerability was first identified on March 20th, 2025, while examining Linkable (linkable.site, now offline), a Lovable-built site for generating websites from LinkedIn profiles. [Image: Linkable] (/img/hxk4f-ZovZ-400.jpeg) An inspection of network requests revealed that modifying a query granted access to all data in the project's "users" table. After we highlighted this on a reply reply (https://x.com/mattppal/status/1902819250395549811) on Lovable's Twitter account, Lovable denied the issue, then deleted their tweets and the site. Linkable was later reinstated with a $2 fee. The core issue was not an exposed public API key as we initially thought (Supabase provides public anon keys by design) but the absent RLS configuration. This allowed unrestricted data retrieval from the exposed table (sample in Appendix A1). To determine if this was an isolated incident, we investigated other Lovable-created sites, starting with those on Lovable Launched (https://launched.lovable.dev/)—a showcase presumably featuring polished projects. Access to the list of these sites was gained by manipulating an endpoint on the Launched site itself, which also lacked RLS. We then developed a script to visit the homepage of each Launched site, capture all external network requests, and filter for those made to external sources. For each identified request, the script attempted to modify the request to select all data from the associated endpoint—an operation equivalent to SELECT *, which RLS would typically prevent. Here is a mermaid diagram of this process: [Image: Lovable Launched Scan] (/img/5cjPUzAW2D-1264.svg) ### Automated Scan Findings (#automated-scan-findings) The scan, completed on March 21st, identified 303 endpoints across 170 projects (approximately 10.3% of the 1645 analyzed) with inadequate RLS settings. This indicates widespread RLS misapplication, potentially highlighting systemic issues in Lovable's platform that may predispose projects to insecure data storage. The following anonymized public endpoints from the scan illustrate the types of sensitive data exposed: This script only analyzed homepages and did not attempt to access login-protected areas or perform deeper site scraping. Authenticated sessions on these vulnerable sites could expose even more sensitive data. Users interacting with Lovable-built sites should exercise extreme caution in the data they submit. ### Data Modification Vulnerability (#data-modification-vulnerability) A follow-up assessment of the Linkable site on May 24th, 2025, (a paid site actively maintained by a Lovable employee) was conducted to verify the current vulnerability state. While the endpoint was relocated to /rest/v1/website_generation and RLS appeared active for authenticated requests, removing the authorization header bypassed all access controls by changing the security context of the request from an authenticated user to an unauthenticated one. This allowed unrestricted read access and, critically, unauthorized write operations. We successfully inserted a new record (details in Appendix A2) by issuing a POST request with a crafted payload, including setting "payment_status": "paid", thereby bypassing the Stripe integration. We have attached anonymized user data extracted from the Linkable site in Appendix A3. [Image: request] (/img/1O1g9-ayf2-400.jpeg) This demonstrates that critical security issues, extending to data manipulation and injection, persist even on company-maintained sites despite prior notification and the "Security Scan" feature. This indicates systemic RLS implementation flaws within the Lovable ecosystem. We have notified the owners of these findings. ### Timeline of Disclosure (#timeline-of-disclosure) We notified Lovable of the vulnerability on March 21st, 2025. They confirmed receipt, but never responded to our email. The initial email can be seen in Appendix B1. We followed up with a formal disclosure on April 14th, 2025, referencing the public exploit, and initiated a 45-day disclosure window. The follow-up email can be seen in Appendix B2. - **March 20, 2025:** Initial vulnerability discovered on linkable.site (now offline). - **March 21, 2025:** Broader RLS misconfiguration issue identified across multiple Lovable projects. Lovable emailed regarding scope and severity. - **March 24, 2025:** Lovable confirmed receipt of the email. - **April 14, 2025:** A Palantir engineer independently discovered (https://x.com/danialasaria/status/1911862269996118272) and publicly tweeted about the same vulnerability, demonstrating active exploitation (e.g., extracting personal debt amounts, home addresses, API keys). - **April 14, 2025:** We re-notified Lovable, referencing the public exploit, and initiated a 45-day disclosure window. - **April 24, 2025:** Lovable released "Lovable 2.0" with a "security scan" feature. This did not address the underlying RLS architectural flaw. - **May 29, 2025:** With no meaningful remediation or user notification from Lovable, we published our CVE. Details of our communications are in Appendix B. ### The need for secure-by-default vibe coding (#the-need-for-secure-by-default-vibe-coding) We are publishing this CVE to highlight the critical importance of secure-by-default configurations and best practices in AI-assisted development platforms, especially when they handle sensitive user data. We believe "vibe coding" and similar paradigms should empower users by inherently promoting security, not facilitating vulnerabilities or shifting the burden of security entirety to users (or worse, blaming them for lack of security). Recent advancements have democratized not only coding but also access to sophisticated developer tooling. Coding agents can now implement services like databases and user authorization—tasks that once required days or weeks for professional developers. However, we observe that without robust default settings or guardrails, or even architectural patterns that allow better defense in depth like having a client/server separation, these solutions can generate security vulnerabilities at scale. Consequently, users unfamiliar with web development best practices inadvertently "vibe code" applications that endanger both themselves and their users. We urge Lovable to take immediate action to notify users and implement secure defaults to prevent systemic data exposure. Technical details have been redacted per responsible disclosure but have been shared with Lovable. ## Appendix (#appendix) ### A. Sample Data Accessed from Vulnerable Endpoints (#a-sample-data-accessed-from-vulnerable-endpoints) ### A1 Linkable Users Table Sample (#a1-linkable-users-table-sample) id email 625ff87e-0c0d-46af-9d2e-a618d648e035 a***@***ble.dev ee83085c-0f51-4033-bf9c-f42eaec696ea u***@***hoo.com c6671c86-85a0-4e32-a567-8011ef616565 m***@***aas.io bea7783d-f7a0-4545-9d81-de8f58ac1f92 t***@***ail.com ### A2 Linkable Data Injection (#a2-linkable-data-injection) { "id": "65cfe5b1-8d1b-4524-82f4-e1b4431199bc", "email": "hello@mattpalmer.io", "linkedin_url": "https://www.linkedin.com/in/matt-palmer/", "generation_id": "00b580fe-83a4-44fa-bb1e-c2f1d65999f9", "payment_status": "paid", "website_url": "https://mattpalmer.io", "created_at": "2025-05-25T10:03:33.87533+00:00", "updated_at": "2025-05-25T10:03:33.87533+00:00", "created_by": null } ### A3 Linkable Users Table Sample (#a3-linkable-users-table-sample) [ { "id": "65cfe5b1-8d1b-4524-82f4-e1b4431165bc", "email": "user1@example.com", "linkedin_url": "https://www.linkedin.com/in/user-profile-1/", "generation_id": "00b580fe-83a4-44fa-bb1e-c2f1d65006f9", "payment_status": "pending", "website_url": null, "created_at": "2025-04-01T10:03:33.87533+00:00", "updated_at": "2025-04-01T10:03:33.87533+00:00", "created_by": null }, { "id": "6420adfa-0952-4d19-acd4-1dcccb139817", "email": "user1@example.com", "linkedin_url": "https://www.linkedin.com/in/user-profile-1/", "generation_id": "39dd80de-0614-4cef-ad2f-a13489d20e91", "payment_status": "pending", "website_url": null, "created_at": "2025-04-01T10:03:42.112132+00:00", "updated_at": "2025-04-01T10:03:42.112132+00:00", "created_by": null }, { "id": "65cfe5b1-8d1b-4524-82f4-e1b4431199bc", "email": "hello@mattpalmer.io", "linkedin_url": "https://www.linkedin.com/in/matt-palmer/", "generation_id": "00b580fe-83a4-44fa-bb1e-c2f1d65999f9", "payment_status": "paid", "website_url": "https://mattpalmer.io", "created_at": "2025-05-25T10:03:33.87533+00:00", "updated_at": "2025-05-25T10:03:33.87533+00:00", "created_by": null } ] ### B. Communications / Responsible Disclosure (#b-communications--responsible-disclosure) ### B1 Initial email sent to Lovable team (#b1-initial-email-sent-to-lovable-team) Lovable Team, I hope this message finds you well. My name is Matt, and I work in Developer Relations at Replit. I'm reaching out in good faith to share a security concern that my colleague Kody Low and I have identified. On March 20, 2025, at 1:27 PM, I discovered a vulnerability in a Lovable marketing site (linkable.site, now offline) that allowed me to extract the emails of approximately 500 users who had engaged with the app. I initially reported this on X ( ), but received feedback that there was no issue. Kody dug a bit deeper and found that several featured Lovable projects had auth endpoints or profiles endpoints that returned personally identifiable information (PII), including usernames, emails, and other identifying user data. For example, [ REDACTED ] profiles table is publicly accessible. Upon further investigation with my colleague Kody, we examined and were able to access information from every project listed on the site from one of Lovable's own Supabase projects. By recursing through these projects, we identified multiple instances with publicly accessible database endpoints on their homepages. This exposed information includes subscriptions, names, phone numbers, API keys, payment details, Google Maps tokens, and various other PII. 303 Supabase endpoints were found across 170 websites, but this was only from examining the homepages. Our hypothesis is that many users do not fully understand Row Level Security (RLS) settings on Supabase, and Lovable's implementation is creating these insecure calls when users connect their Supabase without properly configuring auth and RLS. This combination leads to widespread vulnerabilities in the resulting applications. We believe that many developers using Lovable are unaware that they're exposing sensitive information through these database endpoints. I would be happy to provide more technical details or assist with verification of this vulnerability. We are approaching this disclosure responsibly and have not shared these findings publicly beyond my initial tweet. Could you please acknowledge receipt of this email and provide a timeline for when you expect to investigate this issue? Thank you for your attention to this matter. We're reaching out because we care about the security of the AI builder community and believe that addressing this vulnerability will ultimately strengthen your platform. Best regards, Matt ### B2 Responsible Disclosure Sent to Supabase & Lovable Teams (#b2-responsible-disclosure-sent-to-supabase--lovable-teams) # Coordinated Vulnerability Disclosure: Default missing / insufficient Supabase Row Level Security policies Dear Lovable team, Following [ CERT's guide on coordinated disclosure ]( https://certcc.github.io/CERT-Guide-to-CVD/ ), we intend to publicly disclose the vulnerability discussed here after 45 days. This is a follow-up on an email that Matt Palmer sent on March 21, 2025. Affected products: https://lovable.dev ## What is the vulnerability? [ CWE-732: Incorrect Permission Assignment for Critical Resource ]( https://cwe.mitre.org/data/definitions/732.html ) [ CWE-497: Exposure of Sensitive System Information to an Unauthorized Control Sphere ]( https://cwe.mitre.org/data/definitions/497.html ) [ CWE-359: Exposure of Private Personal Information to an Unauthorized Actor ]( https://cwe.mitre.org/data/definitions/359.html ) Projects generated using code generation tools like Lovable using [ Supabase Row Level Security ]( https://supabase.com/docs/guides/database/postgres/row-level-security ) may have lax / insufficiently set security policies by default (unless the user requests such functionality to be enabled) that allow unauthenticated attackers to read arbitrary database tables / rows that may include API tokens (e.g. Gemini API keys) of the developer of the project and Personally Identifiable Information (PII, e.g. usernames, emails, phone numbers) of the users of the project. No user interaction is needed to successfully perform the attack: The attacker can take any unauthenticated POST request sent directly to Supabase from the client-side application, and modify the query to a `select=*` against any exposed database table. **CVSS v3.1 Vector** [ AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N/E:F/RL:X/RC:R ]( https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N/E:F/RL:X/RC:R&version=3.1 ) ### Proof of Concept [ REDACTED ] ## How does an attacker exploit this vulnerability? By inspecting the network traffic of a vulnerable site, especially on a login / profile page, filtering for traffic for Supabase, and replacing any select query payloads with `select=*`, the attacker can get read access to the full rows of full tables that the page used instead of only the rows intended to be used by the project's developer. The attacker can also copy paste any request hitting against supabase, ask to hit against any tables (users, portfolios, etc.) and any of those tables with incorrect RLS are vulnerable to this. The following script is a proof-of-concept to check whether a single site is vulnerable: [ REDACTED ] ## What does an attacker gain by exploiting this vulnerability? (i.e. what is the impact?) Access to API keys (in multiple cases) owned by the project's developer, that could in turn cause escalation of privileges in other systems or unintended spend. Also, over-the-network exfiltration of PII, any data in tables the app accesses where the table does not have RLS policies set. ## How was the vulnerability discovered? We first discovered a vulnerability in a Lovable marketing site (linkable.site, now offline), where the client could issue manipulated queries directly against Supabase only using the `anon_key` sent from the client. As a proof-of-concept, the query was changed to a `select=*` to return the complete user table including emails of over 500 people who had used the site. Later, we saw the vulnerability on another Lovable site and suspected a wide-spread issue. We wrote a simple script that checked for similar vulnerabilities on the sites listed on https://[ l ]( http://lovable.dev )aunched.lovable.dev and found more instances of projects without RLS set at all or incorrectly, and were leaking user data including phone numbers, emails, developer API Keys, and more as detailed above. ## Is this vulnerability publicly known? Probably (see section below). There was a prior public disclosure of a missing RLS policy set for linkable.site (now offline) but this report of the wider and repeated pattern of these vulnerabilities in Lovable-created sites is novel and hasn't been shared with anyone except for Lovable and Supabase. ## Is there evidence that this vulnerability is being actively exploited? Yes: https://x.com/danialasaria/status/1911862269996118272 ## Do you plan to publicly disclose this vulnerability yourself? Yes, on May 28, 2025 ## Contact Information Name: [ REDACTED ] Organization: Replit _End of Appendix_ matt is ## background I work at SpaceXAI (https://x.ai) (via Cursor (https://cursor.com)), but you might know me from my work at Replit (https://replit.com?utm_source=Matt&utm_medium=matts-blog&utm_campaign=mintlify-changelog) or Conductor (https://conductor.build). My goal is to understand and explain technology in a compelling way. ## writing - Custom desktop DSLR mount Using a monitor arm for an articulating desk mount for your DSLR (/posts/2026/09/desktop-camera-setup/) - Chat is all you need on X Grok Bot has a single output, a message. Agents make rigid interfaces unnecessary — the future of software is chat. (https://x.com/mattyp/status/2089758434921160877) - Intro to Grok Bot on X Grok Bot is an agent with a computer, and the first personal agent that feels like it should. (https://x.com/mattyp/status/2087252651348279296) all posts (/posts) ## talks - The future of Cursor Vercel Ship '26 · October 2026 (/decks/2026-10-vercel-ship/) - Agent skills LinkedIn Learning · July 2026 (/decks/2026-07-agent-skills-course/) all decks (/decks/) ## projects - [Image] (/img/ZCb9wktrnN-32.svg) [Image] (/img/E4mIV-JATJ-32.svg) mcut Open-source video editing SDK and editor for TypeScript apps. (https://mcut.io) - [Image] (/img/HiTT16RN7b-80.webp) Shotwell A screenshot editor for iPhone with device frames, backgrounds, shadows, and more. (https://shotwell.app?utm_source=Matt&utm_medium=matts-blog&utm_campaign=homepage-projects) - [Image] (/img/lc6VL9mMeU-80.webp) Shades A Chrome extension for masking emails and sensitive inputs during tutorials and demos. (https://chromewebstore.google.com/detail/shades-secure-input/ggiikomodghnhiaiambecmeaedkelmjn) ## publications - Agent Skills 2026 Course on application and continual improvement for agent skills with LinkedIn Learning. (https://www.linkedin.com/learning/build-reusable-ai-agent-skills) - Vibe Coding with Replit 2025 DataCamp course on building, securing, and deploying apps with Replit. (https://www.datacamp.com/courses/vibe-coding-with-replit) - Vibe Coding 101 2025 DeepLearning.AI course on agentic code development with Replit. (https://www.deeplearning.ai/courses/vibe-coding-101-with-replit) - Fundamentals of Data Transformation 2024 LinkedIn Learning course for data engineering transformation workflows. (https://www.linkedin.com/learning/fundamentals-of-data-transformation-for-data-engineering) - Understanding ETL 2023 O'Reilly / Databricks technical guide to modern ETL pipelines. (https://learning.oreilly.com/library/view/understanding-etl-updated/0642572226961/) ## beyond work When I'm not working, you'll find me lifting, hiking, riding motorcycles, or tending to my plants. angel investments - [Image] (/img/j1q7U9kkUK-200.jpeg) (https://lancedb.com) - [Image] (/img/1_1B_8gKk8-150.jpeg) (https://roundtable.ai) - [Image] (/img/fHswEU1Ywt-150.webp) (https://sazabi.ai) - [Image] (/img/vPp4P3ByB1-150.jpeg) (https://www.thecompany.company/) ## elsewhere x / twitter (https://x.com/mattyp) youtube (https://youtube.com/@mattpalmer) linkedin (https://www.linkedin.com/in/matt-palmer/) 0 wpm 100% acc caps lock click to focus tab to restart · esc to exit / # matt's agents Matt's Agents is a personal automation app built and operated by Matt Palmer (/). It connects AI agents and scripts that Matt runs to his own Google account, so they can do routine work on his behalf, such as reading and drafting email, checking and scheduling calendar events, and organizing documents. ## Who it's for The app is for Matt's personal use only. It isn't offered to the public, there's no sign-up, and there's nothing to buy. The only Google account it connects to is Matt's. ## How it works - Matt signs in with Google and approves the permissions the agents need. - Google issues an access token, which is stored privately on Matt's own machines and infrastructure. - Agents use the token to call Google APIs for the task Matt asked them to do, and nothing else. - Access can be revoked at any time from Google Account permissions (https://myaccount.google.com/permissions). ## Your data Google user data is never sold, shared with third parties for advertising, or used to train AI models. Read the full privacy policy (/agents/privacy/) and terms of service (/agents/terms/) for details. ## Contact Questions? Email hello@mattpalmer.io (mailto:hello@mattpalmer.io). /agents/ # privacy policy _Last updated: October 14, 2026_ This policy explains how Matt's Agents (/agents/) (the "app"), operated by Matt Palmer, accesses, uses, stores, and shares data when you sign in with Google. The app is a personal tool. Its only intended user is Matt Palmer. ## Data the app accesses When you sign in with Google, the app requests only the permissions (scopes) you approve on Google's consent screen. Depending on those permissions, the app may access: - **Basic profile information:** your name, email address, and profile picture, used to identify the signed-in account. - **Gmail:** email messages, drafts, and labels, used to read, summarize, draft, send, and organize email when you ask an agent to. - **Google Calendar:** calendars and events, used to view your schedule and create or update events when you ask an agent to. - **Google Drive, Docs, and Sheets:** files and their contents, used to find, read, create, and edit documents when you ask an agent to. The app never requests your Google password. ## How the data is used Google user data is used only to carry out tasks you explicitly request, and to provide and improve those user-facing features. It is not used for advertising, profiling, or any purpose unrelated to the app's functionality. To complete a task, the relevant data may be sent to an AI model provider (for example, an LLM API) for processing at that moment. Data is sent only under terms that prohibit the provider from using it to train its models. ## How the data is stored and protected - OAuth access and refresh tokens are stored encrypted on infrastructure controlled by Matt Palmer and are never published or shared. - Google data is processed in memory for the task at hand. The app does not keep a copy of your mailbox, calendar, or files. - Short-lived logs may record which actions an agent took, for debugging. These are kept for no more than 30 days. - All data is transferred over encrypted (HTTPS/TLS) connections. ## Sharing Google user data is not sold, rented, or shared with third parties, except: - with AI model providers, only as needed to complete a task you requested, as described above; - when required by law; or - with your explicit consent. No humans read your Google data unless you give explicit permission, it's needed for security reasons or to comply with the law, or it's been aggregated and anonymized. ## Google API Services User Data Policy The app's use and transfer of information received from Google APIs to any other app will adhere to the Google API Services User Data Policy (https://developers.google.com/terms/api-services-user-data-policy), including the Limited Use requirements. Google Workspace data is not used to develop, improve, or train generalized AI or machine-learning models. ## Retention and deletion Tokens are kept until access is revoked. You can revoke the app's access at any time from your Google Account permissions (https://myaccount.google.com/permissions) page. When access is revoked, the app's stored tokens stop working and are deleted. You can also ask for any stored data to be deleted by emailing the address below, and it will be removed within 30 days. ## Children The app isn't intended for anyone under 13, and doesn't knowingly collect data from children. ## Changes If this policy changes, the updated version will be posted on this page with a new "last updated" date. Use of the app is also governed by the terms of service (/agents/terms/). ## Contact Questions about this policy or your data: hello@mattpalmer.io (mailto:hello@mattpalmer.io). /agents/ # terms of service _Last updated: October 14, 2026_ These terms govern your use of Matt's Agents (/agents/) (the "app"), operated by Matt Palmer. By signing in to or using the app, you agree to these terms. If you don't agree, don't use the app. ## The app Matt's Agents is a personal automation tool that lets AI agents act on a connected Google account, at the account owner's direction. It is intended for Matt Palmer's personal use and is not offered as a public service. ## Eligibility and access Access is limited to accounts explicitly authorized by Matt Palmer. Access may be suspended or revoked at any time, for any reason, without notice. ## Your Google account The app accesses your Google account only with the permissions you grant on Google's consent screen. You're responsible for the tasks you ask agents to perform and for reviewing their results. You can revoke the app's access at any time from your Google Account permissions (https://myaccount.google.com/permissions) page. Your use of Google services remains subject to Google's Terms of Service (https://policies.google.com/terms). ## Acceptable use You agree not to use the app to: - break any law or infringe anyone's rights; - send spam or other unsolicited messages; - access data or accounts you aren't authorized to access; or - interfere with or attempt to disrupt the app or any Google service. ## Privacy How the app handles data is described in the privacy policy (/agents/privacy/), which is part of these terms. ## AI-generated output Agents use AI models that can make mistakes. Drafts, summaries, and actions produced by the app may be inaccurate or incomplete, and you should review them before relying on them. ## No warranty The app is provided "as is" and "as available", without warranties of any kind, express or implied, including fitness for a particular purpose, reliability, or availability. ## Limitation of liability To the fullest extent permitted by law, Matt Palmer is not liable for any indirect, incidental, special, consequential, or punitive damages, or any loss of data, revenue, or profits, arising from your use of the app. ## Changes These terms may be updated from time to time. The updated version will be posted on this page with a new "last updated" date. Continuing to use the app after a change means you accept the updated terms. ## Governing law These terms are governed by the laws of the State of California, without regard to its conflict-of-law rules. ## Contact Questions about these terms: hello@mattpalmer.io (mailto:hello@mattpalmer.io). # beautiful ui libraries A running shelf of components, interaction details, and tiny libraries that make the web feel more tactile. ## links - [Image] (/img/2uvJejG6Oo-400.jpeg) image.jakubantalik.com Image component Jakub Antalik's interactive image component playground for responsive crops, masks, and presentation details. (https://image.jakubantalik.com/) - [Image] (/img/ueDogKM9KE-400.jpeg) [Image] (/img/jN6P-4eRi0-400.jpeg) [Image] (/img/VDhv27PjOo-400.jpeg) [Image] (/img/2uvJejG6Oo-400.jpeg) Scroll Mask Scroll effect A focused mask pattern for scrollable regions that keeps overflow readable and elegant. (https://twilson.net/scroll-mask) - [Image] (/img/jN6P-4eRi0-400.jpeg) [Image] (/img/2uvJejG6Oo-400.jpeg) [Image] (/img/VDhv27PjOo-400.jpeg) Parallax Floating Motion component A Fancy Components image treatment with layered motion and spatial depth. (https://www.fancycomponents.dev/docs/components/image/parallax-floating) - Status Copy Copied Saved Copy TextMotion / slot-text Text animation A dependency-free text roll animation for tiny, tactile UI labels across vanilla, React, and Vue. (https://textmotion.dev/) - ⠋⠉⠙⠚ unicode-animations Text animation Tiny unicode-based animation primitives for expressive terminal-like UI moments. (https://www.npmjs.com/package/unicode-animations)source post (https://x.com/gunnargray/status/2023497853046604227) / # decks - The future of Cursor Vercel Ship '26 (/decks/2026-10-vercel-ship/) October 2026 - Agent skills LinkedIn Learning (/decks/2026-07-agent-skills-course/) July 2026 - Content is code AI Engineer (/decks/2026-06-content-is-code/) June 2026 - Sakana Fugu O'Reilly This Week in AI (/decks/2026-06-oreilly-sakana-fugu/) June 2026 - Show us your agent skills Hugo Bowne-Anderson's podcast (/decks/2026-06-show-us-your-skills/) June 2026 - Social studies DevRel with Matt (/decks/2026-06-social-studies/) June 2026 /decks/ # Content is code AI Engineer · June 2026 [Image: Content is code. AI Engineer] (https://media.mattpalmer.io/decks/2026-06-content-is-code/slides/001.webp?v=588bde9362) 1 / 25 - Slide 1: Content is code. AI Engineer - Slide 2: Code is communication - Slide 3: Technical communication: docs, changelogs, email (product marketing), video, DevRel - Slide 4: The Conductor docs introduction page - Slide 5: The Conductor changelog: a (mostly) boring update - Slide 6: A Conductor update email - Slide 7: A product tour of Conductor - Slide 8: Matt on camera with animated React overlays - Slide 9: An animated walkthrough of a Conductor workflow - Slide 10: Hi, I’m Matt: data engineer, DevRel at Replit, DevEx at Conductor - Slide 11: Three eras: handcrafted, code (expensive), code (cheap) - Slide 12: Three eras: handcrafted, code (expensive), code (cheap), you are here - Slide 13: Era 1, handcrafted: everything is manual, the only levers are time (money) and skill. If I want it, I’d better pay someone (an agency?) - Slide 14: Era 2, expensive code: code and cloud are mature, engineering is the bottleneck. You’d better pay an engineer. Content is not a priority - Slide 15: Era 3: video, docs, design, and motion graphics are all Claude. You’d better pay Anthropic - Slide 16: Code is communication, with “cheap” struck through - Slide 17: Code is communication: the fastest way to build an asset is through code. “My favorite medium is TypeScript.” Imagine hearing that in 2024 - Slide 18: Structure is expensive - Slide 19: Structure is expensive: codebase, brand guidelines, design tokens, clean PRs, internal docs - Slide 20: Structure is the difference - Slide 21: Conscientiousness: the quality of being meticulous, careful, and guided by a strong sense of moral or professional duty - Slide 22: AI rewards conscientiousness, organizational excellence, and structure. Skills - Slide 23: Code is communication: a “shift-left moment” for content, where the codebase becomes the source of truth. Only works with structure and organizational excellence - Slide 24: 2026: year of the creative technologist - Slide 25: 2027: year of the content engineer /decks/ # Sakana Fugu O'Reilly This Week in AI · June 2026 [Image: Sakana Fugu. O] (https://media.mattpalmer.io/decks/2026-06-oreilly-sakana-fugu/slides/001.webp?v=993b6a890f) 1 / 11 - Slide 1: Sakana Fugu. O'Reilly - Slide 2: What is Fugu? A multi-agent system delivered as a single model. Coordinates and orchestrates many models. Delivers models via an API, not a harness - Slide 3: The Sakana Fugu homepage: one model to command them all - Slide 4: The Sakana console models page - Slide 5: This is confusing: Fugu coordinates models, one of those models is Fugu. It’s a recursive coordinator - Slide 6: Simplified architecture: Sakana Fugu orchestrating an LLM pool of open models and itself - Slide 7: The Trinity paper on arXiv: an evolved LLM coordinator - Slide 8: Architecture sketch of the Fugu pipeline - Slide 9: Short version. Query: user input. Coordinator: assembles a “mixture of experts”. Validator: validates output. Response: model output - Slide 10: Using Fugu: it’s a model, so don’t install it, call it. Three ways to use it - Slide 11: Using Fugu: API, OpenCode, Codex /decks/ # Show us your agent skills Hugo Bowne-Anderson's podcast · June 2026 [Image: Show us your agent skills: AI workflows (personal code), my skills, meta points about AI dev and Conductor] (https://media.mattpalmer.io/decks/2026-06-show-us-your-skills/slides/001.webp?v=73f4be385c) 1 / 15 - Slide 1: Show us your agent skills: AI workflows (personal code), my skills, meta points about AI dev and Conductor - Slide 2: AI-pilled on personal code: I used to be skeptical of “just use AI for XYZ”, very skeptical of personal software, and extremely skeptical of editing system configs. Now I do all of these things - Slide 3: AI-pilled on personal code: presentations and video, tools and software, dotfiles and Nix. Documentation? Accounting? Taxes? - Slide 4: Let’s take a look: system config, video, social, and personal software, skills and meta - Slide 5: Let’s take a look: dotfiles (Nix), Matt’s workflows, skills.sh and a personal repo - Slide 6: Demo - Slide 7: Skills are tools - Slide 8: The skill toolbox: you can spend lots of money on tools, but the only tools that matter are the ones you use - Slide 9: A bad workman blames his tools - Slide 10: Good skills are compounding: the best skills are the ones you use, and the ones you improve. This also applies to software - Slide 11: The problem with vibe coding: useful things get more useful with continuous improvement, one-off apps don’t compound, AI tooling favors the conscientious and pedantic. The good news: AI devtools are perfect for this - Slide 12: Compounding is about habits. Habits are about developer experience: feedback loops, flow state, and cognitive load - Slide 13: Skills experience: make skills easy to install, easy to improve, and easy to use - Slide 14: Skills experience: skills.sh, skills.sh, IDE (?) - Slide 15: How I use skills.sh: wire up a private repo to serve your skills, create an alias skills-add, create another alias skill-push /decks/ # Social studies DevRel with Matt · June 2026 [Image: Social studies: devrel with Matt] (https://media.mattpalmer.io/decks/2026-06-social-studies/slides/001.webp?v=d22d0c2343) 1 / 15 - Slide 1: Social studies: devrel with Matt - Slide 2: Developer relations. Community: talk to people. Content: build and share. Product: improve and document a product. whatisdevrel.com - Slide 3: Compounding: learn and teach (content), interact (community), deliver feedback (product). Over time this turns into goodwill, relationships, and trust. But it starts with being useful and expecting nothing in return - Slide 4: Be as useful as possible - Slide 5: Hi, I’m Matt: in a former life I was a data engineer, then I led devrel and content at Replit, now I focus on devex at Conductor - Slide 6: At Replit: content that taught people how to build, community through interaction (online and offline), and using both to improve the product - Slide 7: Good devrel: learn or build something useful, share it, notice patterns, iterate. Good social: have a useful point of view, give people something, notice what works, iterate - Slide 8: What people see: YouTube grew 4x to 100k+, personal audience grew to 100k+, 100k+ took courses, shareholders received value - Slide 9: What people remember: “You’re the person who sends me emails”, “You’re the person from the tutorial videos”, “I took your course, you helped me”. People remember people, not brands. The greatest form of goodwill is being useful - Slide 10: What people remember: “I’ve been to your apartment”, “Do you live at [redacted] address?”, “Nice legs bro”. This is a blessing and a curse - Slide 11: Be as useful as possible - Slide 12: Social capital - Slide 13: Two games: B2C posting is about being useful, B2B posting is about high-quality opinions. Two very different audiences. B2B = reputation - Slide 14: Would you rather have 100k crypto followers, or 100 followers who are CEOs? People do judge you on your content. A banger tweet might not be worth your reputation - Slide 15: What is good devrel? It’s very similar to good social: be useful, be authentic, build in public, have restraint. Community, content, product /decks/ # Agent skills LinkedIn Learning · July 2026 [Image: Agent skills. LinkedIn Learning] (https://media.mattpalmer.io/decks/2026-07-agent-skills-course/slides/001.webp?v=d978acf4df) 1 / 31 - Slide 1: Agent skills. LinkedIn Learning - Slide 2: Three themes: good skills are not complicated skills, skills are software, skills need to be maintained - Slide 3: Lesson 1: run a skill - Slide 4: What is a skill? Describe a task in plain language, the agent scans skill descriptions, loads the matching SKILL.md, and follows a repeatable workflow - Slide 5: Invoking skills. Implicit: describe the task and the agent matches the skill description. Explicit: name the skill and skip the inference. Same workflow either way - Slide 6: Lesson 2: inspect a skill - Slide 7: Skill components: SKILL.md for required instructions, assets for curated data, references for deeper context, scripts for repeatable exact work - Slide 8: Progressive disclosure: metadata stays visible while the agent scans, the skill body enters context after it triggers, extras load only when needed - Slide 9: Anatomy of a skill: frontmatter with a name and a description that triggers it, then the body instructions - Slide 10: Lesson 3: learn from existing skills - Slide 11: Trust checklist: read the skill, installs are easy to game, check the author, check the commit history, watch for AI slop, skills are software - Slide 12: Skill characteristics: installs, commits, and reputation can look strong while the source still needs review. A trustworthy skill is readable, narrow, current, and honest about what it can touch - Slide 13: Hidden instructions: a rendered skill can look harmless while a hidden comment in the source changes the task - Slide 14: Global vs local install. Global: always available, useful for personal habits, can pollute context. Local: scoped to the project, better for targeted updates, more maintenance - Slide 15: Symlinks: one real skill lives in one place, each agent directory points at it, update the source once and every link gets the change - Slide 16: Lesson 4: decide what should be a skill - Slide 17: Should this be a skill? Will this recur? Can the steps be written down? Does local context matter? Would a teammate use it? - Slide 18: Skills vs MCP. Use a skill when the process should travel with the project. Use MCP when the agent needs to touch the world - Slide 19: Where skills meet MCP: skills are repeatable workflows, MCP is live tools and fresh state, and a skill can drive MCP tools - Slide 20: Loading context: MCP is static, with every tool definition loaded up front. Skills are dynamic, loading their body on trigger and resources only when needed - Slide 21: Proactive vs reactive: encode a workflow you already know before the agent needs it, or turn a correction from a real task into a skill - Slide 22: Prompts, scripts, skills, MCP: a prompt is a fast instruction, a script is exact execution, a skill is workflow plus context loaded by trigger, MCP is tools and resources over a live connection - Slide 23: Lesson 5: build a tiny skill - Slide 24: Lesson 6: make it reusable - Slide 25: Understand the workflow: visit svgl.app, search for the company logo, copy the SVG or PNG, paste it into the assets folder, reference it in markdown - Slide 26: Checking coverage: the API covers visiting, searching, and copying, a tool call covers pasting, and the skill must specify how to reference the file - Slide 27: Reusable skill brief: outcome, trigger, inputs, process, constraints, and what belongs in SKILL.md vs references, scripts, and assets - Slide 28: Building skills is iterative: create the smallest useful skill, test it on a real task, observe vague steps and errors, update the instructions - Slide 29: Lesson 7: maintain a skills repo - Slide 30: Maintain the loop: use, inspect, adapt, build, maintain - Slide 31: Continuously curated skills compound /decks/ # The future of Cursor Vercel Ship '26 · October 2026 [Image: Blank slide] (https://media.mattpalmer.io/decks/2026-10-vercel-ship/slides/001.webp?v=44b003539d) 1 / 127 - Slide 1: Blank slide - Slide 2: SpaceX logo animation - Slide 3: The Future of Cursor. Matt Palmer, Ship '26 - Slide 4: Matt Palmer, @mattyp. Developer experience and community - Slide 5: I have a confession - Slide 6: MNPI. Dana Whitfield (Legal) is typing - Slide 7: Where we’re headed - Slide 8: Where we’re headed: SpaceXSI is for builders - Slide 9: Where we’re headed: SpaceXSI is for builders - Slide 10: Where we’re headed: SpaceXSI is for builders. We make tools for people - Slide 11: Where we’re headed: SpaceXSI is for builders. We make tools for people. We’ll keep building them - Slide 12: Where we’re headed: we’re going to keep making Cursor better - Slide 13: Today: how I build with Projects, Cloud Agents, and Vercel - Slide 14: You'll leave with: long running agents - Slide 15: You'll leave with: long running agents, feedback loops (for agents!) - Slide 16: You'll leave with: long running agents, feedback loops, and inputs, outputs, software - Slide 17: An agent with a computer: one chat, an agent, and its own computer with a repo, browser, and apps - Slide 18: An agent with a computer, labeled Projects: one chat, an agent, and its own computer - Slide 19: A Cursor agent chat beside the agent's own desktop - Slide 20: A Cursor Projects chat with the agent's computer open - Slide 21: An agent with a computer, labeled Projects: one chat, an agent, and its own computer - Slide 22: An agent with a computer, labeled Projects and Cursor & Bot - Slide 23: An agent with a computer, labeled Projects and Cursor & Bot. It helps to be in the cloud - Slide 24: Wipe between the Slack template and live Slack - Slide 25: The botpfp.com avatar editor - Slide 26: An agent editing a clip in mcut - Slide 27: Projects - Slide 28: Then: configure development environment - Slide 29: Then: configure development environment, dogfood the product - Slide 30: Then: configure development environment, dogfood the product, break things - Slide 31: Then: configure development environment, dogfood the product, break things, fix and improve the product - Slide 32: Now: configure agent environment - Slide 33: Now: configure agent environment, configure agent feedback loops - Slide 34: Now: configure agent environment, configure agent feedback loops, communicate your vision - Slide 35: Now: configure agent environment, configure agent feedback loops, communicate your vision, fix and improve the product - Slide 36: As engineers... - Slide 37: As engineers... we’ve always automated - Slide 38: As engineers... we’ve always automated. Why should agents be any different? - Slide 39: Prompt: Make a video of Slack, moving the way Slack moves. - Slide 40: Prompt: Make a video of Slack, moving the way Slack moves. Reply: Ok, bet - Slide 41: Prompt: Make a video of Slack, moving the way Slack moves. Reply: Ok, bet. Result: temu slack - Slide 42: An agent recording Slack in its own browser - Slide 43: Wipe between the Slack template and live Slack - Slide 44: Easing curves for the Slack thread and agent templates - Slide 45: Camera scale over time: template versus the Slack reference - Slide 46: Slack easing curves, animated - Slide 47: A Slack thread rendered from a template - Slide 48: Close-up wipe between the Slack thread template and live Slack - Slide 49: A Slack thread rendered from a template - Slide 50: The inner loop: do, check, done - Slide 51: The inner loop: do, check, done, and fix - Slide 52: The inner agent loop: do, check, fix, done. What is “done”? - Slide 53: The outer loop wraps the inner loop: do, check, fix - Slide 54: The outer loop: inputs (references, preferences, access) feed the inner loop, and outputs (artifacts, screenshots, analytics) go to a judge - Slide 55: How? - Slide 56: The outer loop, annotated: examples, product - Slide 57: The outer loop, annotated: examples, product; development environment - Slide 58: The outer loop, annotated: examples, product; development environment; previews, motion curves - Slide 59: Is this software? Kinda - Slide 60: Is this software engineering? Yes. - Slide 61: The botpfp.com avatar editor - Slide 62: Inner loop: a Cursor Cloud Project - Slide 63: Inner loop: a Cursor Cloud Project, a working environment - Slide 64: Inner loop: a Cursor Cloud Project, a working environment, integrating with Vercel - Slide 65: Build history for the botpfp.com project - Slide 66: Environment settings and secrets for the agent - Slide 67: Outer loop: skill definitions - Slide 68: Outer loop: skill definitions, references - Slide 69: Outer loop: skill definitions, references, verification loops - Slide 70: Outer loop: skill definitions, references, verification loops, pstack and poteto mode - Slide 71: Improving inputs - Slide 72: Improving inputs: how do I build a flywheel? - Slide 73: botpfp.com on a phone - Slide 74: A feedback board for botpfp.com - Slide 75: Improving outputs - Slide 76: Improving outputs: how do I give better feedback? - Slide 77: Swiping through botpfp.com avatars - Slide 78: A preset workbench comparing a generated avatar with its reference - Slide 79: botpfp.com analytics: top presets, most exported characters, and visits by referrer - Slide 80: The outer loop wraps the inner loop: do, check, fix - Slide 81: The outer loop: inputs (references, preferences, access) feed the inner loop - Slide 82: The outer loop: inputs (references, preferences, access) feed the inner loop, and outputs (artifacts, screenshots, analytics) go to a judge - Slide 83: The outer loop, annotated: reference swiper, user feedback - Slide 84: The outer loop, annotated: reference swiper, user feedback; analytics, DialKit - Slide 85: The outer loop, annotated: reference swiper, user feedback; analytics, DialKit - Slide 86: The outer loop can be software - Slide 87: The ladder: do (workflow) - Slide 88: The ladder: do (workflow), codify (skill) - Slide 89: The ladder: do (workflow), codify (skill), generalize (abstraction) - Slide 90: The ladder: do (workflow), codify (skill), generalize (abstraction), automate (schedule) - Slide 91: The ladder: do (workflow), codify (skill), generalize (abstraction), automate (schedule) - Slide 92: When it breaks: understand - Slide 93: When it breaks: understand, encode - Slide 94: When it breaks: understand, encode, generalize - Slide 95: When it breaks: understand, encode, generalize, verify - Slide 96: When it breaks: understand, encode, generalize, verify. You’re not a victim of slop! - Slide 97: When it breaks: understand, encode, generalize, verify. You’re not a victim of slop! You’re an engineer! - Slide 98: Let’s build an editor - Slide 99: The old way: build, test, iterate - Slide 100: The old way: build, test, iterate. For a video editor, this is a long loop - Slide 101: The flip. Prompt: Edit this clip in mcut to match my hand edit. - Slide 102: The flip. Prompt: Edit this clip in mcut to match my hand edit. Reply: On it. I'll report every bug I hit. - Slide 103: The loop: edit by hand - Slide 104: The loop: edit by hand, teach (wow) - Slide 105: The loop: edit by hand, teach (wow), observe - Slide 106: The loop: edit by hand, teach (wow), observe, correct - Slide 107: An agent editing a talking-head clip in mcut - Slide 108: Removing retakes in one step in mcut - Slide 109: An agent editing a clip in mcut - Slide 110: An agent finishing an edit in mcut - Slide 111: How? - Slide 112: Pick the right environment - Slide 113: Pick the right environment: Cursor - Slide 114: Pick the right environment: Cursor, Vercel - Slide 115: Pick the right environment. One chat, many agents - Slide 116: Pick the right environment. One chat, many agents - Slide 117: Pick the right environment. One chat, many agents. Don’t live with broken windows - Slide 118: Pick the right environment. One chat, many agents. Don’t live with broken windows. Make it, then make it better - Slide 119: Pick the right environment. One chat, many agents. Don’t live with broken windows. Make it, then make it better. Conquer slop - Slide 120: Pick the right environment. One chat, many agents. Don’t live with broken windows. Make it, then make it better. Conquer slop - Slide 121: One chat, an agent, and its own computer with a repo, browser, and apps - Slide 122: The outer loop: inputs (references, preferences, access) feed the inner loop, and outputs (artifacts, screenshots, analytics) go to a judge - Slide 123: If you can do it - Slide 124: If you can do it, an agent can - Slide 125: Writing a React hero component in a code editor - Slide 126: A Cursor agent cleaning up React effects - Slide 127: SpaceX logo animation [Image: Matt Palmer] (/img/4Uxl90Kn9i-400.jpeg) # Matt Palmer devrel & product x (twitter) (https://x.com/mattyp)youtube (https://youtube.com/@mattpalmer)linkedin (https://www.linkedin.com/in/matt-palmer/)website (/) / # writing - Custom desktop DSLR mount (/posts/2026/09/desktop-camera-setup/) September 20, 2026 - Chat is all you need on X (https://x.com/mattyp/status/2089758434921160877) August 18, 2026 - Intro to Grok Bot on X (https://x.com/mattyp/status/2087252651348279296) August 11, 2026 - Productivity is not progress on X (https://x.com/mattyp/status/2079677432727293953) July 24, 2026 - A beginner's guide to transit in San Francisco on X (https://x.com/mattyp/status/2034312278766739456) April 3, 2026 - What's new in Replit Agent 4 on X (https://x.com/mattyp/status/2030457652078354432) March 11, 2026 - How to build anything on X (https://x.com/mattyp/status/2028643193416499200) March 10, 2026 - Introducing Replit Animation on X (https://x.com/mattyp/status/2024591613432098817) February 19, 2026 - Agent Skills are now available in Replit on X (https://x.com/mattyp/status/2017982570646884353) February 3, 2026 - Run Claude Code on your phone in 5 minutes on X (https://x.com/mattyp/status/2013453084614107136) January 20, 2026 - Vibing with Claude Code & Replit on X (https://x.com/mattyp/status/2008685193088167937) January 7, 2026 - 52 weeks of changelogs (/posts/2025/12/claude-agent-changelog/) December 27, 2025 - 8 rules for better docs (/posts/2025/10/8-rules-for-better-docs/) October 18, 2025 - One of one (/posts/2025/10/one-of-one/) October 4, 2025 - Signal, noise, and storytelling (/posts/2025/09/signal-noise-and-storytelling/) September 29, 2025 - Mapping Apple Color Emoji on X (https://x.com/mattyp/status/1967256571752452096) September 15, 2025 - Content is king (/posts/2025/09/content-is-king/) September 3, 2025 - On demand learning (/posts/2025/08/on-demand-learning/) August 13, 2025 - A web-enabled coding agent in 200 lines (/posts/2025/08/coding-agent-in-200-lines/) August 6, 2025 - What is developer marketing? (/posts/2025/07/developer-marketing/) July 4, 2025 - Hats off to Replit (/posts/2025/06/hats-off-replit/) June 23, 2025 - Precision, salience, and billing (/posts/2025/06/precision-salience-and-billing/) June 10, 2025 - Statement on CVE-2025-48757 (/posts/2025/05/statement-on-CVE-2025-48757/) May 29, 2025 - CVE-2025-48757 (/posts/2025/05/CVE-2025-48757/) May 29, 2025 download all (/assets/presskit/matt-palmer-presskit.zip) # presskit Official bio, links, and media notes for podcasts, events, interviews, and partner pages. ## bio Matt Palmer works on Developer Experience at SpaceXAI (https://x.ai) (via Cursor (https://cursor.com)), focusing on AI developer tools, developer education, and product storytelling. Previously, he led Developer Experience at Conductor (https://conductor.build) and Developer Relations at Replit (https://replit.com?utm_source=Matt&utm_medium=matts-blog&utm_campaign=presskit) during its shift from an online IDE to an AI-native product. His work focuses on making technical ideas clear, useful, and human. Outside work, Matt spends a lot of time lifting, hiking, riding motorcycles, and trying to keep an increasingly unreasonable number of plants alive. His current numbers are: 310 lb bench, 455 lb squat, and 525 lb deadlift. ## short bio Matt Palmer is a DevRel and product leader focused on AI devtools, developer education, and making complex technical concepts accessible through writing and video. Away from the keyboard, he is usually lifting, hiking, riding motorcycles, or caring for plants. ## headshots [Image: Matt Palmer primary headshot] (/img/Sx7QsuBCAM-400.webp) /img/presskit/headshot-primary.png ### primary headshot Preferred for event pages, podcast listings, and speaker announcements. [Image: Matt Palmer alternate headshot] (/img/dpCZX-PI6C-400.jpeg) /img/presskit/headshot-alternate.jpg ### alternate headshot Use when a wider or more editorial crop works better. [Image: Matt Palmer casual headshot] (/img/phz3f23qwM-400.jpeg) /img/presskit/headshot-casual.jpg ### casual headshot Use for informal features, community posts, and video-adjacent contexts. ## video and action images [Image: Matt Palmer speaking in a video frame] (/img/AF7dUCBZE1-400.jpeg) /img/presskit/talking-head-action-2.png [Image: Matt Palmer in a talking-head video frame with agent interface on screen] (/img/YKEnEM791Q-400.webp) /img/presskit/talking-head-action-4.png [Image: Matt Palmer smiling and giving a peace sign in a talking-head video frame] (/img/Bao5HgvMYp-400.jpeg) /img/presskit/talking-head-action-5.png [Image: Matt Palmer talking with two people in a candid conversation] (/img/B7BKKYinwm-400.webp) /img/presskit/candid-group-2026-06-02.png ## links website (https://mattpalmer.io) youtube (https://youtube.com/@mattpalmer) x / twitter (https://x.com/mattyp) linkedin (https://www.linkedin.com/in/matt-palmer/) hello@mattpalmer.io (mailto:hello@mattpalmer.io) creator gear # recording setup Camera, audio, lighting, display, and mobile video gear I use to record YouTube videos, product demos, and walking vlogs, plus the editing tools I cut them with. desk video ## camera & lens The main camera runs HDMI out into a capture card, so it can act like a high-quality 4k webcam. ### core - Camera Sony Alpha 7 IV Full-Frame Mirrorless Camera Main talking-head camera. (https://amzn.to/3GEJvaS) - Lens Sony FE 20mm F1.8 G Full-Frame Prime Lens Wide, bright main lens for the desk setup. (https://amzn.to/4nmSlLB) - Tripod SmallRig CT-20 Camera Tripod Portable support when the camera is not mounted to the desk. (https://amzn.to/4lnBczp) ### mounting & capture - Capture card Elgato HD60 X External Capture Card Brings the HDMI feed into the Mac. (https://amzn.to/4nEfCs1) - Dummy battery Gonine NP-FZ100 Dummy Battery Power Adapter Keeps the camera powered for long sessions. (https://amzn.to/4lDi1RU) - Desk arm VIVO All-in-One Computer Studio Desk Mount Keeps the camera position consistent above the desk. (https://amzn.to/3TpVITT) - Mounting bracket SmallRig Foldable L-Bracket for Sony Alpha 7 IV Adds safer mounting points and positioning options. (https://amzn.to/4eFd60G) - Mount ULANZI U-80L Metal Ball Head Camera Mount Fine-tunes the camera angle. (https://amzn.to/44Bbtwf) ### vesa camera mount how I built it → (/posts/2026/09/desktop-camera-setup/) - Arm Ergotron Monitor Arm A VESA monitor arm carries the camera. The gas piston smooths adjustments and absorbs desk vibration. (https://amzn.to/4hcF7y7) - Plate L-Shape Cheese Plate Bolts to the VESA mount and gives the camera and monitor a shared platform. (https://amzn.to/4jfTqEJ) - Ball mount Ball Mount with Quick-Release Head Aims the camera and pops it off the plate in seconds. (https://amzn.to/4hf7tb5) - Hardware Camera Screw Kit Screws to join the plate, arm, and ball mount. (https://amzn.to/4Apk5VN) - Optional M4 Nuts Spare hardware for the VESA bolts. (https://amzn.to/46B8Gog) walking vlogs ## mobile video For walking and talking, the goal is speed: something stabilized, compact, and easy to start recording with. ### core - Vlog camera DJI Osmo Pocket 3 Creator Combo Small stabilized camera for walking and talking videos. (https://amzn.to/4d3bNcJ) voice first ## audio Shotgun mics stay out of frame and lavs are useful on the move. If the room is noisy, the studio mic is the better bet. ### core - Studio mic Shure SM7B Dynamic Studio Microphone Primary desk mic for controlled recordings. (https://amzn.to/4eATxqa) - Shotgun mic RODE NTG4+ Shotgun Microphone Cleaner frame when a visible mic would distract. (https://amzn.to/3PwtsAu) ### accessories - Shotgun mount RODE SM4-R Rycote Lyre Shockmount Shock mount for the shotgun mic. (https://amzn.to/3GzPo9k) - Rode mount arm RODE PSA1+ Professional Studio Arm Adjustable arm for mic placement. (https://amzn.to/4lLSS7O) - Audio mixer Elgato Wave XLR MK.2 USB Audio Interface XLR input and level control for the Mac. (https://amzn.to/4nq0H52) - Cable Elgato XLR Microphone Cable Simple XLR cable for the mic chain. (https://amzn.to/40Md4OD) ### lavalier - Budget Hollyland Lark MAX 2 Wireless Lavalier Microphone Wireless lav option when price matters. (https://amzn.to/4oq8XBJ) - Tried & true DJI Mic 3 Wireless Microphone Kit Wireless lav setup for dependable mobile audio. (https://amzn.to/47uk4SW) image quality ## lighting Great lighting can make a cheap camera look expensive. Video quality is mostly about controlling light and sound. ### core - Ambiance SmallRig RM120 RGB Video Light Small accent light for shaping the room. (https://amzn.to/4nK5TR6) - Softbox GVM 100W Bi-Color LED Video Light with Lantern Softbox Soft overhead or side fill. (https://amzn.to/451aRjZ) ### room lights - Desk lamps meross Smart Table Lamp Practical background light. (https://amzn.to/3IF04Ee) - Monitor light bar XIAOMI Mi Computer Monitor Light Bar Cleaner desk light without crowding the surface. (https://amzn.to/3GmWnTf) - Floor lamp Govee Floor Lamp 2 Background color and separation. (https://amzn.to/4lQ573a) screen capture ## display The display affects recording resolution and color accuracy. I now recommend a 6k monitor for a larger, sharper recording canvas. ### options - Recommended LG 32U990A-S 32-Inch UltraFine 6K Monitor Preferred high-resolution display for screen recordings. (https://amzn.to/4uLHaP6) - Mount Ergotron LX Vertical Stacking Dual Monitor Arm Keeps the display positioned cleanly. (https://amzn.to/4eJWLHT) work surface ## desk setup The desk and chair matter more than the exact brand. Get something stable, comfortable, and easy to tune for your body. ### desk - Frame Dual Motor Standing Desk Frame Any dual motor standing desk will do. (https://amzn.to/4dA2D6s) - Desktop Sparrow Peak UV Natural Birch Butcher Block Countertop Custom butcher block top. No finish required, waterproof. (https://www.lowes.com/pd/Sparrow-Peak-Desktop-UV-Natural-grain-5-ft-x-30-in-x-1-25-in-UV-Finished-Natural-Birch-Wood-Butcher-block-Countertop/5005339613) ### chair - Chair Steelcase Leap Office Chair Look on Facebook Marketplace for sales. (https://amzn.to/3RCb7mb) - Casters Custom Office Chair Casters Smoother rolling upgrade for the chair. (https://amzn.to/42Rt3f1) post ## editing ### video - [Image] (/img/MWVeQ4qMK9-48.webp) Camtasia Talking-head videos. Recording and editing in one place. (https://techsmith.z6rjha.net/VOvjE6) - [Image] (/img/K0vXp2aolB-48.webp) Screen Studio Quick demos. Sometimes the recording too. (https://screenstudio.lemonsqueezy.com/?aff=NpeDD) - [Image] (/img/wOF9TvgGN3-48.webp) CleanShot X Screenshots, quick captures, and annotations. (https://go.cleanshot.com/matt-palmer) - [Image] (/img/CAUdM7cSqY-48.webp) CapCut Fast cuts when the timeline does not need to be fancy. (https://www.capcut.com) - [Image] (/img/u-EdSkWUyp-48.webp) Adobe Premiere Pro Longer edits that need a real timeline. (https://www.adobe.com/products/premiere.html) - [Image] (/img/cOZcoFhT1m-48.webp) Shots.so Mockups, frames, and looping clips for social. (https://shots.so/) ### audio - [Image] (/img/x29fshQLhz-48.webp) Audio Hijack Realtime mic and system-audio processing. (https://rogueamoeba.com/audiohijack/) - [Image] (/img/h6EqzagwST-48.webp) Loopback Virtual audio devices. Pair with Audio Hijack. (https://rogueamoeba.com/loopback/) jump to - 01 camera & lens (#camera) - 02 mobile video (#mobile) - 03 audio (#audio) - 04 lighting (#lighting) - 05 display (#display) - 06 desk setup (#desk) - 07 editing (#editing)