<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>NHCarrigan</title>
    <link>https://mktg.spellware.dev/blog</link>
    <description>Naomi Carrigan is a software engineer and community builder. Read her writing on developer communities, AI tooling, and professional growth in tech.</description>
    <language>en-us</language>
    <pubDate>Tue, 20 Oct 2026 22:33:12 GMT</pubDate>
    <dc:date>2026-10-20T22:33:12Z</dc:date>
    <dc:language>en-us</dc:language>
    <item>
      <title>Redesigning NHCarrigan</title>
      <link>https://mktg.spellware.dev/blog/redesigning-nhcarrigan</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/redesigning-nhcarrigan" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-01-homepage-hero-after.png" alt="The new homepage hero, with a floating glass header, an availability notice, a highlighted headline phrase and a rose light from the upper right." class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;On 14 October 2026 we released a redesign of nhcarrigan.com, our documentation, our forms and every site that uses our shared page script.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;On 14 October 2026 we released a redesign of nhcarrigan.com, our documentation, our forms and every site that uses our shared page script.&lt;/p&gt; 
&lt;p&gt;This is a redesign. It is not a rebrand. The name, the logo and the colour palette have not changed. What has changed is how the palette is applied.&lt;/p&gt; 
&lt;p&gt;This post explains what we were trying to achieve, how the new look works, how we checked that it stays readable, what went wrong along the way, and what it means for you. It is long on purpose. If you want the short version, start with the next section.&lt;/p&gt; 
&lt;h2&gt;In short&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;The site now uses frosted glass surfaces, lit by a single rose light source.&lt;/li&gt; 
 &lt;li&gt;Every NHCarrigan site now has the same header and the same footer.&lt;/li&gt; 
 &lt;li&gt;Dark mode has been removed from all of our sites.&lt;/li&gt; 
 &lt;li&gt;The homepage has been reworked to make it easier to start a conversation with us.&lt;/li&gt; 
 &lt;li&gt;A new FAQ page answers 62 questions in one place.&lt;/li&gt; 
 &lt;li&gt;Version 6 of our shared page script is a breaking change for developers. Migration notes are further down.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-01-homepage-hero-before.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-01-homepage-hero-before.png" alt="The previous NHCarrigan homepage hero, with a dark purple background and a plain header." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-01-homepage-hero-after.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-01-homepage-hero-after.png" alt="The new homepage hero, with a floating glass header, an availability notice, a highlighted headline phrase and a rose light from the upper right." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; After  
&lt;h2&gt;What we were trying to achieve&lt;/h2&gt; 
&lt;p&gt;We had three goals.&lt;/p&gt; 
&lt;h3&gt;One site, not several&lt;/h3&gt; 
&lt;p&gt;Before this change, our homepage, our documentation, our forms and our small apps shared a colour palette and little else. Some pages had a header. Some did not. The footers differed. A visitor moving between them could reasonably wonder whether they were still on our site.&lt;/p&gt; 
&lt;p&gt;We wanted every page to look and behave like part of one product.&lt;/p&gt; 
&lt;h3&gt;A clearer path for people deciding whether to work with us&lt;/h3&gt; 
&lt;p&gt;NHCarrigan sells services. The homepage has to answer three questions quickly. What do you do? Can I trust you? How do I start?&lt;/p&gt; 
&lt;p&gt;Most of the changes to the homepage exist to answer those questions with less effort from the reader.&lt;/p&gt; 
&lt;h3&gt;Nothing that costs readability&lt;/h3&gt; 
&lt;p&gt;This was the rule that limited everything else. Any effect had to survive a contrast measurement. If an effect made text harder to read, the effect was removed or changed. We did not add decoration for its own sake.&lt;/p&gt; 
&lt;h2&gt;How the look works&lt;/h2&gt; 
&lt;h3&gt;One light source&lt;/h3&gt; 
&lt;p&gt;The palette has not changed. Witch Rose, Witch Plum, Witch Purple and Witch Black are the same colours as before. The difference is that they now behave as if a single lamp lit the page.&lt;/p&gt; 
&lt;p&gt;The lamp is in the upper right. Light arrives from that direction on every surface. The left side, where most text sits, stays in shadow. Shadows are plum and black. We use no second accent colour. The effect comes from light set against dark, and from keeping the direction the same everywhere.&lt;/p&gt; 
&lt;p&gt;On dark sections, the light appears as a glow. On light sections, light cannot glow in the same way, so it appears as colour bleeding in from the corner, a sheen along card edges, shadows tinted rose and plum, and a rose edge glow thrown towards the light.&lt;/p&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-02-services-before.png?width=1280&amp;amp;height=2484&amp;amp;name=nhcarrigan-redesign-02-services-before.png" alt="The previous services section, with plain white cards on a flat background." width="1280" height="2484" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-02-services-after.png?width=1280&amp;amp;height=2485&amp;amp;name=nhcarrigan-redesign-02-services-after.png" alt="The new services section, with frosted cards that have a rose edge glow, on a background washed with rose and mauve." width="1280" height="2485" style="max-width: 100%; height: auto;"&gt; After  
&lt;h3&gt;Glass needs something behind it&lt;/h3&gt; 
&lt;p&gt;Frosted glass only shows when there is colour behind it. A glass card on a plain background looks like a white box. For that reason the light sections carry soft rose and mauve washes, and the cards sit on top of them.&lt;/p&gt; 
&lt;p&gt;The header is dark glass at 72 per cent opacity with an 18 pixel blur, floating 12 pixels from the top of the window. Cards are white glass. On the shared pages, the background artwork is frosted over by the same rose and lavender wash, so the artwork shows softly through it.&lt;/p&gt; 
&lt;h3&gt;Rules we set ourselves&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;Glow is never animated. There is one exception. On the company site, the dot beside our availability notice pulses slowly. It stops for readers who ask their device for reduced motion.&lt;/li&gt; 
 &lt;li&gt;Long lists, such as testimonials and résumé entries, stay on plain white. Blur is costly on low-end phones, and it adds nothing to a long list.&lt;/li&gt; 
 &lt;li&gt;On phones, cards and backgrounds drop the blur.&lt;/li&gt; 
 &lt;li&gt;If a browser does not support blurred backgrounds, or the reader asks for reduced transparency, every surface becomes solid.&lt;/li&gt; 
 &lt;li&gt;Printed pages use plain surfaces with no glow.&lt;/li&gt; 
 &lt;li&gt;The logo is never filtered, tinted or overlaid. Any glow sits on the container behind it.&lt;/li&gt; 
 &lt;li&gt;Glow never replaces the keyboard focus ring.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;How we checked that it stays readable&lt;/h2&gt; 
&lt;p&gt;Text on a translucent surface is a moving target. Its contrast depends on what is behind it, and that changes as the page scrolls and as the screen gets wider.&lt;/p&gt; 
&lt;p&gt;So we did not measure colour pairs in the abstract. We measured the contrast of every piece of text against the background actually rendered behind it, at the brightest and darkest point of that background. We did this at phone and desktop widths on every page we measured. The homepage, the shared header and the shared script were also measured at tablet and ultrawide widths.&lt;/p&gt; 
&lt;p&gt;We measured every page of the company site, 20 documentation pages drawn from every section of the documentation, all 19 of our pages and blog posts hosted on HubSpot, and a test application page. That came to more than 7,000 measurements of distinct text styles.&lt;/p&gt; 
&lt;p&gt;Every one met the WCAG AA requirement, which is 4.5 to 1 for body text and 3 to 1 for large text, with three exceptions:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;The style guide page contains examples that exist to show low contrast, such as swatch labels and a disabled button.&lt;/li&gt; 
 &lt;li&gt;Some HubSpot forms have a disabled Next button. WCAG exempts inactive controls.&lt;/li&gt; 
 &lt;li&gt;One line of red text on a grey background in the content of our report form. The author set those colours in the page itself, and we have noted it for correction.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The lowest ratio we measured on the shared script was 4.63 to 1. The lowest on the homepage was 4.78 to 1.&lt;/p&gt; 
&lt;p&gt;There is a limit to this. For each page we measured every distinct text style, with up to four examples of each, and not every paragraph. We did not measure the documentation pages we did not sample, or applications other than our test page. If you find text that is hard to read, please tell us.&lt;/p&gt; 
&lt;h2&gt;Making it easier to start a conversation&lt;/h2&gt; 
&lt;p&gt;These are the homepage changes, and the reason for each.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;An availability notice. The hero now states that we are taking on new engagements. A visitor should not have to guess whether we have capacity.&lt;/li&gt; 
 &lt;li&gt;Example projects with rough prices. Each service page lists example projects with estimated prices. They are labelled as estimates, not quotes. Pricing is specific to each client, and the examples exist to help you judge scale before you contact us.&lt;/li&gt; 
 &lt;li&gt;A featured testimonial. One mentee, Rain Kalugdan, describes our mentorship programme in full. We show the whole message, not an excerpt, because the detail is what makes it credible.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-10-featured-testimonial.png?width=1280&amp;amp;height=1669&amp;amp;name=nhcarrigan-redesign-10-featured-testimonial.png" alt="The featured testimonial card on the homepage, a dark card with a rose glow holding Rain Kalugdan's full message." width="1280" height="1669" style="max-width: 100%; height: auto;"&gt; 
&lt;ul&gt; 
 &lt;li&gt;A team section. Naomi, Hisho and Tim each have a profile page.&lt;/li&gt; 
 &lt;li&gt;Better phone behaviour. The hero is clearer on small screens. There is a sticky contact button, and each page ends with a contact section.&lt;/li&gt; 
 &lt;li&gt;A newsletter sign-up. It is on the homepage and in the footer.&lt;/li&gt; 
 &lt;li&gt;Confirmation pages. After you submit one of our forms, you now see a page that confirms it and explains what happens next.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-05-homepage-phone-before.png?width=390&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-05-homepage-phone-before.png" alt="The previous homepage on a phone." width="390" height="900" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-05-homepage-phone-after.png?width=390&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-05-homepage-phone-after.png" alt="The new homepage on a phone, with a clearer hero." width="390" height="900" style="max-width: 100%; height: auto;"&gt; After  
