<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://joshcgrossman.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://joshcgrossman.com/" rel="alternate" type="text/html" /><updated>2026-07-18T01:10:03+00:00</updated><id>https://joshcgrossman.com/feed.xml</id><title type="html">Josh Grossman</title><subtitle>A blog about AppSec/InfoSec from your friendly AppSec Ghost! 👻</subtitle><entry><title type="html">Introducing AGHAST: AI-Guided Hybrid Application Static Testing</title><link href="https://joshcgrossman.com/2026/04/14/introducing-aghast/" rel="alternate" type="text/html" title="Introducing AGHAST: AI-Guided Hybrid Application Static Testing" /><published>2026-04-14T01:00:00+00:00</published><updated>2026-04-14T01:00:00+00:00</updated><id>https://joshcgrossman.com/2026/04/14/introducing-aghast</id><content type="html" xml:base="https://joshcgrossman.com/2026/04/14/introducing-aghast/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2026/04/14/Introducing-AGHAST.html">Bounce Security blog</a>.</strong></p>

<h4 id="tldr">tl;dr</h4>

<p>Today, we are releasing AGHAST, an open source framework that combines static discovery with AI prompts to find code-specific and company-specific security issues.</p>

<p><img src="/assets/img/2026-04-aghast-intro/aghastbouncecaption.png" alt="AGHAST logo" class="blog-image" /></p>

<p>If you want to try it out, you can find information here in the <a href="https://github.com/BounceSecurity/aghast">Github repository</a>.</p>

<p>Here is a brief video introduction:</p>

<div style="text-align: center; margin: 1.5em 0;">
  <iframe width="560" height="315" src="https://www.youtube.com/embed/B76A33l1LyI" title="AGHAST Introduction" frameborder="0" allow="picture-in-picture" allowfullscreen=""></iframe>
</div>

<p>If you want to understand more about the background, read on!</p>

<h4 id="introduction">Introduction</h4>

<p>Every security team has asked some version of this question: “Can our scanner check for <em>that</em>?” Usually, the answer is no - or at best, “it’s complicated.” That frustration is exactly where this story begins.</p>

<p><img src="/assets/img/2026-04-aghast-intro/confused.png" alt="Digital art illustration of a security professional looking frustrated at generic vulnerability alerts" class="blog-image" /></p>

<h4 id="the-gap-in-generic-scanning">The Gap in Generic Scanning</h4>

<p>A couple of years ago, we started thinking about a particular problem in application security. There were loads of generic code scanning tools that would tell you about basic technical vulnerabilities, things like SQLi, XSS, the usual suspects. But what if we wanted to find something that was code-specific or company-specific?</p>

<p>Some of the scanning tools provide a custom rules mechanism, which is often very complicated. It was not until <a href="https://semgrep.dev/docs/cli-reference">Semgrep</a> came about that a powerful but simple way of writing custom code scanning rules was available.</p>

<h4 id="the-questions-nobody-else-could-answer">The Questions Nobody Else Could Answer</h4>

<p>The sorts of questions we wanted to answer were:</p>

<ul>
  <li>Has this custom business verification been implemented correctly?</li>
  <li>Has the company’s custom authorization mechanism been used correctly and consistently?</li>
  <li>Are API endpoints returning too wide a data set?</li>
</ul>

<p>These are questions that are specific to a codebase or organization, and basically impossible for generic static security scanners to answer. They require <em>context</em> to understand how things <em>should</em> work, not just what is technically vulnerable.</p>

<p><img src="/assets/img/2026-04-aghast-intro/puzzle-pieces.png" alt="Illustration of three interconnected puzzle pieces containing security question mark symbols" class="blog-image" /></p>

<h4 id="from-research-to-real-world-use">From Research to Real-World Use</h4>

<p>Working with <a href="https://www.linkedin.com/in/michal-kamensky-a65804247/">Michal Kamensky</a>, we delivered a <a href="https://www.youtube.com/watch?v=KuNZzDjvMlg">talk</a> and eventually <a href="https://www.bouncesecurity.com/training/bughunting">a training course</a> focused around this area of custom tests. The research initially focused on static testing, and for the training course also included dynamic testing.</p>

<p>The aim was simple: provide a straightforward way to get an answer to a codebase-specific or company-specific question.</p>

<p>We also started using this mechanism with clients to secure their codebases. However, the simplicity was sometimes a drawback. Diagnosing certain problems could be very complex or even impossible for a static rule alone to establish.</p>

<p>Something was missing. Static rules could get us part of the way but were not good enough for every circumstance.</p>

<h4 id="enter-llms">Enter LLMs</h4>

<p>Thankfully at this point, LLMs came to the rescue. Suddenly we had LLMs and coding agents that were very good at understanding and interpreting code.</p>

<p>We started by moving from static rules to LLM prompts and quickly discovered that once you reached a certain codebase size, the results became unreliable. Feeding an entire codebase to an LLM and asking “find the problems” is a bit like asking someone to proofread the internet: technically possible, practically useless.</p>

<p>That led to a key realization: why not combine these two techniques?</p>

<ul>
  <li>Use <strong>static rules</strong> to narrow down to particular areas of the codebase.</li>
  <li>Use an <strong>AI prompt</strong> to investigate that specific area in depth.</li>
</ul>

<p>Think of it as a metal detector on a beach: the static rule tells you where to dig, and the AI carefully examines what you find.</p>

<p><img src="/assets/img/2026-04-aghast-intro/funnel.png" alt="Illustration of a two-stage funnel: static rules filter code files down for AI analysis" class="blog-image" /></p>

<h4 id="the-orchestration-problem">The Orchestration Problem</h4>

<p>That approach works conceptually, but it introduces another challenge:</p>

<p>How do you orchestrate this at scale, in a repeatable way, and in a way that integrates with existing processes?</p>

<p>Running a one-off combination of a Semgrep rule plus an LLM prompt is one thing. Doing it consistently across multiple codebases, in CI pipelines, with structured output requires slightly more plumbing.</p>

<h4 id="the-answer-aghast">The Answer: AGHAST</h4>

<p>The answer is <strong>AGHAST</strong>.</p>

<p>This is a framework that has been germinating for about six months.</p>

<p><strong>AGHAST</strong> stands for <strong>AI-Guided Hybrid Application Static Testing</strong>, and it allows us to orchestrate tests based on this custom test philosophy.</p>

<p>Today, we at Bounce Security are open sourcing this tool for you to use and build on as well.</p>

<h4 id="what-aghast-does">What AGHAST Does</h4>

<p>AGHAST helps you run three types of checks:</p>

<ul>
  <li><strong>Pure AI scanning rules</strong> - let the LLM do all the analysis</li>
  <li><strong>A combination of a static mechanism and an AI scanning rule</strong> - the sweet spot for most use cases</li>
  <li><strong>Purely static rules</strong> - for completeness, when a traditional static rule is all you need</li>
</ul>

<p>The beauty of the approach is what you <em>don’t</em> need:</p>

<ul>
  <li>You don’t need to modify the code</li>
  <li>You don’t need to build something into the codebase</li>
  <li>You don’t need to write code in the language of the codebase</li>
</ul>

<p>All you need is:</p>

<ul>
  <li>Access to the codebase</li>
  <li>An understanding of the problem you are trying to discover</li>
  <li>The ability to write some simple rules</li>
</ul>

<p>There are almost certainly other ways of achieving this, but to our mind, this approach is <strong>straightforward</strong>, <strong>deterministic</strong>, and very <strong>powerful</strong>.</p>

<h4 id="built-for-the-real-world">Built for the Real World</h4>

<p>Aside from the basic function, there are several features designed to make AGHAST easy to use and integrate into your existing workflow.</p>

<p><img src="/assets/img/2026-04-aghast-intro/architecture.png" alt="Isometric illustration showing AGHAST as a central hub connected to CI/CD pipeline, LLM providers, output formats, and configuration" class="blog-image" /></p>

<h5 id="1-ci-pipeline-integration">1. CI Pipeline Integration</h5>

<p>From the beginning, AGHAST was designed to work easily within automated CI pipelines, utilizing a simple install process, text based configuration and a single CLI call to run.</p>

<p>We anticipate that most teams will find it most useful in this context in the long run. Set it up once, and it runs with every build.</p>

<h5 id="2-pluggable-components">2. Pluggable Components</h5>

<p>The current implementation uses Claude Code and the Claude Agent SDK for AI analysis, and Semgrep Community Edition for static scanning.</p>

<p>However, the design intentionally allows for pluggability across the stack:</p>

<ul>
  <li>Different LLM and API providers</li>
  <li>Different SAST engines (e.g., OpenGrep or other static discovery mechanisms)</li>
  <li>Language agnostic by design</li>
</ul>

<p>No vendor lock-in here - use whatever works best for your team.</p>

<h5 id="3-pluggable-outputs-and-actions">3. Pluggable Outputs and Actions</h5>

<p>Output formats are designed to be pluggable - currently supporting JSON and SARIF, with other formats straightforward to add.</p>

<p>Beyond output formats, the platform could also be extended to support pluggable actions, enabling integration with ticketing systems, notifications, or other downstream workflows.</p>

<h5 id="4-flexible-configuration-mechanism">4. Flexible Configuration Mechanism</h5>

<p>AGHAST includes a flexible configuration mechanism.</p>

<p>This allows checks to be specified in multiple ways:</p>

<ul>
  <li><strong>Per-codebase configuration</strong> - tailor checks to individual projects</li>
  <li><strong>Central configuration containing definitions for multiple codebases</strong> - manage everything from one place</li>
</ul>

<p>This makes it easy to define a CI job in one place using a single configuration file, which can then apply across multiple codebases that use the same CI job.</p>

<h4 id="protection-of-our-work">Protection of our work</h4>

<p>We want AGHAST to be widely available, whether you are a practitioner using it in your own environment, or a consultant implementing it for clients.</p>

<p>AGHAST is licensed under AGPL to keep it open and ensure improvements flow back to the community. If you are interested in commercial licensing, professional support, or help implementing AGHAST in your organization, <a href="https://www.bouncesecurity.com/contact">get in touch</a>.</p>

<h4 id="prior-and-parallel-work">Prior and Parallel Work</h4>

<p>No tool exists in a vacuum. We want to acknowledge some prior and parallel work in this area that both inspired and validated the direction we took.</p>

<h5 id="semgrep">Semgrep</h5>

<p><a href="https://semgrep.dev/"><img src="/assets/img/2026-04-aghast-intro/semgrep.svg" alt="Semgrep" class="blog-image" /></a></p>

<p>Firstly, we want to give a shout out to the team at <strong><a href="https://semgrep.dev/">Semgrep, Inc</a></strong> for building a fantastic tool for simple static scanning. They truly revolutionized this space and we stand on their shoulders as do many others.</p>

<p><a href="https://semgrep.dev/docs/cli-reference">Semgrep Community Edition</a> is a key part of AGHAST today, although the system is also extensible and could support OpenGrep or other static discovery mechanisms.</p>

<h5 id="anthropic-code-security-et-al">Anthropic Code Security (et al)</h5>

<p>A few weeks ago, Anthropic released their code security product to much fanfare (and gnashing of shareholder teeth) followed by similar releases from other LLM providers.</p>

<p>That product appears to focus on giving an LLM a codebase and seeing what vulnerabilities emerge.</p>

<p>AGHAST differs in a key way:</p>

<ul>
  <li>Those tools are exploratory - “tell me what you find”</li>
  <li>AGHAST is goal-driven - “tell me about <em>this specific thing</em>”</li>
</ul>

<p>AGHAST looks for <strong>specific issues you define</strong> and returns a <strong>specific, well-formed answer</strong> about those issues.</p>

<h5 id="raptor">RAPTOR</h5>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣤⣀⣀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠿⠿⠟
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⣀⣤⣴⣶⣶⣶⣤⣿⡿⠁⠀⠀⠀
⣀⠤⠴⠒⠒⠛⠛⠛⠛⠛⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⣿⣿⣿⡟⠻⢿⡀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⢿⣿⠟⠀⠸⣊⡽⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⣿⡁⠀⠀⠀⠉⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⠿⣿⣧⠀ Get them bugs.....⠀⠀⠀⠀⠀⠀⠀⠀
                                                 
</code></pre></div></div>

<p>(We had to reproduce their <a href="https://github.com/gadievron/raptor/blob/main/README.md">funky ASCII art</a>)</p>

