<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Credential Security on RockB</title><link>https://baeseokjae.github.io/tags/credential-security/</link><description>Recent content in Credential Security on RockB</description><image><title>RockB</title><url>https://baeseokjae.github.io/images/og-default.png</url><link>https://baeseokjae.github.io/images/og-default.png</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 17 Sep 2026 19:01:22 +0000</lastBuildDate><atom:link href="https://baeseokjae.github.io/tags/credential-security/index.xml" rel="self" type="application/rss+xml"/><item><title>Sallyport: A Mac Vault Running Authenticated Agent Actions over MCP</title><link>https://baeseokjae.github.io/posts/sallyport-mac-vault-authenticated-agent-actions-mcp/</link><pubDate>Thu, 17 Sep 2026 19:01:22 +0000</pubDate><guid>https://baeseokjae.github.io/posts/sallyport-mac-vault-authenticated-agent-actions-mcp/</guid><description>Sallyport is a free Mac vault that runs authenticated API and SSH actions for AI agents over MCP, so the agent gets the operation, never the key.</description><content:encoded><![CDATA[<p>Sallyport is a free Mac app that holds your API and SSH credentials in an encrypted local vault and executes authenticated actions for your AI agents over MCP. The agent gets the operation, never the key: there is no command that reveals a stored credential and no export route. It runs entirely on your Apple Silicon Mac, gated by Secure Enclave and Touch ID, with no account, cloud, or telemetry.</p>
<h2 id="why-your-ai-agent-shouldnt-hold-your-keys">Why Your AI Agent Shouldn&rsquo;t Hold Your Keys</h2>
<p>Coding agents like Claude Code, Cursor, and Codex read <code>.env</code> files, shell variables, and config files to do their job — and so does every package they pull in. When a token sits in those locations, the agent and all its dependencies can read it, and a single malicious dependency or prompt injection can walk it out the door.</p>
<p>The result is not theoretical. Three recent supply-chain attacks targeted exactly these secrets on developer machines. In May 2026, the TanStack ecosystem was hit with 84 malicious package versions across 42 packages designed to harvest cloud credentials, GitHub tokens, and SSH keys at install time. In late 2025, the Shai-Hulud worm stole secrets on install and used the stolen credentials to infect the next round of packages. In August 2025, the Nx S1ngularity attack scanned developer machines for secrets and shipped the loot out through the GitHub CLI.</p>
<p>All three harvested keys from environment variables and files on disk. More quietly, a prompt-injected agent — following instructions, as it was built to do — can copy a token and send it somewhere new without any malware at all. GitGuardian&rsquo;s &ldquo;State of Secrets Sprawl 2026&rdquo; report (March 2026) found 28.65 million secrets exposed in public repositories, with AI-assisted commits showing a 3.2% leak rate. The same report surfaced 24,008 unique credentials inside MCP config files like <code>claude_desktop_config.json</code> and <code>.cursor/settings.json</code>, with Google API keys around 20% of MCP leaks and PostgreSQL connection strings accounting for roughly 14%.</p>
<p>The lesson is structural: the workload itself is untrusted. Traditional secret managers deliver the secret to the workload, which works fine when the workload is your own trusted binary — and fails badly when the workload is an agent that reads everything and obeys instructions. Sallyport exists because of this inversion.</p>
<h2 id="what-sallyport-is--a-local-mac-vault-that-runs-the-action-not-the-secret">What Sallyport Is — A Local Mac Vault That Runs the Action, Not the Secret</h2>
<p>Sallyport is a free, open-source (Apache-2.0) Mac application that inverts the secret-manager model. Instead of handing a credential to your agent, it executes the authenticated operation on the agent&rsquo;s behalf.</p>
<p>The flow looks like this. Your agent sends a request over MCP: &ldquo;call this API&rdquo; or &ldquo;run this command on that host.&rdquo; Sallyport attaches the credential inside the app, executes the call, and returns the result. The key never appears in the agent&rsquo;s environment, and a sealed journal shows what ran and who approved it.</p>
<p>The project launched publicly on July 14, 2026, written mainly in Swift (with a Go core), and as of the September 2026 data update it holds around 201 GitHub stars and 6 forks across topics like <code>ai-agents</code>, <code>credentials</code>, <code>mcp</code>, <code>secrets-management</code>, and <code>security</code>. It requires an Apple Silicon Mac running macOS 14 or newer — Intel Macs are not supported.</p>
<p>The agent connection is a standard MCP server, so any MCP client can use the bundled <code>sp mcp</code> shim to reach <code>http.request</code>, <code>ssh.exec</code>, and configured upstream MCP servers. The tools Claude Code, Cursor, and Codex are what the project tests against.</p>
<h2 id="the-trust-model-a-fixed-decision-ladder-no-policy-language">The Trust Model: A Fixed Decision Ladder (No Policy Language)</h2>
<p>The most distinctive design choice is that Sallyport replaces policy configuration with a compiled decision ladder. Every action climbs the same four-step gate, and there is no policy language to misconfigure and no rules engine to rot.</p>
<ol>
<li><strong>Vault gate.</strong> The vault starts locked. Secure Enclave and Touch ID gate access to its data, and until the vault is unlocked Sallyport denies every agent action and all management operations except status.</li>
<li><strong>Per-call approval.</strong> A key marked &ldquo;Approval per call&rdquo; requires a click or Touch ID for every single use — including inside an already-approved session.</li>
<li><strong>Session gate.</strong> A new agent process shows one card with its code-signing authority. Approval lasts until the process exits, you revoke it, or the vault locks.</li>
<li><strong>Observe.</strong> With per-session approval off, actions run without session cards and remain in the journal, with the vault gate and per-call approvals still enforced.</li>
</ol>
<p>Mapped onto a procurement document, this is: zero standing privileges for a non-human identity, access granted just in time, and a human in the loop. Approvals use a click or Touch ID and resolve in process; they are not signed grants. A separate Secure Enclave signer signs audit rows and integrity anchors.</p>
<h2 id="how-sallyport-executes-authenticated-operations-over-mcp-http-ssh-remote--local-mcp">How Sallyport Executes Authenticated Operations over MCP (HTTP, SSH, Remote &amp; Local MCP)</h2>
<p>Sallyport supports three families of channels through the MCP gate, all walking the same ladder into the same journal.</p>
<p><strong>HTTP APIs.</strong> Via <code>http.request</code>, the app makes the HTTPS call itself and injects the credential only during execution — for example <code>http.request → api.github.com</code>. Cross-host redirects do not carry the credential, so a redirect hop cannot leak your token to a different host.</p>
<p><strong>SSH.</strong> Via <code>ssh.exec</code> (for example <code>ssh.exec → deploy@prod-03</code>), a stateless helper opens the connection while the app itself handles SSH signatures, pins host keys, and seals a session recording. The private key never enters the helper. Sallyport sees the full SSH command before it runs and checks the host key, which closes the &ldquo;opaque command&rdquo; gap that many SSH gateways ignore.</p>
<p><strong>MCP servers.</strong> Sallyport can proxy local stdio or remote MCP servers through the same gate — for example piping <code>github.create_issue</code> from an upstream server through the approval and journaling layer. It seals and refreshes OAuth 2.1 tokens for these upstream servers, matching the direction the MCP spec has moved: the spec mandates OAuth 2.1 with mandatory PKCE, Dynamic Client Registration (RFC 7591), Resource Indicators (RFC 8707), Protected Resource Metadata (RFC 9728), and CIMD for authentication.</p>
<h2 id="installing-sallyport-and-pointing-your-agent-at-the-mcp-gate">Installing Sallyport and Pointing Your Agent at the MCP Gate</h2>
<p>Setup takes about two minutes, according to the project README. The install command is:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 376 25"
      >
      <g transform='translate(8,16)'>