&lt;h3&gt;One place for every question&lt;/h3&gt; 
&lt;p&gt;Eight of our pages carried their own FAQ sections. Together they held 88 questions, and many of them repeated across the service pages.&lt;/p&gt; 
&lt;p&gt;All of them now live in one place. The FAQ page at &lt;a href="https://nhcarrigan.com/faq/"&gt;https://nhcarrigan.com/faq/&lt;/a&gt; answers 62 questions, and each question appears once. Every FAQ section elsewhere on the site shows its questions as cards, and each card links to its answer on that page. Each answer also lists the pages it applies to.&lt;/p&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-03-faq-before.png?width=1280&amp;amp;height=876&amp;amp;name=nhcarrigan-redesign-03-faq-before.png" alt="The previous FAQ, a plain list of questions that expand." width="1280" height="876" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-03-faq-after.png?width=1280&amp;amp;height=931&amp;amp;name=nhcarrigan-redesign-03-faq-after.png" alt="The new FAQ section, a grid of question cards with rose edge glows." width="1280" height="931" style="max-width: 100%; height: auto;"&gt; After  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-11-faq-page.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-11-faq-page.png" alt="The new FAQ page, with a topic index and full answers on lit cards." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; 
&lt;h2&gt;One header and one footer, everywhere&lt;/h2&gt; 
&lt;p&gt;Every site that loads our shared script now has the same header as the homepage. It carries the same logo and the same links. The text beside the logo is the title of the page you are on.&lt;/p&gt; 
&lt;p&gt;The footer is a copy of the homepage footer. It has the same columns, the same links, the same address and the same Tree-Nation badge.&lt;/p&gt; 
&lt;p&gt;The community invitation popup has the same lit surface and rose glow as the rest of the site. The page content sits on a lit, frosted card. Buttons, tables, quotations and code blocks follow the same styling.&lt;/p&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-06-app-page-before.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-06-app-page-before.png" alt="A sample application page before the change, with a plain white card and a small footer." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-06-app-page-after.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-06-app-page-after.png" alt="The same page after the change, with a glass header, a frosted card and the full footer." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; After  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-07-discord-popup-before.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-07-discord-popup-before.png" alt="The previous community popup." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-07-discord-popup-after.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-07-discord-popup-after.png" alt="The new community popup, with a rose glow and a glowing button." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; After  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-04-footer-before.png?width=1280&amp;amp;height=572&amp;amp;name=nhcarrigan-redesign-04-footer-before.png" alt="The previous footer." width="1280" height="572" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-04-footer-after.png?width=1280&amp;amp;height=669&amp;amp;name=nhcarrigan-redesign-04-footer-after.png" alt="The new footer, with a glowing top edge, link markers and a rose key light." width="1280" height="669" style="max-width: 100%; height: auto;"&gt; After  
&lt;p&gt;Our forms and our blog are hosted on HubSpot, which uses its own theme. They now load the same script. HubSpot's own header and footer are hidden, and ours are shown in their place.&lt;/p&gt; 
&lt;h2&gt;Why we removed dark mode&lt;/h2&gt; 
&lt;p&gt;We removed dark mode from every site. This is the decision most likely to change how you read our pages, so here is the reasoning.&lt;/p&gt; 
&lt;p&gt;The homepage never had a dark theme. To make every site look the same as the homepage, we had two options. We could design a second, dark version of every lit surface, every glow and every frosted panel. Or we could drop the toggle.&lt;/p&gt; 
&lt;p&gt;Light behaves differently on a dark page. The rose light on a dark surface is a glow. On a light surface it is colour bleeding in from a corner. A dark version of the look is not a recolouring. It is a second design, and doing it badly would have been worse than not doing it. We chose one theme, done properly.&lt;/p&gt; 
&lt;p&gt;There is a cost. Some readers prefer dark pages, and they lose that option on our sites. We know that. We kept the other accommodations: reduced motion, reduced transparency, visible focus rings, and measured contrast.&lt;/p&gt; 
&lt;p&gt;If the change makes our sites harder for you to read, please tell us at &lt;a href="mailto:contact@nhcarrigan.com"&gt;contact@nhcarrigan.com&lt;/a&gt;. We would rather hear it than guess.&lt;/p&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-08b-docs-page-before-dark.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-08b-docs-page-before-dark.png" alt="The previous documentation page in dark mode." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; Before, in dark mode  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-08-docs-page-after.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-08-docs-page-after.png" alt="The new documentation page, light only, with a glass header and frosted panels." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; After  
&lt;h2&gt;The documentation&lt;/h2&gt; 
&lt;p&gt;The documentation at &lt;a href="https://docs.nhcarrigan.com"&gt;https://docs.nhcarrigan.com&lt;/a&gt; has the same changes.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;The header carries the same links as the homepage, with the page title beside the logo, and keeps search.&lt;/li&gt; 
 &lt;li&gt;The sidebar, the content and the contents panel sit on frosted surfaces.&lt;/li&gt; 
 &lt;li&gt;Cards, buttons, tables and notes use the same lit surfaces.&lt;/li&gt; 
 &lt;li&gt;The footer is the homepage footer.&lt;/li&gt; 
 &lt;li&gt;The theme picker is gone. Code blocks use the dark code theme, to match the style guide.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Two policy updates were released with it. The Privacy Policy now discloses our newsletter and marketing emails. The change notice dated 14 October 2026 lists the sections affected. The Professional Services and Commission Policy now states that example prices are estimates, and names social media management as a service.&lt;/p&gt; 
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-09-docs-home-before.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-09-docs-home-before.png" alt="The previous documentation home page." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; Before  
&lt;img src="https://mktg.spellware.dev/hs-fs/hubfs/blog/redesign-2026-10/nhcarrigan-redesign-09-docs-home-after.png?width=1280&amp;amp;height=900&amp;amp;name=nhcarrigan-redesign-09-docs-home-after.png" alt="The new documentation home page, with a lit hero card and link cards." width="1280" height="900" style="max-width: 100%; height: auto;"&gt; After  
&lt;h2&gt;For developers: version 6 of the shared script&lt;/h2&gt; 
&lt;p&gt;If you build pages that load &lt;a href="https://cdn.nhcarrigan.com/headers/index.js"&gt;https://cdn.nhcarrigan.com/headers/index.js&lt;/a&gt;, version 6.0.0 is a breaking change.&lt;/p&gt; 
&lt;h3&gt;What was removed&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;The dark theme, the footer theme toggle and the &lt;code&gt;is-dark&lt;/code&gt; class. Nothing adds &lt;code&gt;is-dark&lt;/code&gt; any more.&lt;/li&gt; 
 &lt;li&gt;The &lt;code&gt;--page-overlay&lt;/code&gt; token.&lt;/li&gt; 
 &lt;li&gt;The old footer element ids.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;What is new&lt;/h3&gt; 
&lt;ul&gt; 
 &lt;li&gt;A header on every page, unless you exclude it or the page already has its own &lt;code&gt;header.site-header&lt;/code&gt;.&lt;/li&gt; 
 &lt;li&gt;A &lt;code&gt;header&lt;/code&gt; value for the exclusion attribute.&lt;/li&gt; 
 &lt;li&gt;A &lt;code&gt;data-nhcarrigan-title&lt;/code&gt; attribute for the header text.&lt;/li&gt; 
 &lt;li&gt;Opt-in components for cards, notices, quotations, pills, FAQ cards, grids and buttons.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;To set the header text:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;html lang="en-GB" data-nhcarrigan-title="Tarot"&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;If you leave it out, the script takes the page title and removes the NHCarrigan name and its separators. Long titles are cut short with an ellipsis.&lt;/p&gt; 
&lt;p&gt;To skip the header on a page that has its own:&lt;/p&gt; 
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;html lang="en-GB" data-nhcarrigan-exclude="header"&amp;gt;&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Every component class starts with &lt;code&gt;nhc-&lt;/code&gt;, for example &lt;code&gt;nhc-card&lt;/code&gt; and &lt;code&gt;nhc-btn nhc-btn-primary&lt;/code&gt;. We prefixed them so that they cannot collide with the styles in your own application.&lt;/p&gt; 
&lt;p&gt;The semantic variables still exist, and they keep their light values, so existing pages continue to work. The style guide at &lt;a href="https://nhcarrigan.com/style/"&gt;https://nhcarrigan.com/style/&lt;/a&gt; lists every component, every variable and the full upgrade table. The release notes are at &lt;a href="https://github.com/nhcarrigan/website-headers/releases"&gt;https://github.com/nhcarrigan/website-headers/releases&lt;/a&gt;.&lt;/p&gt; 
&lt;h2&gt;What went wrong&lt;/h2&gt; 
&lt;p&gt;We found these problems while building this. We are listing them because they are the useful part.&lt;/p&gt; 
&lt;h3&gt;A dark section turned pale&lt;/h3&gt; 
&lt;p&gt;We wrote a style rule for the light sections. It also matched our dark process section, and replaced its dark background with a pale one. The text became pale on pale, a contrast of about 1 to 1. Measuring found it. It happened a second time when we added a narrow-screen variant of the same rule. We now scope that rule explicitly, and the project documentation records the trap.&lt;/p&gt; 
&lt;h3&gt;The glowing seam stopped short on an ultrawide screen&lt;/h3&gt; 
&lt;p&gt;Each section has a glowing line along its top edge. We had inset it from both sides, which looked like a soft fade on a normal monitor. On an ultrawide monitor it visibly stopped before the edges. It now spans the full width. Our earlier tests had not included a screen that wide.&lt;/p&gt; 
&lt;h3&gt;A background that painted nothing&lt;/h3&gt; 
&lt;p&gt;To stop page content scrolling behind the floating header, we added a strip of frosted artwork behind it. The strip did nothing. A CSS background shorthand allows a plain colour only in its last layer, and we had put one above the image. The browser discarded the entire declaration without a warning. A screenshot showed the fault straight away. Reading the code had not.&lt;/p&gt; 
&lt;h3&gt;Blue text in the HubSpot header&lt;/h3&gt; 
&lt;p&gt;On HubSpot pages, the header title and button rendered in blue. HubSpot's theme styles links with a more specific selector than ours, so theirs won. We raised the specificity of the header and button rules. This would have affected any application with strong link styles, including ones outside HubSpot. HubSpot's theme also colours its links violet, which fell below the 4.5 to 1 requirement on our lit card. We set its link colour variables to ours.&lt;/p&gt; 
&lt;p&gt;Each of these was found by measuring or by looking at a rendered page. None was found by reading code.&lt;/p&gt; 
&lt;h2&gt;If something looks wrong&lt;/h2&gt; 
&lt;p&gt;If a page looks wrong to you, please send us the address, the size of your screen and a screenshot. Our contact address is &lt;a href="mailto:contact@nhcarrigan.com"&gt;contact@nhcarrigan.com&lt;/a&gt;.&lt;/p&gt; 
&lt;p&gt;Thank you for reading.&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fredesigning-nhcarrigan&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Fri, 16 Oct 2026 21:36:04 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/redesigning-nhcarrigan</guid>
      <dc:date>2026-10-16T21:36:04Z</dc:date>
    </item>
    <item>
      <title>How I Approach Events Differently</title>
      <link>https://mktg.spellware.dev/blog/how-i-approach-events-differently</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/how-i-approach-events-differently" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="How I Approach Events Differently" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;Most event recaps list what happened. I try to do something different. I treat an event as a set of decisions I make before, during and after, and I check each decision against data.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Most event recaps list what happened. I try to do something different. I treat an event as a set of decisions I make before, during and after, and I check each decision against data.&lt;/p&gt; 
&lt;p&gt;This post describes three of those habits. They come from two events this year: AI Hackathon @ Berkeley and a developer conference expo in San Jose.&lt;/p&gt; 
&lt;h2&gt;Before the event: every resource gets its own link&lt;/h2&gt; 
&lt;p&gt;Before I run a workshop, I give every resource its own short link: signup, the guide, the docs, each repository, the slide deck and the Discord invite. If I do not, I can only guess what people did after the session.&lt;/p&gt; 
&lt;p&gt;At Berkeley, I ran a workshop for about 50 people. The tracked signup link recorded 243 signups, close to five times the size of the room. The guide was the second most-used link, with 209 clicks, ahead of the documentation. I write the guide so that it works without me in the room, and the numbers suggest that people used it that way.&lt;/p&gt; 
&lt;p&gt;The links travelled. They were shared in Discord, placed in repository READMEs and passed around a hackathon of almost 400 projects. A session of 50 people reached several times its own size.&lt;/p&gt; 
&lt;p&gt;The data also changed my plan. About 74 percent of the repository clicks were for Python, so I now write workshop content for Python first.&lt;/p&gt; 
&lt;h2&gt;During the event: I watch the room and change what I show&lt;/h2&gt; 
&lt;p&gt;A booth is a set of decisions made during the event. Here is how that worked at the expo in San Jose.&lt;/p&gt; 
&lt;p&gt;The day before the expo opened, I scoped the venue and confirmed our booth space. That gave me time to plan the screen before the first visitor arrived. The booth ran two looping slides. One carried QR codes for Discord, signup and documentation. The other carried a single code for our conference. Every code was a tracked short link, so I could see what visitors actually used.&lt;/p&gt; 
&lt;p&gt;After about an hour, I replaced the documentation code with a careers code. I made that change because I watched who stopped at the booth. Students kept asking about jobs and internships, and the docs link was not what they wanted. The careers link finished third of five, with around 60 clicks. The documentation link finished last, with about ten. The data confirmed what I had seen.&lt;/p&gt; 
&lt;p&gt;The audience was mixed: working engineers, students, and founders and technical leads. Founders asked about our startup programme. I run it, so I could answer them directly. I covered the booth for both full days and scanned well over 100 leads myself. Whenever a visitor asked for a demo, the demo took priority over the slides.&lt;/p&gt; 
&lt;p&gt;Presence matters as much as the screen. At Berkeley, I was the only sponsor who stayed all night, and I wrote about that in &lt;a href="https://mktg.spellware.dev/blog/ai-hackathon-berkeley-2026"&gt;I Stayed for 36 Hours Straight. Here Is Why&lt;/a&gt;. During those 36 hours, 42 people personally invited me to see their project. Our track was not decided in the two-hour judging window. It was decided over the 24 hours before it, because I had watched the winning team build.&lt;/p&gt; 
&lt;h2&gt;After the event: I read the numbers honestly&lt;/h2&gt; 
&lt;p&gt;I report tracked numbers as floors. At Berkeley, some tables also had generic QR codes that I could not measure, so real engagement was higher. Every untracked code is engagement I cannot count. I also do not stretch a number past what it shows. The careers and internship links had generic codes beside them, so I draw no recruiting conclusion from either.&lt;/p&gt; 
&lt;p&gt;I keep numbers that answer different questions apart. At the expo, we recorded over 200 link clicks and at least 20 registrations for our conference. Clicks are interactions. Registrations are conversions. I do not call them a funnel until the registration data has been reconciled. In my event report, I leave a field empty when I do not have the data yet. A blank is better than a guess.&lt;/p&gt; 
&lt;p&gt;The same habit applies to sponsor tracks. I do not judge a track by its submission count. Ours received 141 submissions at Berkeley, third-most of any sponsor. But the event had 398 projects, and each team could enter as many tracks as it liked. A count measures how easy a track was to enter as much as how appealing it was.&lt;/p&gt; 
&lt;p&gt;So I compare tracks by adoption rate against integration effort. I rate effort on four levels: none, low, medium and high. The track with the most submissions asked teams to build with a coding assistant that most of them already used. Another asked for a backend addition to an app that already existed. Ours needed a new modality. A team had to capture a microphone stream, connect speech-to-text or a voice agent, and handle real-time audio. Our criteria also said that voice must be essential to the experience, not added on. That works as a quality filter.&lt;/p&gt; 
&lt;p&gt;On that measure, 141 teams, 35 percent of the event, chose to put voice at the core of their project when nothing forced them to. The prize was four games consoles, while other sponsors offered cash, credits or an office hour. Arguably, that is the strongest result on the board.&lt;/p&gt; 
&lt;p&gt;A track that asks for more has to give more help. That is why I ran a workshop and shared starter repositories in Python and JavaScript for it. When I report a result like this, I give both numbers. The count is real. The adoption rate explains it.&lt;/p&gt; 
&lt;h2&gt;What connects these habits&lt;/h2&gt; 
&lt;p&gt;Each habit answers one question. Before the event: how will I know what happened? During the event: what is the room telling me, and what should I change? After the event: what do the numbers actually show, and what do they not show?&lt;/p&gt; 
&lt;p&gt;An event is not something I attend and then summarise. It is something I can plan, adjust and measure. That is the difference I try to bring to every event I work.&lt;/p&gt; 
&lt;p&gt;If you run events for a developer audience, start with one change: give every resource its own link. Everything else follows from being able to see what people did.&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fhow-i-approach-events-differently&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Mon, 12 Oct 2026 22:49:25 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/how-i-approach-events-differently</guid>
      <dc:date>2026-10-12T22:49:25Z</dc:date>
    </item>
    <item>
      <title>How to Spot a Fake Email</title>
      <link>https://mktg.spellware.dev/blog/how-to-spot-fake-emails</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/how-to-spot-fake-emails" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="How to Spot a Fake Email" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;Here’s the thing about phishing emails: the ones that actually catch people are not the obvious, typo-riddled ones with subjects like “URGENT!!! Your account will be DELETED!!!” Those are easy to laugh off. The dangerous ones are polished, they look legitimate, and they come at a moment when you’re distracted or stressed. That’s by design.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Here’s the thing about phishing emails: the ones that actually catch people are not the obvious, typo-riddled ones with subjects like “URGENT!!! Your account will be DELETED!!!” Those are easy to laugh off. The dangerous ones are polished, they look legitimate, and they come at a moment when you’re distracted or stressed. That’s by design.&lt;/p&gt; 