<p>At the start of the year, our friends <a href="https://www.linkedin.com/in/gadievron/">Gadi Evron</a>, <a href="https://www.linkedin.com/in/daniel-cuthbert0x/">Daniel Cuthbert</a> <a href="https://github.com/gadievron/raptor#raptor---autonomous-offensivedefensive-security-research-framework-based-on-claude-code">and others</a> released RAPTOR, which performs agentic code scanning for various scenarios.</p>

<p>It is particularly strong for offensive work and moving from vulnerability discovery to exploitation.</p>

<p>While we initially considered building on Raptor, the design philosophy differs enough that it ultimately made more sense to build separately.</p>

<h5 id="openant">OpenAnt</h5>

<p><a href="https://www.knostic.ai/openant"><img src="/assets/img/2026-04-aghast-intro/open-ant-black.png" alt="OpenAnt" class="blog-image" /></a></p>

<p>More recently, <a href="https://www.linkedin.com/in/gadievron/">Gadi Evron</a> and <a href="https://www.knostic.ai/">team</a> released <a href="https://www.knostic.ai/blog/oss-scan">OpenAnt</a>. We saw it about a week before release and thought their approach for focusing the LLM’s attention on particular areas of code at a time was super interesting.</p>

<p>The key difference again is that AGHAST is oriented around specific checks and explicit criteria that determine which parts of the codebase are examined. That said, we liked the OpenAnt approach enough that AGHAST now supports OpenAnt datasets as an input source. This means you can use OpenAnt as a discovery method for targeted checks, feeding pre-extracted code units with call graph context into AGHAST’s AI analysis pipeline.</p>

<p>It was encouraging to see them independently arrive at the same conclusion:</p>

<blockquote>
  <p>A key differentiator for LLM-based analysis is guiding the model to the specific area of the codebase it should examine rather than trying to load a whole codebase into context.</p>
</blockquote>

<h4 id="what-comes-next">What Comes Next</h4>

<p>We think tools like <strong>AGHAST</strong> are going to be an important step in taking static code analysis to the next level of usability and accuracy. The combination of deterministic static rules and intelligent AI analysis opens up possibilities that neither approach could achieve alone.</p>

<p>We are looking forward to seeing your feedback - and even more, to seeing what custom checks you build with it!</p>]]></content><author><name>Josh Grossman</name></author><category term="Security" /><category term="Technical" /><category term="SAST" /><category term="AI" /><category term="AppSec" /><category term="Semgrep" /><category term="Open Source" /><summary type="html"><![CDATA[Introducing AGHAST, an open source framework that combines static discovery with AI prompts to find code-specific and company-specific security issues.]]></summary></entry><entry><title type="html">Getting multiple GitHub accounts on one Windows machine – 2026 update</title><link href="https://joshcgrossman.com/2026/03/29/getting-multiple-github-accounts-on-one-windows-machine-2026-update/" rel="alternate" type="text/html" title="Getting multiple GitHub accounts on one Windows machine – 2026 update" /><published>2026-03-29T04:00:00+00:00</published><updated>2026-03-29T04:00:00+00:00</updated><id>https://joshcgrossman.com/2026/03/29/getting-multiple-github-accounts-on-one-windows-machine-2026-update</id><content type="html" xml:base="https://joshcgrossman.com/2026/03/29/getting-multiple-github-accounts-on-one-windows-machine-2026-update/"><![CDATA[<h3 id="introduction"><a href="#introduction">Introduction</a></h3>

<p>I wrote the <a href="/2022/01/26/getting-multiple-github-accounts-on-one-linux-wsl-machine/">original version</a> of this blogpost back in 2022 and then an <a href="/2023/01/03/getting-multiple-github-accounts-on-one-linux-wsl-machine-2023-update/">updated 2023 version</a> which simplified things by using SSH commit signing instead of GPG.</p>

<p>Since then, my setup has changed quite a bit. I moved from Linux/WSL to native Windows and started storing my SSH keys in <a href="https://1password.com/">1Password</a> instead of as local files. The 2023 solution relied on <code class="language-plaintext highlighter-rouge">ssh-agent</code> and <code class="language-plaintext highlighter-rouge">core.sshCommand</code> which worked but had an annoying problem: 1Password’s SSH agent would offer all of its keys in order and I had to manually deny the wrong ones before it tried the correct one.</p>

<p>In this updated post, I want to summarize how I solved that problem using SSH host aliases, <code class="language-plaintext highlighter-rouge">IdentitiesOnly</code>, and URL rewrites so that the correct key is used automatically every time.</p>

<p>Please read it carefully as there are some fixes and some updated gotchas! 😀</p>

<h3 id="git-your-configuration-on">Git your configuration on</h3>

<h4 id="the-setup">The setup</h4>

<h4 id="git-the-right-version-number">Git the right version number</h4>

<p>Note that I am using <a href="https://gitforwindows.org/">Git for Windows</a> rather than the git that comes with WSL. Make sure you have a reasonably recent version installed as some of the features used here (like SSH commit signing) require version 2.34 or higher.</p>

<h4 id="let-me-google-that-for-you">Let me Google that for you</h4>

<p>My primary resources for how to get the multiple users set up was a combination of the following two links which were really useful but I got caught in a few issues on the way, (not necessarily the fault of the posts through).</p>

<ul>
  <li><a href="https://www.freecodecamp.org/news/manage-multiple-github-accounts-the-ssh-way-2dadc30ccaca/">https://www.freecodecamp.org/news/manage-multiple-github-accounts-the-ssh-way-2dadc30ccaca/</a></li>
  <li><a href="https://blog.gitguardian.com/8-easy-steps-to-set-up-multiple-git-accounts/#stepbystep">https://blog.gitguardian.com/8-easy-steps-to-set-up-multiple-git-accounts/#stepbystep</a></li>
</ul>

<p>The first thing I liked from the GitGuardian link was having two separate paths for Work and Personal projects using two separate GitHub identities.</p>

<p>Based on their instructions, I created “Work” and “Personal” folders, created the relevant ssh keys (now stored in 1Password) and then the relevant configuration files. Each identity has its own folder on disk and its own per-directory gitconfig loaded via <code class="language-plaintext highlighter-rouge">includeIf</code> in the main <code class="language-plaintext highlighter-rouge">~/.gitconfig</code>. Obviously I also had to copy my ssh public keys into the <a href="https://github.com/settings/keys">relevant page</a> of the GitHub UI for each account.</p>

<p>If you want to sign commits using the same SSH key you use for authentication, note that you will need to upload it twice in the GitHub UI. Once as an authentication key and once as a signing key.</p>

<h4 id="git-configuration-files">Git configuration files</h4>

<p>Here are the git configurations I ended up with. I will add some more information about parts of them below. Note that the heading within which each configuration line sits is important.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ~/.gitconfig

[includeIf "gitdir:C:/Users/josh/Documents/Personal/"]  #redacted path
path = C:/Users/josh/Documents/Personal/.pers.gitconfig  #redacted path

[includeIf "gitdir:C:/Users/josh/Documents/Work/"]  #redacted path
path = C:/Users/josh/Documents/Work/.work.gitconfig  #redacted path

[core]
excludesfile = ~/.gitignore
sshCommand = C:/Windows/System32/OpenSSH/ssh.exe
autocrlf = true
</code></pre></div></div>

<p>Note that I am using Windows-style paths in the <code class="language-plaintext highlighter-rouge">includeIf</code> sections. I am also specifying <code class="language-plaintext highlighter-rouge">sshCommand</code> globally to use the Windows OpenSSH client since 1Password’s SSH agent integrates with it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ~/Work/.work.gitconfig

[user]
email = josh@acmesecurity.com  #redacted email address
name = joshacmesecurity  #redacted username
signingkey = ssh-ed25519 AAAA...ilry  #redacted public key

[github]
user = joshacmesecurity  #redacted username

[gpg]
format = ssh  #specifies that we want to use ssh signing

[gpg "ssh"]
program = "C:/Users/josh/AppData/Local/Microsoft/WindowsApps/op-ssh-sign.exe"  #redacted path

[commit]
gpgsign = true  #should force signing on commits

[core]
sshCommand = C:/Windows/System32/OpenSSH/ssh.exe

[url "git@github-work:"]
insteadOf = git@github.com:
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ~/Personal/.pers.gitconfig

[user]
email = jzzzzzzzzz@hotmail.com  #redacted email address
name = tghosth  #username
signingkey = ssh-ed25519 AAAA...R7pY  #redacted public key

[github]
user = tghosth  #username

[gpg]
format = ssh  #specifies that we want to use ssh signing

[gpg "ssh"]
program = "C:/Users/josh/AppData/Local/Microsoft/WindowsApps/op-ssh-sign.exe"  #redacted path

[commit]
gpgsign = true  #should force signing on commits

[core]
sshCommand = C:/Windows/System32/OpenSSH/ssh.exe

[url "git@github-personal:"]
insteadOf = git@github.com:
</code></pre></div></div>

<p>A few things to note about these configuration files:</p>

<ul>
  <li>
    <p><strong><code class="language-plaintext highlighter-rouge">signingkey</code></strong> is set to the actual SSH public key from 1Password. Git uses this together with the <code class="language-plaintext highlighter-rouge">op-ssh-sign.exe</code> program from 1Password to sign commits.</p>
  </li>
  <li>
    <p><strong><code class="language-plaintext highlighter-rouge">core.sshCommand</code></strong> points to the Windows OpenSSH client. The correct key selection for each identity is handled by the SSH config (explained below).</p>
  </li>
  <li>
    <p><strong>The <code class="language-plaintext highlighter-rouge">url</code> rewrite section</strong> at the bottom of each per-directory gitconfig is the magic that makes everything work automatically (more on this shortly).</p>
  </li>
</ul>

<h4 id="git-gotchas">Git Gotchas</h4>

<p>There were a couple of issues at this stage that had me scratching my head for a while during various iterations of doing this so I thought I would preserve them for posterity!</p>

<h5 id="browsing-to-the-wrong-path">Browsing to the wrong path</h5>

<p>Since I am using Windows paths in <code class="language-plaintext highlighter-rouge">includeIf</code>, the configuration will only work if I navigate to the repository using the same path style. If I somehow browse to the same folder via a different path (e.g. a mapped drive or a symlink), the <code class="language-plaintext highlighter-rouge">includeIf</code> won’t match and the wrong identity might be used.</p>

<h5 id="wrong-file-names">Wrong file names</h5>

<p>I had used dots in my file names for the personal and work configuration files whereas the main configuration file from the GitGuardian link used hyphens. This took me longer than I care to admit to figure out… This was certainly a <a href="https://en.wikipedia.org/wiki/PEBCAK">PEBKAC</a> issue.</p>

<h5 id="messed-up-double-quotes">Messed up double quotes</h5>

<p>I was getting parsing errors for a very long time on my main configuration file. I tried all sorts of things including not using soft-links but using the full paths from the root instead. After much faffing, I realised that when I copied the same configuration files from the GitGuardian site, it had used “<a href="https://typographyforlawyers.com/straight-and-curly-quotes.html">curly double quotes</a>” instead of the regular double quotes and this was tripping up git 🤦‍♂️.</p>

<h3 id="ssh-authentication-and-signing">SSH authentication and Signing</h3>

<h4 id="the-problem-with-1passwords-ssh-agent">The problem with 1Password’s SSH agent</h4>

<p>1Password has a great <a href="https://developer.1password.com/docs/ssh/">SSH agent feature</a> that stores your SSH keys securely and provides them to SSH when needed. The problem is that when you have multiple keys, the agent offers all of them in sequence. For every git operation, I would get prompted to deny the wrong keys before the right one was tried. This was really annoying.</p>

<h4 id="the-solution-ssh-host-aliases-with-identitiesonly">The solution: SSH host aliases with IdentitiesOnly</h4>

<p>The fix involves three pieces working together: public key files, SSH host aliases, and URL rewrites.</p>

<h5 id="1-public-key-files-ssh">1. Public key files (~/.ssh/)</h5>

<p>I exported the public key for each identity from 1Password into its own file in <code class="language-plaintext highlighter-rouge">~/.ssh/</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">~/.ssh/personal_github.pub</code></li>
  <li><code class="language-plaintext highlighter-rouge">~/.ssh/work_github.pub</code></li>
</ul>

<p>These are the same public keys that appear in each gitconfig’s <code class="language-plaintext highlighter-rouge">signingkey</code> field. They are used by SSH to tell the 1Password agent which private key to use, without the private key ever leaving 1Password.</p>