<path d='M 104,0 L 112,0' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>b</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>w</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>-</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>g</text>
<text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>k</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>v</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>t</text>
</g>

    </svg>
  
</div>
<p>(You can equally download the signed and notarized DMG from sallyport.dev or the Releases page.)</p>
<p>With the app installed, launch it, create the vault, add a credential, and point any MCP client at the gate with a command like:</p>



<div class="goat svg-container ">
  
    <svg
      xmlns="http://www.w3.org/2000/svg"
      font-family="Menlo,Lucida Console,monospace"
      
        viewBox="0 0 624 25"
      >
      <g transform='translate(8,16)'>
<path d='M 200,0 L 208,0' fill='none' stroke='currentColor'></path>
<text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>u</text>
<text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text>
<text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>A</text>
<text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>i</text>
<text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>l</text>
<text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>y</text>
<text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>r</text>
<text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>.</text>
<text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>C</text>
<text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>o</text>
<text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>e</text>
<text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>n</text>
<text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>t</text>
<text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>M</text>
<text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>a</text>
<text text-anchor='middle' x='536' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>O</text>
<text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>S</text>
<text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>/</text>
<text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>s</text>
<text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>p</text>
<text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>m</text>
<text text-anchor='middle' x='600' y='4' fill='currentColor' style='font-size:1em'>c</text>
<text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>p</text>
</g>

    </svg>
  