&lt;p&gt;So let’s talk about what to look for.&lt;/p&gt; 
&lt;h2&gt;The Sender’s Address Is Where You Start&lt;/h2&gt; 
&lt;p&gt;The display name - the “from” label you see in your inbox - can say anything. “PayPal Support”, “Apple ID Security Team”, your bank’s name. That part is trivially easy to fake and tells you nothing.&lt;/p&gt; 
&lt;p&gt;What matters is the actual email address. Click or tap on the sender’s name to reveal it. A real email from PayPal comes from &lt;code&gt;@paypal.com&lt;/code&gt;. Full stop. Not &lt;code&gt;@paypal-support.net&lt;/code&gt;, not &lt;code&gt;@paypal.accounts.info&lt;/code&gt;, and definitely not &lt;code&gt;@gmail.com&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;Some things to watch for:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;The domain doesn’t match the organisation’s website (e.g. &lt;code&gt;support@amazon-billing.com&lt;/code&gt; vs &lt;code&gt;amazon.com&lt;/code&gt;)&lt;/li&gt; 
 &lt;li&gt;Subtle misspellings - &lt;code&gt;micros0ft.com&lt;/code&gt;, &lt;code&gt;arnazon.com&lt;/code&gt; - designed to look right at a glance&lt;/li&gt; 
 &lt;li&gt;Long strings of random characters before the &lt;code&gt;@&lt;/code&gt;&lt;/li&gt; 
 &lt;li&gt;Free email services for anything claiming to be an official company&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Be Suspicious of Generic Greetings&lt;/h2&gt; 
&lt;p&gt;Legitimate companies that hold your account know who you are. They’ll use your name. If an email from your bank opens with “Dear Valued Customer” or “Hello Account Holder”, that’s a signal - they’re sending this to thousands of people and don’t actually have your details.&lt;/p&gt; 
&lt;h2&gt;Urgency Is a Red Flag, Not a Reason to Act&lt;/h2&gt; 
&lt;p&gt;Scammers are in the business of making you panic before you can think clearly. Phrases like these are designed to do exactly that:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Your account will be permanently closed within 24 hours if you do not verify your details immediately.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;No legitimate company communicates with you this way. If you’re worried the email might be real, go directly to the company’s website by typing the URL yourself - not by clicking anything in the email.&lt;/p&gt; 
&lt;h2&gt;Hover Before You Click&lt;/h2&gt; 
&lt;p&gt;Before clicking any link, hover your mouse over it and look at the destination URL in your browser’s status bar. On mobile, press and hold to preview it.&lt;/p&gt; 
&lt;p&gt;Ask yourself: does the domain actually match the company? A common trick is to make the real domain look like a subdomain - &lt;code&gt;accounts.microsoft.com.verify.ru&lt;/code&gt; looks almost right at a glance, but the actual domain there is &lt;code&gt;verify.ru&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;When in doubt, navigate to the company’s website yourself. Type it in. Don’t follow the link.&lt;/p&gt; 
&lt;h2&gt;Unexpected Attachments Are a No&lt;/h2&gt; 
&lt;p&gt;Real organisations rarely send you attachments out of nowhere. If an email you weren’t expecting arrives with a file attached - especially something like &lt;code&gt;.exe&lt;/code&gt;, &lt;code&gt;.zip&lt;/code&gt;, &lt;code&gt;.doc&lt;/code&gt;, or &lt;code&gt;.pdf&lt;/code&gt; - don’t open it.&lt;/p&gt; 
&lt;p&gt;If the email claims to be an invoice or important document, log in to the company’s website directly to find it there. If it’s real, it’ll be in your account.&lt;/p&gt; 
&lt;h2&gt;What to Do If You’re Not Sure&lt;/h2&gt; 
&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;Don’t click anything or open any attachments&lt;/strong&gt; until you’ve verified the email is legitimate&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Contact the company directly&lt;/strong&gt; using contact details from their official website, not anything in the email&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Report it&lt;/strong&gt; to your email provider’s spam or phishing reporting tool&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Delete it&lt;/strong&gt;&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;If you did click something before reading this - change your passwords immediately and enable two-factor authentication on any accounts that might be affected.&lt;/p&gt;  
&lt;p&gt;Everyone can be caught off guard. Scammers are good at what they do, and they put a lot of effort into making things look real. Being cautious is not paranoia - it’s just good practice. When in doubt, don’t click~&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fhow-to-spot-fake-emails&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Thu, 08 Oct 2026 01:53:25 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/how-to-spot-fake-emails</guid>
      <dc:date>2026-10-08T01:53:25Z</dc:date>
    </item>
    <item>
      <title>How to Ask for Help (And How to Give It)</title>
      <link>https://mktg.spellware.dev/blog/asking-and-giving-help</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/asking-and-giving-help" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="How to Ask for Help (And How to Give It)" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;Something I’ve noticed after years of running communities and helping people learn: most problems with getting help aren’t about knowledge gaps at all. They’re about communication. Either the person asking hasn’t given enough to work with, or the person helping has jumped straight to the answer and accidentally short-circuited the learning.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Something I’ve noticed after years of running communities and helping people learn: most problems with getting help aren’t about knowledge gaps at all. They’re about communication. Either the person asking hasn’t given enough to work with, or the person helping has jumped straight to the answer and accidentally short-circuited the learning.&lt;/p&gt; 
&lt;p&gt;Both sides of this are worth understanding. So here are both.&lt;/p&gt; 
&lt;h2&gt;Asking for Help Well&lt;/h2&gt; 
&lt;p&gt;There’s a difference between &lt;em&gt;asking for help&lt;/em&gt; and &lt;em&gt;asking someone to do the thing for you&lt;/em&gt;. Neither is wrong, exactly, but they produce very different outcomes.&lt;/p&gt; 
&lt;h3&gt;Tell us what you’ve already tried&lt;/h3&gt; 
&lt;p&gt;This is the single most useful thing you can include in a help request. Not just “I’m stuck” or “this isn’t working” - but what did you try? What happened when you tried it? What did you expect to happen instead?&lt;/p&gt; 
&lt;p&gt;“My code doesn’t work” is not a question. “I expected this function to return a sum, but it’s returning &lt;code&gt;NaN&lt;/code&gt;, and I’ve checked that the values are numbers going in” - that’s a question. The second version gives anyone helping you something to actually respond to.&lt;/p&gt; 
&lt;p&gt;It also forces you to think through what you actually know, which is often where the answer lives.&lt;/p&gt; 
&lt;h3&gt;Give us something minimal and reproducible&lt;/h3&gt; 
&lt;p&gt;If you’re sharing code, please don’t paste your entire 300-line file. Strip it down to the smallest version that still shows the problem. This is useful for you &lt;em&gt;and&lt;/em&gt; for the person helping - it removes the noise and makes the actual issue much easier to spot.&lt;/p&gt; 
&lt;p&gt;I’d say the majority of times I’ve asked someone to do this, they find the problem themselves in the process of making the minimal example. That’s not a coincidence.&lt;/p&gt; 
&lt;h3&gt;Ask about the thing you actually don’t understand&lt;/h3&gt; 
&lt;p&gt;It sounds obvious. It isn’t. A lot of people ask about &lt;em&gt;symptoms&lt;/em&gt; rather than the underlying confusion. “Why does JavaScript do this?” is less useful than “I thought variables were always accessible throughout a function - why is &lt;code&gt;x&lt;/code&gt; undefined inside this loop?”&lt;/p&gt; 
&lt;p&gt;The more specific you can be about where your mental model breaks down, the faster someone can help you fix it.&lt;/p&gt; 
&lt;h3&gt;Don’t paste the error and disappear&lt;/h3&gt; 
&lt;p&gt;Share the error message, yes. Also share what line it happened on, what you were doing when it appeared, and any relevant context. An error message without context is just noise. With context, it’s a road map.&lt;/p&gt; 
&lt;h2&gt;Giving Help Well&lt;/h2&gt; 
&lt;p&gt;Now for the other side.&lt;/p&gt; 
&lt;p&gt;The temptation, when someone asks you a question you know the answer to, is to just… answer it. I get it. It feels efficient. It solves the immediate problem.&lt;/p&gt; 
&lt;p&gt;The thing is, it doesn’t actually help.&lt;/p&gt; 
&lt;h3&gt;The answer they can find themselves is worth more&lt;/h3&gt; 
&lt;p&gt;When someone works through a problem and reaches the answer through their own reasoning, something clicks that doesn’t happen when you just hand them the solution. They own it. It becomes part of how they think, not just something they copy-pasted.&lt;/p&gt; 
&lt;p&gt;If you hand someone the answer, they’ll be back with the same question in a different shape next week. If you guide them to it, they probably won’t need to ask again.&lt;/p&gt; 
&lt;h3&gt;The Socratic method works&lt;/h3&gt; 
&lt;p&gt;I’ve written about this before in the context of code specifically, but the principle applies everywhere. Rather than answering directly, ask questions. Start broad, then get narrower as the person gets closer.&lt;/p&gt; 
&lt;p&gt;The goal is to get them thinking about &lt;em&gt;what they already know&lt;/em&gt; that applies here - because usually, they know more than they think.&lt;/p&gt; 
&lt;p&gt;For something like “how do I find the sum of numbers in an array?”, instead of writing the function for them, try: “How would you do this on paper, without any code?” Almost everyone knows how to add a list of numbers. The answer to that question usually unlocks the rest.&lt;/p&gt; 
&lt;p&gt;Each question you ask should narrow in, based on where they are. If they’ve understood the logic but are stuck on syntax, ask about syntax. If they don’t yet understand the underlying concept, stay at the concept level a bit longer.&lt;/p&gt; 
&lt;h3&gt;Tell them what to try, not what the answer is&lt;/h3&gt; 
&lt;p&gt;There’s a version of helping that lives in between “here’s the answer” and “ask endless questions”. It looks like: “I’d look at how your variable is being scoped here” or “check what type that value actually is at that point in the function.”&lt;/p&gt; 
&lt;p&gt;You’re giving them direction without removing the work. They still have to figure out &lt;em&gt;what&lt;/em&gt; the scoping issue is, or &lt;em&gt;why&lt;/em&gt; the type is unexpected. You’ve just pointed them at the right corner of the room.&lt;/p&gt; 
&lt;h3&gt;Don’t shame people for asking&lt;/h3&gt; 
&lt;p&gt;I’ve seen helpers make people feel embarrassed for asking “basic” questions. It’s counterproductive and unkind. There are no questions that don’t deserve a genuine response. If someone is asking, they’re trying to learn, and that’s a good thing.&lt;/p&gt; 
&lt;p&gt;The goal of giving help is not to demonstrate what you know. It’s to help the other person understand something. Those are different things.&lt;/p&gt;  
&lt;p&gt;Learning happens in the space between confusion and understanding. Good questions make that space navigable. Good guidance keeps the learner in the driver’s seat. Both take a bit of practice, but they’re worth developing - you’ll use them constantly, in every community and every project, for the rest of your life~&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fasking-and-giving-help&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Thu, 08 Oct 2026 01:53:22 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/asking-and-giving-help</guid>
      <dc:date>2026-10-08T01:53:22Z</dc:date>
    </item>
    <item>
      <title>Optimising Your Token Usage in Claude Code</title>
      <link>https://mktg.spellware.dev/blog/optimising-claude-code-tokens</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/optimising-claude-code-tokens" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="Optimising Your Token Usage in Claude Code" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;I had to write this guide for some colleagues of mine, and decided it was worth sharing more widely because the same handful of habits come up every time I watch someone burn through their usage cap faster than they should.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;I had to write this guide for some colleagues of mine, and decided it was worth sharing more widely because the same handful of habits come up every time I watch someone burn through their usage cap faster than they should.&lt;/p&gt; 