<h5 id="2-ssh-config-sshconfig">2. SSH config (~/.ssh/config)</h5>

<p>I created host aliases which are fake hostnames that SSH resolves to the real server but with a specific key pinned:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Host github-personal
    HostName github.com
    User git
    IdentityFile C:/Users/josh/.ssh/personal_github.pub  #redacted path
    IdentitiesOnly yes

Host github-work
    HostName github.com
    User git
    IdentityFile C:/Users/josh/.ssh/work_github.pub  #redacted path
    IdentitiesOnly yes
</code></pre></div></div>

<p>The key setting here is <code class="language-plaintext highlighter-rouge">IdentitiesOnly yes</code>. This tells SSH to <strong>only</strong> offer the key specified by <code class="language-plaintext highlighter-rouge">IdentityFile</code>, instead of letting the agent offer everything it has. Combined with pointing <code class="language-plaintext highlighter-rouge">IdentityFile</code> at the public key file, SSH tells the 1Password agent exactly which private key it needs.</p>

<h5 id="3-url-rewrites-in-per-directory-gitconfigs">3. URL rewrites in per-directory gitconfigs</h5>

<p>Each folder’s gitconfig already gets loaded via <code class="language-plaintext highlighter-rouge">includeIf</code> in <code class="language-plaintext highlighter-rouge">~/.gitconfig</code>. The URL rewrite section in each one handles the final piece:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[url "git@github-work:"]
insteadOf = git@github.com:
</code></pre></div></div>

<p>This makes Git silently rewrite <code class="language-plaintext highlighter-rouge">git@github.com:</code> to <code class="language-plaintext highlighter-rouge">git@github-work:</code> for any repo under that folder. So when you clone, fetch, or push with a normal GitHub SSH URL, Git transparently routes it through the correct SSH host alias, which uses the correct key.</p>

<p>You never need to type <code class="language-plaintext highlighter-rouge">github-work</code> yourself. Just use the normal SSH URL from GitHub and everything works.</p>

<h4 id="how-it-all-connects">How it all connects</h4>

<p>Here is what happens when you run a git operation in the Work folder:</p>

<pre><code class="language-mermaid">flowchart TD
    A["git fetch\n(in Work/ folder)"] --&gt; B["gitconfig URL rewrite\ngit@github.com: → git@github-work:"]
    B --&gt; C["SSH config lookup\ngithub-work → github.com + work key only"]
    C --&gt; D["1Password agent\nasked for that specific key"]
    D --&gt; E["✅ No more 'deny the wrong key' prompt"]
</code></pre>

<p>This means cloning is now a single step too. I just copy the SSH URL from GitHub, run <code class="language-plaintext highlighter-rouge">git clone</code> in the correct folder, and the URL rewrite ensures the right key is used automatically.</p>

<p>You can see the correct clone link for SSH in this image:</p>

<p><img src="/assets/img/posts/2023/01/image.png" alt="" class="blog-image" /></p>

<h4 id="signing-commits">Signing Commits</h4>

<p>If you followed the instructions above, commit signing should work seamlessly. The per-directory gitconfig tells git to force signing (<code class="language-plaintext highlighter-rouge">gpgsign = true</code>), to use SSH format (<code class="language-plaintext highlighter-rouge">format = ssh</code>), and which public key to sign with (<code class="language-plaintext highlighter-rouge">signingkey</code>). The <code class="language-plaintext highlighter-rouge">op-ssh-sign.exe</code> program from 1Password handles the actual signing using the private key stored in 1Password.</p>

<p>No GPG, no ssh-agent, no passphrases to enter. 1Password handles it all. If it is all working you should see the commits as verified in GitHub.</p>

<p><img src="/assets/img/posts/2023/01/image-1.png" alt="" class="blog-image" /></p>

<h3 id="in-closing">In closing</h3>

<p>Thanks for reading, I hope this is a useful summary and makes it easier for you to set up this functionality. If you have comments or feedback, the easiest option is to reach out to me on <a href="https://bsky.app/profile/joshcgrossman.com">Bluesky</a>, <a href="https://Infosec.Exchange/@JoshCGrossman">Mastodon</a>, <a href="https://twitter.com/JoshCGrossman">Twitter</a>, or <a href="https://www.linkedin.com/in/joshcgrossman/">LinkedIn</a>!</p>]]></content><author><name>Josh Grossman</name></author><category term="Technical" /><category term="git" /><category term="Information Security" /><category term="SSH" /><category term="Windows" /><category term="1Password" /><summary type="html"><![CDATA[A guide on how to manage multiple GitHub accounts on a single Windows machine using 1Password and SSH host aliases, updated for 2026.]]></summary></entry><entry><title type="html">Running two Claude Code accounts on one Windows PC (without them fighting)</title><link href="https://joshcgrossman.com/2026/02/04/claude-two-accounts-windows/" rel="alternate" type="text/html" title="Running two Claude Code accounts on one Windows PC (without them fighting)" /><published>2026-02-04T07:51:50+00:00</published><updated>2026-02-04T07:51:50+00:00</updated><id>https://joshcgrossman.com/2026/02/04/claude-two-accounts-windows</id><content type="html" xml:base="https://joshcgrossman.com/2026/02/04/claude-two-accounts-windows/"><![CDATA[<p>I wanted to run <strong>Claude Code</strong> under <em>two different accounts</em> (let’s call them <code class="language-plaintext highlighter-rouge">.claude-personal</code> + <code class="language-plaintext highlighter-rouge">.claude-work</code>) on the <strong>same Windows machine</strong>, simultaneously.</p>

<p>On paper, this should be easy: there’s guidance floating around that suggests setting the <code class="language-plaintext highlighter-rouge">CLAUDE_CONFIG_DIR</code> environment variable so each instance uses a different “home” or “config” directory.</p>

<p>However, in reality, Claude Code doesn’t only store state under <code class="language-plaintext highlighter-rouge">~\.claude\</code>. It also keeps <strong>global state right in <code class="language-plaintext highlighter-rouge">~\.claude.json</code></strong>. This means that when both instances share the same real home directory, they will inevitably step on each other.</p>

<p>Rather than wrestle with extensions and ad-hoc env vars or other weird copying solutions, I made each instance run with a <strong>fully separate home directory</strong>, to avoid conflict plus a couple of <strong>symbolic links</strong> to avoid duplicating binaries and tool state.</p>

<p>Here’s what I did (obviously with Claude’s help).</p>

<h2 id="what-was-conflicting">What was conflicting?</h2>

<p>Claude Code maintains a mix of:</p>

<ul>
  <li><strong>Project settings:</strong> <code class="language-plaintext highlighter-rouge">.claude\settings.json</code> / <code class="language-plaintext highlighter-rouge">.claude\settings.local.json</code> inside a repo</li>
  <li><strong>Global settings:</strong> Mostly stored inside <code class="language-plaintext highlighter-rouge">~\.claude\</code> including files and folders such as <code class="language-plaintext highlighter-rouge">.credentials.json</code>, <code class="language-plaintext highlighter-rouge">file-history</code>, <code class="language-plaintext highlighter-rouge">plugins</code>, <code class="language-plaintext highlighter-rouge">tasks</code>, etc.</li>
</ul>

<p>Why mostly? Because it also stores config inside <code class="language-plaintext highlighter-rouge">~\.claude.json</code>. When you try to run two accounts against the same <code class="language-plaintext highlighter-rouge">~</code> (home) directory, even when you specify different config directories, this file becomes the shared point of failure.</p>

<p><img src="/assets/img/posts/2026/02/folderclashes.png" alt="Diagram of two Windows home folders (&quot;Personal&quot; and &quot;Work&quot;) each with their own `.claude.json`, showing a red conflict icon when they point to the same file, and a green check when separated; clean infographic style." /></p>

<h2 id="step-1-use-the-native-installation-for-claude">Step 1: Use the native installation for Claude</h2>

<p>I followed the instructions <a href="https://code.claude.com/docs/en/setup">from here</a> to do a native PowerShell install of Claude.</p>

<p>Maybe you can make it work in other ways as well, but this seemed simplest and should hopefully ensure automatic updates keep working.</p>

<h2 id="step-2-stop-relying-on-the-ide-extension">Step 2: Stop relying on the IDE extension</h2>

<p>This was the first pragmatic decision.</p>

<p>The IDE extensions felt janky for this specific setup, and I couldn’t get them to consistently respect the environment overrides anyway.</p>

<p>So I disabled the extension entirely and ran Claude Code <strong>from a terminal</strong> inside the IDE. I haven’t yet missed the lack of IDE integration…</p>

<h2 id="step-3-create-a-dedicated-home-per-account">Step 3: Create a dedicated “home” per account</h2>

<p>I’m using PowerShell as my primary shell, so I added a helper function to my PowerShell profile.</p>

<h3 id="where-is-the-powershell-profile">Where is the PowerShell profile?</h3>

<p>For PowerShell 7+ on Windows, the common path is:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1</code></li>
</ul>

<p>You can also just print your actual profile path:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="bp">$PROFILE</span><span class="w">
</span></code></pre></div></div>

<h3 id="the-idea">The idea</h3>

<p>Create two folders like:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.claude-split\.claude-personal\</code></li>
  <li><code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.claude-split\.claude-work\</code></li>
</ul>

<p>Then, when starting Claude Code, set environment variables so the process believes <em>that folder is home</em>.</p>

<p>The variable that seems to matter on Windows is <code class="language-plaintext highlighter-rouge">$USERPROFILE</code>.</p>

<p><img src="/assets/img/posts/2026/02/foldersok.png" alt="Fixed folders." /></p>

<h3 id="the-path-variable">The path variable</h3>

<p>Claude will also expect to find <code class="language-plaintext highlighter-rouge">claude.exe</code> at <code class="language-plaintext highlighter-rouge">~\.local\bin\claude.exe</code>. I had hoped to do something funky with symbolic links to the original install to avoid multiple copies of <code class="language-plaintext highlighter-rouge">claude.exe</code>. In practice, I could not make it work, because the symlink seemed to keep getting overwritten by the actual executable. If you manage to do it, please let me know 😀.</p>

<h3 id="example-powershell-launcher-functions">Example: PowerShell launcher functions</h3>