</div>
<p>That registers Sallyport as a regular MCP server in Claude Code, and the same pattern covers Cursor and Codex. Your first gated call — from install to approval prompt — should land in about two minutes. Release checksums are published with each release and in <code>https://sallyport.dev/downloads/manifest.json</code>, so you can verify what you are running.</p>
<h2 id="approvals-the-audit-journal-and-secure-enclave--touch-id">Approvals, the Audit Journal, and Secure Enclave / Touch ID</h2>
<p>Sallyport packages the vault, the approvals, and the journal into one signed Mac app.</p>
<p><strong>Encrypted at rest.</strong> The vault encrypts key names, providers, host bindings, SSH inventory, settings, and secret values — not just the values but the surrounding metadata.</p>
<p><strong>Touch ID for changes.</strong> Configuration changes can require Touch ID, and a synthetic click cannot satisfy the biometric prompt. This defeats the classic malware trick of faking a click.</p>
<p><strong>Process provenance.</strong> Each approval card shows the caller&rsquo;s code-signing authority and its parent chain, including any unsigned process. You see who is actually asking before you approve.</p>
<p><strong>Write-blind journal.</strong> Sallyport encrypts agent runs and per-call activity as it writes them, then links the ciphertext with a hash chain. A tampered journal entry breaks the chain and is visible in the audit trail.</p>
<p><strong>Observe mode</strong> removes session cards but retains the vault gate, per-call approval, and the journal — useful once a workflow is trusted, while keeping a record.</p>
<p>There is deliberately <strong>no recovery</strong> path: no export, no reveal, and no recovery key. If you lose the vault, you re-initialize it and re-issue the stored credentials at their providers.</p>
<h2 id="the-2026-mcp-credential-problem--why-this-category-exists">The 2026 MCP-Credential Problem — Why This Category Exists</h2>
<p>The tool-building moment is real. Anthropic shipped MCP tunnels on May 19, 2026, so credentials no longer have to ride inside the agent&rsquo;s context — a structural fix that validates the &ldquo;keep the key out of the agent&rdquo; model. GitGuardian&rsquo;s 2026 data gives the problem a concrete shape: 28.6 million new secrets exposed in public GitHub commits in 2025, with 24,008 unique credentials surfaced inside MCP config files and 2,117 of those still valid. Google API keys account for about 20% of MCP leaks and PostgreSQL connection strings about 14%.</p>
<p>The uncomfortable part is that most of these secrets sit in <code>.env</code> files and configs that the agent is <em>supposed</em> to read — which is exactly why traditional secret managers are insufficient. A tool that returns a credential into an environment the agent can read has not actually solved the problem. Sallyport&rsquo;s answer is to make the agent the caller rather than the holder, so there is no key in its environment to skip past — even under YOLO-style flags that disable an agent&rsquo;s own confirmation prompts. As the project notes, those prompts never protected the keys; the keys were readable in the environment all along.</p>
<h2 id="sallyport-vs-alternatives-1password-wardn-rapg-arcadedev-securecode">Sallyport vs. Alternatives (1Password, wardn, rapg, Arcade.dev, SecureCode)</h2>
<p>Several tools approach the same problem from different directions. The table below is a quick comparison.</p>
<table>
  <thead>
      <tr>
          <th>Tool</th>
          <th>Approach</th>
          <th>Where the key lives</th>
          <th>Best for</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Sallyport</strong></td>
          <td>Executor: vault runs the action, agent never sees the key</td>
          <td>Local encrypted vault on the Mac; no export</td>
          <td>Mac-only, on-device, approval-heavy security</td>
      </tr>
      <tr>
          <td><strong>1Password Environments MCP</strong></td>
          <td>Injector: secrets injected at runtime for an authorized process; MCP returns names, not values</td>
          <td>Cloud-backed vault</td>
          <td>Teams already on 1Password, macOS/Linux, Cursor &amp; Codex</td>
      </tr>
      <tr>
          <td><strong>wardn</strong></td>
          <td>Proxy with placeholder tokens; real key injected at the network layer</td>
          <td>Local proxy between agent and tools</td>
          <td>Open-source, structural guarantee, Claude Code/Cursor</td>
      </tr>
      <tr>
          <td><strong>rapg</strong></td>
          <td>Local-first <code>run</code> wrapper that injects env-tagged secrets into child processes; transcript redaction</td>
          <td>Local, master-password gated</td>
          <td>Keeping secrets off disk and scrollback</td>
      </tr>
      <tr>
          <td><strong>Arcade.dev</strong></td>
          <td>Cloud action runtime that vaults credentials and executes with account permissions at use-time</td>
          <td>Centralized cloud vault</td>
          <td>Execution-time authorization across a fleet</td>
      </tr>
      <tr>
          <td><strong>SecureCode / GitGuardian</strong></td>
          <td>Zero-knowledge inject into a 0600 session file the tool sources</td>
          <td>Local session file</td>
          <td>Practical Claude Code secrets management</td>
      </tr>
  </tbody>