&lt;p&gt;If you’re on a Pro or Max plan, your budget is generous - but finite. The good news is that most token waste comes from a small set of fixable patterns, and fixing them doesn’t require changing how you fundamentally work with Claude Code. It mostly requires changing a few habits.&lt;/p&gt; 
&lt;h2&gt;Quick Reference&lt;/h2&gt; 
&lt;table&gt;
 &lt;thead&gt;
  &lt;tr&gt;
   &lt;th&gt;Wasteful Habit&lt;/th&gt;
   &lt;th&gt;Better Approach&lt;/th&gt;
  &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
  &lt;tr&gt;
   &lt;td&gt;Pasting entire files&lt;/td&gt;
   &lt;td&gt;Reference by file path&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Long-running conversations&lt;/td&gt;
   &lt;td&gt;Start fresh after each task&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Vague requests&lt;/td&gt;
   &lt;td&gt;Specify the exact file, function, and problem&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Asking Claude to explain everything&lt;/td&gt;
   &lt;td&gt;Only ask when you genuinely need to understand&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Re-explaining project context&lt;/td&gt;
   &lt;td&gt;Use CLAUDE.md&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Indexing irrelevant files&lt;/td&gt;
   &lt;td&gt;Use .claudeignore&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Retyping repeated prompts&lt;/td&gt;
   &lt;td&gt;Create custom slash commands&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Opus for everything&lt;/td&gt;
   &lt;td&gt;Match the model to the task&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Confirmation loops&lt;/td&gt;
   &lt;td&gt;Tell Claude to act; specify constraints upfront&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Pasting screenshots of text&lt;/td&gt;
   &lt;td&gt;Copy the text directly&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Asking Claude to summarise what it just did&lt;/td&gt;
   &lt;td&gt;Look at the changes yourself&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Using Claude Code for quick questions&lt;/td&gt;
   &lt;td&gt;Use claude.ai instead&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Multiple related changes as separate messages&lt;/td&gt;
   &lt;td&gt;Batch them into one prompt&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Jumping into a complex task&lt;/td&gt;
   &lt;td&gt;Use /plan first&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Simple one-line edits&lt;/td&gt;
   &lt;td&gt;Just do it yourself&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Extended thinking left on by default&lt;/td&gt;
   &lt;td&gt;Enable only for genuinely complex reasoning&lt;/td&gt;
  &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt; 
&lt;h2&gt;Understand What’s Actually Costing You&lt;/h2&gt; 
&lt;p&gt;Tokens are consumed by everything in the context window - not just your latest message, but the entire conversation history, every file you’ve pasted, every response Claude has given.&lt;/p&gt; 
&lt;p&gt;One thing a lot of people don’t realise: &lt;strong&gt;output tokens cost roughly three to five times more than input tokens.&lt;/strong&gt; This means asking Claude for lengthy explanations, verbose generated code, or exhaustive documentation is disproportionately expensive. If you don’t need the explanation, say so upfront. “Make the change, no explanation needed” saves more than you might expect.&lt;/p&gt; 
&lt;p&gt;The single biggest lever you have is context window size. A bloated conversation is the most common cause of hitting limits early.&lt;/p&gt; 
&lt;h2&gt;Start Fresh Conversations Aggressively&lt;/h2&gt; 
&lt;p&gt;This is probably the most impactful single change you can make. After a task is complete, that conversation history is dead weight. If you’re starting a new task - even a loosely related one - open a new conversation.&lt;/p&gt; 
&lt;p&gt;Signs you should start fresh:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;The task is different from what you were just doing&lt;/li&gt; 
 &lt;li&gt;You’ve been in the same conversation for over an hour&lt;/li&gt; 
 &lt;li&gt;Claude has already completed what you asked and you’re moving on&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;In Claude Code, use &lt;code&gt;/clear&lt;/code&gt; to reset the context without closing the session.&lt;/p&gt; 
&lt;h2&gt;Don’t Paste Files - Let Claude Read Them&lt;/h2&gt; 
&lt;p&gt;A very common pattern:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Here’s my code: [pastes 400 lines]… can you fix the bug in the &lt;code&gt;validateUser&lt;/code&gt; function?”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;This is expensive. Claude Code can read files directly from your filesystem. Instead:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Fix the bug in the &lt;code&gt;validateUser&lt;/code&gt; function in &lt;code&gt;src/auth/validator.ts&lt;/code&gt;.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Claude will read exactly what it needs. You save hundreds or thousands of tokens per interaction.&lt;/p&gt; 
&lt;p&gt;The rule: if the file exists on disk, reference it by path. Never paste unless Claude explicitly cannot access it.&lt;/p&gt; 
&lt;p&gt;You can also type &lt;code&gt;#&lt;/code&gt; in the Claude Code prompt to attach a specific file directly to the conversation - more precise than asking Claude to go find something on its own.&lt;/p&gt; 
&lt;h2&gt;Be Specific From the Start&lt;/h2&gt; 
&lt;p&gt;Vague requests lead to expensive back-and-forth. Every clarifying message adds to the context. Front-load the specifics:&lt;/p&gt; 
&lt;p&gt;Instead of:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Can you help me with my authentication system?”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Try:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“The &lt;code&gt;refreshToken&lt;/code&gt; endpoint in &lt;code&gt;src/routes/auth.ts&lt;/code&gt; is returning a 401 on valid tokens. The token validation logic lives in &lt;code&gt;src/middleware/auth.ts&lt;/code&gt;. Please diagnose and fix the issue.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;One well-specified message replaces three or four vague ones.&lt;/p&gt; 
&lt;p&gt;It is also worth being explicit about what &lt;em&gt;not&lt;/em&gt; to touch:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Only change the render method. Do not modify the props interface or the styles.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;This prevents unwanted refactoring and saves the correction turns that follow it.&lt;/p&gt; 
&lt;h2&gt;Ask for Exactly What You Need&lt;/h2&gt; 
&lt;p&gt;Some phrases generate unnecessary tokens:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;“Can you explain what you just did?” - skip this unless you genuinely need to understand it&lt;/li&gt; 
 &lt;li&gt;“What do you think about…” - open-ended questions invite long responses&lt;/li&gt; 
 &lt;li&gt;“Walk me through your reasoning” - only ask when the reasoning matters to you&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Some phrases that keep responses tight:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;“Fix X in Y” - just do it, no explanation needed&lt;/li&gt; 
 &lt;li&gt;“List only the changed files”&lt;/li&gt; 
 &lt;li&gt;“Give me the updated function only, not the full file”&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;And - don’t ask Claude to summarise what it just did. The changes are right there in your editor. Reading them yourself is free.&lt;/p&gt; 
&lt;h2&gt;Use CLAUDE.md for Persistent Context&lt;/h2&gt; 
&lt;p&gt;If you find yourself re-explaining the same things at the start of every conversation - your project structure, your coding standards, your preferred tools - you’re wasting tokens every single time.&lt;/p&gt; 
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; files solve this. Claude Code reads them automatically at the start of each session. Create one in your project root with:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Tech stack and architecture overview&lt;/li&gt; 
 &lt;li&gt;Key file locations&lt;/li&gt; 
 &lt;li&gt;Standards and conventions&lt;/li&gt; 
 &lt;li&gt;Anything you’d otherwise have to re-explain&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Keep it lean. Every line is read at the start of every session. A bloated &lt;code&gt;CLAUDE.md&lt;/code&gt; costs you tokens before you’ve typed a single message. Include only what Claude genuinely needs - and review it occasionally to remove anything that has gone stale.&lt;/p&gt; 
&lt;h2&gt;Break Large Tasks Into Focused Sub-Tasks&lt;/h2&gt; 
&lt;p&gt;Large tasks generate large context, large responses, and more opportunities for misunderstanding that requires correction.&lt;/p&gt; 
&lt;p&gt;Better approach:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;“Audit &lt;code&gt;src/components/&lt;/code&gt; and list every component that’s missing prop validation.” - small, focused&lt;/li&gt; 
 &lt;li&gt;Review the list, pick the ones you want fixed&lt;/li&gt; 
 &lt;li&gt;“Add prop validation to the components in &lt;code&gt;src/components/forms/&lt;/code&gt;.” - targeted fix&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Each conversation stays lean. You only spend tokens on what you actually need.&lt;/p&gt; 
&lt;h2&gt;Avoid Confirmation Loops&lt;/h2&gt; 
&lt;p&gt;A surprisingly common pattern:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;You: “Can you update the button styles?” Claude: “Sure! I’ll update the button styles by modifying the CSS to…” You: “Yes, go ahead.” Claude: [does the thing]&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;That middle exchange cost you tokens and time. Claude Code is an agent - you can just tell it to do things. If you have concerns about scope, be explicit upfront: “Update the button styles - check with me before touching anything outside &lt;code&gt;src/styles/buttons.css&lt;/code&gt;.”&lt;/p&gt; 
&lt;h2&gt;Compact Long Conversations&lt;/h2&gt; 
&lt;p&gt;When you want to continue work rather than start fresh, use &lt;code&gt;/compact&lt;/code&gt; in Claude Code. This summarises the conversation history into a condensed form, freeing up context window space whilst preserving the key information.&lt;/p&gt; 
&lt;p&gt;Use this when:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;You’re mid-task and can’t start fresh&lt;/li&gt; 
 &lt;li&gt;The conversation is getting slow or hitting context limits&lt;/li&gt; 
 &lt;li&gt;You’ve completed several sub-tasks and want to continue&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Choose the Right Model for the Task&lt;/h2&gt; 
&lt;p&gt;Claude Opus is powerful but expensive. Claude Sonnet is the sweet spot for most development work. Claude Haiku is excellent for simple, high-volume tasks.&lt;/p&gt; 
&lt;p&gt;If your plan gives you model selection, use it deliberately:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Haiku:&lt;/strong&gt; boilerplate generation, simple refactors, quick lookups&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Sonnet:&lt;/strong&gt; most coding tasks, debugging, code review&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Opus:&lt;/strong&gt; genuinely complex architecture decisions, nuanced analysis&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Using Opus for everything is like driving a lorry to pick up groceries. It gets the job done, but it’s not the right tool.&lt;/p&gt; 
&lt;h2&gt;Use .claudeignore to Exclude Irrelevant Files&lt;/h2&gt; 
&lt;p&gt;When Claude Code starts a session, it indexes your project. If your repository contains large generated directories, build artefacts, or vendor files, Claude is spending context on things it will never need to touch.&lt;/p&gt; 
&lt;p&gt;Create a &lt;code&gt;.claudeignore&lt;/code&gt; file in your project root using the same syntax as &lt;code&gt;.gitignore&lt;/code&gt;:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;node_modules/
dist/
build/
.next/
coverage/
*.log&lt;/code&gt;&lt;/pre&gt; 
&lt;h2&gt;Create Custom Slash Commands for Repeated Workflows&lt;/h2&gt; 
&lt;p&gt;If you find yourself typing the same prompt over and over - running a code review, generating a specific type of test, writing a commit message - turn it into a custom slash command.&lt;/p&gt; 
&lt;p&gt;Custom commands live in &lt;code&gt;.claude/commands/&lt;/code&gt; in your project (or &lt;code&gt;~/.claude/commands/&lt;/code&gt; for global commands available in every project). Each command is a markdown file where the filename becomes the command name. &lt;code&gt;.claude/commands/review.md&lt;/code&gt; becomes &lt;code&gt;/review&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;This ensures consistent, well-structured prompts every time - no more accidentally leaving out key context because you were typing from memory.&lt;/p&gt; 
&lt;h2&gt;Don’t Paste Screenshots When Text Will Do&lt;/h2&gt; 
&lt;p&gt;Images are token-heavy. Pasting a screenshot of an error message, a terminal output, or a UI bug costs significantly more than just copying the text.&lt;/p&gt; 
&lt;p&gt;Screenshots are genuinely useful for visual layout bugs, design feedback, or anything where the visual content itself is the point. For error messages, terminal output, and code - just paste the text. If you could copy-paste it, do that instead.&lt;/p&gt; 
&lt;h2&gt;Use the Right Interface for the Task&lt;/h2&gt; 
&lt;p&gt;Claude Code is a powerful agentic tool. That power comes with overhead. Loading it up to ask a quick question is like starting a car to walk to the end of the driveway.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Use claude.ai for:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Quick questions (“what does this regex do?”, “explain this algorithm”)&lt;/li&gt; 
 &lt;li&gt;Brainstorming and ideation&lt;/li&gt; 
 &lt;li&gt;Drafting text or documentation without file context&lt;/li&gt; 
 &lt;li&gt;Anything where you’d just be typing a question and reading an answer&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;Use Claude Code for:&lt;/strong&gt;&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Anything that requires reading, writing, or editing files&lt;/li&gt; 
 &lt;li&gt;Debugging with access to your actual codebase&lt;/li&gt; 
 &lt;li&gt;Multi-step agentic tasks&lt;/li&gt; 
 &lt;li&gt;Anything that benefits from your project’s &lt;code&gt;CLAUDE.md&lt;/code&gt; context&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Keeping this distinction in mind means your Claude Code sessions stay focused on work that genuinely needs them.&lt;/p&gt; 