<p>In order to automate this, add something like this to your PowerShell profile:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">function</span><span class="w"> </span><span class="nf">Invoke-ClaudeWithProfile</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="p">[</span><span class="n">CmdletBinding</span><span class="p">()]</span><span class="w">
    </span><span class="kr">param</span><span class="p">(</span><span class="w">
        </span><span class="p">[</span><span class="n">Parameter</span><span class="p">(</span><span class="n">Mandatory</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="p">)]</span><span class="w">
        </span><span class="p">[</span><span class="n">ValidateNotNullOrEmpty</span><span class="p">()]</span><span class="w">
        </span><span class="p">[</span><span class="n">string</span><span class="p">]</span><span class="nv">$ProfileName</span><span class="p">,</span><span class="w">

        </span><span class="p">[</span><span class="n">Parameter</span><span class="p">(</span><span class="n">ValueFromRemainingArguments</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="p">)]</span><span class="w">
        </span><span class="nv">$ClaudeArgs</span><span class="w">
    </span><span class="p">)</span><span class="w">

    </span><span class="c"># Build the base directory, fake home directory,</span><span class="w">
    </span><span class="c"># and profile-specific bin directory Claude will use.</span><span class="w">
    </span><span class="nv">$baseDir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Join-Path</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">USERPROFILE</span><span class="w"> </span><span class="s2">".claude-split"</span><span class="w">
    </span><span class="nv">$targetPath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Join-Path</span><span class="w"> </span><span class="nv">$baseDir</span><span class="w"> </span><span class="nv">$ProfileName</span><span class="w">
    </span><span class="nv">$profileBin</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Join-Path</span><span class="w"> </span><span class="nv">$targetPath</span><span class="w"> </span><span class="s2">".local\bin"</span><span class="w">

    </span><span class="c"># Create the fake home directory and the fake claude directory</span><span class="w">
    </span><span class="n">New-Item</span><span class="w"> </span><span class="nt">-ItemType</span><span class="w"> </span><span class="nx">Directory</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nv">$profileBin</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-Null</span><span class="w">

    </span><span class="c"># Store the current user home and path variables</span><span class="w">
    </span><span class="nv">$oldUserProfile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">USERPROFILE</span><span class="w">
    </span><span class="nv">$oldPath</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">PATH</span><span class="w">

    </span><span class="kr">try</span><span class="w"> </span><span class="p">{</span><span class="w">
		
        </span><span class="c"># Set the USERPROFILE variable to our fake directory</span><span class="w">
        </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">USERPROFILE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$targetPath</span><span class="w">
        </span><span class="c"># Add the fake Claude directory to the PATH variable</span><span class="w">
        </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">PATH</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"</span><span class="nv">$profileBin</span><span class="s2">;</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">PATH</span><span class="s2">"</span><span class="w">
        </span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"--- Claude Instance: [</span><span class="nv">$ProfileName</span><span class="s2">] (</span><span class="nv">$targetPath</span><span class="s2">) ---"</span><span class="w"> </span><span class="nt">-ForegroundColor</span><span class="w"> </span><span class="nx">Cyan</span><span class="w">

        </span><span class="c"># Run claude (with these variables set) and whatever arguments were passed</span><span class="w">
        </span><span class="o">&amp;</span><span class="w"> </span><span class="n">claude</span><span class="w"> </span><span class="err">@</span><span class="nx">ClaudeArgs</span><span class="w">
    </span><span class="p">}</span><span class="w">
    </span><span class="kr">finally</span><span class="w"> </span><span class="p">{</span><span class="w">
		
        </span><span class="c"># Return the USERPROFILE and PATH variables back to their</span><span class="w">
        </span><span class="c"># previous values when claude exits</span><span class="w">
        </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">USERPROFILE</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$oldUserProfile</span><span class="w">
        </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">PATH</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$oldPath</span><span class="w">
        </span><span class="n">Write-Host</span><span class="w"> </span><span class="s2">"--- Profile Restored ---"</span><span class="w"> </span><span class="nt">-ForegroundColor</span><span class="w"> </span><span class="nx">Gray</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="some-subtleties-about-what-is-happening-here">Some subtleties about what is happening here</h3>

<p>When you run this function for the first time (assuming we are running it for <code class="language-plaintext highlighter-rouge">.claude-personal</code>), this is what happens:</p>

<ol>
  <li>
    <p>We add our fake home directory to <code class="language-plaintext highlighter-rouge">PATH</code>.</p>
  </li>
  <li>
    <p>PowerShell looks for <code class="language-plaintext highlighter-rouge">claude.exe</code> at <code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.claude-split\.claude-personal\.local\bin\claude.exe</code>, but it won’t find it because it doesn’t exist yet.</p>
  </li>
  <li>
    <p>PowerShell then looks for <code class="language-plaintext highlighter-rouge">claude.exe</code> at <code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.local\bin\claude.exe</code>, which the original Claude install put on the system path. It should find it and run it.</p>
  </li>
  <li>
    <p>Since the fake home directory is set, Claude itself expects <code class="language-plaintext highlighter-rouge">claude.exe</code> at <code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.claude-split\.claude-personal\.local\bin\claude.exe</code>, doesn’t find it, and downloads a fresh copy to that location.</p>
  </li>
</ol>

<p>On subsequent uses of this function, it immediately uses <code class="language-plaintext highlighter-rouge">claude.exe</code> at <code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.claude-split\.claude-personal\.local\bin\claude.exe</code>.</p>

<p>Aside from duplicating <code class="language-plaintext highlighter-rouge">.exe</code> files, one downside is that <code class="language-plaintext highlighter-rouge">C:\Users\&lt;you&gt;\.local\bin\claude.exe</code> may not get updated if you always launch Claude through profile functions. In practice, that should not be a major issue in this setup.</p>

<h2 id="step-4-calling-this-function">Step 4: Calling this function</h2>

<p>You would then create shortcut functions for each separate Claude instance you want to run.</p>

<p>So for example, I might have:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">function</span><span class="w"> </span><span class="nf">claude-work</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">Invoke-ClaudeWithProfile</span><span class="w"> </span><span class="nt">-ProfileName</span><span class="w"> </span><span class="s2">".claude-work"</span><span class="w"> </span><span class="err">@</span><span class="nx">args</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="kr">function</span><span class="w"> </span><span class="nf">claude-personal</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">Invoke-ClaudeWithProfile</span><span class="w"> </span><span class="nt">-ProfileName</span><span class="w"> </span><span class="s2">".claude-personal"</span><span class="w"> </span><span class="err">@</span><span class="nx">args</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This forces <strong>each instance</strong> to have its own:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">~\.claude.json</code></li>
  <li><code class="language-plaintext highlighter-rouge">~\.claude\...</code></li>
</ul>

<p>…beneath the fake home folder we defined, which should prevent cross-account corruption.</p>

<h2 id="step-5-fix-the-weird-side-effects-with-symbolic-links">Step 5: Fix the weird side effects with symbolic links</h2>

<p>Once you “fake” a different home directory, some tools start looking for binaries and configs in places you didn’t expect.</p>

<p>To avoid maintaining duplicate copies (and to keep tools happy), I created some symlinks. In the end, the only one I kept was for the <code class="language-plaintext highlighter-rouge">glab-cli</code> config directory, as the others either didn’t work out or were not necessary. You may need to do something similar for other CLI tools you use.</p>

<p>As I noted above, I tried doing this for <code class="language-plaintext highlighter-rouge">claude.exe</code>, but it didn’t work out, so I have to live with some duplication there.</p>

<p><strong>Important:</strong> On many Windows setups, creating symlinks requires an <strong>elevated PowerShell</strong> session (Run as Administrator). Some machines allow Developer Mode symlinks without elevation, but assume you’ll need admin unless you know otherwise.</p>

<h3 id="symlink-for-gitlab-cli-config">Symlink for GitLab CLI config</h3>

<p>I had to do this for GitLab CLI (<code class="language-plaintext highlighter-rouge">glab</code>).</p>

<p>On Windows, <code class="language-plaintext highlighter-rouge">glab</code> stores its config under:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">%LOCALAPPDATA%\glab-cli\</code> (including <code class="language-plaintext highlighter-rouge">config.yml</code>)</li>
</ul>

<p>(At least in my installation.)</p>

<p>With our fake home directories, we may want each Claude profile to see the same <code class="language-plaintext highlighter-rouge">glab</code> state.</p>

<p>Run in elevated PowerShell:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$realGlabDir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Join-Path</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">LOCALAPPDATA</span><span class="w"> </span><span class="s2">"glab-cli"</span><span class="w">
</span><span class="nv">$homeGlabDir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:\Users\&lt;you&gt;\.claude-split\.claude-work\AppData\Local\glab-cli"</span><span class="w">

</span><span class="n">New-Item</span><span class="w"> </span><span class="nt">-ItemType</span><span class="w"> </span><span class="nx">Directory</span><span class="w"> </span><span class="nt">-Force</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="p">(</span><span class="n">Split-Path</span><span class="w"> </span><span class="nv">$homeGlabDir</span><span class="p">)</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-Null</span><span class="w">
</span><span class="nx">New-Item</span><span class="w"> </span><span class="nt">-ItemType</span><span class="w"> </span><span class="nx">SymbolicLink</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nv">$homeGlabDir</span><span class="w"> </span><span class="nt">-Target</span><span class="w"> </span><span class="nv">$realGlabDir</span><span class="w">
</span></code></pre></div></div>

<h3 id="github-cli-note">GitHub CLI note</h3>

<p>Interestingly, I <strong>didn’t</strong> have to add extra symlinks for GitHub tooling in my setup. It just worked with Claude having the fake home directory…</p>

<h2 id="current-status">Current status</h2>

<p>So far, I haven’t found any unwanted side effects.</p>

<p>There may be additional state Claude Code relies on that could get confused in edge cases, but for now, this approach has been stable: two accounts, one Windows PC, no cross-contamination.</p>

<p>Let me know how you get on and if you find any bugs!</p>]]></content><author><name>Josh Grossman</name></author><category term="Technical" /><category term="Claude" /><category term="Developer Experience" /><category term="Productivity" /><category term="Windows" /><category term="AI Tools" /><summary type="html"><![CDATA[How I isolated Personal and Work Claude Code accounts on a single Windows machine by faking per-account home directories and a handful of symlinks.]]></summary></entry><entry><title type="html">Explain the Exploit: Why Security Communication Is So Bad</title><link href="https://joshcgrossman.com/2025/12/03/explain-the-exploit/" rel="alternate" type="text/html" title="Explain the Exploit: Why Security Communication Is So Bad" /><published>2025-12-03T07:51:50+00:00</published><updated>2025-12-03T07:51:50+00:00</updated><id>https://joshcgrossman.com/2025/12/03/explain-the-exploit</id><content type="html" xml:base="https://joshcgrossman.com/2025/12/03/explain-the-exploit/"><![CDATA[<h2 id="the-problem">The Problem</h2>

<p>&lt;rant&gt;</p>

<p>Why is everyone so insanely terrible at explaining security vulnerabilities? I don’t care how technically clever it is, or how many different steps you had to go through in order to achieve this. Well, that’s not true. I do care actually. I do appreciate a good high-quality vulnerability. But right now, what I want to know is:</p>

<ul>
  <li><strong>How bad is this?</strong></li>
  <li><strong>What is the risk?</strong></li>
  <li><strong>How fast do I need to deal with this?</strong></li>
  <li><strong>What is going to happen if someone exploits this?</strong></li>
</ul>

<p>These are the questions that I need answered if I’m going to decide how fast to fix it. Do I walk? Do I run?</p>

<p><img src="/assets/img/posts/2025/12/walkrun.png" alt="Stylized digital illustration showing two contrasting paths: one path labeled 'WALK' with a relaxed figure strolling, and another path labeled 'RUN' with a figure sprinting urgently. The paths diverge from a central point marked with a vulnerability icon. Use a split-screen composition with cool blues for the 'walk' side and urgent reds for the 'run' side. Modern vector art style with clean lines and strategic use of negative space." /></p>

<p>I’m currently dealing with two cases in parallel where I am struggling desperately to understand: What is the problem here? What could actually go wrong?</p>

<h2 id="case-study-1-the-dll-hijacking-that-wasnt">Case Study 1: The DLL Hijacking That Wasn’t</h2>

<p>In the first case, from the initial explanation, I thought it was like DLL hijacking on Windows—where a low-security process could write a malicious DLL file to a location on disk, and the vulnerable application would read that DLL file before it gets to the actual DLL file it should be reading, and therefore execute some code in the context of the application.</p>

<p><img src="/assets/img/posts/2025/12/windows.png" alt="Technical illustration showing a Windows file system with DLL files in a directory tree structure. Show a malicious DLL file (in red) being placed in a folder, with an arrow showing it being loaded before the legitimate DLL file (in green). Include folder icons, file paths, and a simplified application icon attempting to load the DLL. Use a clean technical diagram style with a white background, blue system elements, and red/green color coding for malicious vs legitimate files." /></p>

<p>Now, yes, that would be bad. That is an understandable outcome.</p>

<p>But no, that’s not quite the case. After much digging, it turns out that the risk is that if an attacker is able to write into the application folder itself, then they can escalate to code execution—effectively escalating a write primitive to a code execution primitive.</p>

<p>Now, that is not ideal. I’d rather that didn’t happen. But it certainly doesn’t require me to run and fix this, especially because the only way of doing this bypasses various other platform-level controls we wouldn’t expect a regular user to be using anyway.</p>

<p>The amount of back and forth it took to discover that that was actually the case was just ridiculous.</p>

<h2 id="case-study-2-the-http-smuggling-mystery">Case Study 2: The HTTP Smuggling Mystery</h2>

<p>The second case related to a bug bounty with an HTTP smuggling vulnerability—or maybe it was a desync. It doesn’t really matter. I’ve got two levels of triage investigating this to decide whether it’s a real issue or not.</p>

<p><img src="/assets/img/posts/2025/12/smuggling.png" alt="Abstract digital illustration of HTTP request smuggling concept. Show two HTTP requests (represented as stylized data packets or envelopes) being combined or overlapping in an unexpected way, passing through a server/proxy gateway. Use flowing lines to represent data flow, with one request 'smuggled' inside another. Color scheme of purple and cyan for the requests, dark gray for the server infrastructure. Modern tech illustration style with glowing effects and clean geometry." /></p>

<p>All I want to know is: <strong>What can an attacker actually do in our application with this vulnerability?</strong></p>

<p>I understand how these attacks work. I just want to know what is going to happen in this case. What is the attack scenario? What is the step-by-step?</p>