</table>
<p>The key contrast is between injectors and executors. 1Password, rapg, and SecureCode hand the credential to a process (which on a developer machine tends to be the agent). Sallyport and Arcade.dev instead execute on the agent&rsquo;s behalf and return the result, so nothing hands a usable credential to the agent in the first place. wardn is closer to Sallyport&rsquo;s spirit but uses a network-layer proxy with placeholder tokens rather than an in-app executor. Two differences matter for evaluation: Sallyport is entirely on-device and Mac-only, while Arcade.dev is a cloud runtime; and wardn replaces values at the network layer while Sallyport attaches and spends the key inside the vault app.</p>
<h2 id="what-sallyport-deliberately-does-not-do--residual-risk-and-limits">What Sallyport Deliberately Does Not Do — Residual Risk and Limits</h2>
<p>Sallyport&rsquo;s official docs are explicit about the boundary it does not cross. The guarantee is <em>credential isolation</em>: the stored key never leaves the app. That is not the same as guaranteeing every result is clean.</p>
<ul>
<li><strong>Results can contain secrets.</strong> Executor responses are returned as received. A target service that echoes your key back in its own response is showing you its bug, and the journal shows you the call that caught it — but the value did pass back through the agent&rsquo;s context.</li>
<li><strong>Upstream MCP results are not scrubbed either.</strong> Configured upstream servers get the credential you bind to them, so bind the narrowest one you have and flag it for per-call approval. Tool descriptions from upstream servers also land in the model&rsquo;s context as instructions, so they are instructions the agent will follow.</li>
<li><strong>No reveal, export, or recovery route.</strong> This is deliberate — it closes the exfiltration path — but it means that losing the vault requires re-issuing credentials at their providers. There is no back door.</li>
<li><strong>The agent still runs with your rights.</strong> Sallyport does not sandbox the agent or the rest of your machine; it protects credentials specifically. Treat the MCP dependency like any dependency that runs with your privileges, and route its calls through the gate and the journal.</li>
</ul>
<h2 id="is-sallyport-right-for-you-a-practical-decision-checklist">Is Sallyport Right for You? A Practical Decision Checklist</h2>
<p>Use this to decide quickly.</p>
<ul>
<li><strong>You run an Apple Silicon Mac on macOS 14+</strong>, and every alternative that fits your stack is on-device-first. If you need Linux or Windows, Sallyport is not for you.</li>
<li><strong>Your agent touches real infrastructure</strong> — Cloudflare, GitHub, SSH hosts — where one leaked token is expensive. The executor model removes the credential from the agent&rsquo;s environment entirely.</li>
<li><strong>You want a fixed, auditable trust model</strong> rather than a policy language. The four-step ladder with per-call and per-session gates is deliberately hard to misconfigure.</li>
<li><strong>You are happy to re-issue keys on vault loss</strong> and value a no-recovery design that closes export exfiltration. If you need a team recovery story, a cloud vault like 1Password Environments may fit better.</li>
<li><strong>You want to keep secrets fully on-device</strong> with no cloud, no account, and no telemetry — and you are willing to audit a public Apache-2.0 codebase yourself.</li>
</ul>
<p>If you are on a Mac with the agent touching real infrastructure and you want the workload to operate rather than possess your keys, Sallyport is a two-minute install to a first gated call — and the whole credential-spending path is public on GitHub for you to audit before you trust it with anything.</p>
<h2 id="faq">FAQ</h2>
<p><strong>Is Sallyport free and open source?</strong>
Yes. The core is free and open source under Apache-2.0, with no account, no cloud service, and no telemetry. It runs entirely on your Mac. Planned enterprise components will use a commercial license.</p>
<p><strong>Does Sallyport ever reveal my keys to the agent?</strong>
No. There is no command that reveals a stored credential, no export, and no recovery route. The app attaches the key inside itself, executes the call, and returns only the target&rsquo;s response. The key never appears in the agent&rsquo;s environment.</p>
<p><strong>Which MCP clients does Sallyport work with?</strong>
Any MCP client can use the bundled <code>sp mcp</code> shim to reach <code>http.request</code>, <code>ssh.exec</code>, and configured upstream MCP servers. The project tests against Claude Code, Cursor, and Codex, and you register it with a command like <code>claude mcp add sallyport -- /Applications/Sallyport.app/Contents/MacOS/sp mcp</code>.</p>
<p><strong>What hardware and macOS do I need?</strong>
An Apple Silicon Mac running macOS 14 or newer. Intel Macs are not supported. Installation takes about two minutes via <code>brew install --cask olegsotnikov/tap/sallyport</code>.</p>
<p><strong>What happens if I lose the vault?</strong>
There is no recovery key, export, or reveal route by design. You re-initialize the vault and re-issue the stored credentials at their providers. That is the deliberate trade-off: closing the exfiltration path also means no back door.</p>
]]></content:encoded></item></channel></rss>