&lt;h2&gt;Batch Related Changes Into One Request&lt;/h2&gt; 
&lt;p&gt;One request that makes five related edits is far cheaper than five separate requests.&lt;/p&gt; 
&lt;p&gt;Instead of:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Rename &lt;code&gt;getUserById&lt;/code&gt; to &lt;code&gt;fetchUser&lt;/code&gt;.” “Now update the call in the controller.” “Now update the call in the tests.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Try:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Rename &lt;code&gt;getUserById&lt;/code&gt; to &lt;code&gt;fetchUser&lt;/code&gt; everywhere - the function definition in &lt;code&gt;src/db/users.ts&lt;/code&gt;, its calls in &lt;code&gt;src/controllers/userController.ts&lt;/code&gt;, and its usage in &lt;code&gt;test/users.spec.ts&lt;/code&gt;.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Each separate message carries the full conversation history as overhead. Batching related changes keeps the context window lean.&lt;/p&gt; 
&lt;h2&gt;Use Plan Mode for Complex Tasks&lt;/h2&gt; 
&lt;p&gt;Before Claude starts making changes on a large or uncertain task, use &lt;code&gt;/plan&lt;/code&gt; to have it outline its approach first. Reviewing a plan is cheap. Undoing a series of incorrect edits is expensive.&lt;/p&gt; 
&lt;p&gt;This is especially useful for refactors that touch many files, new features where the architecture isn’t obvious, or anything where you want to validate the approach before changes are made.&lt;/p&gt; 
&lt;h2&gt;Know When Not to Use Claude&lt;/h2&gt; 
&lt;p&gt;Just do these yourself:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Changing a single string or variable name in one place&lt;/li&gt; 
 &lt;li&gt;Adding a single line of code you already know&lt;/li&gt; 
 &lt;li&gt;Fixing an obvious typo&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Use your editor’s search for these:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Finding where a function is defined&lt;/li&gt; 
 &lt;li&gt;Finding all usages of a variable&lt;/li&gt; 
 &lt;li&gt;Checking which files import a module&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Ctrl+Shift+F or Cmd+Shift+F is instant and free. Save Claude for work that genuinely benefits from AI reasoning.&lt;/p&gt; 
&lt;h2&gt;Use Git to Recover, Not Claude&lt;/h2&gt; 
&lt;p&gt;If Claude makes a change you didn’t want, reach for git rather than asking Claude to undo it:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;git restore src/path/to/file.ts&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;This is instant, free, and precise. Asking Claude to revert its own changes costs tokens and risks introducing new problems. Once the file is restored, re-prompt with a more specific instruction.&lt;/p&gt;  
&lt;h2&gt;Advanced Techniques&lt;/h2&gt; 
&lt;p&gt;The tips above will eliminate most waste. These are for when you have the basics down and want to go further.&lt;/p&gt; 
&lt;h3&gt;Understand the CLAUDE.md Hierarchy&lt;/h3&gt; 
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; files are inherited, and Claude Code reads multiple levels:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; - user-level, applies to every project on your machine&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;/your-project/CLAUDE.md&lt;/code&gt; - project-level, applies to that project&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;/your-project/src/CLAUDE.md&lt;/code&gt; - subdirectory-level, applies when working in that folder&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;Put genuinely universal preferences at the user level. Put project-specific context at the project level. Most people only use the project-level file and end up duplicating preferences across every project - centralising shared context at the user level means you write it once.&lt;/p&gt; 
&lt;h3&gt;Use Subagents for Parallel, Independent Tasks&lt;/h3&gt; 
&lt;p&gt;Claude Code can spawn subagents - separate instances that each handle a focused task with their own clean context window. Instead of doing three things sequentially in one growing conversation, three subagents can work in parallel, each staying lean and focused.&lt;/p&gt; 
&lt;p&gt;This is especially powerful for independent tasks:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Audit the API routes for missing input validation, audit the database queries for missing error handling, and audit the frontend components for missing loading states - run these in parallel.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Each subagent handles one audit without the others polluting its context.&lt;/p&gt; 
&lt;h3&gt;Discipline Your Output Format&lt;/h3&gt; 
&lt;p&gt;Every word Claude writes costs tokens. When you only need a specific piece of output, ask for exactly that:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;“Return only the updated function, not the full file”&lt;/li&gt; 
 &lt;li&gt;“List the affected files as a bullet list, nothing else”&lt;/li&gt; 
 &lt;li&gt;“Respond with JSON only, no explanation”&lt;/li&gt; 
 &lt;li&gt;“Give me a one-line summary”&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Use Skills for Complex Repeated Workflows&lt;/h3&gt; 
&lt;p&gt;For multi-step workflows that go beyond a single prompt, Claude Code supports skills - reusable sequences of instructions that can span multiple steps, use tools, and handle conditional logic.&lt;/p&gt; 
&lt;p&gt;Skills live in &lt;code&gt;~/.claude/skills/&lt;/code&gt; and are documented in a &lt;code&gt;SKILL.md&lt;/code&gt; file. Well-written skills pay enormous dividends for workflows you run repeatedly.&lt;/p&gt; 
&lt;h3&gt;Extend Claude with MCP Servers&lt;/h3&gt; 
&lt;p&gt;MCP (Model Context Protocol) servers give Claude direct access to external systems - databases, APIs, project management tools, documentation, and more. When Claude can query a system directly, you stop needing to paste data in from it.&lt;/p&gt; 
&lt;p&gt;Without an MCP server:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Here’s the current database schema: [pastes 200 lines]… can you write a migration?”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;With an MCP server:&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;“Write a migration based on the current schema.”&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;Claude fetches what it needs directly. This eliminates an entire category of token waste for teams that regularly work against external systems.&lt;/p&gt; 
&lt;h3&gt;Understand How Caching Works&lt;/h3&gt; 
&lt;p&gt;Claude Code automatically caches stable context - your &lt;code&gt;CLAUDE.md&lt;/code&gt;, system prompts, and other content that doesn’t change between turns. Cached tokens consume roughly 10% of your compute budget compared to fresh tokens.&lt;/p&gt; 
&lt;p&gt;What this means in practice:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;The cache has a 5-minute TTL.&lt;/strong&gt; If you step away for more than five minutes and come back, your next message pays full price. Use &lt;code&gt;/compact&lt;/code&gt; before stepping away rather than leaving a context-heavy session idle.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Don’t modify your CLAUDE.md mid-session.&lt;/strong&gt; Every change invalidates the cache for that content. Make &lt;code&gt;CLAUDE.md&lt;/code&gt; changes between sessions, not during them.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Short, frequent interactions benefit more than long, sparse ones.&lt;/strong&gt; Staying engaged within the TTL window means your stable context stays cached across multiple turns.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h3&gt;Use Extended Thinking Sparingly&lt;/h3&gt; 
&lt;p&gt;Extended thinking is the right tool for complex architectural decisions, subtle debugging where the cause isn’t obvious, and problems that genuinely require multi-step reasoning.&lt;/p&gt; 
&lt;p&gt;It is not the right tool for routine code generation, simple refactors, or any task where a direct answer is clearly possible. If you’re hitting your limits faster than expected and extended thinking is enabled, try disabling it and see how much of a difference it makes.&lt;/p&gt; 
&lt;h3&gt;Automate Repetitive Setup with Hooks&lt;/h3&gt; 
&lt;p&gt;Hooks are scripts that Claude Code runs automatically in response to specific events - before or after a file edit, after a bash command, before a session ends, and so on.&lt;/p&gt; 
&lt;p&gt;A hook that automatically runs your linter after every file edit means you never have to type “run the linter” - Claude just handles it. Hooks are configured in your Claude Code settings. Start with one hook for your most repeated manual step and go from there.&lt;/p&gt; 
&lt;h3&gt;Pre-approve Trusted Tools to Avoid Interruptions&lt;/h3&gt; 
&lt;p&gt;Every time Claude needs to run a command or use a tool you haven’t pre-approved, it pauses and waits. In a long task, repeated approval prompts break flow - and sometimes cause Claude to lose track of what it was doing, requiring a recovery turn.&lt;/p&gt; 
&lt;p&gt;In Claude Code settings, you can pre-approve tools and shell commands you trust. If you always want Claude to be able to run your test suite or your linter without asking, add those permissions once and never be interrupted for them again.&lt;/p&gt;  
&lt;p&gt;If you have questions or suggestions, feel free to come find me~&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Foptimising-claude-code-tokens&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Thu, 08 Oct 2026 01:53:21 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/optimising-claude-code-tokens</guid>
      <dc:date>2026-10-08T01:53:21Z</dc:date>
    </item>
    <item>
      <title>The 'Help Me Debug My Repo' NPM Scam</title>
      <link>https://mktg.spellware.dev/blog/malicious-npm-package-scam</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/malicious-npm-package-scam" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="The 'Help Me Debug My Repo' NPM Scam" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;This scam is particularly clever because it exploits something genuinely good about developers: we like helping each other.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;This scam is particularly clever because it exploits something genuinely good about developers: we like helping each other.&lt;/p&gt; 
&lt;p&gt;Someone reaches out and asks you to take a look at their repository. Maybe they need help debugging something, maybe they’re asking for a code review. The repo looks like a real project. You clone it, you run &lt;code&gt;npm install&lt;/code&gt;, and that’s it - you’re already compromised.&lt;/p&gt; 
&lt;h2&gt;What Actually Happens&lt;/h2&gt; 
&lt;p&gt;The malicious npm package in the repository contains a &lt;strong&gt;post-install script&lt;/strong&gt;. NPM runs these automatically when you install dependencies. You never had to run their code explicitly, never had to open a file, never had to do anything except type &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;The script reads your browser’s local cache and extracts stored credentials - including your Discord token - and sends them to the attacker. With your Discord token, they have full access to your account.&lt;/p&gt; 
&lt;h2&gt;Why This Works So Well&lt;/h2&gt; 
&lt;p&gt;The reason this scam catches people who really do know better comes down to a few things.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;You’re trying to help.&lt;/strong&gt; The request isn’t suspicious on its face. Someone needs debugging help, and you’re a decent person who helps people with code. Nothing about that set of circumstances triggers alarm bells.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Most people’s mental model of “running untrusted code” is wrong.&lt;/strong&gt; The common advice is “don’t run untrusted executables.” But &lt;code&gt;npm install&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; running code. It executes scripts from every package in the dependency tree, automatically, as part of the install process. If any of those packages is malicious, you’re done.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;The repository can look entirely legitimate.&lt;/strong&gt; Sensible directory structure, a readme, some commit history. Nothing in the code itself is obviously wrong.&lt;/p&gt; 
&lt;h2&gt;The Full Picture of What “Running Code” Means&lt;/h2&gt; 
&lt;p&gt;When you interact with someone else’s project, here’s a more complete picture of what actually executes:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;code&gt;npm install&lt;/code&gt; - runs post-install scripts from every package in the dependency tree&lt;/li&gt; 
 &lt;li&gt;&lt;code&gt;npm run &amp;lt;anything&amp;gt;&lt;/code&gt; - obviously executes code&lt;/li&gt; 
 &lt;li&gt;Build tools like webpack or vite - their config files are executable JavaScript&lt;/li&gt; 
 &lt;li&gt;Some IDE extensions - these can run things automatically when you open a project&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The rule I’d suggest: if you do not know where the code came from and cannot verify it, treat the whole thing as potentially hostile - including the install step.&lt;/p&gt; 