<h2 id="the-toilet-plunger-parable">The Toilet Plunger Parable</h2>

<p>In the end, I tried to come up with a parable to try and illustrate to the people in both cases what I am talking about. The parable is as ridiculous as I felt the situation warranted.</p>

<ol>
  <li>The attacker throws a toilet plunger very hard at a wall</li>
  <li>It sticks to the wall</li>
  <li>The attacker calls the user’s name</li>
  <li>The user runs towards the attacker who is standing near the wall</li>
  <li>The user runs into the handle and knocks themselves out, Looney Tunes style</li>
  <li>The attacker steals the unconscious user’s wallet</li>
</ol>

<p><img src="/assets/img/posts/2025/12/plunger.png" alt="Whimsical cartoon-style illustration in the style of Looney Tunes showing a sequence of events: An attacker throwing a red toilet plunger at a wall where it sticks." /></p>

<p>Now I don’t care:</p>
<ul>
  <li>What the wall is made out of</li>
  <li>What glue was on the head of the plunger</li>
  <li>Whether a normal person could throw hard enough to make this work</li>
</ul>

<p>I just want to understand: <strong>What is the attack scenario? What is the series of events that has to happen in order for this to become a problem?</strong></p>

<h2 id="the-bottom-line">The Bottom Line</h2>

<p>I cannot understand for the life of me why people are so bad at explaining this.</p>

<p>When you’re reporting a security vulnerability, please, for the love of all that is holy:</p>

<ol>
  <li><strong>Start with the impact</strong> - What can an attacker actually do?</li>
  <li><strong>Describe the attack scenario</strong> - What sequence of events leads to the bad outcome?</li>
  <li><strong>Explain the prerequisites</strong> - What access or conditions are needed?</li>
  <li><strong>Then</strong>, and only then, dive into the technical details</li>
</ol>

<p><img src="/assets/img/posts/2025/12/pyramid.png" alt="Clean infographic-style illustration showing a numbered pyramid or hierarchy with 4 levels. From bottom to top: Level 1 'Impact' (with shield/explosion icon), Level 2 'Attack Scenario' (with step-by-step arrows icon), Level 3 'Prerequisites' (with checklist icon), Level 4 'Technical Details' (with code/gear icon). Use a professional color gradient from urgent red at the bottom to calm blue at the top. Modern minimalist design with clear typography and iconography." /></p>

<p>Your job as a security researcher or engineer isn’t just to find the vulnerability—it’s to communicate it in a way that allows stakeholders to make informed decisions about risk and remediation priority.</p>

<p><strong>The coolest vulnerability report in the world is worthless if nobody can understand what they need to do about it.</strong></p>

<p>Rant over.</p>

<p>For now.</p>

<p><strong>Update 4-Dec-2025:</strong> Further to this, <a href="https://www.linkedin.com/in/planetlevel">Jeff Williams</a> wrote a great article a few years ago about how to report vulnerabilities in a clear way!
https://www.linkedin.com/pulse/how-vulnerability-jeff-williams</p>]]></content><author><name>Josh Grossman</name></author><category term="Technical" /><category term="Application Security" /><category term="Appsec" /><category term="cybersecurity" /><category term="Information Security" /><category term="Security" /><summary type="html"><![CDATA[This post is a rant about how many people in the security community are bad at communicating the actual risk of an issue .]]></summary></entry><entry><title type="html">Making your preparations</title><link href="https://joshcgrossman.com/2025/07/09/making-your-preparations/" rel="alternate" type="text/html" title="Making your preparations" /><published>2025-07-09T12:30:00+00:00</published><updated>2025-07-09T12:30:00+00:00</updated><id>https://joshcgrossman.com/2025/07/09/making-your-preparations</id><content type="html" xml:base="https://joshcgrossman.com/2025/07/09/making-your-preparations/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2025/07/09/making-your-preparations.html">Bounce Security blog</a>.</strong></p>

<h4 id="introduction">Introduction</h4>

<p>Your course is confirmed and you’re ready to go? Fantastic! That’s a major milestone worth celebrating. 🎉 Now that you’re past the proposal phase, it’s time to think about the practical preparations that will ensure your course is not just delivered, but delivered smoothly and memorably.</p>

<p>In this post, I’ll walk you through essential pre-course considerations, from visa planning to organizing handouts, so that nothing catches you off guard when it’s go time.</p>

<h4 id="visa-considerations-more-than-just-a-stamp">Visa Considerations: More Than Just a Stamp</h4>

<p>While not strictly a financial issue, visa planning is a <em>must-do</em> if you’re delivering a course abroad. Skipping this step could derail your training before it begins.</p>

<h5 id="getting-a-visa-to-enter-the-country">Getting a Visa to Enter the Country</h5>

<p>Ask yourself: <em>How easy is it for me to enter the country where I’ll be training?</em></p>

<p>If you don’t have the privilege of a streamlined entry process (like the US ESTA or the upcoming EU ETIAS), start early. Some visa applications take weeks or even months. Even the time between your course being accepted and the conference date might not be enough, especially if there is a backlog.</p>

<p><img src="/assets/img/bhseries/31.jpeg" alt="image" /></p>

<h5 id="getting-a-visa-that-lets-you-get-paid">Getting a Visa That Lets You Get Paid</h5>

<p>Here’s where things get tricky. I’m not a lawyer (technically, I’m an accountant but I’ve never practiced), so this isn’t legal advice. However, be aware: Getting paid to train abroad might require a different visa than just attending a conference.</p>

<p>Check whether you’ll legally be allowed to <em>work</em> (i.e. deliver training) and <em>get paid</em> while in that country. Conferences often won’t have definitive guidance on this either so it’s your responsibility to do the homework.</p>

<p>Note that the US in particular has gotten stricter about this and in some cases people were detained at immigration for attempting to come in to work on the wrong visa.</p>

<h4 id="supplementary-materials-going-the-extra-mile">Supplementary Materials: Going the Extra Mile</h4>

<p>Beyond your core curriculum, it’s a great idea to prepare supplementary materials that enhance the learning experience. These can include physical goodies, printed handouts, or digital resources. Let’s break them down.</p>

<h6 id="goodies-and-extras">Goodies and Extras</h6>

<p>If you’ve promised participants anything extra (such as stickers, USB drives, branded gear) now’s the time to deliver. But beware: You might not know your final headcount until right before the event.</p>

<p>At Black Hat 2023, my attendee numbers jumped by 50% in just two weeks.</p>

<p>So, plan generously. It’s better to have a few extras than run out and disappoint attendees. For expensive items, consider a cutoff: for example, <em>“Free hardware kit for sign-ups received 3+ weeks in advance.”</em></p>

<p><img src="/assets/img/bhseries/32.jpeg" alt="image" /></p>

<h5 id="course-materials">Course Materials</h5>

<p>For in-person training, I’m a big fan of printed materials. When exercises require referring to multiple documents, printed copies save participants from constantly alt-tabbing between different screens.</p>

<p>I always print and bring these materials myself to ensure quality and timing. The exception? At Black Hat, they print a course workbook for you which is a nice touch!</p>

<p><strong>Pro Tip:</strong> Never pack your printed materials in checked luggage. Ask me why one day :)</p>

<p><img src="/assets/img/bhseries/workbooks.jpg" alt="image" class="blog-image" /></p>

<p>On one occasion where I wanted to go a little above and beyond, I ordered plastic document folders via Amazon to the venue. Each participant got one to organize their handouts, plus some extras like sticky dividers and branded stickers.</p>

<p><img src="/assets/img/bhseries/attendee_pack.jpg" alt="image" class="blog-image" /></p>

<p>Printed materials are great during the course, but soft copies are critical for long-term reference. I provide PDFs unless I want students to edit the files themselves.</p>

<p>I “drip feed” exercise materials throughout the course, which adds a bit of complexity but keeps students from jumping ahead. For slides, I always ensure the numbering on the handouts matches the numbering on screen. This keeps everyone on track.</p>

<p><img src="/assets/img/bhseries/33.jpeg" alt="image" /></p>

<h4 id="dont-overthink-it-focus-on-delivery">Don’t Overthink It: Focus on Delivery</h4>

<p>While all this prep helps, don’t lose sight of what truly matters, the <em>content</em> and the <em>experience</em>. Your delivery, your energy, and the interactions you create with participants are what make your course memorable.</p>

<p>Handouts and goodies? They’re just the icing on the cake.</p>

<h4 id="looking-for-more">Looking for more?</h4>

<p>That’s a wrap for now but I have more ideas in the pipeline. Did you find this helpful? Let me know what you’d like to hear more about or which questions you want answered next.</p>

<p>Let’s make your next training the best one yet. 🚀</p>]]></content><author><name>Josh Grossman</name></author><category term="Training" /><category term="Security" /><category term="Black Hat" /><category term="Training" /><category term="Conferences" /><summary type="html"><![CDATA[In this post, I talk through the preparations you will want to make before delivering the course.]]></summary></entry><entry><title type="html">Selling and Marketing your course</title><link href="https://joshcgrossman.com/2025/06/24/selling-your-course/" rel="alternate" type="text/html" title="Selling and Marketing your course" /><published>2025-06-24T12:30:00+00:00</published><updated>2025-06-24T12:30:00+00:00</updated><id>https://joshcgrossman.com/2025/06/24/selling-your-course</id><content type="html" xml:base="https://joshcgrossman.com/2025/06/24/selling-your-course/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2025/06/24/selling-your-course.html">Bounce Security blog</a>.</strong></p>

<h4 id="introduction">Introduction</h4>

<p>So your course got accepted? <strong>Congratulations!</strong> 🎉 That’s a huge win and shows your proposal resonated with the review board. But now comes the real challenge: <strong>getting people to actually attend</strong>.</p>

<p>However, on this specific topic, I cannot pretend to be an expert or to have seen major success when doing this. I will try and provide my perspective and ideas but your mileage will certainly vary. There are plenty of other things you might want to try and maybe even consult with other people with more expertise.</p>

<h4 id="size-of-the-field-size-of-the-audience">Size of the Field, Size of the Audience</h4>

<p>Before diving into promotion tactics, it’s worth understanding the competitive landscape.</p>

<p>Three big factors influence how easy or hard it will be to sell your course:</p>

<ol>
  <li>How many people usually attend training at the conference?</li>
  <li>How many <strong>other</strong> courses are being offered?</li>
  <li>Can you attract people to attend the conference <strong>specifically for your course</strong>?</li>
</ol>

<p><img src="/assets/img/bhseries/11.jpeg" alt="Cybersecurity" /></p>

<p>For example:</p>

<ul>
  <li><strong>OWASP Global AppSec</strong> events often run up to 10 courses in parallel, with around 100-200 total attendees at the training and around 800 attendees at the conference.</li>
  <li><strong>Black Hat USA</strong>, on the other hand, may feature <strong>~140</strong> courses. I remember seeing a lunch room packed with <strong>1,000+ people</strong> during training days.</li>
</ul>

<p>Whilst the numbers at Black Hat may sound particularly promising, note that <strong>distribution isn’t equal</strong>. Some courses are held in huge rooms with dozens of students, while others are tucked away with small groups.</p>

<p>This makes it crucial to <strong>stand out</strong>.
I’ve already discussed finding your niche at length in previous posts. In addition to this, I have also tried ensuring the course appears relatively high up in the course list through an “optimised” title and also adding an explanatory video to the course description wherever possible (but more on videos further down).</p>

<p>I can’t say it dramatically boosted my numbers, but who knows what would’ve happened if I had named the course “Zebra Security Essentials”? 🤷‍♂️</p>

<p>Either way, <strong>don’t rely solely on being listed on the website</strong>. You probably want to do more than that.</p>

<h4 id="what-you-actually-get-from-the-conference">What You Actually Get from the Conference</h4>

<p>Many conferences do <strong>minimal marketing</strong> for individual courses.</p>

<p>Most conferences might tweet or post once or twice on LinkedIn or other social media networks. I haven’t trained at TyphoonCon, but it’s one of the only instances I’ve seen a conference do <strong>promoted tweets for individual courses</strong>.</p>

<p>You can sometimes ask organizers to amplify your own posts — but their support is limited and often hinges on you already having a <strong>strong social media presence</strong>.</p>

<p><img src="/assets/img/bhseries/12.jpeg" alt="Cybersecurity" /></p>

<h4 id="building-your-social-media-presence">Building Your Social Media Presence</h4>

<p>If you have a strong social media presence then you are clearly already good at self-promotion so that will help a lot in this context. You can probably skip this section (although I’d love to hear your tips 😀).</p>

<p>I made an effort to increase my social media activity a little (specifically on Twitter and LinkedIn) in the run up to these courses. I grew my follower count slightly and made an effort to post more frequently and try and engage with other posts. I would usually focus on subject matter related to my course including sometimes mentioning work I was doing on developing the course.</p>

<p>I also made an effort to prepare some short videos as this seemed to be a good way of communicating information in an effective way and be in line with how many people seem to consume information right now. I was also able to outsource the video editing which saved time. I varied between videos specifically about the course and also short videos on related topics.</p>

<p>Some interesting discussions developed from both text and video posts and in general I think that having an active social media presence has professional benefits anyway. It does however require a lot of effort to have an impact.</p>

<p><img src="/assets/img/bhseries/13.jpeg" alt="Cybersecurity" /></p>

<h2 id="other-ways-to-build-awareness">Other Ways to Build Awareness</h2>

<p>If social media isn’t your thing (or isn’t enough), here are some alternative channels I explored:</p>

<h5 id="-conference-talks">🎤 Conference Talks</h5>

<p>Speaking at conferences, specifically on your training topic or even on other related topics, is a great way to <strong>build credibility</strong>, expand your presence in the industry, and push your course at the same time.</p>

<p>I couldn’t fund travel to overseas events, but I looked for conferences that <strong>cover speaker expenses</strong>. My largest target was Black Hat USA 2023 so I pushed particularly hard in late 2022/early 2023.</p>

<p>This resulted in:</p>
<ul>
  <li><strong>October 2022</strong> - OWASP Global AppSec San Francisco (Training + Talk)</li>
  <li><strong>January 2023</strong> - NDC Security in Oslo + OWASP Oslo Meetup</li>
  <li><strong>February 2023</strong> - OWASP Global AppSec Dublin (Training)</li>
  <li><strong>April 2023</strong> - QCon London (Speaking)</li>
  <li><strong>June 2023</strong> - DevTalks Bucharest (Speaking)</li>
</ul>

<p>I’d often mention my course at the end of talks or during conversations at these events.</p>

<p><img src="/assets/img/bhseries/14.jpeg" alt="Cybersecurity" /></p>

<h5 id="️-podcasts">🎙️ Podcasts</h5>

<p>Podcasts are another great channel. Less travel but plenty of reach.</p>

<p>I got some great benefits from being a podcast guest including:</p>

<ul>
  <li>Exposure to new audiences</li>
  <li>Valuable feedback on course content</li>
  <li>Deeper conversations that helped refine the material</li>
</ul>

<p>Many podcasts welcome guest suggestions — especially if you have an interesting perspective or topic to pitch.</p>

<h5 id="-paid-marketing-spoiler-meh">💸 Paid Marketing (Spoiler: Meh)</h5>

<p>We tried a small LinkedIn ad campaign. Honestly? No real impact.</p>

<p>Paid ads likely require consistent investment and long-term strategy to pay off. Not ideal for one-off training sessions or a short-term effort.</p>

<h4 id="the-hard-part-self-promotion-is-draining">The Hard Part: Self-Promotion is Draining</h4>

<p>In truth, I found this part <strong>exhausting</strong>.</p>

<ul>
  <li>I’m not a fan of social media pressure.</li>
  <li>I don’t want to be constantly travelling.</li>
  <li>Conference prep takes a toll on time, not just money.</li>
</ul>

<p>All this meant I wasn’t as consistent or aggressive in promoting as I could have been.</p>

<p>For me, the bottom line was that every time I asked course attendees how they found out about the course, they almost exclusively said it was from browsing the conference website and not from social media posts or any other channel.</p>

<p>So lately, I’ve toned it down. Just a few social posts and that’s it.</p>

<h4 id="the-stress-of-gono-go">The Stress of GO/NO-GO</h4>

<p>As I mentioned in a previous post, there’s often a GO/NO-GO decision looming.</p>

<ul>
  <li>Will enough seats sell to justify running the course?</li>
  <li>Will it hit both your <strong>and</strong> the conference’s break-even point?</li>
</ul>

<p>That decision can be stressful. And a little embarrassing if the course gets pulled after a few people have already signed up.</p>

<p>In 2023 I made this more difficult for myself as I planned a family vacation around Black Hat USA. If the training had been cancelled, I’d have had to pay all my own expenses to get to the conference or skip the conference but still travel to the US for holiday.</p>

<p>Luckily, it worked out. But I stressed about it for months. The feeling of relief and excitement I felt when Black Hat confirmed my course was running was unreal.</p>

<p><img src="/assets/img/bhseries/15.jpeg" alt="Cybersecurity" /></p>

<h4 id="a-specialist-discipline">A Specialist Discipline</h4>

<p>Let’s be realistic, sales and marketing is a specialist skillset. You’ll find countless experts who can speak to it better than I can. Hopefully this section provided some useful context although I accept that it is not super actionable.</p>

<p>My most active period was late 2022 through early 2023. While I can’t say how much the self-promotion effort changed the outcome, the opportunity to train at Black Hat and attend DEFCON made it <strong>absolutely worthwhile</strong>.</p>

<p>Only you can decide how much time, energy, and budget to invest in promoting your course. What’s right for you might not be right for someone else.</p>

<h5 id="next-post-make-your-preparations">Next Post: Make Your Preparations</h5>

<p>If your course is confirmed, amazing! In the next post, I’ll walk through the preparations you’ll want to make to help ensure it’s a success.</p>]]></content><author><name>Josh Grossman</name></author><category term="Training" /><category term="Security" /><category term="Black Hat" /><category term="Training" /><category term="Conferences" /><summary type="html"><![CDATA[In this post, I talk through my experience of marketing the course and try and provide some pointers.]]></summary></entry><entry><title type="html">Writing your Submission</title><link href="https://joshcgrossman.com/2025/06/03/writing-your-submission/" rel="alternate" type="text/html" title="Writing your Submission" /><published>2025-06-03T06:30:00+00:00</published><updated>2025-06-03T06:30:00+00:00</updated><id>https://joshcgrossman.com/2025/06/03/writing-your-submission</id><content type="html" xml:base="https://joshcgrossman.com/2025/06/03/writing-your-submission/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2025/06/03/writing-your-submission.html">Bounce Security blog</a>.</strong></p>

<h4 id="introduction">Introduction</h4>

<p>You’ve figured out your finances, carved out your niche, and built exercises that pack a punch. Now comes a critical step: getting your course accepted by a conference.</p>

<p>In this post, we’ll dive into how to write a compelling Call for Training (CfT) submission that catches the eye of both review boards and potential attendees.</p>

<p><img src="/assets/img/bhseries/1.png" alt="Cybersecurity" class="blog-image-tall" /></p>

<h4 id="plan-ahead-timing-is-everything">Plan Ahead: Timing is Everything</h4>

<p>CfTs (Calls for Training) often open earlier than the main Call for Papers (CfP). For example, Black Hat and OWASP usually open their CfTs more than <strong>six months</strong> before the event, and they might only stay open for a month.</p>

<p>Failing to plan here could mean missing your window altogether. This means you are going to want to put CfT dates in your calendar and prepare your materials well in advance.</p>

<p><img src="/assets/img/bhseries/2.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<h4 id="know-your-audiences">Know Your Audiences</h4>

<p>Crafting a strong CfT submission requires you to speak to <strong>two distinct audiences</strong>:</p>

<h5 id="1-the-conference-review-board">1. The Conference Review Board</h5>

<p>This group is experienced, discerning, and reviewing dozens (if not hundreds) of submissions. You generally have lots of space to provide them with information about the course and you want to try and differentiate it from the many other submissions they are reviewing.</p>

<p>The key things they care about will be:</p>

<ul>
  <li>How your course <strong>stands out</strong> from the rest.</li>
  <li>Its <strong>technical depth</strong> and relevance.</li>
  <li>Your <strong>credibility</strong> as a trainer.</li>
  <li>How interactive your course is.</li>
</ul>

<p>💡 <strong>Pro Tip</strong>: Include testimonials, past success stories, and why you’re uniquely positioned to teach this subject.</p>

<h5 id="2-your-potential-attendees">2. Your Potential Attendees</h5>

<p>Potential attendees will often only see a small portion of your submission — usually the <strong>abstract</strong> or <strong>summary</strong>. You need to ensure that you include the right information to help them make a decision. You may also have character/word limits to comply with here. (However, I invite you to compare the Black Hat abstract word limit to the length of the abstracts on the training pages 🙃)</p>

<p>Potential attendees are going to want to know:</p>

<ul>
  <li>“Is this course <strong>useful</strong> to me?”</li>
  <li>“Will it help me solve <strong>real problems</strong>?”</li>
  <li>“Will I walk away with <strong>tangible skills</strong>?”</li>
</ul>

<p>Arguably the third audience is your potential attendee’s manager who they are going to have to justify the course to but I think the considerations are similar to those of the potential attendee. To be absolutely clear, you are writing a sales pitch and you need to be thinking and writing accordingly.</p>

<p><img src="/assets/img/bhseries/3.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<h5 id="writing-for-both-key-tips">Writing for Both: Key Tips</h5>

<p>Here’s my approach to balancing both needs:</p>

<ul>
  <li>Keep it short, <strong>punchy</strong>, and <strong>scannable</strong>.</li>
  <li>Address a <strong>relatable pain point</strong>. You want to make your reader think, “That’s me! I need help solving that problem!”</li>
  <li>Be clear on <strong>what participants will do</strong> during the course.</li>
  <li>Make it <strong>sound like fun</strong> to take part in the course!</li>
  <li>Highlight clear, <strong>actionable takeaways</strong>.</li>
</ul>

<p><img src="/assets/img/bhseries/4.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<h4 id="align-with-conference-guidance">Align with Conference Guidance</h4>

<p>Each conference will have its own specific perspective and content that it is looking for. If you are clearly not aligned with that, your submission may be doomed from the start.</p>

<p>Read the CfT guidelines carefully and align your submission accordingly. If the guidance includes sample abstracts or criteria, read through them and ask yourself how well your submission compares to them.</p>

<p><img src="/assets/img/bhseries/5.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<h4 id="for-first-time-trainers">For First-Time Trainers</h4>

<p>New to delivering public training courses? You’ll likely need to prove you can <strong>teach effectively</strong>.
For example, Black Hat requests new trainers to provide ~60 slides of training material plus other information as well so make sure you have that ready in time for the CfT as well.</p>

<p>Any prior experience or videos or whatever else you can provide at this stage will help if you are not a well known trainer. I have also provided ratings and testimonials I have received when training and speaking at other venues.</p>

<h4 id="mission-essential-task-list-metl">Mission Essential Task List (METL)</h4>

<p>Full credit for this point has to go to <a href="https://www.linkedin.com/in/daniel-cuthbert0x/">Daniel Cuthbert</a> from the Black Hat Training Review board who has made it clear on a number of occasions that this is something he expects to see from submissions. Also to <a href="https://www.linkedin.com/in/avidouglen/">AviD</a>, my boss at Bounce Security who has made this standard procedure within Bounce for training course preparation.</p>

<p>A METL is a detailed course breakdown, how long you’ll spend on each topic and what will be covered.</p>

<p>This shows you’ve done more than brainstormed ideas, you’ve <strong>planned</strong> and <strong>structured</strong> the course in a logical way. It should include:</p>
<ul>
  <li>Topic breakdowns with time estimates</li>
  <li>Timings for hands-on to show the lecture-to-exercise ratio</li>
  <li>How learning outcomes map to course segments</li>
</ul>

<p><img src="/assets/img/bhseries/6.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<p>As a some-time CfT reviewer, I can tell you: if your submission lacks this level of clarity, it’s unlikely to make the cut.</p>

<h4 id="get-feedback-before-you-submit">Get Feedback Before You Submit</h4>

<p>Even seasoned trainers benefit from a second pair of eyes.</p>

<p>Tips:</p>
<ul>
  <li>Ask a <strong>former CfT reviewer</strong> if possible.</li>
  <li>At the very least, someone familiar with the material.</li>
  <li>If English isn’t your first language, get a <strong>native speaker</strong> to review.</li>
</ul>

<p>At Bounce Security, our internal review process made a huge difference — right down to workshop titles and tagline polish.</p>