&lt;h2&gt;If You Want to Help Anyway&lt;/h2&gt; 
&lt;p&gt;There are safer ways to review someone’s code without putting yourself at risk:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Read the code before you run anything.&lt;/strong&gt; Check &lt;code&gt;package.json&lt;/code&gt; - specifically the &lt;code&gt;scripts&lt;/code&gt; section and the dependency list. Look up anything unfamiliar on &lt;a href="https://www.npmjs.com"&gt;npmjs.com&lt;/a&gt; before installing it.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Use a sandboxed environment.&lt;/strong&gt; A VM, a container, or a disposable environment you don’t mind wiping if something goes wrong.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Ask yourself why they’re contacting you.&lt;/strong&gt; Legitimate collaboration requests usually happen in context - through an existing relationship, in a community channel, with some shared history. Out-of-nowhere DMs asking you to run their code are a pattern worth questioning.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;If You Think You’ve Already Been Caught&lt;/h2&gt; 
&lt;p&gt;If you ran &lt;code&gt;npm install&lt;/code&gt; on a suspicious repository and you’re worried:&lt;/p&gt; 
&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;Change your Discord password immediately&lt;/strong&gt; - this invalidates existing tokens&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Log out of all devices&lt;/strong&gt; - go to User Settings &amp;gt; Devices in Discord&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Check your account&lt;/strong&gt; for any messages you did not send&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Rotate other credentials&lt;/strong&gt; that might have been stored in your browser&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Enable 2FA on Discord&lt;/strong&gt; if you haven’t already&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;This one is a good reminder that security isn’t just about what you actively run - it extends to every step of setting up and working with code.&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fmalicious-npm-package-scam&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Thu, 08 Oct 2026 01:53:20 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/malicious-npm-package-scam</guid>
      <dc:date>2026-10-08T01:53:20Z</dc:date>
    </item>
    <item>
      <title>Common Discord Scams to Watch Out For</title>
      <link>https://mktg.spellware.dev/blog/common-discord-scams</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/common-discord-scams" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="Common Discord Scams to Watch Out For" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;I run a lot of communities on Discord. Which means I see the same scams come through, over and over, in different servers, in different forms, like some kind of horrible recurring nightmare. Compromised accounts spread these things &lt;em&gt;fast&lt;/em&gt; - once one person gets caught, their account immediately starts messaging everyone they know.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;I run a lot of communities on Discord. Which means I see the same scams come through, over and over, in different servers, in different forms, like some kind of horrible recurring nightmare. Compromised accounts spread these things &lt;em&gt;fast&lt;/em&gt; - once one person gets caught, their account immediately starts messaging everyone they know.&lt;/p&gt; 
&lt;p&gt;So here’s a rundown of the ones I see most often, and what you should know about each.&lt;/p&gt; 
&lt;h2&gt;Nitro Scams&lt;/h2&gt; 
&lt;p&gt;The most common one, and self-propagating. Someone - usually a victim of the same scam - sends you a “free Nitro” link, either in a DM or dropped into multiple channels at once.&lt;/p&gt; 
&lt;p&gt;These links use &lt;strong&gt;URL hijacking&lt;/strong&gt;: they &lt;em&gt;look&lt;/em&gt; like legitimate Discord domains, but they are not. If you click through and “log in”, you have just handed the attacker your account token. Your account then starts sending the same message to everyone you know.&lt;/p&gt; 
&lt;p&gt;I’d say the giveaway is the urgency and the fact that it arrived out of nowhere - but honestly, the links are often convincing enough that people who know better still click. The best defence is simply: free Nitro offers in DMs are always scams. Every single time.&lt;/p&gt; 
&lt;h2&gt;”Try My Game” Scams&lt;/h2&gt; 
&lt;p&gt;Someone asks you to play-test their game. The game is usually hosted on itch.io and looks like a real project.&lt;/p&gt; 
&lt;p&gt;When you download and run it, the executable reads your Discord client’s local data and extracts your token. The attacker now has your account and uses it to continue spreading the same messages.&lt;/p&gt; 
&lt;p&gt;Don’t download games from strangers on Discord. That’s the whole rule.&lt;/p&gt; 
&lt;h2&gt;Steam Report Scam&lt;/h2&gt; 
&lt;p&gt;You get a DM from someone claiming they accidentally reported you to Steam, and that you need to speak with their friend who “works at Valve” to sort it out.&lt;/p&gt; 
&lt;p&gt;They did not report you. Their friend does not work at Valve. This is social engineering - they want you to follow a link or hand over account information. Steam does not resolve disputes over Discord DMs, and Valve employees do not work this way.&lt;/p&gt; 
&lt;p&gt;Ignore it and move on.&lt;/p&gt; 
&lt;h2&gt;Fake Discord Official Announcement Scams&lt;/h2&gt; 
&lt;p&gt;You get a DM from an account that looks like it is from Discord - it mentions a beta test, the moderator programme, HypeSquad events, or something similarly official-sounding, with a link to a form to apply.&lt;/p&gt; 
&lt;p&gt;This is not legitimate, &lt;strong&gt;even if the message includes a link to a real Discord server&lt;/strong&gt;. Real system messages from Discord come through a special account with a &lt;code&gt;system&lt;/code&gt; badge, not a regular user account. If there is no system badge, it is not Discord.&lt;/p&gt; 
&lt;h2&gt;QR Code Scams&lt;/h2&gt; 
&lt;p&gt;You join a server and it tells you to scan a QR code with your Discord mobile app to verify or unlock access.&lt;/p&gt; 
&lt;p&gt;Do not scan it.&lt;/p&gt; 
&lt;p&gt;The only legitimate QR code you should ever scan with the Discord app is the one on the official &lt;code&gt;discord.com/login&lt;/code&gt; page (and watch the URL carefully there too) or within the Discord desktop app itself. Any third-party use of this is a scam. Scanning a malicious QR code logs the attacker into your account from their device.&lt;/p&gt; 
&lt;p&gt;I see this one a lot targeted at NSFW servers and job-related servers, where people feel more urgency or more reason to trust an extra verification step.&lt;/p&gt; 
&lt;h2&gt;Small Text Markdown Exploit&lt;/h2&gt; 
&lt;p&gt;Discord added a small text markdown feature that took scammers approximately two seconds to start abusing. The technique lets them hide fake instructions or misleading fine print in messages.&lt;/p&gt; 
&lt;p&gt;One thing worth knowing: in legitimate use, the &lt;code&gt;Dismiss message&lt;/code&gt; option in an ephemeral bot response is &lt;strong&gt;never&lt;/strong&gt; a link to anywhere. It just clears the message. If you see small text that contains a link formatted like a dismiss button, that is a red flag.&lt;/p&gt; 
&lt;h2&gt;General Advice&lt;/h2&gt; 
&lt;p&gt;The best defence against all of these is not a verification bot or an anti-phishing tool. It’s basic internet habits:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;Don’t click unfamiliar links, even from people you trust - their account may be compromised&lt;/li&gt; 
 &lt;li&gt;Don’t sign in to websites you do not recognise&lt;/li&gt; 
 &lt;li&gt;Always check the full URL before entering any credentials&lt;/li&gt; 
 &lt;li&gt;Use a password manager - most of them will not autofill credentials on the wrong site, which catches spoofed login pages automatically&lt;/li&gt; 
 &lt;li&gt;If you see a server invite being spammed across multiple channels, there’s a very good chance that server is not safe - don’t join it&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;And if someone in a server you’re in sends you something suspicious, let a moderator know. We’d rather investigate a false alarm than miss a compromised account that then messages another two hundred people.&lt;/p&gt; 
&lt;p&gt;Stay safe out there~&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fcommon-discord-scams&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Thu, 08 Oct 2026 01:53:19 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/common-discord-scams</guid>
      <dc:date>2026-10-08T01:53:19Z</dc:date>
    </item>
    <item>
      <title>Spotting Fake Job Post Scams</title>
      <link>https://mktg.spellware.dev/blog/spotting-fake-job-post-scams</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/spotting-fake-job-post-scams" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="Spotting Fake Job Post Scams" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;Someone I know nearly fell for one of these. They’re a smart, experienced person who knows better - and it almost got them anyway. That’s the thing about fake LinkedIn job posts: they work precisely because they target you at your most vulnerable. You want the offer to be real, so your guard comes down.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;Someone I know nearly fell for one of these. They’re a smart, experienced person who knows better - and it almost got them anyway. That’s the thing about fake LinkedIn job posts: they work precisely because they target you at your most vulnerable. You want the offer to be real, so your guard comes down.&lt;/p&gt; 
&lt;p&gt;Let’s talk about how to tell whether it is.&lt;/p&gt; 
&lt;h2&gt;Step One: Does the Email Come From the Right Domain?&lt;/h2&gt; 
&lt;p&gt;A real email from a company should come from that company’s domain. If the company is NaomiCorp and their website is &lt;code&gt;naomicorp.com&lt;/code&gt;, an email from &lt;code&gt;naomicorp@gmail.com&lt;/code&gt; or &lt;code&gt;careers@naomicorp.org&lt;/code&gt; is a red flag - those domains don’t match.&lt;/p&gt; 
&lt;p&gt;I know this sounds obvious. But when you’re excited about a job opportunity, it’s easy to notice the company name and not look closely at what follows the &lt;code&gt;@&lt;/code&gt;.&lt;/p&gt; 
&lt;h2&gt;Step Two: Check the Email Headers&lt;/h2&gt; 
&lt;p&gt;Even an email address that looks right can be spoofed. The real test is in the headers.&lt;/p&gt; 
&lt;p&gt;Most email clients let you view the raw message. In Gmail, click the three-dot menu on a message and choose &lt;strong&gt;Show original&lt;/strong&gt;. In Outlook, go to &lt;strong&gt;File &amp;gt; Properties&lt;/strong&gt; and look under &lt;strong&gt;Internet headers&lt;/strong&gt;. You’re looking for something like this:&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.i=@nhcarrigan.com header.s=mail header.b="P0TRq/Qy";
       spf=pass (google.com: domain of naomi@nhcarrigan.com designates 128.199.1.98 as permitted sender)&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;Both &lt;code&gt;dkim&lt;/code&gt; and &lt;code&gt;spf&lt;/code&gt; should say &lt;code&gt;pass&lt;/code&gt;. If either says &lt;code&gt;fail&lt;/code&gt;, the email may be spoofed.&lt;/p&gt; 
&lt;h3&gt;What is SPF?&lt;/h3&gt; 
&lt;p&gt;SPF stands for &lt;strong&gt;Sender Policy Framework&lt;/strong&gt;. It is a DNS record that lets a domain owner specify which mail servers are allowed to send email on their behalf.&lt;/p&gt; 
&lt;p&gt;When you receive an email claiming to be from &lt;code&gt;naomicorp.com&lt;/code&gt;, the receiving server checks the SPF record for that domain and confirms whether the email actually came from an authorised server. If it didn’t, SPF fails.&lt;/p&gt; 
&lt;p&gt;You can check a domain’s SPF record yourself - run &lt;code&gt;dig TXT nhcarrigan.com +short&lt;/code&gt; in your terminal and look for the &lt;code&gt;v=spf1&lt;/code&gt; entry.&lt;/p&gt; 
&lt;h3&gt;What is DKIM?&lt;/h3&gt; 
&lt;p&gt;DKIM stands for &lt;strong&gt;DomainKeys Identified Mail&lt;/strong&gt;. It is a cryptographic signature that proves the email has not been tampered with in transit.&lt;/p&gt; 
&lt;p&gt;It works in two parts:&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;A DKIM record in the domain’s DNS (you can look one up yourself with &lt;code&gt;dig TXT mail._domainkey.nhcarrigan.com +short&lt;/code&gt;)&lt;/li&gt; 
 &lt;li&gt;A DKIM-Signature header inside the email itself&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;The receiving mail server uses the DNS record to verify the signature in the header. If the signatures match, the email arrived intact. If they do not, it may have been tampered with.&lt;/p&gt; 