<p>It is highly unlikely you will get feedback from the conference or the review board during the CfT process, regardless of how early you submit. It is possible you might be able to get some feedback if the submission gets rejected but that is already a little too late…</p>

<p><img src="/assets/img/bhseries/7.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<h4 id="add-goodies-if-you-can">Add Goodies (If You Can)</h4>

<p>Extras can make your course more appealing:</p>

<ul>
  <li>Hardware courses might include take-home <strong>tool-kits</strong>.</li>
  <li>Some courses provide pre-configured <strong>VMs</strong> or helpful documents.</li>
  <li>One course at Black Hat even included a <strong>laptop</strong> for every attendee (ambitious, but memorable!).</li>
</ul>

<p>I don’t think these bonuses necessarily replace a compelling course proposition but they might sweeten the deal..</p>

<p>I include special working documents in my course. We’ve also considered offering <strong>follow-up consulting hours</strong> though we haven’t gone forward with that for now. I did notice however that this year Sensepost is <a href="https://x.com/sensepost_train/status/1924472706931528093">offering an extended Q&amp;A session</a> after their courses.</p>

<h4 id="the-waiting-game">The Waiting Game</h4>

<p>Once you’ve submitted… now you wait.</p>

<p>Don’t be discouraged by rejections, they happen to all of us.</p>

<p>I have been rejected multiple times, even after I have been accepted at previous events from the same organisation. E.g. I have never been accepted by Black Hat Asia and was accepted by Black Hat EU for a couple of years but then rejected the following year.</p>

<p>Always ask for feedback (if available) and <strong>iterate</strong> on your submission.</p>

<p><img src="/assets/img/bhseries/8.jpeg" alt="Cybersecurity" class="blog-image-tall" /></p>

<h4 id="coming-up-selling-your-course">Coming Up: Selling Your Course</h4>

<p>If you got accepted, congrats! That’s a huge achievement. 🎉 🎉</p>

<p>But now comes the next big challenge: <strong>getting people to sign up</strong>.</p>

<p>In the next post, I’ll talk about marketing your course in an attempt to turn your hard work into a full classroom.</p>]]></content><author><name>Josh Grossman</name></author><category term="Training" /><category term="Security" /><category term="Black Hat" /><category term="Training" /><category term="Conferences" /><summary type="html"><![CDATA[In this post, I provide some ideas on how to write a convincing conference submission for your training course.]]></summary></entry><entry><title type="html">Planning the Practical</title><link href="https://joshcgrossman.com/2025/05/13/planning-the-practical/" rel="alternate" type="text/html" title="Planning the Practical" /><published>2025-05-13T06:30:00+00:00</published><updated>2025-05-13T06:30:00+00:00</updated><id>https://joshcgrossman.com/2025/05/13/planning-the-practical</id><content type="html" xml:base="https://joshcgrossman.com/2025/05/13/planning-the-practical/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2025/05/13/planning-the-practical.html">Bounce Security blog</a>.</strong></p>

<h4 id="introduction">Introduction</h4>

<p>Let’s be honest: nobody’s signing up for a training course just to sit and listen to someone talk for 7–8 hours a day.</p>

<p>People come to <em>do the things</em>, to get their hands dirty, apply what they’ve learned, and leave with new skills they can actually use. And that’s your edge.</p>

<p>In this post, I talk about how to make the hands-on elements of your course not just functional, but an integral part of the course.</p>

<p><img src="/assets/img/bhseries/practicalintro.png" alt="alt text" /></p>

<h4 id="splitting-the-day-labs-vs-lectures">Splitting the Day: Labs vs. Lectures</h4>

<p>The Black Hat Call for Training (CFT) recommends at least <strong>40% of your course be practical</strong>. That’s great news as it means you only need to talk for 4–5 hours a day.</p>

<p>But here’s the catch: you now need to figure out how to entertain your attendees the remaining time.</p>

<p>I can tell you that the practical segments are <em>the</em> part attendees remember most and will define their takeaway experience. You therefore need to make these segments sound compelling enough for the review board and the potential attendees.</p>

<h4 id="traditional-practical-labs">Traditional, Practical Labs</h4>

<p>If you’re teaching a very hands-on skill such as offensive security, reverse engineering, cloud config hardening, practical labs are likely your core.</p>

<p>But just saying “we have labs” isn’t enough, how are these labs going to work.</p>

<h5 id="initial-considerations">Initial Considerations</h5>

<p>Here’s what you need to think through — trust me, these details matter:</p>

<ul>
  <li>How do you make tasks challenging <strong>yet solvable</strong>?</li>
  <li>Are the exercises <strong>accessible and achievable</strong> by people at a variety of skill levels?</li>
  <li>Will you want to <strong>centrally track</strong> attendee process and if so, how?</li>
  <li>Is it a <strong>competition</strong> or self-paced?</li>
  <li>Can you stretch your <strong>most advanced students</strong>?</li>
  <li>How can you test that your <strong>labs still work</strong> before each run? Is that manual or automated?</li>
  <li>How will you keep lab content <strong>up to date</strong> if your subject moves fast?</li>
  <li>How many students can you <strong>realistically support and troubleshoot</strong> for at once?</li>
  <li>Do you have an automated way to reset your labs to a <strong>known good state</strong>?</li>
</ul>

<p><img src="/assets/img/bhseries/labconsiderations.png" alt="alt text" /></p>

<h5 id="providing-lab-access">Providing Lab Access</h5>

<p>One of the biggest questions: how will students access the labs?</p>

<ul>
  <li>Do they need to <strong>install software</strong>?</li>
  <li>Is specific <strong>hardware/software</strong> required?</li>
  <li>Do they need a <strong>virtualization tool</strong>?</li>
  <li>Will they need <strong>admin access</strong>?</li>
</ul>

<p>If the answer to any of these is yes, expect friction and maybe a few frustrated emails.</p>

<h4 id="hosted-labs-the-modern-standard">Hosted Labs: The Modern Standard</h4>

<p>In the old days, trainers would lug laptops and servers into conference venues. Now? <strong>Cloud-hosted labs</strong> are the norm.</p>

<p>Sensepost set the gold standard for this almost 10 years ago as they discuss in <a href="https://sensepost.com/blog/2015/into-the-cloud/">their blogpost here</a>. Nowadays, this should probably just be the standard.</p>

<p>But don’t let the cloud fool you into thinking it’s simpler. It comes with its own set of questions:</p>

<ul>
  <li>Can your labs <strong>scale</strong> to support dozens (or hundreds) of users?</li>
  <li>How will you <strong>automate provisioning</strong> for each user?</li>
  <li>Do students <strong>authenticate individually</strong>? How will you set this up</li>
  <li>Have you secured your environment from… creative attendees?
    <ul>
      <li><em>Yes, someone once deployed ransomware in someone’s training lab environment. Be ready!</em></li>
    </ul>
  </li>
  <li>Will students have <strong>post-course access</strong>? (Bonus points if yes.)</li>
</ul>

<p><img src="/assets/img/bhseries/labcloud.png" alt="alt text" /></p>

<h4 id="when-hacking-isnt-the-goal-creative-practical-exercises">When “Hacking” Isn’t the Goal: Creative Practical Exercises</h4>

<p>What if your course isn’t about hacking or coding? Then you’ll need to get creative.</p>

<p>In my case, I built two distinct types of exercises from scratch and they actually became the highlight of the course.</p>

<p>(I go into quite a lot of detail here but I hope it helps other people with their thought process. Feel free to skip if you don’t want the details)</p>

<h5 id="vulnerability-triage">Vulnerability Triage</h5>

<p>The first was a <strong>vulnerability triage</strong> challenge.</p>

<p>Students worked in groups, reviewing fictional findings from tools like SAST, DAST, and SCA. They had a simulated codebase or running app as a reference, but running tools wasn’t the goal or even part of the exercise.</p>

<p>Instead, they had to prioritize the vulnerabilities and more importantly, <strong>explain their reasoning</strong>.</p>

<p>Why? Because the course and therefore the exercises focused on <em>process</em> and <em>decision-making</em>, not tool mastery.</p>

<p><img src="/assets/img/bhseries/vulntriage.png" alt="alt text" /></p>

<p>This exercise built on the lectures and gave students a chance to the new ideas in a low-stakes setting.</p>

<h5 id="organisational-process">Organisational Process</h5>

<p>The second type of exercise asked: <em>How do you roll out a security tool across an organisation?</em></p>

<p>Students had to plan an implementation using a working document based on the content in the lectures. Sounds fun, right? Well I had to work a little on the fun part.</p>

<p>Since attendees came from diverse backgrounds I created a <strong>fictional company case study</strong> for everyone to work from. I also prepared a tech stack, fake team leads, and a slightly sinister app to keep things fun. All teams worked from the same case study as I felt it would make it easier for teams to compare notes afterwards.</p>

<p>But how would they provide their feedback?</p>

<p>Enter: <strong>The Simulated Stakeholder</strong></p>

<p>Rather than each team presenting their plan to the entire class (which might be a little repetitive), I introduced the simulated stakeholder, in most cases the CTO of the simulated company.</p>

<p>(I think this was probably inspired by exercises we did back when I worked at Deloitte as part of training for more senior roles.)</p>

<p>Each team nominated one person to role-play the CTO. All the “CTOs” joined me in a breakout session, where I worked through the working document with them and guided them on the kinds of questions and expectations a real executive might have and how best to interact with the teams.</p>

<p>Then, each CTO joined a <em>different</em> team, and that team had to <strong>justify their plan to a skeptical senior stakeholder</strong>.</p>

<p>Aside from the simulated stakeholders getting much amusement from their sudden promotion to CTO (although I think on one occasion a stakeholder was actually a CTO in their day job), this was a great way to give the teams something to work towards and also stimulate discussion.</p>

<p>It was also an important illustration of one of the key lessons from the course which is the importance of getting senior level buy-in.</p>

<p><img src="/assets/img/bhseries/stakeholder.png" alt="alt text" /></p>

<h4 id="other-ideas">Other Ideas</h4>

<p>I’ll save the story about the time I built a multi-player game based on the ASVS entirely in Google Sheets for another post, or over a beer. 🍻</p>

<p>Just know that even if it sounds weird, it might actually work.</p>

<h4 id="the-real-point">The Real Point</h4>

<p>Yes, I wanted to show off some my cool ideas but the real message is this: You can go beyond traditional “labs” and build creative, impactful exercises that magnify the impact of your course.</p>

<p>Don’t limit yourself to hacking and code. Remember that the rarest and most valuable skill in security is the ability to communicate and persuade. Think about courses which build that muscle as well.</p>

<h4 id="next-post-putting-pen-to-paper">Next Post: Putting pen to paper</h4>

<p>So now, having figured out your financials, your niche, and your exercises, you now need to actually get accepted by a conference.</p>

<p>In the next post, I’ll walk through how to write a compelling Call for Training (CFT) submission.</p>]]></content><author><name>Josh Grossman</name></author><category term="Training" /><category term="Security" /><category term="Black Hat" /><category term="Training" /><category term="Conferences" /><summary type="html"><![CDATA[In this post, I want to talk through my thought process for practical exercises for the participants to do rather than just listening to me.]]></summary></entry><entry><title type="html">Finding your niche/selling point</title><link href="https://joshcgrossman.com/2025/04/20/find-your-niche/" rel="alternate" type="text/html" title="Finding your niche/selling point" /><published>2025-04-20T05:31:00+00:00</published><updated>2025-04-20T05:31:00+00:00</updated><id>https://joshcgrossman.com/2025/04/20/find-your-niche</id><content type="html" xml:base="https://joshcgrossman.com/2025/04/20/find-your-niche/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2025/04/20/find-your-niche.html">Bounce Security blog</a>.</strong></p>

<h4 id="introduction">Introduction</h4>

<p>If you’re thinking about creating a training course, one of the biggest questions you’ll face early on is: <em>Will anyone actually want this?</em> In this post, we’ll explore how you can evaluate whether your course idea stands a chance in an already packed marketplace for professional training.</p>

<p><img src="/assets/img/bhseries/crossroads.png" alt="alt text" /></p>

<h4 id="market-research">Market Research</h4>

<p>In a previous post, I mentioned that I already had a good grasp of the application security training landscape, which gave me an edge in knowing which topics were oversaturated. If you’re not yet as familiar with your own niche, I would suggest you do some market research. Here are some areas you might want to consider.</p>