&lt;h3&gt;What is DMARC?&lt;/h3&gt; 
&lt;p&gt;DMARC is the enforcement layer. A domain owner uses a DMARC policy to specify what should happen when an email fails SPF or DKIM checks: do nothing, route it to spam, or reject it outright.&lt;/p&gt; 
&lt;p&gt;It is not foolproof - some domains do not have a DMARC policy configured, or have it set to “do nothing” - but combined with SPF and DKIM, it is a meaningful signal.&lt;/p&gt; 
&lt;h2&gt;The Red Flags That Actually Matter&lt;/h2&gt; 
&lt;p&gt;Even if the authentication checks pass, watch for these patterns:&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;They always want to hire you.&lt;/strong&gt; Legitimate recruitment involves screening, interviews, and - yes - rejection. If a “recruiter” seems keen to hire you immediately with no real process, their goal is your personal information or your money, not your skills.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;They want you to pay for something.&lt;/strong&gt; No legitimate employer requires you to pay for training, equipment, or onboarding. If money flows from you to them before you have even started, it’s a scam.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;They want to send you a cheque.&lt;/strong&gt; This is the classic overpayment scam - they send you a cheque for more than agreed, ask you to cash it and wire back the difference, and then the cheque bounces. You are out the wired funds and they are long gone.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;It feels too good to be true.&lt;/strong&gt; If the salary is unusually high, the role seems perfectly tailored to your CV, and there is pressure to accept quickly - slow down. That urgency is deliberate.&lt;/p&gt; 
&lt;h2&gt;What to Do If You Are Not Sure&lt;/h2&gt; 
&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;Do not respond or click any links&lt;/strong&gt; until you have verified the offer is legitimate&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Find the company’s official contact independently&lt;/strong&gt; - look up their real website and reach out through a contact form or email address you found yourself, not one from the message&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Check whether the company has a Discord or community space&lt;/strong&gt; - you can often verify directly with current employees&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Ask a trusted technical friend&lt;/strong&gt; to look at the headers with you&lt;/li&gt; 
&lt;/ol&gt; 
&lt;p&gt;Anyone can be caught off guard by a well-crafted scam, especially when they’re under the stress of a job search. It very nearly happened to me, and I work in tech and know what to look for. Knowing the signs doesn’t make you immune - but it does mean you’re much less likely to be caught out.&lt;/p&gt; 
&lt;p&gt;Stay safe out there~&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fspotting-fake-job-post-scams&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Thu, 08 Oct 2026 01:53:18 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/spotting-fake-job-post-scams</guid>
      <dc:date>2026-10-08T01:53:18Z</dc:date>
    </item>
    <item>
      <title>Security Incident Report: Compromise of Self-Hosted Gitea and Plausible</title>
      <link>https://mktg.spellware.dev/blog/security-incident-october-2026</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/security-incident-october-2026" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="Security Incident Report: Compromise of Self-Hosted Gitea and Plausible" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;&lt;em&gt;Incident reference: 2026-10-breach. Status: contained and closed. All times are Pacific Time (UTC-7) unless stated.&lt;/em&gt;&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;&lt;em&gt;Incident reference: 2026-10-breach. Status: contained and closed. All times are Pacific Time (UTC-7) unless stated.&lt;/em&gt;&lt;/p&gt; 
&lt;h2&gt;Summary&lt;/h2&gt; 
&lt;p&gt;On 6 October 2026 we confirmed that two self-hosted services on our production server had been compromised through publicly disclosed vulnerabilities that we had not yet patched: &lt;strong&gt;Gitea&lt;/strong&gt;, which hosted our source code, and &lt;strong&gt;Plausible Community Edition&lt;/strong&gt;, which provided website analytics. Attackers used both to run cryptocurrency miners (XMRig, mining Monero) on our server.&lt;/p&gt; 
&lt;p&gt;We found no evidence that the server itself was taken over, no implants still running, and no evidence that data was copied off the server.&amp;nbsp;Both services have been permanently decommissioned, our code now lives on GitHub, and we have hardened the server.&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Detected:&lt;/strong&gt; 6 October 2026.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Earliest malicious activity:&lt;/strong&gt; 30 August 2026 (Gitea, an exploit-confirmation scan), 16 September 2026 (Plausible).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Impact:&lt;/strong&gt; unauthorised code execution inside two containers, resource theft (mining), and exposure of the secrets those services held.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Not found:&lt;/strong&gt; evidence of root-level access, credential-stealing code, staged data or misuse of our mail account.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;What was affected&lt;/h2&gt; 
&lt;table&gt; 
 &lt;thead&gt; 
  &lt;tr&gt; 
   &lt;th&gt;Service&lt;/th&gt; 
   &lt;th&gt;What happened&lt;/th&gt; 
   &lt;th&gt;Status&lt;/th&gt; 
  &lt;/tr&gt; 
 &lt;/thead&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Gitea (code hosting)&lt;/td&gt; 
   &lt;td&gt;Exploited through CVE-2026-59774 (GHSA-6v53-hr58-556r), an unauthenticated file-read flaw that leads to remote code execution. A scanner confirmed the weakness on 30 August. A cryptominer operator took over on 28 and 29 September, and a miner ran from 30 September until we stopped the service on 6 October.&lt;/td&gt; 
   &lt;td&gt;Compromised. Decommissioned.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Plausible CE v3.1.0 (analytics)&lt;/td&gt; 
   &lt;td&gt;Exploited through CVE-2026-8467 (GHSA-mhcv-h7gf-57cf), an unauthenticated remote code execution flaw in the &lt;code&gt;/storybook&lt;/code&gt; endpoint, fixed in version 3.2.1. Miners ran from at least 16 September.&lt;/td&gt; 
   &lt;td&gt;Compromised. Decommissioned.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;The production server&lt;/td&gt; 
   &lt;td&gt;No evidence of root-level compromise.&lt;/td&gt; 
   &lt;td&gt;Hardened. Further updates in progress.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Our other self-hosted services&lt;/td&gt; 
   &lt;td&gt;No evidence of compromise. Several (including our forum, task tracker, file host and translation service) were retired as a precaution.&lt;/td&gt; 
   &lt;td&gt;Retired or being reviewed.&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;h2&gt;Timeline&lt;/h2&gt; 
&lt;table style="width: 100%; height: 813px;"&gt; 
 &lt;thead&gt; 
  &lt;tr style="height: 32px;"&gt; 
   &lt;th style="width: 20.7154%; height: 32px;"&gt;When&lt;/th&gt; 
   &lt;th style="width: 78.243%; height: 32px;"&gt;What&lt;/th&gt; 
  &lt;/tr&gt; 
 &lt;/thead&gt; 
 &lt;tbody&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;3 June&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;Plausible publishes a critical advisory (fixed in 3.2.1). We were running 3.1.0.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 32px;"&gt; 
   &lt;td style="width: 20.7154%; height: 32px;"&gt;2 August&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 32px;"&gt;Gitea publishes the file-read advisory (fixed in 1.27.1).&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 88px;"&gt; 
   &lt;td style="width: 20.7154%; height: 88px;"&gt;30 August&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 88px;"&gt;A scanner injects a hook into one repository on our Gitea. It writes a marker file and makes a DNS callback to confirm the server is exploitable.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 32px;"&gt; 
   &lt;td style="width: 20.7154%; height: 32px;"&gt;16 September&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 32px;"&gt;Earliest evidence of attacker code running in our Plausible container.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;22 September to 6 October&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;About 580 probe requests to the vulnerable Plausible endpoint, all answered until the service was stopped.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;28 September, 19:30&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;First request in our retained logs from the Gitea attacker. The first full exploit round follows at 21:11.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;29 September, 03:31&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;The attacker installs a malicious setting in Gitea's global git configuration.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;30 September&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;A miner starts (09:33). From 1 October a supervisor process keeps it running.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 32px;"&gt; 
   &lt;td style="width: 20.7154%; height: 32px;"&gt;6 October, 11:43&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 32px;"&gt;We stop Gitea after an update leaves it failing to start.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;6 October, 12:04 to 13:43&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;We restart Gitea while troubleshooting. Git fetches begin timing out after exactly 60 seconds.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;6 October, about 13:40&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;We identify the malicious hook as the cause of the timeouts.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;6 October, 13:43&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;Containment: Gitea is stopped again and outbound traffic to the known attacker addresses is blocked.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;6 October, 13:45 to 15:05&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;We capture evidence from the Plausible container, then remove it.&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr style="height: 60px;"&gt; 
   &lt;td style="width: 20.7154%; height: 60px;"&gt;6 October, evening&lt;/td&gt; 
   &lt;td style="width: 78.243%; height: 60px;"&gt;Binary analysis in an isolated sandbox, a host-wide sweep for leftovers, cleanup, and a final verification at about 20:00.&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;h2&gt;How the attacks worked&lt;/h2&gt; 
&lt;h3&gt;Gitea&lt;/h3&gt; 
&lt;p&gt;The flaw let an anonymous visitor read arbitrary files from the server through the repository markup renderer. The attacker used it to read Gitea's configuration, including an internal API token. That token let them call an internal logging endpoint, which they abused to plant a global git setting (&lt;code&gt;uploadpack.packObjectsHook&lt;/code&gt;) pointing at a shell script. From then on, every &lt;code&gt;git fetch&lt;/code&gt; or clone against the server ran their script.&lt;/p&gt; 
&lt;p&gt;The script downloaded a second-stage installer and a bot, which started and supervised XMRig miners. The miners connected to public Monero pools over TLS. The bot could also have installed persistence on the host, but we found no sign that it did.&lt;/p&gt; 
&lt;h3&gt;Plausible&lt;/h3&gt; 
&lt;p&gt;The vulnerable endpoint allowed anonymous remote code execution inside the Plausible container. The attacker started two miners as children of the application process.&lt;/p&gt; 
&lt;h2&gt;How we found it&lt;/h2&gt; 
&lt;p&gt;After updating Gitea on 6 October, &lt;code&gt;git pull&lt;/code&gt; against it kept failing: the first request succeeded and the next one hung for exactly 60 seconds, which is our proxy timeout. A process listing during one of those hangs showed the fetch running through a script that did not belong there, and reading that script showed it was a dropper.&lt;/p&gt; 
&lt;h2&gt;What we know about data exposure&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Proven:&lt;/strong&gt; the attacker held Gitea's internal token and could run commands inside the Gitea container as its service account, with access to everything in its data directory (database, configuration, repositories).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;The file read itself:&lt;/strong&gt; about 6.5 KB was returned in total, so it was not used to copy data in bulk. The files read were most likely the configuration file.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Binary and script analysis:&lt;/strong&gt; we analysed all four binaries and the installer in an isolated sandbox. The miners are stock XMRig. We found no credential-harvesting, archiving or upload logic in any of them, and none in the installer (moderate confidence).&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;No staged data:&lt;/strong&gt; no archives or dumps were found in the containers or on the host, and no unexplained outbound connections appeared in the snapshots we took.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Mail:&lt;/strong&gt; the account that Gitea used to send email logged in from our own server only, with no sign of misuse.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Limits:&lt;/strong&gt; we have no network flow logs, our web logs only reached back to 22 September, and the period from 30 August to 6 September is not covered by retained mail logs. Absence of evidence is therefore weaker than we would like.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;&lt;strong&gt;What this means for you:&lt;/strong&gt; if you had an account on our Gitea (git.nhcarrigan.com), assume its username, email address, password hash, two-factor secret and any access tokens were exposed. If you reused that password anywhere else, change it. The service no longer exists.&lt;/p&gt; 
&lt;h2&gt;What we did&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;Stopped the affected services, blocked the attacker addresses (now a persistent firewall rule) and preserved the evidence.&lt;/li&gt; 
 &lt;li&gt;Analysed the binaries and scripts in an isolated sandbox, then verified the evidence bundle and moved it to cold storage.&lt;/li&gt; 
 &lt;li&gt;Swept the host for leftovers: no attacker files outside the evidence folders, no live connections to any attacker address, no unexpected persistence.&lt;/li&gt; 
 &lt;li&gt;Decommissioned Gitea and Plausible and removed their data directories, along with the root-level runners that depended on Gitea.&lt;/li&gt; 
 &lt;li&gt;Moved our repositories to GitHub as clean mirrors (server-side hooks and configuration were not copied), and scrubbed the secrets that scanning found in the history of the repositories that had them.&lt;/li&gt; 
 &lt;li&gt;Removed an over-broad passwordless sudo rule, restored correct ownership on our web server configuration, and restarted a monitoring service that had been leaking memory.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Root cause and contributing factors&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Unpatched, internet-facing software.&lt;/strong&gt; Both flaws were publicly documented weeks or months before they were used against us, and our update process did not catch them.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Services published directly through Docker.&lt;/strong&gt; Docker's port publishing bypassed the host firewall, so several services were reachable without going through our web server.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Too much shared privilege.&lt;/strong&gt; The services ran under the same user ID as our other applications.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Thin logging.&lt;/strong&gt; Fourteen days of web logs and no outbound network logging limited what we could prove.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;What we are doing next&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;Applying the pending operating-system updates and rebooting the server onto the current kernel.&lt;/li&gt; 
 &lt;li&gt;Keeping logs longer and shipping them off the server, including outbound connection logs.&lt;/li&gt; 
&lt;/ul&gt; 
&lt;h2&gt;Indicators of compromise&lt;/h2&gt; 
&lt;p&gt;We are sharing these so that others running similar software can check themselves. None of the addresses were contacted during our investigation.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Addresses (outbound traffic to all six is now blocked):&lt;/strong&gt; the first four are attacker infrastructure (download hosts and command servers). The last two are miner endpoints that our Plausible container was connected to.&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;38.127.244.12
162.4.173.102
80.87.206.86
85.137.54.178
103.189.234.47
45.77.67.39&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;strong&gt;Git configuration:&lt;/strong&gt; any &lt;code&gt;uploadpack.packObjectsHook&lt;/code&gt; entry in a global git config that points at a script, in particular &lt;code&gt;sh /data/gitea/home/.pwn&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Files and names:&lt;/strong&gt; &lt;code&gt;.pwn&lt;/code&gt;, &lt;code&gt;.ext4402&lt;/code&gt;, &lt;code&gt;.boat_landed&lt;/code&gt;, &lt;code&gt;ksoftirqd&lt;/code&gt; (outside the kernel thread list), &lt;code&gt;linuxsys&lt;/code&gt;, &lt;code&gt;syslog-ng-ee175527&lt;/code&gt;, &lt;code&gt;/var/tmp/.syslog-*&lt;/code&gt;, &lt;code&gt;.upd_boat&lt;/code&gt;, the abstract socket &lt;code&gt;boat_mutex_v4&lt;/code&gt;, and the bot string &lt;code&gt;boatnet-unit-v2&lt;/code&gt;. Marker file &lt;code&gt;/tmp/CVE-2026-59774.proof&lt;/code&gt; and DNS callbacks to &lt;code&gt;oob.smartdnslog[.]com&lt;/code&gt; belong to the earlier scanner.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;Request patterns:&lt;/strong&gt; &lt;code&gt;POST /{owner}/{repo}/markup&lt;/code&gt; with an Org-mode include directive, calls to &lt;code&gt;/api/internal/manager/add-logger&lt;/code&gt;, and &lt;code&gt;GET /storybook/iframe/button&lt;/code&gt; requests carrying &lt;code&gt;topic=pwn&lt;/code&gt;.&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;SHA-256:&lt;/strong&gt;&lt;/p&gt; 
&lt;table&gt; 
 &lt;thead&gt; 
  &lt;tr&gt; 
   &lt;th&gt;File&lt;/th&gt; 
   &lt;th&gt;SHA-256&lt;/th&gt; 
  &lt;/tr&gt; 
 &lt;/thead&gt; 
 &lt;tbody&gt; 
  &lt;tr&gt; 
   &lt;td&gt;linuxsys (XMRig)&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;4d17d32ed6efe92ea61d16602f9c6cd5261cdd3820adec427ee68d4088b6ab5b&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;syslog-ng-ee175527 (XMRig)&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;b20f39fc00d242e706b6c30367ad811c676e0575050a4ec2f30104b696944b49&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;Stage-2 installer (&lt;code&gt;/i&lt;/code&gt;)&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;fb3c23572dfd971ac52dd7991373631978b2107c8f2fa4348f87924d7d834dd8&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;ksoftirqd (XMRig), prefix only&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;315217b58357...&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
  &lt;tr&gt; 
   &lt;td&gt;.ext4402 (bot), prefix only&lt;/td&gt; 
   &lt;td&gt;&lt;code&gt;676149d53fe8...&lt;/code&gt;&lt;/td&gt; 
  &lt;/tr&gt; 
 &lt;/tbody&gt; 
&lt;/table&gt; 
&lt;h2&gt;How confident are we?&lt;/h2&gt; 
&lt;p&gt;We are confident about the Gitea findings and timeline, and confident that the server itself was not breached and that nothing was exfiltrated. The main gaps are the command channel the bot used (we saw the addresses but never the traffic), earlier versions of the attacker's script that were overwritten, and the unlogged period from 30 August to 6 September. If we learn anything that changes these conclusions, we will update this post.&lt;/p&gt; 
&lt;h2&gt;References&lt;/h2&gt; 
&lt;ul&gt; 
 &lt;li&gt;&lt;a href="https://github.com/go-gitea/gitea/security/advisories/GHSA-6v53-hr58-556r"&gt;Gitea advisory GHSA-6v53-hr58-556r (CVE-2026-59774)&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://github.com/plausible/analytics/security/advisories/GHSA-mhcv-h7gf-57cf"&gt;Plausible advisory GHSA-mhcv-h7gf-57cf (CVE-2026-8467)&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;a href="https://docs.nhcarrigan.com/legal/security/"&gt;Our security policy&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fsecurity-incident-october-2026&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Wed, 07 Oct 2026 03:12:42 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/security-incident-october-2026</guid>
      <dc:date>2026-10-07T03:12:42Z</dc:date>
    </item>
    <item>
      <title>I Stayed for 36 Hours Straight. Here Is Why</title>
      <link>https://mktg.spellware.dev/blog/ai-hackathon-berkeley-2026</link>
      <description>&lt;div class="hs-featured-image-wrapper"&gt; 
 &lt;a href="https://mktg.spellware.dev/blog/ai-hackathon-berkeley-2026" title="" class="hs-featured-image-link"&gt; &lt;img src="https://mktg.spellware.dev/hubfs/Generated_Image_September_29_2026_-_9_34PM.jpg" alt="I Stayed for 36 Hours Straight. Here Is Why" class="hs-featured-image" style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"&gt; &lt;/a&gt; 
&lt;/div&gt; 
&lt;p&gt;I attended AI Hackathon @ Berkeley 2026 representing Deepgram, and I did not sleep. I did not have to stay awake. I could not bring myself to leave.&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;I attended AI Hackathon @ Berkeley 2026 representing Deepgram, and I did not sleep. I did not have to stay awake. I could not bring myself to leave.&lt;/p&gt; 
&lt;p&gt;I was there for all of it: the sponsor booth, the workshop, the judging and the full 36 hours. I want to be direct about why. The reason was the people. The hackers at Berkeley were some of the most enthusiastic builders I have been around. They were not only building. They wanted to talk, and they wanted to show me what they were making. They had questions at 2am, at 4am and at 6am, and they did not care what time it was.&lt;/p&gt; 
&lt;p&gt;That is not an energy I could walk away from.&lt;/p&gt; 
&lt;h2&gt;What We Were There For&lt;/h2&gt; 
&lt;p&gt;Deepgram sponsored the Best Use of Deepgram track. The track asked teams to build real-time voice into their projects as an essential part, not an afterthought. Out of 398 total projects, 141 teams did this. That is 35% of the hackathon building voice-first.&lt;/p&gt; 
&lt;p&gt;I knew beforehand that voice is a difficult integration. It cannot simply be added at the end. Even so, team after team came to our booth. They told me what they were building, how they were using the API, what was working and where they were stuck. I had those conversations all night.&lt;/p&gt; 
&lt;p&gt;No metric captures one part of this: the look on someone's face when their real-time transcription works for the first time.&lt;/p&gt; 
&lt;h2&gt;How I Showed Up&lt;/h2&gt; 
&lt;p&gt;I think much of why this event felt different came from a choice I made before I arrived. I decided to do more than is usual for a sponsor.&lt;/p&gt; 
&lt;p&gt;We set up a monitor at the booth with QR codes. I ran a workshop. I was the only sponsor who stayed all night.&lt;/p&gt; 
&lt;p&gt;While everyone else went home, I stayed. At 2am, people were doing push-ups in the aisles to stay awake. By 6am, there were hackers asleep on the floor next to my booth. I was still standing, so I was still having conversations. They were real conversations about what people were building, what was working, what they were stuck on and what they wanted to do next.&lt;/p&gt; 
&lt;p&gt;Two figures reflect this. There were 243 workshop signups from a room of 50 people. There were 42 people who personally invited me to see their project in our sponsor Slack channel. Both followed from my being present throughout the event.&lt;/p&gt; 
&lt;h2&gt;Judging&lt;/h2&gt; 
&lt;p&gt;There were 141 submissions in our track and a two-hour judging window. I walked the floor and visited as many as I could of the teams that had contacted me personally. While I walked, other teams saw me and called me over to their tables. The whole floor was in motion.&lt;/p&gt; 
&lt;p&gt;When the window closed and I submitted my winner, people began lining up at the booth. For another 90 minutes, team after team came to show me what they had built with Deepgram and to talk about what came next. No one was aggressive. Everyone was polite. They wanted to talk about the API, their project and where it was going. That is rare at a sponsored event.&lt;/p&gt; 
&lt;p&gt;The team behind aside.ai won our track. The reason was not the two-hour judging window. It was the 24 hours before it. They had been in conversation with me for the entire event. They showed me their work, iterated and checked back in. By the time I submitted the winner, I had watched them build the project.&lt;/p&gt; 
&lt;h2&gt;The Projects&lt;/h2&gt; 
&lt;p&gt;The range of what people built was very wide. I want to be specific about that. Every hackathon recap mentions a variety of projects, and most do not mean it in the way I mean it.&lt;/p&gt; 
&lt;p&gt;voxaid is a speech pipeline fine-tuned for dysarthric speech. It is intended for people with ALS, cerebral palsy and stroke, whose voices existing recognition software does not understand. The team fine-tuned a wav2vec2 model on the TORGO dysarthric speech dataset. They used an architecture from a paper published two days before the hackathon started. They then passed the output through Claude for semantic reconstruction and Deepgram Aura for voice response. The result was a full speech-to-speech system that could take unclear input and produce natural output. It is intended for people who currently depend on a human interpreter to communicate. It was built at a hackathon.&lt;/p&gt; 
&lt;p&gt;aintercept is a scam interception tool. It listens on a call and alerts someone as soon as it detects the patterns of a scam in progress. The builder's grandmother had fallen for a fake police officer scam the year before. They explained to me exactly what had happened, and then they showed me what they had built. Those two conversations were about thirty seconds apart.&lt;/p&gt; 
&lt;p&gt;crisisroom was built by a veteran who served on a landing ship in Singapore. He described damage control at sea. There is a steel passageway with strobing lights and smoke filling the space. Water rises past your boots. A general alarm is so loud that you cannot hear the person next to you. A grease pencil on a plexiglass board is the only shared source of truth for everyone making life-or-death decisions. He built a real-time, voice-powered incident response system because he had lived with the cost of not having one. The problem statement he gave me at the booth was one of the most striking things I heard all weekend.&lt;/p&gt; 
&lt;p&gt;moggie is a portable party-game kiosk running on a Raspberry Pi. Players compete in facial expression matching and hand-motion challenges, with Deepgram powering the social and voice layer. It was built as a response to looksmaxxing culture. The hypothesis is that if people laugh and compete together in person over topics they would otherwise obsess over alone, something changes. It was also chaotic and enjoyable, to the point that I forgot I was 18 hours into a hackathon.&lt;/p&gt; 
&lt;p&gt;séance lets you point your phone at any object. The object becomes a character and talks back to you. You can argue with your water bottle, or have a stapler answer back. I will not claim this solved a critical global problem. It was one of the most enjoyable things I saw all weekend, and the group of people around it was always laughing.&lt;/p&gt; 
&lt;p&gt;The last project was aside.ai.&lt;/p&gt; 
&lt;p&gt;The team behind aside.ai won our track and deserved to. Their project was a clip-on wearable. A Raspberry Pi camera streamed frames over Wi-Fi to a laptop. The laptop used Claude for vision and Deepgram for both speech-to-text and text-to-speech. It narrated the world around the wearer in real time, in a personality of their choice, such as a hype man, a goth therapist or an epic quest narrator. The same moment was described differently depending on the personality chosen. The project was technically strong and funny, and the team had clearly thought hard about both.&lt;/p&gt; 
&lt;p&gt;What stayed with me about aside.ai was not only the project but the team. They were at my booth constantly. They were not asking for help. They were showing me things and iterating. At one point I noticed that the booth next to mine had been empty for the entire event. Someone from their team was now sitting there, editing and publishing social media content in real time to build interest in what they were making. They had not only built a startup. They had shipped a startup. At a hackathon.&lt;/p&gt; 
&lt;h2&gt;The Workshop&lt;/h2&gt; 
&lt;p&gt;We ran a workshop during the event with about 50 people in the room. The links we handed out produced 243 console signups. That is nearly five times the number of people who were physically present. The resources spread across the whole 398-project event, and I watched it happen in real time from the booth. Links appeared in Discord servers, were added to READMEs and were passed between teams who had never attended the session.&lt;/p&gt; 
&lt;h2&gt;What I Am Taking Away&lt;/h2&gt; 
&lt;p&gt;I have been to many events. Berkeley felt different. The students there were not only looking for a prize. They were building things they were excited about, and they wanted to talk to the people behind the tools they were using. That is rare.&lt;/p&gt; 
&lt;p&gt;I stayed for 36 hours because the room kept giving me reasons to stay. I would do it again without hesitation.&lt;/p&gt;  
&lt;img src="https://track-na2.hubspot.com/__ptq.gif?a=247600308&amp;amp;k=14&amp;amp;r=https%3A%2F%2Fmktg.spellware.dev%2Fblog%2Fai-hackathon-berkeley-2026&amp;amp;bu=https%253A%252F%252Fmktg.spellware.dev%252Fblog&amp;amp;bvt=rss" alt="" width="1" height="1" style="min-height:1px!important;width:1px!important;border-width:0!important;margin-top:0!important;margin-bottom:0!important;margin-right:0!important;margin-left:0!important;padding-top:0!important;padding-bottom:0!important;padding-right:0!important;padding-left:0!important; "&gt;</content:encoded>
      <pubDate>Wed, 24 Jun 2026 12:00:00 GMT</pubDate>
      <author>naomi@nhcarrigan.com (Naomi Carrigan)</author>
      <guid>https://mktg.spellware.dev/blog/ai-hackathon-berkeley-2026</guid>
      <dc:date>2026-06-24T12:00:00Z</dc:date>
    </item>
  </channel>
</rss>