<h5 id="history-of-training-courses-at-your-target-conferences">History of training courses at your target conferences</h5>

<p>Conferences will often maintain historical websites where you can see the courses they have previously offered.</p>

<p>Black Hat maintains an archive of past training courses with <a href="https://www.blackhat.com/us-25/training/schedule/">predictable</a> <a href="https://www.blackhat.com/asia-24/training/schedule/">URL</a> <a href="https://www.blackhat.com/eu-23/training/schedule/">patterns</a>, making it easy to explore previous years’ offerings. Keep in mind that not all Black Hat events are created equal, Black Hat USA is significantly larger than its counterparts in Asia and Europe.</p>

<p>As for satellite events like Black Hat Spring Trainings, I have never seen a formal Call for Training. It’s possible these are invite-only affairs, favoring trainers with a strong sales history, but that’s just a theory.</p>

<p><img src="/assets/img/bhseries/sleuthing.png" alt="alt text" /></p>

<p>If you’re targeting an OWASP event, they also have a historical record of courses available though it might require a bit of sleuthing. (Hint: all recent OWASP websites have been based in GitHub).</p>

<p>For smaller or more specialized conferences, you’ll need to do your own detective work to uncover historical data.</p>

<h5 id="check-out-offensive-security">Check out Offensive Security</h5>

<p>Offensive Security offers a well-known set of training programs. You can probably guess where their focus lies. If your topic overlaps with theirs, take a hard look at how your course differentiates itself. Their content is well-established and widely respected so if you’re in their arena, you need a unique angle.</p>

<h5 id="review-the-sans-catalogue">Review the SANS catalogue</h5>

<p>SANS offers a massive range of courses often at premium prices but keep in mind that many of them run for six full days. That means a shorter, more affordable course on a similar subject might still appeal to time and budget conscious learners.</p>

<p><img src="/assets/img/bhseries/comparison.png" alt="alt text" /></p>

<h5 id="explore-other-sources">Explore other sources</h5>

<p>Don’t stop there. Depending on your topic, there may be other educational offerings in your space: self-paced labs, YouTube tutorials, Massive Open Online Courses (MOOCs), and more.</p>

<p>Ask yourself: <em>What will learners gain from your course that they can’t easily get from free or low-cost resources?</em> Your value proposition needs to go beyond content, it should include engagement, interaction, depth, or application.</p>

<h4 id="finding-the-niche">Finding the niche</h4>

<p>The sweet spot for your course lies at the intersection of three things:</p>

<ol>
  <li>A subject you’re passionate and knowledgeable about</li>
  <li>A real gap in the current training market</li>
  <li>A compelling reason for someone to pay for it</li>
</ol>

<p>If you’re struggling to identify that gap, don’t be discouraged. It <em>is</em> a tricky balancing act. The ideal topic needs to be fresh and interesting to the conference review board <em>and</em> relevant enough that your potential attendees can justify the time and money it takes to attend.</p>

<p><img src="/assets/img/bhseries/journey.png" alt="alt text" /></p>

<p>On the other hand, the cybersecurity field in particular is constantly evolving. New technologies and threats emerge regularly each one a chance for you to carve out your own area of expertise. There are plenty of unsolved problems and overlooked areas that need attention.</p>

<p>Remember, your audience will often have to convince their manager that your course is worth the investment. Help them make the case by choosing a topic that solves real problems, enhances valuable skills, or supports strategic goals.</p>

<h4 id="next-post-practical-exercises">Next Post: Practical Exercises</h4>

<p>In the next post, we’ll focus on the major differentiator that can elevate your course above the rest: hands-on activities. Because let’s face it no one wants to sit through two days of lectures. What participants get to <em>do</em> during your course is just as important as what they learn.</p>]]></content><author><name>Josh Grossman</name></author><category term="Training" /><category term="Security" /><category term="Black Hat" /><category term="Training" /><category term="Conferences" /><summary type="html"><![CDATA[In this post, I want to talk about one of the key deciders of your course's success - how you will differentiate it from everything else on the market.]]></summary></entry><entry><title type="html">Working out your financials</title><link href="https://joshcgrossman.com/2025/04/01/work-out-your-financials/" rel="alternate" type="text/html" title="Working out your financials" /><published>2025-04-01T04:30:00+00:00</published><updated>2025-04-01T04:30:00+00:00</updated><id>https://joshcgrossman.com/2025/04/01/work-out-your-financials</id><content type="html" xml:base="https://joshcgrossman.com/2025/04/01/work-out-your-financials/"><![CDATA[<p class="notice--info"><strong>This post was originally published on the <a href="https://www.bouncesecurity.com/blog/2025/04/01/work-out-your-financials.html">Bounce Security blog</a>.</strong></p>

<p><img src="/assets/img/bhseries/financials.png" alt="image" class="blog-image" /></p>

<h4 id="introduction">Introduction</h4>

<p>In my previous post, I briefly touched on the financial realities of delivering training courses. In this post, I’ll explore this topic in greater depth.</p>

<h4 id="making-your-millions">Making your millions??</h4>

<p>If your goal is a big financial success, training courses will probably not be your golden ticket. That is why I want to tackle this topic up front. You will need to think about this but you also need to be realistic.</p>

<p>Developing a course requires significant upfront investment, and even after launch, it demands ongoing “care and feeding”. Keeping content fresh and relevant is crucial, and that means regularly:</p>

<ul>
  <li>Updating material to reflect industry changes</li>
  <li>Incorporating new insights and experiences</li>
  <li>Ensuring training exercises still function as intended</li>
  <li>Revising and updating technical components of exercises</li>
  <li>Adapting exercises to maintain relevance</li>
  <li>Applying feedback from previous course sessions</li>
</ul>

<p>If you can identify a subject that is broad enough to appeal to a large audience, different enough from existing options to generate demand, and also low-maintenance in terms of updates, then you might have a high-profit opportunity.</p>

<p>But let me know if you do manage that as it is pretty tough…</p>

<p><img src="/assets/img/bhseries/mazetomoney.png" alt="alt text" class="blog-image" /></p>

<p>In my case, the course I built falls into a slightly specialized category. While maintenance isn’t overwhelming, I have only delivered it 5–8 times so far.</p>

<h4 id="non-financial-benefits">Non-financial benefits</h4>

<p>On the other hand, there are other benefits that may be attractive to you.</p>

<p>The experience of preparing and delivering a course at this level may itself be valuable to your professional development. It may also bring a boost to your public profile and your resume.</p>

<p>Depending on your planned career trajectory, these aspects may be more valuable than money. However, your current employer may not see things this way. 🙃</p>

<h4 id="recouping-your-investment">Recouping your investment</h4>

<p>Before you make a profit you are going to need to cover your initial investment.</p>

<p>The most basic investment is any actual money you spend to deliver the course in person. There are a bunch of considerations for this so I will cover it in more detail a little further down.</p>

<p>The real cost however is (or at least should be) the time you spend building the course. You certainly don’t want to spend a lot of time building a course which no one wants to attend.</p>

<p>To try and mitigate this risk for my course, I took a phased approach. I started by delivering a one-hour overview at the Open Security Summit, followed by a one-day version at OWASP Global AppSec.</p>

<p>These smaller commitments helped me gauge audience reception and refine my content. The one-day course also allowed me to test my experimental exercises (more on that later). While this phase didn’t compensate for all the time investment, it provided some validation that the course was worthwhile and effective and therefore I should continue investing in it.</p>

<p>Striking the right balance is key: invest enough to prove the concept and demonstrate viability, but avoid committing excessive resources to an untested idea that might not work out.</p>

<p><img src="/assets/img/bhseries/balance.png" alt="alt text" class="blog-image" /></p>

<h4 id="breaking-even-on-an-single-course-session">Breaking even on an single course session</h4>

<p>If you are able to afford to pay expenses to deliver training “for the exposure”, then you don’t need to worry about this. In most cases however, you will need to have a plan.</p>

<p>Virtual training is simpler financially: as long as you cover your time cost, you’re in the clear. In-person training, however, introduces a host of additional expenses and complications and I will try and cover some of these here.</p>

<p>For in-person training, you need to figure out what your break even point is. Specifically, how many people need to register to get the revenue required to break even. Obviously the first thing you need to know is what share of the course income you will receive from the venue which is hosting the training. This might be fixed or flexible depending on the conference.</p>

<p><img src="/assets/img/bhseries/model.png" alt="alt text" class="blog-image" /></p>

<p>You can then start building a break even and profit calculator based on your expected expenses. I usually prepare an Excel spreadsheet or Google Doc to work this out and recalculate dynamically.</p>

<h5 id="key-expenses-to-consider">Key Expenses to Consider</h5>

<p>Different conferences handle costs in different ways. Expenses that conferences might cover for you (wholly or partly) include:</p>

<ul>
  <li>Flight costs</li>
  <li>Hotel costs</li>
  <li>Venue costs including food and beverage.</li>
</ul>

<p>Examples that I have seen:</p>

<ul>
  <li>Black Hat publishes information on the <a href="https://www.blackhat.com/call-for-training.html">public CFT page</a>.</li>
  <li>OWASP covers all venue costs but not hotel and flight.</li>
  <li>Other conferences equally split costs and then equally split the profit as well.</li>
</ul>

<p>All conferences I have seen also give trainers a free conference ticket which could also be worth a lot.</p>

<p><img src="/assets/img/bhseries/expenses.png" alt="alt text" class="blog-image" /></p>

<p>Beyond travel and accommodation, factor in additional costs such as:</p>

<ul>
  <li>Airport transfers</li>
  <li>Meals and per diems</li>
  <li>Printed materials or giveaways for participants, (I have a bunch of print-outs I prepare and hand-out over the duration of the course and I often buy and bring snacks as well)</li>
</ul>

<h5 id="the-hidden-cost-your-time">The Hidden Cost: Your Time</h5>

<p>If you’re self-employed or consulting, or you are doing this separate to your employer, your time has an opportunity cost. Time spent training is time you could have spent on client work or other revenue-generating activities. Don’t forget to include travel days as well.</p>

<h5 id="revenue-per-course-day">Revenue Per Course Day</h5>

<p>Different conferences handle pricing and revenue shares in various ways:</p>

<ul>
  <li><strong>OWASP</strong> charges attendees a flat daily rate for training, meaning a three-day course costs three times as much as a one-day course, with no discounts. Trainers receive 40% of net revenue up to $10,000 USD, and 50% of net revenues above $10,000 USD (this is public information).</li>
  <li>As can be seen on <a href="https://www.blackhat.com/us-25/training/schedule/index.html">the public training page</a>, <strong>Black Hat</strong> training ticket costs vary between courses and there are also various early-bird stages. Interestingly, a four-day Black Hat course doesn’t necessarily cost attendees twice as much as a two-day course.</li>
  <li><strong>Some conferences</strong> pay a fixed rate per course, contingent on meeting a minimum number of registrants.</li>
</ul>

<p>Note that at <strong>Black Hat USA</strong>, trainers can deliver the same two-day course twice (Sat-Sun and Mon-Tues).</p>

<h5 id="set-your-minimum-up-front">Set Your minimum up-front</h5>

<p>Most conferences will ask what your financial minimum is upfront and you should be able to use the information above, plus the conference’s specific terms, to figure that out.  You’ll need to maintain close communication with them to determine when to make a GO/NO-GO decision.</p>

<p><img src="/assets/img/bhseries/fork.png" alt="alt text" class="blog-image" /></p>

<h4 id="the-conferences-minimum">The conference’s minimum</h4>

<p>Conferences themselves will generally have financial break-even points, and they won’t run training that doesn’t meet minimum registration numbers. Be clear on these thresholds and when there conference team will make their final decision.</p>

<p>Also, keep in mind that waiting on a conference decision could mean higher flight costs if you delay booking.</p>

<h4 id="next-post-figuring-out-your-selling-point">Next Post: Figuring out your selling point</h4>

<p>The financial aspects are only a small part of the equation. To truly succeed, you need a compelling reason for people to choose your course over others in a crowded market. In the next post, I’ll discuss how to carve out your unique value proposition and attract the right audience.</p>]]></content><author><name>Josh Grossman</name></author><category term="Training" /><category term="Security" /><category term="Black Hat" /><category term="Training" /><category term="Conferences" /><summary type="html"><![CDATA[In this post, I want to talk through the financial aspects of training including expenses and effort to take into account and some thoughts on how different conferences do pricing.]]></summary></entry></feed>