{
  "frontmatter": {
    "title": "BOTTLENECK",
    "subtitle": "No. 01 \u2014 The Untrusted Clone",
    "author": "ArmyknifeLabs",
    "language": "en",
    "type": "magazine",
    "theme": "bottleneck",
    "date": "2026-08-10",
    "magazine_html": "\n<section class=\"cover securegit-cover\" id=\"cover\">\n  <div class=\"rulefield-full\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 612 400\" preserveAspectRatio=\"none\" class=\"rulefield-svg\"><line x1=\"0\" y1=\"40.00\" x2=\"612\" y2=\"40.00\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"42.69\" x2=\"612\" y2=\"42.69\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"45.38\" x2=\"612\" y2=\"45.38\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"48.07\" x2=\"612\" y2=\"48.07\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"50.76\" x2=\"612\" y2=\"50.76\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"53.45\" x2=\"612\" y2=\"53.45\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"56.13\" x2=\"612\" y2=\"56.13\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"58.82\" x2=\"612\" y2=\"58.82\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"61.51\" x2=\"612\" y2=\"61.51\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"64.20\" x2=\"612\" y2=\"64.20\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"66.89\" x2=\"612\" y2=\"66.89\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"69.58\" x2=\"612\" y2=\"69.58\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"72.27\" x2=\"612\" y2=\"72.27\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"74.96\" x2=\"612\" y2=\"74.96\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 77.65 L 364 77.65 C 371 77.65, 371 71.65, 365 71.65 L 40 71.65\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"80.34\" x2=\"612\" y2=\"80.34\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"83.03\" x2=\"612\" y2=\"83.03\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"85.71\" x2=\"612\" y2=\"85.71\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"88.40\" x2=\"612\" y2=\"88.40\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"91.09\" x2=\"612\" y2=\"91.09\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"93.78\" x2=\"612\" y2=\"93.78\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"96.47\" x2=\"612\" y2=\"96.47\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"99.16\" x2=\"612\" y2=\"99.16\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"101.85\" x2=\"612\" y2=\"101.85\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"104.54\" x2=\"612\" y2=\"104.54\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"107.23\" x2=\"612\" y2=\"107.23\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"109.92\" x2=\"612\" y2=\"109.92\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 112.61 L 364 112.61 C 371 112.61, 371 106.61, 365 106.61 L 40 106.61\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"115.29\" x2=\"612\" y2=\"115.29\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"117.98\" x2=\"612\" y2=\"117.98\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"120.67\" x2=\"612\" y2=\"120.67\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"123.36\" x2=\"612\" y2=\"123.36\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"126.05\" x2=\"612\" y2=\"126.05\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"128.74\" x2=\"612\" y2=\"128.74\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"131.43\" x2=\"612\" y2=\"131.43\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"134.12\" x2=\"612\" y2=\"134.12\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"136.81\" x2=\"612\" y2=\"136.81\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"139.50\" x2=\"612\" y2=\"139.50\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"142.18\" x2=\"612\" y2=\"142.18\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"144.87\" x2=\"612\" y2=\"144.87\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"147.56\" x2=\"612\" y2=\"147.56\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 150.25 L 364 150.25 C 371 150.25, 371 144.25, 365 144.25 L 40 144.25\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"152.94\" x2=\"612\" y2=\"152.94\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"155.63\" x2=\"612\" y2=\"155.63\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"158.32\" x2=\"612\" y2=\"158.32\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"161.01\" x2=\"612\" y2=\"161.01\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"163.70\" x2=\"612\" y2=\"163.70\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"166.39\" x2=\"612\" y2=\"166.39\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"169.08\" x2=\"612\" y2=\"169.08\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"171.76\" x2=\"612\" y2=\"171.76\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"174.45\" x2=\"612\" y2=\"174.45\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"177.14\" x2=\"612\" y2=\"177.14\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"179.83\" x2=\"612\" y2=\"179.83\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 182.52 L 364 182.52 C 371 182.52, 371 176.52, 365 176.52 L 40 176.52\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"185.21\" x2=\"612\" y2=\"185.21\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"187.90\" x2=\"612\" y2=\"187.90\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"190.59\" x2=\"612\" y2=\"190.59\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"193.28\" x2=\"612\" y2=\"193.28\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"195.97\" x2=\"612\" y2=\"195.97\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"198.66\" x2=\"612\" y2=\"198.66\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"201.34\" x2=\"612\" y2=\"201.34\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"204.03\" x2=\"612\" y2=\"204.03\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"206.72\" x2=\"612\" y2=\"206.72\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"209.41\" x2=\"612\" y2=\"209.41\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"212.10\" x2=\"612\" y2=\"212.10\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"214.79\" x2=\"612\" y2=\"214.79\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"217.48\" x2=\"612\" y2=\"217.48\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 220.17 L 364 220.17 C 371 220.17, 371 214.17, 365 214.17 L 40 214.17\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"222.86\" x2=\"612\" y2=\"222.86\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"225.55\" x2=\"612\" y2=\"225.55\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"228.24\" x2=\"612\" y2=\"228.24\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"230.92\" x2=\"612\" y2=\"230.92\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"233.61\" x2=\"612\" y2=\"233.61\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"236.30\" x2=\"612\" y2=\"236.30\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"238.99\" x2=\"612\" y2=\"238.99\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"241.68\" x2=\"612\" y2=\"241.68\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"244.37\" x2=\"612\" y2=\"244.37\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"247.06\" x2=\"612\" y2=\"247.06\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"249.75\" x2=\"612\" y2=\"249.75\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"252.44\" x2=\"612\" y2=\"252.44\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"255.13\" x2=\"612\" y2=\"255.13\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 257.82 L 364 257.82 C 371 257.82, 371 251.82, 365 251.82 L 40 251.82\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"260.50\" x2=\"612\" y2=\"260.50\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"263.19\" x2=\"612\" y2=\"263.19\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"265.88\" x2=\"612\" y2=\"265.88\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"268.57\" x2=\"612\" y2=\"268.57\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"271.26\" x2=\"612\" y2=\"271.26\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"273.95\" x2=\"612\" y2=\"273.95\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"276.64\" x2=\"612\" y2=\"276.64\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"279.33\" x2=\"612\" y2=\"279.33\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"282.02\" x2=\"612\" y2=\"282.02\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"284.71\" x2=\"612\" y2=\"284.71\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"287.39\" x2=\"612\" y2=\"287.39\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"290.08\" x2=\"612\" y2=\"290.08\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"292.77\" x2=\"612\" y2=\"292.77\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 295.46 L 364 295.46 C 371 295.46, 371 289.46, 365 289.46 L 40 289.46\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"298.15\" x2=\"612\" y2=\"298.15\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"300.84\" x2=\"612\" y2=\"300.84\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"303.53\" x2=\"612\" y2=\"303.53\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"306.22\" x2=\"612\" y2=\"306.22\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"308.91\" x2=\"612\" y2=\"308.91\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"311.60\" x2=\"612\" y2=\"311.60\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"314.29\" x2=\"612\" y2=\"314.29\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"316.97\" x2=\"612\" y2=\"316.97\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"319.66\" x2=\"612\" y2=\"319.66\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"322.35\" x2=\"612\" y2=\"322.35\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"325.04\" x2=\"612\" y2=\"325.04\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"327.73\" x2=\"612\" y2=\"327.73\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<path d=\"M 0 330.42 L 364 330.42 C 371 330.42, 371 324.42, 365 324.42 L 40 324.42\" stroke=\"#FF4A1C\" stroke-width=\"0.6\" fill=\"none\"/>\n<line x1=\"0\" y1=\"333.11\" x2=\"612\" y2=\"333.11\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"335.80\" x2=\"612\" y2=\"335.80\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"338.49\" x2=\"612\" y2=\"338.49\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"341.18\" x2=\"612\" y2=\"341.18\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"343.87\" x2=\"612\" y2=\"343.87\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"346.55\" x2=\"612\" y2=\"346.55\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"349.24\" x2=\"612\" y2=\"349.24\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"351.93\" x2=\"612\" y2=\"351.93\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"354.62\" x2=\"612\" y2=\"354.62\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"357.31\" x2=\"612\" y2=\"357.31\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"0\" y1=\"360.00\" x2=\"612\" y2=\"360.00\" stroke=\"#111114\" stroke-width=\"0.4\"/>\n<line x1=\"367\" y1=\"32\" x2=\"367\" y2=\"368\" stroke=\"#111114\" stroke-width=\"0.6\"/></svg></div>\n  <div class=\"cover-inner\">\n    <div class=\"masthead\">BOTTLENECK</div>\n    <div class=\"issue-line\">NO. 01 \u00b7 THE UNTRUSTED CLONE \u00b7 A LIMITED SERIES</div>\n    <div class=\"coverlines\"><div class=\"coverline-row\"><code>git clone</code></div>\n<div class=\"coverline-row\">RUNS</div>\n<div class=\"coverline-row\">SOMEBODY</div>\n<div class=\"coverline-row\">ELSE'S CODE.</div></div>\n    <div class=\"subcover\">You knew that. You did it anyway, this week, probably twice.\nA field guide to SecureGit \u2014 and to git that can prove what happened.</div>\n    <div class=\"teasers\"><div class=\"teaser\"><span class=\"teaser-head\">DAY ONE</span><span class=\"teaser-body\">installed and scanning in ten minutes</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">ACQUIRE, NOT CLONE</span><span class=\"teaser-body\">why archive-first defeats hooks</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">TWELVE SCANNERS</span><span class=\"teaser-body\">the built-in set, and the plugin ladder</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">THE GUARDED COMMIT</span><span class=\"teaser-body\">a gate that does not slow you down</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">RECEIPTS</span><span class=\"teaser-body\">cryptographic chain of custody for every push</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">SARIF + GITLAB SAST</span><span class=\"teaser-body\">reports that upload where your dashboards already live</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">BASELINES</span><span class=\"teaser-body\">adopting on legacy code without failing every build</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">AUDIT LOG</span><span class=\"teaser-body\">hash-chained, tamper-evident, SIEM-ready</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">HANDLES, NOT VALUES</span><span class=\"teaser-body\">giving agents credentials they never see</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">THE 30-DAY ROLLOUT</span><span class=\"teaser-body\">solo, then team, then org</span></div>\n<div class=\"teaser\"><span class=\"teaser-head\">TWELVE OBJECTIONS</span><span class=\"teaser-body\">answered honestly, including the ones we lose</span></div></div>\n  </div>\n  <div class=\"cover-strap\">PLUS: THE MATURITY TABLE \u2014 every feature marked SHIPPED, DESIGNED, OR PLANNED \u00b7 CURRENT VERSION 0.12.16</div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 2</div>\n  <h1>MASTHEAD &amp; HOW TO READ THIS ISSUE</h1>\n  \n  <div class=\"\"><p><strong>BOTTLENECK</strong>\n<em>The magazine for engineers becoming AI engineers.</em></p>\n<p>Issue 01 \u00b7 The Untrusted Clone</p>\n<p>Published by ArmyknifeLabs.</p>\n<p><strong>About this issue.</strong> Most tooling magazines are written by people who want you to be impressed. This one is written by the people who build the tool, for people who are deciding whether to type one command. Being impressed is not the goal. Typing the command is.</p>\n<p><strong>How to read this issue</strong></p>\n<p><strong>If you have ten minutes:</strong> page 11. That is the quickstart. It ends with you having scanned a real repository. Everything else in this issue is optional after that.</p>\n<p><strong>If you have an evening:</strong> Part I, pages 10\u201323. That is Day One in full \u2014 install, first acquire, first scan, reading findings, aliases, the pre-commit hook, and a straight answer on speed. You will finish it with SecureGit in your daily workflow.</p>\n<p><strong>If you are evaluating this for a team:</strong> start at page 56 (the twelve objections), then page 53 (the 30-day rollout), then come back to Part II for the mental model. You need the objections first because your team will raise them before you finish your first sentence.</p>\n<p><strong>If you are in a regulated environment:</strong> Parts III and IV, pages 38\u201351. SBOM anchoring, CVE state over time, license posture, and the credential boundary. Then the maturity table on page 63, because some of what you need is <span class=\"badge designed\">DESIGNED</span> and not yet <span class=\"badge shipped\">SHIPPED</span>, and you should know which before you plan around it.</p>\n<p><strong>The maturity markers</strong></p>\n<p>Every capability in this issue is marked. Here is exactly what the three words mean, and we hold ourselves to them:</p>\n<p><strong><span class=\"badge shipped\">SHIPPED</span></strong> \u2014 in a released build. You can install it today and use it. If you cannot, that is a bug and we want the report.</p>\n<p><strong><span class=\"badge designed\">DESIGNED</span></strong> \u2014 the architecture is decided and written down in an accepted design record, and implementation is partial or scheduled. It is real engineering intent, not a wish. It is not something you can run.</p>\n<p><strong><span class=\"badge planned\">PLANNED</span></strong> \u2014 identified, scoped, not yet designed in detail. Directional only. Do not build a procurement plan on a <span class=\"badge planned\">PLANNED</span> row.</p>\n<p>If a page describes something and you cannot find the marker, treat it as <span class=\"badge planned\">PLANNED</span> and write to us, because we made a mistake.</p>\n<p><strong>Corrections</strong> run at the front of the next issue, at the same size as the original claim.</p>\n<hr></div>\n</section>\n\n\n<section class=\"page-break contents-page\">\n  <div class=\"small caps graphite\">Page 3</div>\n  <h1>CONTENTS</h1>\n  <div class=\"contents-body\"><p><strong>ISSUE 01 \u00b7 THE UNTRUSTED CLONE</strong></p>\n<p><strong>FRONT</strong>\n04 \u2014 Editor's Letter: <em>The Repository Is The Attack Surface</em>\n06 \u2014 Ninety Seconds: what actually happens when you clone\n08 \u2014 What SecureGit Is, And What It Is Not</p>\n<p><strong>PART I \u00b7 DAY ONE</strong>\n10 \u2014 Section opener\n11 \u2014 <strong>The Ten-Minute Quickstart</strong>\n12 \u2014 Install, and your first acquire\n14 \u2014 Your first scan, and how to read a finding\n16 \u2014 Making it feel like git: aliases and muscle memory\n18 \u2014 The pre-commit hook: your first guardrail\n20 \u2014 \"Will this slow me down?\" \u2014 the honest performance page\n22 \u2014 Day One checklist, and what to do when it goes wrong</p>\n<p><strong>PART II \u00b7 THE MENTAL MODEL</strong>\n24 \u2014 Section opener\n25 \u2014 <strong>Acquire, not clone</strong>: why archive-first defeats hooks\n28 \u2014 <strong>Scan</strong>: twelve built-in scanners and the plugin ladder\n31 \u2014 <strong>The guarded commit</strong>: staged scanning without the friction\n34 \u2014 <strong>The chain of custody</strong>: receipts, tiers, and the push gate</p>\n<p><strong>PART III \u00b7 SUPPLY CHAIN &amp; GOVERNANCE</strong>\n38 \u2014 Section opener\n39 \u2014 SBOM, OSV, and the number that matters more than zero\n42 \u2014 Lock files, licenses, and what changed when\n44 \u2014 <strong>SARIF, GitLab SAST, and baselines</strong>: reports that upload where your dashboards live\n45 \u2014 <strong>The audit log &amp; compliance report</strong>: hash-chained evidence, OWASP + NIST SSDF mapping</p>\n<p><strong>PART IV \u00b7 SECRETS &amp; AGENTS</strong>\n46 \u2014 Section opener\n47 \u2014 Handles, not values: the secret broker\n49 \u2014 Server discovery and the credential boundary\n50 \u2014 SecureGit for agents: the MCP surface</p>\n<p><strong>PART V \u00b7 ADOPTION</strong>\n52 \u2014 Section opener\n53 \u2014 <strong>The 30-day rollout</strong>: solo \u2192 team \u2192 org\n56 \u2014 <strong>Twelve objections</strong>, answered honestly\n58 \u2014 CI/CD integration that people do not disable\n60 \u2014 Troubleshooting and the false-positive playbook</p>\n<p><strong>BACK</strong>\n62 \u2014 Command reference card\n63 \u2014 The maturity table \u00b7 Glossary\n64 \u2014 Back cover</p>\n<hr></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 4\u20135</div>\n  <h1>EDITOR'S LETTER</h1>\n  <div class=\"deck\">You have spent your career securing what you deploy. The thing you never secured is the moment code arrives on your machine \u2014 and that moment now happens dozens of times a week, increasingly without you watching.</div>\n  <div class=\"two-col\"><h2>The Repository Is The Attack Surface</h2>\n<hr>\n<p>Here is a thing every engineer knows and almost nobody acts on.</p>\n<p><code>git clone</code> is not a download. It is a download plus a filesystem write plus, depending on what is in the archive and what you do next, code execution. Hooks. Submodule configuration. Filter drivers. Path handling quirks that have produced real CVEs in real git releases more than once. The repository is not data that you then choose to run. It is a bundle of data and executable configuration that you have invited onto your machine, inside your home directory, with your permissions.</p>\n<p>You know this. So does everyone. And then a colleague posts a link, and you clone it, because the alternative is not cloning it, and that is not a real alternative.</p>\n<p>The reason this became urgent rather than merely true is that the volume changed.</p>\n<p>Two years ago you cloned a handful of repositories a month, and you had usually heard of them. Now an agent working on your behalf pulls down a dependency you have not evaluated, to check whether its API does what a search result claimed. It does that at three in the morning while you are asleep, using your credentials, on your machine. The judgment step that used to sit between \"I found this repository\" and \"it is now on my disk\" has been compressed to nothing, and in many workflows removed entirely.</p>\n<p>That is the bottleneck this issue is about. Not \"can I get the code\" \u2014 that was solved decades ago. <strong>Can I get the code without the act of getting it being the vulnerability, and can I prove afterward what arrived and what I did with it.</strong></p>\n<hr>\n<div class=\"pullquote\">git clone is not a download. It is a download that has been granted permission to run.</div>\n<hr>\n<p>There is a second half to this, and it is the half that turns a security tool into an engineering tool.</p>\n<p>Once you accept that code arrival is an event worth controlling, you notice that code <em>departure</em> is too. A push is the first moment your work crosses onto shared infrastructure. It is the moment where \"I wrote this\" becomes \"we shipped this.\" And in almost every organization, that moment carries no durable proof of anything: not who wrote which lines, not which of those lines came from a human versus an agent, not whether any scanner ever looked at it, not what the dependency posture was at the time.</p>\n<p>Six months later somebody asks. They ask because of an audit, or an incident, or a customer questionnaire, or a diligence process. And the honest answer is that you have git history, which tells you what changed and who the commit claims to be from, and nothing else. Git history is a record of assertions, not a record of evidence. It was never designed to be otherwise \u2014 Linus was solving a different problem, and solving it very well.</p>\n<p>SecureGit is one answer to both halves. It puts a thin gate on arrival, a thin gate on departure, and a cryptographic receipt on both. That is the whole idea, and the rest of this issue is about whether the gates are thin enough that you will actually keep them.</p>\n<hr>\n<div class=\"pullquote\">Git history is a record of assertions, not a record of evidence. That was the right design for 2005. It is an expensive gap in 2026.</div>\n<hr>\n<p>Now the part where I tell you what this is going to cost you, because adoption articles that skip this are the reason engineers distrust adoption articles.</p>\n<p><strong>It will cost you a new verb.</strong> You will type <code>acquire</code> instead of <code>clone</code> for untrusted repositories. That is a habit change, and habit changes fail unless you make them cheap. Page 16 is entirely about making it cheap.</p>\n<p><strong>It will cost you seconds, and occasionally a minute.</strong> Scanning is not free. On a normal working repository it is fast enough that you will stop noticing. On a very large repository it is not, and page 20 gives you real numbers rather than reassurance.</p>\n<p><strong>It will cost you some false positives.</strong> Every scanner produces them. A scanner that claims otherwise is either lying or not looking hard. Page 60 is a playbook for driving them down, because a tool that cries wolf gets disabled in week two, and a disabled tool has negative value \u2014 it provides the feeling of security with none of it.</p>\n<p><strong>And it will cost you a conversation with your team</strong>, which is the expensive part. Page 56 gives you the twelve objections you will hear, with honest answers, including the three cases where the honest answer is \"you are right, do not use this for that.\"</p>\n<p>What you get for it: code that arrives without executing, commits that cannot silently ship a secret, pushes that carry proof, and \u2014 the part that surprises people \u2014 a genuine answer to the question \"what did we know about our supply chain in March,\" which is a question that currently ends most enterprise sales conversations in an uncomfortable silence.</p>\n<p>Start at page 11. It takes ten minutes and it ends with you having scanned something real.</p>\n<p>\u2014 <em>The Editors</em></p>\n<hr></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 6\u20137</div>\n  <h1>NINETY SECONDS (INFOGRAPHIC SPREAD)</h1>\n  <div class=\"deck\">Two timelines, same repository. The top is <code>git clone</code>. The bottom is <code>securegit acquire</code>. Read them against each other.</div>\n  <div class=\"two-col\"><p><strong>Spread headline (spans gutter):</strong> WHAT ACTUALLY HAPPENS</p>\n<h2>Panel A (left page, upper) \u2014 TIMELINE ONE: <code>git clone</code></h2>\n<p><strong>Format:</strong> a horizontal timeline, left to right, with events as ticks above the line and <em>trust state</em> as a colored band below it. The band starts neutral and turns signal-orange at the first execution point, staying orange to the end.</p>\n<table>\n<thead>\n<tr>\n<th>Moment</th>\n<th>What happens</th>\n<th>Your exposure</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>t+0</code></td>\n<td>Network fetch begins</td>\n<td>None yet</td>\n</tr>\n<tr>\n<td><code>t+2s</code></td>\n<td>Objects written into <code>.git</code></td>\n<td>Disk write, your permissions</td>\n</tr>\n<tr>\n<td><code>t+3s</code></td>\n<td><strong>Hook scripts land in <code>.git/hooks</code></strong></td>\n<td><strong>Dormant executables now on disk</strong></td>\n</tr>\n<tr>\n<td><code>t+3s</code></td>\n<td><code>.gitmodules</code>, <code>.gitattributes</code>, config land</td>\n<td>Filter and submodule directives, unread</td>\n</tr>\n<tr>\n<td><code>t+4s</code></td>\n<td>Working tree checkout</td>\n<td>Paths written per attacker-influenced names</td>\n</tr>\n<tr>\n<td><code>t+4s</code></td>\n<td>Clone reports success</td>\n<td><strong>You believe you have data. You have data and configuration.</strong></td>\n</tr>\n<tr>\n<td><code>t+30s</code></td>\n<td>You <code>cd</code> in and run anything git-adjacent</td>\n<td><strong>Hooks may execute. Filters may execute.</strong></td>\n</tr>\n<tr>\n<td><code>t+45s</code></td>\n<td>You open the repo in an editor with extensions</td>\n<td>Editor-level execution surface</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Annotation (signal, pointing at <code>t+3s</code>):</strong> <em>Nothing has run yet. That is the entire window in which a decision is still cheap. <code>git clone</code> gives you no place to stand inside it.</em></p>\n<h2>Panel B (left page, lower) \u2014 TIMELINE TWO: <code>securegit acquire</code></h2>\n<table>\n<thead>\n<tr>\n<th>Moment</th>\n<th>What happens</th>\n<th>Your exposure</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>t+0</code></td>\n<td><strong>Fetched as an archive, not as a live repo</strong></td>\n<td>Inert bytes</td>\n</tr>\n<tr>\n<td><code>t+2s</code></td>\n<td>Archive extracted to a staging path</td>\n<td>Files on disk, no git configuration active</td>\n</tr>\n<tr>\n<td><code>t+2s</code></td>\n<td><strong>Hooks stripped</strong></td>\n<td>Dormant executables removed, not merely ignored</td>\n</tr>\n<tr>\n<td><code>t+3s</code></td>\n<td>Scanners run across the tree</td>\n<td>Findings collected</td>\n</tr>\n<tr>\n<td><code>t+4s</code></td>\n<td>A sanitization report is written alongside</td>\n<td>You have something to read</td>\n</tr>\n<tr>\n<td><code>t+5s</code></td>\n<td>Converted into a normal git repository</td>\n<td><strong>Now</strong> it is a repo \u2014 after inspection</td>\n</tr>\n<tr>\n<td><code>t+5s</code></td>\n<td><span class=\"badge shipped\">SHIPPED</span> \u2014 a chain receipt records the acquisition</td>\n<td>Provenance from moment zero</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Annotation:</strong> <em>The order is the product. Fetch, strip, inspect, then confer git-ness. <code>git clone</code> confers git-ness first and inspects never.</em></p>\n<h2>Panel C (right page, full) \u2014 THE THREE THINGS PEOPLE GET WRONG</h2>\n<p><strong>Format:</strong> three tall cards, each with a myth in condensed caps, a correction in serif body, and a one-line takeaway in mono.</p>\n<p><strong>MYTH 1 \u00b7 \"I only clone repos I trust.\"</strong>\nYou clone repos your dependencies trust, which is a different set and a much larger one. You also clone repos an agent selected on your behalf from a search result. The trusted-source model assumes a human evaluated the source. Increasingly, none did.\n<code>\u2192 The question is not \"do I trust this\" but \"did anyone actually look.\"</code></p>\n<p><strong>MYTH 2 \u00b7 \"My scanner catches this.\"</strong>\nMost scanning runs in CI, which is after the clone, on a machine that is not yours, looking at the tree rather than the repository metadata. The hooks that concern us are in <code>.git</code>, which most scanners exclude by default and which CI usually does not receive.\n<code>\u2192 Scan the .git directory. Most tools do not, by default. Check yours.</code></p>\n<p><strong>MYTH 3 \u00b7 \"This is theoretical.\"</strong>\nHook execution and path-handling behavior in git have produced real, patched CVEs on multiple occasions. The mitigation is always \"upgrade git,\" which works until the next one, and which does nothing about the window between disclosure and your fleet actually upgrading.\n<code>\u2192 Defense in depth exists because point fixes arrive late and unevenly.</code></p>\n<div class=\"reversed\"><p><strong>The honest framing</strong>\nSecureGit does not make untrusted code safe. Nothing does. It makes the <em>acquisition</em> of untrusted code non-executing, and it gives you a place to stand \u2014 a moment where the code is on your disk, inert, and you can look at it before it becomes a live repository.</p>\n<p>That moment did not previously exist. That is the entire contribution. Everything else in this issue is built on it.</p></div></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 8\u20139</div>\n  <h1>THE ONE-PAGE MENTAL MODEL</h1>\n  <div class=\"deck\">Before any command, this. Five minutes here saves an hour of confusion later.</div>\n  <div class=\"two-col\"><hr>\n<h3>The one sentence</h3>\n<p><strong>SecureGit is a git wrapper that puts a gate on the two moments code crosses a trust boundary \u2014 arrival and departure \u2014 and writes a signed receipt for each crossing.</strong></p>\n<p>Everything else is elaboration. If you remember one thing, remember: <strong>arrival, departure, receipt.</strong></p>\n<h3>The four layers</h3>\n<p>Think of it as four layers stacked, each usable without the ones above it. <strong>You can adopt one layer and stop.</strong> Most people should, at first.</p>\n<p><strong>LAYER 1 \u00b7 ACQUISITION</strong> <span class=\"badge shipped\">SHIPPED</span>\nFetch untrusted code without executing it. <code>securegit acquire</code> replaces <code>git clone</code> for anything you did not write. Archive-first (ZIP-with-history by default; <code>zip-only</code> and <code>bare-checkout</code> also available), hooks stripped, twelve scanners run, then converted to a normal git repo.\n<em>Adopt this alone and you have already gotten most of the day-one value.</em></p>\n<p><strong>LAYER 2 \u00b7 SCANNING</strong> <span class=\"badge shipped\">SHIPPED</span>\nTwelve built-in Rust scanners \u2014 secrets, patterns, entropy, binary, encoding (Trojan Source), supply chain, CI/CD, container, IaC, deserialization, dangerous files, git internals \u2014 plus a plugin system that wraps tools you already use. Run it on a path, on staged changes, on a diff, or in CI. Outputs pretty, JSON, <strong>SARIF 2.1.0</strong>, or <strong>GitLab SAST v15</strong>. <code>--write-baseline</code> lets you adopt on a legacy codebase without failing every build.\n<em>Adopt this second. It is the layer that changes your commit habits.</em></p>\n<p><strong>LAYER 3 \u00b7 CHAIN OF CUSTODY &amp; GOVERNANCE</strong> <span class=\"badge shipped\">SHIPPED</span> (core, audit log, layered policy) / <span class=\"badge designed\">DESIGNED</span> (offline chain, batch lookup)\nEvery trust-boundary operation emits a cryptographically signed receipt. Push can be gated on whether the commits in range have receipts. Blame can show provenance per line. A separate <strong>hash-chained audit log</strong> captures every security-relevant event (scan, block, hook, baseline write, credential change) and exports as JSONL or CEF for SIEM ingest. <strong>Layered configuration</strong> (<code>/etc/securegit</code> \u2192 user \u2192 repo \u2192 env) with <code>[policy] min_fail_on</code>, <code>locked_keys</code>, and <code>audit_required</code> gives security teams a governance surface developers can tighten but not weaken.\n<em>Adopt this when you need to prove things to someone who was not there. Two of its three pieces (audit log, policy) work without any signing infrastructure.</em></p>\n<p><strong>LAYER 4 \u00b7 SUPPLY-CHAIN PROVENANCE</strong> <span class=\"badge shipped\">SHIPPED</span> (anchoring, SBOM, compliance report) / <span class=\"badge designed\">DESIGNED</span> (parts)\nSBOM anchoring, CVE state at a point in time, license posture, lock-file change records \u2014 all attached to the same chain. Releases ship a CycloneDX SBOM, <code>SHA256SUMS</code>, and a <strong>Sigstore keyless (cosign)</strong> signature. <strong><code>securegit compliance report</code></strong> maps findings to OWASP Top 10 (2021) via CWE and summarizes NIST SSDF v1.1 practice coverage \u2014 markdown or JSON, straight into a review packet.\n<em>Adopt this when procurement, audit, or a customer questionnaire forces the question.</em></p>\n<hr>\n<hr>\n<h3>What it is not</h3>\n<p>This section exists because mis-set expectations are the most common cause of abandonment, and every item below has caused a real \"wait, I thought it did X\" moment.</p>\n<p><strong>It is not a replacement for git.</strong> It wraps git. Your repositories stay normal git repositories. Your teammates who do not use SecureGit are unaffected. You can stop using it at any time and lose nothing but the receipts.</p>\n<p><strong>It is not a malware sandbox.</strong> It prevents <em>acquisition-time</em> execution and it scans. It does not analyze behavior, emulate, or detonate anything. Code you subsequently choose to build and run is code you chose to build and run.</p>\n<p><strong>It is not a SAST platform.</strong> It is not competing with Semgrep or SonarQube on depth of static analysis. It wraps them. If you need deep dataflow analysis, you need those tools, and SecureGit's job is to run them at the right moment and anchor their output.</p>\n<p><strong>It is not a secrets manager.</strong> It brokers access to secrets that live in a real secrets manager. It is not where your secrets should live. (Page 47.)</p>\n<p><strong>It is not a bundled vulnerability scanner</strong> \u2014 with one deliberate, narrow exception documented on page 40. It anchors the output of the scanners you already run. That boundary was a decision, it was contested internally, and page 40 explains why it moved once and why it will not move again casually.</p>\n<p><strong>It does not make your history trustworthy retroactively.</strong> Commits made before you adopted it have no receipts. There is a path for retro-attestation, and it is honest about being a different tier of evidence. A receipt that says \"we checked afterward\" is worth less than one that says \"we watched it happen,\" and the system records which one you have.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The three-word test</h4><p>Whenever you are unsure whether SecureGit is involved in something, ask: <strong>is this a boundary crossing?</strong></p>\n<p>Code arriving from elsewhere \u2014 yes. Code leaving to shared infrastructure \u2014 yes. Anything purely local, in your working tree, between you and your own disk \u2014 no.</p>\n<p><code>status</code>, <code>log</code>, <code>diff</code>, <code>add</code>, <code>checkout</code>, <code>branch</code>, <code>stash</code> cross no boundary and emit no receipts. That is deliberate. A tool that instruments everything gets turned off.</p></aside></div>\n</section>\n\n\n<section class=\"section-opener\">\n  <div class=\"section-opener-part\">PART ONE</div>\n  <div class=\"section-opener-name\">DAY ONE</div>\n  <div class=\"opener-line\">Ten minutes to your first scan.\nAn evening to a changed workflow.</div><div class=\"opener-line\">Nothing on these pages requires\na decision from anyone but you.</div>\n  <div class=\"section-opener-foot\">11 \u2192 23</div>\n</section>\n\n\n<section class=\"quickstart\">\n  <div class=\"quickstart-header\">\n    <div class=\"small caps signal\">Page 11 \u00b7 Ten-Minute Quickstart</div>\n    <h1 class=\"quickstart-headline\">TEN MINUTES</h1>\n    <div class=\"deck\">Five commands. No configuration. No account. Nothing to uninstall afterward except one binary.</div>\n  </div>\n  <div class=\"quickstart-steps\">\n    <div class=\"quickstart-step\">  <div class=\"quickstart-num\">1</div>  <div class=\"quickstart-content\">    <h3 class=\"quickstart-title\">Install</h3>    <pre><code>curl -fsSL https://<span class=\"placeholder\">&lt;release-host&gt;</span>/securegit/install.sh | sh\n</code></pre>\n<p><strong>Before you paste that:</strong> you are about to pipe a script from the internet into a shell, in an issue about not trusting code from the internet. We are aware of the irony and you should be too. If you would rather not, download the release binary, verify it with the shipped <code>SHA256SUMS</code> + <strong>Sigstore keyless signature (cosign)</strong>, and put it on your <code>PATH</code>. Both paths are supported and the second one is the one we would pick. Page 12 has the verify commands.</p>\n<p>Verify:</p>\n<pre><code>securegit --version\n</code></pre>  </div></div>\n<div class=\"quickstart-step\">  <div class=\"quickstart-num\">2</div>  <div class=\"quickstart-content\">    <h3 class=\"quickstart-title\">Acquire something real</h3>    <p>Pick a public repository you have never inspected. Small is better for a first run.</p>\n<pre><code>securegit acquire https://github.com/toml-lang/toml /tmp/first-acquire\n</code></pre>  </div></div>\n<div class=\"quickstart-step\">  <div class=\"quickstart-num\">3</div>  <div class=\"quickstart-content\">    <h3 class=\"quickstart-title\">Confirm the hooks are gone</h3>    <pre><code>ls -la /tmp/first-acquire/.git/hooks\n</code></pre>\n<p><strong>This should be empty.</strong> That is the whole product in one directory listing. A normal clone of the same repository will have sample hooks in there and, from a hostile source, could have live ones.</p>  </div></div>\n<div class=\"quickstart-step\">  <div class=\"quickstart-num\">4</div>  <div class=\"quickstart-content\">    <h3 class=\"quickstart-title\">Read the report</h3>    <pre><code>cat /tmp/first-acquire/.securegit-report.json\n</code></pre>  </div></div>\n<div class=\"quickstart-step\">  <div class=\"quickstart-num\">5</div>  <div class=\"quickstart-content\">    <h3 class=\"quickstart-title\">Scan something you care about</h3>    <pre><code>securegit scan . --fail-on high\n</code></pre>\n<p>Run this in a repository you actually work in. It will finish in seconds on a normal project.</p>\n<hr>  </div></div>\n  </div>\n  <div class=\"reversed foot-block\"><blockquote>\n<p><strong>You are done.</strong> That is the tool. Everything after this page makes it faster, quieter, and more useful \u2014 but you have already prevented the class of problem this issue is about.</p>\n<p>If step 5 produced findings you disagree with, that is expected and it is not a failure. Go to page 60.</p>\n</blockquote>\n<hr></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 12\u201313</div>\n  <h1>GETTING IT ON YOUR MACHINE</h1>\n  <div class=\"deck\">Longer than the quickstart, with the parts that go wrong.</div>\n  <div class=\"two-col\"><hr>\n<h3>Platforms</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> on Linux and macOS, both Intel and Apple Silicon. Windows support is <strong>experimental</strong> \u2014 it works, and it is not yet held to the same bar, and you should expect rough edges around path handling and shell integration. If you are on Windows and this is a blocker, tell us; that feedback moves priority.</p>\n<p>Container use is supported and is the recommended way to try it if you do not want a binary on your host.</p>\n<h3>The three install paths</h3>\n<p><strong>Path A \u2014 release binary.</strong> Download, verify, place on <code>PATH</code>. Most auditable. Recommended for anyone who reads the rest of this magazine and takes it seriously. Every release ships with a <strong>CycloneDX SBOM</strong>, <code>SHA256SUMS</code>, and a <strong>Sigstore keyless signature (cosign)</strong>:</p>\n<pre><code>sha256sum -c SHA256SUMS --ignore-missing\ncosign verify-blob \\\n  --certificate SHA256SUMS.pem --signature SHA256SUMS.sig \\\n  --certificate-identity-regexp 'github.com/armyknifelabs-tools/securegit' \\\n  --certificate-oidc-issuer https://token.actions.githubusercontent.com \\\n  SHA256SUMS\n</code></pre>\n<p><strong>Path B \u2014 install script.</strong> Fastest. Reasonable for a laptop, not for a fleet.</p>\n<p><strong>Path C \u2014 build from source.</strong> Requires a Rust toolchain. Slowest, most transparent. Use this if your organization requires building security tooling from source, which some do.</p>\n<p><strong>For a fleet:</strong> do not use Path B. Package it, sign it, and distribute it the way you distribute everything else. A security tool installed by curl-pipe-shell across two hundred machines is a supply-chain finding of its own, and somebody will eventually point that out in a review. Distribute a system-wide <code>/etc/securegit/config.toml</code> at the same time \u2014 that is where org policy lives (page 45).</p>\n<h3>What gets created</h3>\n<pre><code>~/.config/securegit/config.toml\n~/.config/securegit/plugins/\n~/.local/share/securegit/audit/           # tamper-evident audit log\n</code></pre>\n<p>No daemon, no service, no background process, nothing in your login items. Uninstall is deleting the binary and those directories.</p>\n<h3>Corporate networks</h3>\n<p>SecureGit reads the OS trust store by default, so a TLS-inspecting corporate proxy with a properly installed root CA \u201cjust works.\u201d For private CAs held in a file rather than the trust store, point <code>SECUREGIT_CA_BUNDLE=/etc/ssl/corp-bundle.pem</code> at a PEM bundle (mirroring <code>curl --cacert</code>). Explicit proxying uses standard <code>HTTP_PROXY</code>/<code>HTTPS_PROXY</code>/<code>NO_PROXY</code>, plus <code>SECUREGIT_PROXY</code> (or <code>[network] proxy</code>) as an override. These cover the two failure modes we hear about most from enterprise pilots.</p>\n<p><strong>This matters for adoption more than it sounds.</strong> A tool that is trivially reversible gets tried. A tool that installs infrastructure gets a meeting.</p>\n<hr>\n<h3>Your first acquire, in detail</h3>\n<pre><code>securegit acquire <span class=\"placeholder\">&lt;url&gt;</span> <span class=\"placeholder\">&lt;destination&gt;</span>\n</code></pre>\n<p>You can also acquire into the current directory:</p>\n<pre><code>securegit acquire <span class=\"placeholder\">&lt;url&gt;</span> .\n</code></pre>\n<p><strong>What happens, in order:</strong></p>\n<ol>\n<li>The remote is resolved and the content is fetched <strong>as an archive</strong>, not as a live repository. This is the load-bearing step. No git configuration is active at any point during transfer.</li>\n<li>The archive is extracted to the destination.</li>\n<li><strong>Hooks are stripped.</strong> Not disabled, not renamed \u2014 removed.</li>\n<li>The configured scanners run across the extracted tree.</li>\n<li>A report is written to <code>.securegit-report.json</code> in the destination.</li>\n<li>The tree is converted into a normal git repository, with history.</li>\n<li><span class=\"badge shipped\">SHIPPED</span> A chain receipt is emitted recording the remote URL and the resolved <code>HEAD</code> at acquisition time. Acquisition never blocks on receipt failure \u2014 if the signing daemon is unreachable, you get a warning and your clone.</li>\n</ol>\n<p><strong>Point 7 deserves a note.</strong> The receipt records what you got, from where, at what commit, at what time. Later, if the upstream force-pushes and history changes underneath you, your receipt still says what you actually received. That is a surprisingly practical thing to have and it costs nothing at acquisition time.</p>\n<h3>What you can do with the result</h3>\n<p>Everything. It is a normal repository.</p>\n<pre><code>cd /tmp/first-acquire\ngit log\ngit diff\ngit checkout -b my-branch\n</code></pre>\n<p>There is no lock-in, no proprietary format, no wrapper you have to keep using. If you delete SecureGit tomorrow, the repository you acquired is unaffected.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The three things that go wrong on first install</h4><p><strong>\"Command not found\" after the install script.</strong> The binary landed somewhere not on your <code>PATH</code>. Check <code>~/.local/bin</code> and <code>/usr/local/bin</code>, then restart your shell.</p>\n<p><strong>Acquire is slow on a very large repository.</strong> Archive-first fetch has different performance characteristics than a shallow clone. For repositories in the hundreds of megabytes, expect seconds, not milliseconds, for acquisition and then a separate scan pass. Page 20 has numbers.</p>\n<p><strong>A private repository fails to acquire.</strong> You have not registered credentials yet. Page 49. For a first run, use a public repository \u2014 do not start your evaluation by debugging auth.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 14\u201315</div>\n  <h1>WHAT THE SCANNER SEES</h1>\n  <div class=\"deck\">Findings are only useful if you can act on them within about ten seconds of reading them. Here is how to get to that.</div>\n  <div class=\"two-col\"><hr>\n<h3>The command surface</h3>\n<pre><code>securegit scan <span class=\"placeholder\">&lt;path&gt;</span>\nsecuregit scan .\nsecuregit scan . --fail-on high\nsecuregit scan . --min-severity high\nsecuregit scan . --include-git\nsecuregit scan . --format json\nsecuregit scan . --format sarif  --report-output f.sarif\nsecuregit scan . --format gitlab --report-output gl.json\nsecuregit scan --staged\nsecuregit scan . --write-baseline &quot;<span class=\"placeholder\">&lt;reason&gt;</span>&quot;\nsecuregit scan . --baseline .securegit/baseline.json\n</code></pre>\n<p><code>--include-git</code> deserves emphasis. Most scanners in the industry exclude <code>.git</code> by default, and the acquisition threat this magazine opens with lives precisely there. When you are evaluating an unfamiliar repository, include it.</p>\n<p><code>--format sarif</code> and <code>--format gitlab</code> upload directly into the dashboards your reviewers already read \u2014 GitHub Advanced Security, Azure DevOps, SonarQube, DefectDojo on one side; the GitLab security dashboard and MR widget on the other. <code>--report-output</code> writes the report even when the scan gates, so one CI step can both report <em>and</em> enforce. Page 44 is a full worked example.</p>\n<h3>The twelve built-in scanners</h3>\n<p>All <span class=\"badge shipped\">SHIPPED</span>, all compiled into the binary, all sub-millisecond to low-millisecond per file. Load automatically; no configuration required.</p>\n<table>\n<thead>\n<tr>\n<th>Scanner</th>\n<th>Looks for</th>\n<th>Typical severity</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>secrets</code></td>\n<td>AWS keys, GitHub/Slack tokens, private keys, DB passwords, OpenAI keys</td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>patterns</code></td>\n<td>Dynamic execution, shell injection, reverse shells, persistence indicators</td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>entropy</code></td>\n<td>High-entropy strings that look like keys but match no known format</td>\n<td>High / Medium</td>\n</tr>\n<tr>\n<td><code>binary</code></td>\n<td>Unexpected ELF, PE, Mach-O executables in source trees</td>\n<td>Critical / Medium</td>\n</tr>\n<tr>\n<td><code>encoding</code></td>\n<td><strong>Trojan Source (CVE-2021-42574)</strong> \u2014 BiDi overrides, homoglyphs, zero-width chars</td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>supply-chain</code></td>\n<td>36 known typosquatted packages (npm/PyPI/Ruby), malicious lifecycle hooks, dependency confusion</td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>ci-cd</code></td>\n<td><code>pull_request_target</code> abuse, unpinned actions, input injection into <code>run:</code>, cache poisoning, <code>curl\\|bash</code> in pipelines, secret exfiltration, Jenkins <code>@Grab</code></td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>container</code></td>\n<td>Privileged pods, Docker socket mounts, <code>cap_add: ALL</code>, RBAC wildcards, host namespaces</td>\n<td>Critical / Medium</td>\n</tr>\n<tr>\n<td><code>iac</code></td>\n<td>Open security groups, public S3 buckets, <code>local-exec</code> provisioners, Ansible shell pipes</td>\n<td>Critical / Medium</td>\n</tr>\n<tr>\n<td><code>deserialization</code></td>\n<td>Python <code>pickle</code>/<code>marshal</code>, <code>yaml.load()</code> without SafeLoader, Java <code>ObjectInputStream</code>, XXE</td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>dangerous-files</code></td>\n<td><code>.gitmodules</code> path traversal (CVE-2018-17456), fsmonitor hooks, filter drivers</td>\n<td>Critical / High</td>\n</tr>\n<tr>\n<td><code>git-internals</code></td>\n<td>Unexpected hooks after sanitization, dangerous git config keys</td>\n<td>Critical / High</td>\n</tr>\n</tbody>\n</table>\n<p><strong><code>entropy</code> is the one that produces the most false positives</strong>, by a wide margin, and it is also the one that catches the credentials that no pattern list knows about yet. That is the trade and it is not resolvable \u2014 you tune it, you do not fix it. Page 60.</p>\n<p><strong><code>encoding</code> and <code>dangerous-files</code> are the ones that will most surprise you the first time they fire.</strong> Trojan Source findings look wrong until you view the file with an editor that shows invisible codepoints; <code>.gitmodules</code> findings are usually benign in your own repos and are the whole reason <code>--include-git</code> exists for someone else's.</p>\n<h3>Anatomy of a finding</h3>\n<pre><code>CRITICAL  secrets           src/config/settings.py:14\n          AWS Access Key\n          AKIA\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\n          \u2192 Rotate this credential, then remove from history.\n</code></pre>\n<p><strong>Severity</strong> \u2014 <code>critical</code>, <code>high</code>, <code>medium</code>, <code>low</code>. Drives <code>--fail-on</code> and <code>--min-severity</code>.</p>\n<p><strong>Scanner</strong> \u2014 which plugin found it. Useful because it tells you the false-positive profile immediately. A finding from <code>secrets</code> is usually real. A finding from <code>entropy</code> needs a human look.</p>\n<p><strong>Location</strong> \u2014 file and line. Always. A finding without a location is a bug.</p>\n<p><strong>The redacted value</strong> \u2014 enough to identify it, never enough to use it. <strong>Findings never print full secret values</strong>, including in JSON output. This matters because scan output ends up in CI logs, which are frequently more readable than the repository was.</p>\n<p><strong>The action</strong> \u2014 what to do. A finding that does not tell you what to do is a notification, not a finding.</p>\n<hr>\n<h3>The ten-second triage</h3>\n<p>For each finding, in order:</p>\n<ol>\n<li><strong>Is it in a test fixture or an example?</strong> Very common, usually benign, and the reason <code>--skip-paths</code> exists. But check that the example key is actually fake \u2014 real keys get pasted into examples more often than anyone admits.</li>\n<li><strong>Is it a real credential?</strong> If yes, stop reading this magazine. Rotate it. Removing it from the file is not sufficient; it is in history, and if the repository was ever public or ever shared, assume compromise. Rotation is the only remediation.</li>\n<li><strong>Is it high entropy but not a secret?</strong> A hash, a test vector, a base64 asset, a minified bundle. Suppress it by path, not by disabling the scanner.</li>\n<li><strong>Is it a pattern finding you disagree with?</strong> Read the line. <code>patterns</code> findings are usually about dynamic execution, and \"I know what I'm doing here\" is sometimes correct and sometimes the exact sentence that precedes an incident.</li>\n</ol>\n<hr>\n<aside class=\"sidebar\"><h4>The first scan is always the worst one</h4><p>Your first scan of a mature repository will produce more findings than every subsequent scan combined. Years of accumulated fixtures, examples, vendored code, and one or two genuine problems.</p>\n<p><strong>Do not try to get to zero on day one.</strong> The correct first move is:</p>\n<ol>\n<li>Run it. Look at the <code>critical</code> findings only. There are usually few.</li>\n<li>Fix or rotate anything real.</li>\n<li>Set <code>--fail-on high</code> and configure <code>skip_paths</code> for vendor and dependency directories.</li>\n<li><strong>From this point forward, gate on new findings, not total findings.</strong></li>\n</ol>\n<p>Teams that try to reach zero before adopting never adopt. Teams that gate the delta and burn down the backlog over a quarter succeed almost every time.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 16\u201317</div>\n  <h1>MUSCLE MEMORY</h1>\n  <div class=\"deck\">The single biggest predictor of whether you are still using this in a month is whether typing it costs you anything. Spend thirty seconds now.</div>\n  <div class=\"two-col\"><hr>\n<p>The command is called <code>securegit</code> because the name should be unambiguous when someone reads it in a script, a runbook, or a CI file six months from now. It is not meant to be typed nine characters at a time, forty times a day.</p>\n<p><strong>Alias it. Immediately. Before you do anything else in this issue.</strong></p>\n<h3>Bash / Zsh</h3>\n<p>Add to <code>~/.bashrc</code> or <code>~/.zshrc</code>:</p>\n<pre><code>alias sgit='securegit'\nalias sg='securegit'\n</code></pre>\n<h3>Fish</h3>\n<p>Add to <code>~/.config/fish/config.fish</code>:</p>\n<pre><code>alias sgit='securegit'\nalias sg='securegit'\n</code></pre>\n<h3>PowerShell</h3>\n<p>Add to your profile:</p>\n<pre><code>Set-Alias -Name sgit -Value securegit\nSet-Alias -Name sg -Value securegit\n</code></pre>\n<h3>Tab completion follows the alias</h3>\n<pre><code># bash\ncomplete -F _securegit sgit\ncomplete -F _securegit sg\n\n# zsh\ncompdef sgit=securegit\ncompdef sg=securegit\n</code></pre>\n<p>Do this. An alias without completion is worse than no alias, because you lose discoverability and you will forget the flags.</p>\n<hr>\n<h3>The workflow alias set</h3>\n<p>This is the part that actually changes behavior. Create <code>~/.securegit_aliases</code>:</p>\n<pre><code># Acquire instead of clone, with a name that reminds you why\nalias safe-clone='securegit acquire'\n\n# Scan right here\nalias scan-here='securegit scan .'\n\n# Scan including repository metadata \u2014 for unfamiliar code\nalias scan-deep='securegit scan . --include-git'\n\n# Scan only what you are about to commit\nalias scan-staged='securegit scan --staged --fail-on high'\n\n# Scan only what changed against your main branch\nalias scan-diff='git diff main --name-only | xargs securegit scan'\n\n# Skip the usual noise\nalias scan-clean='securegit scan . --skip-paths &quot;**/node_modules/**,**/vendor/**&quot;'\n\n# Plugin housekeeping\nalias plugin-status='securegit plugin list &amp;&amp; securegit plugin check-updates'\n</code></pre>\n<p>Source it:</p>\n<pre><code>echo &quot;source ~/.securegit_aliases&quot; &gt;&gt; ~/.bashrc\n</code></pre>\n<p><strong><code>scan-diff</code> is the one you will use most</strong> and it is the one people discover last. Scanning only what changed against your main branch turns a multi-second operation into a sub-second one and makes the whole habit sustainable on large repositories.</p>\n<h3>Environment defaults</h3>\n<p>Set these once and stop passing flags:</p>\n<pre><code>export SECUREGIT_FAIL_ON=high\nexport SECUREGIT_SKIP_PATHS=&quot;**/node_modules/**:**/vendor/**:**/target/**:**/dist/**&quot;\n</code></pre>\n<hr>\n<aside class=\"sidebar\"><h4>On the name</h4><p>We have been asked, repeatedly, whether the binary should be called <code>sgit</code> or <code>safegit</code> or something shorter.</p>\n<p>The current answer is no, and the reasoning is worth stating because it is a general principle. <strong>Optimize the written name for the reader; optimize the typed name for the typist.</strong> A script, a CI file, and a runbook are read far more often than they are written, and by people with less context than the author had. <code>securegit scan . --fail-on high</code> is self-explaining to someone who has never heard of the tool. <code>sg scan . --fail-on high</code> is not.</p>\n<p>Your shell is yours. Alias it to a single character if you like. The canonical name stays long on purpose.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 18\u201319</div>\n  <h1>YOUR FIRST GUARDRAIL</h1>\n  <div class=\"deck\">One file, six lines. This is the point where SecureGit stops being a thing you remember to run and starts being a thing that protects you when you forget.</div>\n  <div class=\"two-col\"><hr>\n<p>Everything up to this page has been manual. Manual controls work exactly as long as your attention does, which is to say not on the Friday afternoon when it matters.</p>\n<p>The pre-commit hook is where that changes.</p>\n<h3>The hook</h3>\n<p>Create <code>.git/hooks/pre-commit</code>:</p>\n<pre><code>#!/bin/bash\nsecuregit scan --staged --fail-on high || {\n  echo &quot;SecureGit: staged changes contain high-severity findings. Commit blocked.&quot;\n  echo &quot;Review with: securegit scan --staged&quot;\n  echo &quot;Override once with: git commit --no-verify&quot;\n  exit 1\n}\n</code></pre>\n<p>Make it executable:</p>\n<pre><code>chmod +x .git/hooks/pre-commit\n</code></pre>\n<p><strong>Three deliberate choices in those six lines, and they are all about adoption rather than security.</strong></p>\n<p><strong>It scans only staged changes.</strong> Not the repository. Staged-only keeps it fast \u2014 typically well under a second \u2014 which is the difference between a hook you keep and a hook you delete in week two.</p>\n<p><strong>It fails at <code>high</code>, not <code>medium</code>.</strong> Start permissive. You can tighten later, and tightening a working gate is easy. Loosening a gate that everyone has learned to bypass is nearly impossible, because you have already taught the team that the gate is noise.</p>\n<p><strong>It tells you how to override.</strong> <code>--no-verify</code> is printed right there. This looks like a weakness and it is the opposite. A gate with no visible escape hatch gets bypassed by a <em>global</em> mechanism \u2014 someone disables hooks entirely, or removes the file, and now you have no gate at all and no signal. A gate with a visible per-commit override gets bypassed once, deliberately, in a way that is visible in the reflog and in the person's memory.</p>\n<hr>\n<div class=\"pullquote\">A gate with no visible escape hatch does not get respected. It gets removed. Print the override.</div>\n<hr>\n<h3>Distributing hooks to a team</h3>\n<p><code>.git/hooks</code> is not version controlled, which is a genuine problem and not one SecureGit invented.</p>\n<p>Three options, in increasing order of robustness:</p>\n<p><strong>A \u00b7 A setup script in the repository.</strong> <code>scripts/setup-hooks.sh</code>, run once by each developer, documented in the README. Simple, and it depends on people running it.</p>\n<p><strong>B \u00b7 <code>core.hooksPath</code>.</strong> Point git at a version-controlled directory:</p>\n<pre><code>git config core.hooksPath .githooks\n</code></pre>\n<p>Commit <code>.githooks/pre-commit</code>. Now the hook travels with the repository. Still requires the one-time config, but it is a single command and it can go in your onboarding script.</p>\n<p><strong>C \u00b7 Managed configuration.</strong> Push <code>core.hooksPath</code> and SecureGit policy through whatever manages developer workstations. This is the enterprise answer and it is covered on page 55.</p>\n<p><strong>Do not rely on option A alone past about five engineers.</strong> The failure is silent: someone joins, never runs the script, and is unprotected while believing they are protected. That is worse than no hook, because the team's collective sense of coverage is now wrong.</p>\n<h3>The pre-push hook</h3>\n<p>Same idea, wider net, runs less often:</p>\n<pre><code>#!/bin/bash\nsecuregit scan --fail-on critical || exit 1\n</code></pre>\n<p>Push happens less frequently than commit, so you can afford a broader scan. Note the threshold is <code>critical</code> here rather than <code>high</code> \u2014 a push gate that blocks frequently gets bypassed with <code>--no-verify</code> reflexively, and then it protects nothing.</p>\n<p><strong>The general rule: the more disruptive the gate, the higher the bar for tripping it.</strong></p>\n<hr>\n<aside class=\"sidebar\"><h4>What to do the first time it blocks you</h4><p>It will block you, and the first time will be at a bad moment. That is when adoption is decided.</p>\n<p><strong>Do not reach for <code>--no-verify</code> reflexively.</strong> Take ninety seconds:</p>\n<p><code>securegit scan --staged</code> \u2014 read the actual finding.</p>\n<p><strong>If it is real:</strong> you were about to commit a credential. The hook just did the single most valuable thing it will ever do for you. Rotate and move on.</p>\n<p><strong>If it is a false positive:</strong> add a <code>skip_paths</code> entry or narrow the scanner set. Fix it <em>now</em>, in that moment, while it is annoying \u2014 because the alternative is that you <code>--no-verify</code> past it every day for a month and eventually delete the hook.</p>\n<p>The failure mode of security tooling is never a dramatic bypass. It is quiet, incremental erosion by people who were busy.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 20\u201321</div>\n  <h1>THE HONEST PERFORMANCE PAGE</h1>\n  <div class=\"deck\">Yes, somewhat, in specific places. Here is exactly where, with numbers, so you can decide rather than find out.</div>\n  <div class=\"two-col\"><hr>\n<p>Performance is where security tooling adoption actually dies. Not in the evaluation, where everyone is patient. In week three, when a scan adds nine seconds to a loop somebody runs forty times a day.</p>\n<p>So: the numbers.</p>\n<h3>Single file</h3>\n<p>A small source file, all twelve built-in scanners plus one external plugin:</p>\n<table>\n<thead>\n<tr>\n<th>Scanner</th>\n<th>Findings</th>\n<th>Duration</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>secrets</code> / <code>patterns</code> / <code>entropy</code> (built-in)</td>\n<td>6</td>\n<td>~5 ms combined</td>\n</tr>\n<tr>\n<td><code>encoding</code> / <code>supply-chain</code> / <code>ci-cd</code> / <code>container</code> / <code>iac</code> (built-in)</td>\n<td>0</td>\n<td>~4 ms combined</td>\n</tr>\n<tr>\n<td><code>deserialization</code> / <code>dangerous-files</code> / <code>git-internals</code> / <code>binary</code> (built-in)</td>\n<td>0</td>\n<td>~3 ms combined</td>\n</tr>\n<tr>\n<td>External plugin (Python)</td>\n<td>3</td>\n<td>~23 ms</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Total: about 35 ms</strong>, of which the external plugin is roughly two-thirds.</p>\n<p>That ratio is the whole performance story of this tool, and it repeats at every scale.</p>\n<h3>Throughput</h3>\n<table>\n<thead>\n<tr>\n<th>Plugin type</th>\n<th>Files per second</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Built-in (Rust, in-process)</td>\n<td>~1,000\u20135,000</td>\n</tr>\n<tr>\n<td>External (subprocess)</td>\n<td>~50\u2013200</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Built-in scanners are between ten and a hundred times faster than external ones</strong>, because an external plugin pays process-spawn cost \u2014 roughly 20\u201325 ms for an interpreted tool, 5\u201310 ms for a compiled one \u2014 on every invocation.</p>\n<h3>Large repository</h3>\n<p>A substantial open-source repository, several hundred megabytes, five thousand-plus files:</p>\n<table>\n<thead>\n<tr>\n<th>Phase</th>\n<th>Time</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Acquisition</td>\n<td>~2.5 s</td>\n</tr>\n<tr>\n<td>Extraction</td>\n<td>~1.8 s</td>\n</tr>\n<tr>\n<td>Scan, all 12 built-in scanners</td>\n<td>~35 s</td>\n</tr>\n<tr>\n<td>Scan, with ~10 external plugins</td>\n<td>~2\u20133 min</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Read those last two rows carefully.</strong> Built-in scanners scan a large repository in about half a minute. Adding ten external plugins makes it four to six times slower. That is the trade, stated plainly.</p>\n<h3>Resource use</h3>\n<table>\n<thead>\n<tr>\n<th>Scale</th>\n<th>CPU</th>\n<th>Memory</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Under 100 files</td>\n<td>1 core, under 10%</td>\n<td>under 50 MB</td>\n</tr>\n<tr>\n<td>5,000+ files</td>\n<td>1\u20134 cores, 40\u201380%</td>\n<td>100\u2013500 MB</td>\n</tr>\n<tr>\n<td>Per external plugin</td>\n<td>process overhead</td>\n<td>+10\u201350 MB each</td>\n</tr>\n</tbody>\n</table>\n<hr>\n<div class=\"pullquote\">Built-in scanners scan a large repository in about half a minute. Ten external plugins make it four to six times slower. Choose your plugins like you choose dependencies.</div>\n<hr>\n<h3>The five rules that keep it fast</h3>\n<p><strong>1 \u00b7 Scan the diff, not the tree.</strong> In daily work you almost never need a full scan.</p>\n<pre><code>git diff main --name-only | xargs securegit scan\nsecuregit scan --staged\n</code></pre>\n<p>This is the single highest-leverage habit in this issue.</p>\n<p><strong>2 \u00b7 Configure <code>skip_paths</code> once, properly.</strong> <code>node_modules</code>, <code>vendor</code>, <code>target</code>, <code>dist</code>, <code>build</code>, <code>.venv</code>, and whatever your ecosystem's equivalent is. Most first-time \"this is slow\" reports are a scan of a dependency directory.</p>\n<p><strong>3 \u00b7 Built-in scanners for the hot path.</strong> Pre-commit hooks and CI gates should run built-in scanners only. Save the external plugins for scheduled deep scans.</p>\n<p><strong>4 \u00b7 Match the plugin to the stack.</strong> A Python security scanner on a Rust repository costs you process-spawn time on every file and finds nothing. Enable only what applies.</p>\n<p><strong>5 \u00b7 Deep scan on a schedule, not on every commit.</strong> Nightly or per-PR full scans with the whole plugin set; fast built-in scans inline.</p>\n<h3>What is not fast yet, and is known</h3>\n<p>Stated so you can plan rather than discover:</p>\n<ul>\n<li><strong>File walking is sequential.</strong> Plugins run concurrently per file, but the walker itself is single-threaded. <span class=\"badge planned\">PLANNED</span> \u2014 parallel file processing, expected to be a multiple-times speedup on large trees.</li>\n<li><strong>There is no incremental cache.</strong> Every scan rescans everything in scope. <span class=\"badge planned\">PLANNED</span> \u2014 hash-based incremental scanning, which would be a large win on repeat scans.</li>\n<li><strong>No <code>--jobs</code> flag yet</strong> to control parallelism explicitly. <span class=\"badge planned\">PLANNED</span>.</li>\n<li><strong>Very large repositories (over a gigabyte) are slow.</strong> Known. Use <code>--skip-paths</code> aggressively and scan the diff.</li>\n<li><strong>First run of an external plugin may fetch a binary.</strong> One-time delay, surprising if unexpected.</li>\n</ul>\n<p><strong>None of that is hidden and none of it is fixed today.</strong> If your evaluation depends on incremental scanning, it is <span class=\"badge planned\">PLANNED</span>, and you should plan for the current behavior.</p>\n<hr>\n<aside class=\"sidebar\"><h4>How this compares</h4><p>Honest positioning against tools you may already run:</p>\n<p><strong>Gitleaks</strong> \u2014 very fast, secrets only. SecureGit's built-in <code>secrets</code> scanner is in the same performance class and the same scope. If secrets are all you need and you already run gitleaks, you do not need SecureGit for that. You might still want it for acquisition.</p>\n<p><strong>Semgrep</strong> \u2014 moderate speed, much deeper pattern analysis. <strong>Not a competitor.</strong> Wrap it as an external plugin. SecureGit runs it at the right moment and anchors its output.</p>\n<p><strong>SonarQube</strong> \u2014 slow, comprehensive, a different category entirely. Complementary. SonarQube analyzes code quality and security in depth; SecureGit secures the acquisition boundary and the commit gate.</p>\n<p><strong>The general rule:</strong> SecureGit is an acquisition and boundary layer that happens to include fast scanning. It is not trying to win a static-analysis depth comparison, and a vendor who tells you their tool wins every comparison is telling you something about the vendor.</p></aside></div>\n</section>\n\n\n<section class=\"checklist-page\">\n  <div class=\"small caps signal\">Pages 22\u201323 \u00b7 Day One Checklist</div>\n  <h1>WHERE YOU SHOULD BE</h1>\n  <div class=\"deck\">If you have worked through Part I, this is your state. Tick it off honestly \u2014 the gaps are where you will fail next week.</div>\n  <div class=\"checklist-body\"><hr>\n<h3>Installed and verified</h3>\n<ul>\n<li>[ ] <code>securegit --version</code> returns a version</li>\n<li>[ ] <code>~/.config/securegit/</code> exists</li>\n<li>[ ] I know how to uninstall it (delete binary + that directory)</li>\n</ul>\n<h3>Acquisition</h3>\n<ul>\n<li>[ ] I have acquired at least one repository</li>\n<li>[ ] I checked <code>.git/hooks</code> and confirmed it was empty</li>\n<li>[ ] I read a <code>.securegit-report.json</code></li>\n<li>[ ] <code>safe-clone</code> is aliased and I have used it once instead of <code>git clone</code></li>\n</ul>\n<h3>Scanning</h3>\n<ul>\n<li>[ ] I have scanned a repository I actually work in</li>\n<li>[ ] I looked at every <code>critical</code> finding</li>\n<li>[ ] I rotated anything real (or confirmed there was nothing real)</li>\n<li>[ ] <code>skip_paths</code> is configured for my dependency directories</li>\n<li>[ ] I have run <code>scan-diff</code> at least once and seen how fast it is</li>\n</ul>\n<h3>Muscle memory</h3>\n<ul>\n<li>[ ] <code>sgit</code> (or equivalent) is aliased</li>\n<li>[ ] Tab completion works on the alias</li>\n<li>[ ] <code>~/.securegit_aliases</code> exists and is sourced</li>\n<li>[ ] <code>SECUREGIT_FAIL_ON</code> and <code>SECUREGIT_SKIP_PATHS</code> are set</li>\n</ul>\n<h3>The guardrail</h3>\n<ul>\n<li>[ ] A pre-commit hook is installed in at least one repository</li>\n<li>[ ] I have seen it pass</li>\n<li>[ ] I know how to override it (<code>--no-verify</code>) and why I should not do so reflexively</li>\n</ul>\n<hr>\n<h2>WHEN IT GOES WRONG</h2>\n<p><strong>\"It found 400 things and I stopped reading.\"</strong>\nYou scanned a dependency directory. Configure <code>skip_paths</code>, rescan, and look at <code>critical</code> only. Then gate on new findings rather than total findings. Page 15.</p>\n<p><strong>\"The scan takes too long.\"</strong>\nYou are scanning the tree when you should be scanning the diff. <code>securegit scan --staged</code> or <code>git diff main --name-only | xargs securegit scan</code>. Page 20.</p>\n<p><strong>\"The hook blocks me constantly.\"</strong>\nYour threshold is too low or your <code>skip_paths</code> is wrong. Move to <code>--fail-on high</code>, fix the paths, and \u2014 importantly \u2014 fix it the first time it annoys you rather than the tenth. Page 19.</p>\n<p><strong>\"It won't acquire my private repository.\"</strong>\nCredentials are not registered. Page 49. Do not debug this during your first hour; use public repositories to evaluate.</p>\n<p><strong>\"My teammate doesn't have the hook.\"</strong>\n<code>.git/hooks</code> is not version controlled. Use <code>core.hooksPath</code> with a committed hook directory. Page 18.</p>\n<p><strong>\"I forgot to use it.\"</strong>\nExpected, and the actual reason most adoptions fail. The fix is not discipline. The fix is the pre-commit hook, which runs whether you remember or not, plus a shell alias short enough that <code>acquire</code> is not more expensive to type than <code>clone</code>.</p>\n<hr></div>\n  <div class=\"reversed foot-block\"><blockquote>\n<p><strong>You can stop here.</strong></p>\n<p>Part I is a complete, coherent adoption. Acquisition plus scanning plus a pre-commit hook is a real improvement in your security posture and it costs you almost nothing ongoing.</p>\n<p>Parts II through V are for when you want to understand <em>why</em> it works the way it does, prove things to other people, or bring a team along. None of it is required. A reader who stops at this page and keeps the habit has gotten more value than a reader who finishes the issue and installs nothing.</p>\n</blockquote>\n<hr></div>\n</section>\n\n\n<section class=\"section-opener\">\n  <div class=\"section-opener-part\">PART TWO</div>\n  <div class=\"section-opener-name\">THE MENTAL<br>MODEL</div>\n  <div class=\"opener-line\">Four mechanisms.\nWhy each one is shaped the way it is,\nand what it costs.</div>\n  <div class=\"section-opener-foot\">25 \u2192 37</div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 25\u201327</div>\n  <h1>ACQUIRE, NOT CLONE</h1>\n  <div class=\"deck\">The entire security argument rests on one ordering decision. Understanding it takes about four minutes and makes everything else in the tool obvious.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Mechanism 01</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span></p>\n<h2>Why archive-first defeats hooks</h2>\n<hr>\n<h3>The problem with clone, precisely</h3>\n<p><code>git clone</code> does several things that are individually reasonable and collectively a problem.</p>\n<p>It negotiates with a remote and transfers objects. Fine. It writes those objects into a <code>.git</code> directory. Fine. It <strong>materializes the repository's configuration and hook directory as live, on-disk state</strong>. And it checks out a working tree using path names that the remote controls.</p>\n<p>The trouble is that steps three and four happen before you have looked at anything. By the time <code>clone</code> returns success, executable configuration is resident on your filesystem, under your user, and the next git-adjacent command you run \u2014 or the next editor you open, or the next build you kick off \u2014 may execute it.</p>\n<p>There are mitigations. Modern git does not run hooks on clone by default; protocol restrictions exist; <code>core.hooksPath</code> can be constrained. All of that is good and none of it is a substitute for not having the executable content on disk in the first place, because mitigations are point fixes against known techniques and your fleet is always some distance behind the current release.</p>\n<h3>The reordering</h3>\n<p>SecureGit changes one thing: <strong>the order of operations.</strong></p>\n<pre><code>git clone:                fetch \u2192 materialize repo \u2192 (inspect never)\nsecuregit acquire:        fetch archive \u2192 strip \u2192 inspect \u2192 materialize repo\n</code></pre>\n<p>That is the whole mechanism. Everything else follows from it.</p>\n<p><strong>Fetch as an archive.</strong> The content arrives as inert bytes. There is no live repository during transfer, so there is nothing for repository configuration to act on.</p>\n<p><strong>Strip.</strong> Hooks are removed, not disabled. Removal is stronger than disabling because disabling is a configuration state and configuration states can be changed, inherited, or overridden.</p>\n<p><strong>Inspect.</strong> Scanners run on a tree that is not yet a repository. This is the moment that does not exist in the normal flow \u2014 code on your disk, inert, before it has been granted repository status.</p>\n<p><strong>Materialize.</strong> Now it becomes a real git repository. From this point it is indistinguishable from a cloned one, which is exactly what you want: no lock-in, no wrapper, no special format.</p>\n<hr>\n<div class=\"pullquote\">The order is the product. Fetch, strip, inspect, then confer git-ness. Clone confers git-ness first and inspects never.</div>\n<hr>\n<h3>What this does not protect you from</h3>\n<p>Stated clearly, because overclaiming here would be dishonest and would eventually be found out.</p>\n<p><strong>It does not make the code safe to run.</strong> If you acquire a repository, read the report, and then run <code>npm install &amp;&amp; npm start</code>, you have executed the code. That was your decision and it was outside the tool's scope.</p>\n<p><strong>It does not detect novel malware.</strong> The scanners look for known patterns, credentials, dangerous constructs, and anomalous entropy. A sophisticated, targeted implant designed to look like ordinary code will not be caught by pattern matching, and no tool in this category will catch it.</p>\n<p><strong>It does not protect against a compromised upstream you already trust.</strong> If a maintainer account is taken over and a malicious commit is published, acquisition works exactly as designed and delivers you the malicious commit \u2014 inertly, scanned, with a receipt. The receipt is actually useful afterward. The acquisition did not prevent it.</p>\n<p><strong>What it does do</strong> is close the window in which merely <em>obtaining</em> code can compromise you. That window is narrow, it is real, it is exploited, and it was previously unaddressed by anything in your workflow.</p>\n<h3>The report</h3>\n<p>Every acquisition writes <code>.securegit-report.json</code> into the destination.</p>\n<pre><code>.securegit-report.json\n</code></pre>\n<p>It records what was fetched, what was stripped, what the scanners found, and when. <strong>Read it the first ten times.</strong> After that you will have calibrated intuition for what a normal report looks like, and the abnormal one will stand out. That calibration is the actual skill; the file is just how you acquire it.</p>\n<h3>Acquisition and the chain</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Acquisition emits a chain receipt recording the remote URL and the resolved <code>HEAD</code> commit at the moment of acquisition.</p>\n<p><strong>Acquisition never blocks on the chain.</strong> If the signing daemon is unreachable, you get a warning and your repository. This is a deliberate policy choice: read-side operations warn, write-side operations block. A security tool that prevents you from obtaining code because a daemon is down will be uninstalled by lunchtime, and rightly.</p>\n<p>The receipt matters later. If upstream force-pushes and rewrites history, your receipt still records what you actually received, at what SHA, at what time. That is the difference between \"I think I cloned it in March\" and a signed statement about what arrived.</p>\n<hr>\n<aside class=\"sidebar\"><h4>When to use plain `git clone`</h4><p>There are cases where <code>acquire</code> is the wrong tool and you should say so out loud:</p>\n<p><strong>Your own repositories, on infrastructure you control.</strong> You wrote it. The acquisition threat model does not apply. Use <code>clone</code>.</p>\n<p><strong>Extremely large repositories where you need a shallow or partial clone.</strong> Archive-first fetch has different characteristics. If you need <code>--depth 1</code> on a multi-gigabyte monorepo, use git and scan afterward.</p>\n<p><strong>Anything inside a build system that expects <code>git clone</code> semantics.</strong> Do not fight your toolchain. Scan the result instead.</p>\n<p>A tool that claims to be correct in every situation is a tool whose recommendations you should discount. <code>acquire</code> is for code you did not write, from sources you have not audited. That is a large and growing category, and it is not everything.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 28\u201330</div>\n  <h1>SCAN</h1>\n  <div class=\"deck\">The architecture is a deliberate two-tier split: a fast core of twelve scanners that always runs, and a slow periphery you opt into. Understanding which tier you are in explains every performance question you will have.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Mechanism 02</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span></p>\n<h2>Twelve scanners, and a ladder you climb slowly</h2>\n<hr>\n<h3>The two tiers</h3>\n<p><strong>Built-in (Rust, in-process).</strong> Twelve scanners compiled into the binary \u2014 <code>secrets</code>, <code>patterns</code>, <code>entropy</code>, <code>binary</code>, <code>encoding</code> (Trojan Source), <code>supply-chain</code>, <code>ci-cd</code>, <code>container</code>, <code>iac</code>, <code>deserialization</code>, <code>dangerous-files</code>, <code>git-internals</code>. Zero startup cost, shared runtime, direct memory access to file contents, sub-millisecond to low-millisecond per file. These always run.</p>\n<p><strong>External (any language, subprocess).</strong> Wraps existing tools \u2014 the mature security tooling ecosystem that already exists and that you may already run. Language-agnostic, community-maintainable without Rust knowledge, isolated by OS process boundary. Costs 20\u201350 ms of startup per plugin invocation. Managed by <code>securegit plugin list|check-updates|update</code> with a manifest-driven update source; CI can gate on stale plugins with <code>--fail-on-outdated</code> and <code>--fail-on-security</code>.</p>\n<p><strong>The design principle:</strong> do not rebuild the security ecosystem in Rust. The tools that exist are good, well-maintained, and specialized. Build a fast core for the things that must run on every commit, and wrap everything else.</p>\n<h3>The plugin ladder</h3>\n<p>Climb this slowly. Each rung is optional and each adds time.</p>\n<p><strong>RUNG 0 \u00b7 Built-in only.</strong> <span class=\"badge shipped\">SHIPPED</span> Twelve scanners, no configuration. This is where you start and where most teams correctly stop. Fast enough for pre-commit hooks and CI gates. Between them they cover code (<code>secrets</code>, <code>patterns</code>, <code>entropy</code>, <code>encoding</code>), dependencies (<code>supply-chain</code>, <code>dangerous-files</code>), pipelines (<code>ci-cd</code>), infrastructure (<code>container</code>, <code>iac</code>), runtime execution paths (<code>deserialization</code>), binaries (<code>binary</code>), and repository metadata (<code>git-internals</code>).</p>\n<p><strong>RUNG 1 \u00b7 One external plugin that matches your stack.</strong> A Python security scanner on a Python codebase; a Go one on Go. One plugin, chosen deliberately. Scheduled scans, not the hot path.</p>\n<p><strong>RUNG 2 \u00b7 A secrets specialist.</strong> Wrap a dedicated secrets tool alongside the built-in <code>secrets</code> scanner. Different pattern databases catch different things and the overlap is not total.</p>\n<p><strong>RUNG 3 \u00b7 A SAST engine.</strong> Semgrep or similar, with rules tuned for your codebase. This is where scan times get long and where the value gets deep. Nightly or per-PR.</p>\n<p><strong>RUNG 4 \u00b7 Specialized and compliance.</strong> Container linting, IaC scanning, license detection, malware signatures. Enable per-repository based on what that repository actually contains.</p>\n<p><strong>Most teams should live at rung 0 for the hot path and rung 2 or 3 on a schedule.</strong> Running rung 4 on every commit is how you end up with a two-minute pre-commit hook and a team that has learned to use <code>--no-verify</code>.</p>\n<hr>\n<h3>Writing a plugin</h3>\n<p>The protocol is deliberately trivial: <strong>a plugin is an executable that takes a file path and prints JSON to stdout.</strong></p>\n<p>Python:</p>\n<pre><code>#!/usr/bin/env python3\nimport json, sys\n\nfile_path = sys.argv[1]\nfindings = []\n\n# your logic here\n\nprint(json.dumps({\n    &quot;plugin_name&quot;: &quot;my-scanner&quot;,\n    &quot;findings&quot;: findings,\n    &quot;scanned_files&quot;: 1\n}))\n</code></pre>\n<p>Bash:</p>\n<pre><code>#!/bin/bash\nFILE=&quot;$1&quot;\necho '{&quot;plugin_name&quot;:&quot;my-scanner&quot;,&quot;findings&quot;:[],&quot;scanned_files&quot;:1}'\n</code></pre>\n<p>Install:</p>\n<pre><code>cp my-plugin ~/.config/securegit/plugins/\nchmod +x ~/.config/securegit/plugins/my-plugin\nsecuregit scan /path/to/test/file\n</code></pre>\n<p><strong>That is the entire interface.</strong> No SDK, no registration, no manifest, no compilation. If you can write a script that prints JSON, you can write a plugin. This was chosen over a richer interface specifically because organizational security policies are idiosyncratic, and the tool that gets custom policies written for it is the one where writing them takes an afternoon.</p>\n<h3>Plugin types, current and future</h3>\n<table>\n<thead>\n<tr>\n<th>Type</th>\n<th>Location</th>\n<th>Performance</th>\n<th>Status</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Built-in (Rust)</td>\n<td>compiled in</td>\n<td>0 ms startup</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr>\n<td>External (any language)</td>\n<td><code>~/.config/securegit/plugins/</code></td>\n<td>20\u201350 ms startup</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr>\n<td>Native dynamic (Rust + FFI)</td>\n<td><code>plugins/*.so</code></td>\n<td>near-native</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n<tr>\n<td>WebAssembly</td>\n<td><code>plugins/*.wasm</code></td>\n<td>fast, sandboxed</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n</tbody>\n</table>\n<p><strong>WASM is the interesting future one</strong> and it is <span class=\"badge planned\">PLANNED</span>, not <span class=\"badge designed\">DESIGNED</span>. The appeal is capability-based sandboxing: a plugin with no filesystem or network access unless explicitly granted. Today, external plugins run as subprocesses and inherit your permissions \u2014 which is worth stating plainly, because <strong>installing a plugin is installing software with your privileges.</strong></p>\n<h3>The plugin trust problem</h3>\n<p>We are going to state this directly rather than bury it, because it is the honest weak point of any plugin architecture.</p>\n<p>An external plugin runs as a subprocess with your user's permissions. It can read your filesystem. Depending on the tool, it can make network calls. <strong>A malicious plugin in a security tool is an excellent attack.</strong></p>\n<p>Mitigations available today, all of them procedural rather than technical:</p>\n<ol>\n<li>Read the plugin source. They are small by design \u2014 that is a security property, not just a convenience.</li>\n<li>Prefer plugins that wrap well-known tools, and verify the wrapped binary independently.</li>\n<li>Test in a container before putting one on your working machine.</li>\n<li>Do not install a plugin because a search result recommended it.</li>\n</ol>\n<p><span class=\"badge planned\">PLANNED</span> WASM sandboxing addresses this properly. Until it lands, <strong>plugin installation deserves the same scrutiny as adding a dependency</strong>, and we would rather say so than let you discover it.</p>\n<hr>\n<aside class=\"sidebar\"><h4>What the built-in scanners actually catch</h4><p>From a deliberately seeded test file of about twenty lines, the built-in set found nine issues in roughly four milliseconds:</p>\n<p><strong>2 critical</strong> \u2014 a cloud access key, a hardcoded password\n<strong>3 high</strong> \u2014 a database password, a dynamic execution call, a hardcoded secret\n<strong>1 medium</strong> \u2014 a high-entropy string\n<strong>3 low</strong> \u2014 development comments flagged by an external plugin</p>\n<p>That is the shape of a normal result: the built-in scanners handle credentials and dangerous constructs quickly and well, and external plugins add breadth at a cost in time.</p>\n<p>Note the low-severity items came from the external plugin. That is typical, and it is why external plugins belong on a schedule rather than in your commit path.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 31\u201333</div>\n  <h1>THE GUARDED COMMIT</h1>\n  <div class=\"deck\">Any gate can stop a bad commit. The engineering problem is stopping it without teaching people to route around the gate.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Mechanism 03</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span></p>\n<h2>A gate that survives contact with a deadline</h2>\n<hr>\n<h3>The commands</h3>\n<pre><code>securegit status                     # working tree state\nsecuregit scan --staged              # scan what is staged\nsecuregit safe-commit -m &quot;message&quot;   # scan, then commit if clean\nsecuregit commit -m &quot;message&quot;        # commit with chain receipt\nsecuregit findings                   # review current findings\nsecuregit review                     # guided review of changes\nsecuregit diff                       # inspect changes\n</code></pre>\n<p><code>safe-commit</code> is the one to build a habit around. It scans staged changes and commits only if they pass, in a single step, which removes the \"I meant to scan first\" failure entirely.</p>\n<h3>Why the gate is at commit and not earlier</h3>\n<p>You could gate at <code>add</code>. It would be worse.</p>\n<p>Staging is exploratory. People stage, unstage, restage, and split changes across several commits. A gate at <code>add</code> fires constantly during normal work, most firings are irrelevant because the content changes again before it is committed, and the annoyance-to-value ratio is terrible.</p>\n<p><strong>Commit is the first moment the content is declared finished.</strong> That is where a gate belongs \u2014 at a natural boundary the developer has already decided to stop at, rather than in the middle of their thinking.</p>\n<h3>Why the <em>enforcement</em> gate is at push</h3>\n<p>There is a second gate, and it is not at commit. It is at push. This is worth its own explanation because it is counterintuitive and it is deliberate.</p>\n<p><strong>Commits are local and revisable.</strong> You can rewrite them, amend them, squash them, and \u2014 importantly for the chain \u2014 attest them retroactively. A commit that is missing a receipt today can have one added tomorrow with no loss of meaning, because the commit has not gone anywhere.</p>\n<p><strong>Push is the first crossing onto shared infrastructure.</strong> It is the moment your work becomes other people's problem. It is the last point at which stopping is cheap and the first point at which not stopping is expensive.</p>\n<p>So the policy is: <strong>commit-time scanning is advisory and fast; push-time chain enforcement is blocking.</strong> Different gates, different jobs, different costs.</p>\n<hr>\n<div class=\"pullquote\">Commits are local and revisable. Push is the first crossing onto shared infrastructure. Gate where stopping is still cheap.</div>\n<hr>\n<h3>The full working surface</h3>\n<p>SecureGit wraps the git operations you use daily. All <span class=\"badge shipped\">SHIPPED</span>:</p>\n<p><strong>Everyday</strong> \u2014 <code>status</code>, <code>add</code>, <code>commit</code>, <code>safe-commit</code>, <code>diff</code>, <code>log</code>, <code>show</code>, <code>blame</code>\n<strong>Branching</strong> \u2014 <code>branch_create</code>, <code>branch_list</code>, <code>branch_delete</code>, <code>checkout</code>, <code>merge</code>\n<strong>Remote</strong> \u2014 <code>push</code>, <code>remote_list</code>, <code>server_add</code>, <code>server_list</code>, <code>server_push</code>\n<strong>History</strong> \u2014 <code>stash_save</code>, <code>stash_pop</code>, <code>stash_list</code>, <code>undo</code>, <code>tag_create</code>, <code>tag_list</code>\n<strong>Security</strong> \u2014 <code>scan</code>, <code>scan_staged</code>, <code>findings</code>, <code>posture</code>, <code>review</code>\n<strong>Repository</strong> \u2014 <code>repo_create</code>, <code>worktree_add</code>, <code>worktree_list</code>, <code>worktree_lock</code>\n<strong>Backup</strong> \u2014 <code>backup_add</code>, <code>backup_list</code>, <code>backup_push</code>\n<strong>Escape hatch</strong> \u2014 <code>git_raw</code></p>\n<p><strong><code>git_raw</code> matters more than its placement in that list suggests.</strong> It passes a command straight through to git. It exists because no wrapper covers everything, and a wrapper that traps you when it hits its limits is a wrapper you will abandon at the first sharp edge. The escape hatch is a feature, and it is documented, and using it is not a failure.</p>\n<h3>Which operations emit receipts</h3>\n<p>Not everything does, and the boundary is principled.</p>\n<table>\n<thead>\n<tr>\n<th>Operation</th>\n<th>Receipt</th>\n<th>Why</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>acquire</code> / <code>clone</code></td>\n<td><strong>Yes</strong></td>\n<td>Code arrives from outside</td>\n</tr>\n<tr>\n<td><code>commit</code></td>\n<td><strong>Yes</strong></td>\n<td>Content declared finished</td>\n</tr>\n<tr>\n<td><code>push</code></td>\n<td><strong>Yes</strong></td>\n<td>Crosses to shared infrastructure</td>\n</tr>\n<tr>\n<td><code>merge</code></td>\n<td><strong>Yes</strong></td>\n<td>Joins two histories</td>\n</tr>\n<tr>\n<td><code>scan</code></td>\n<td><strong>Yes</strong></td>\n<td>Findings become evidence</td>\n</tr>\n<tr>\n<td><code>fetch</code> / <code>pull</code></td>\n<td><strong>Yes</strong></td>\n<td>Content arrives from outside</td>\n</tr>\n<tr>\n<td><code>blame</code></td>\n<td><strong>Yes</strong></td>\n<td>Read-only provenance query</td>\n</tr>\n<tr>\n<td><code>status</code>, <code>log</code>, <code>diff</code>, <code>add</code>, <code>checkout</code>, <code>branch</code>, <code>tag</code>, <code>stash</code>, <code>config</code></td>\n<td><strong>No</strong></td>\n<td>No boundary crossed</td>\n</tr>\n</tbody>\n</table>\n<p>That last row is the one that keeps the tool usable. <strong>Instrumenting everything is how you build something people turn off.</strong></p>\n<h3>Workflow scripts</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Guided scripts for common operations, exposed so you do not need to know where they live:</p>\n<pre><code>securegit workflow list\nsecuregit workflow info dev-flow\nsecuregit workflow install\nsecuregit workflow run dev-flow --dry-run\nsecuregit workflow run commit-craft\n</code></pre>\n<p>Bundled workflows install to <code>~/.config/securegit/workflows</code>. Every workflow supports <code>--dry-run</code> at the wrapper level, which prints what would happen and executes nothing. Some workflows have their own internal dry-run, passed through after <code>--</code>:</p>\n<pre><code>securegit workflow run pr-prepare -- --dry-run\n</code></pre>\n<p><strong><code>--dry-run</code> before every unfamiliar workflow.</strong> It costs one flag and it is the difference between learning a tool and being surprised by it.</p>\n<p>SecureGit also shows contextual tips after some interactive commands \u2014 after branch operations it may suggest <code>dev-flow</code>; after staged commit work, <code>commit-craft</code>. Tips are local, throttled, and suppressed for <code>--json</code>, <code>--quiet</code>, and <code>--compact</code>. Turn them off entirely:</p>\n<pre><code>SECUREGIT_WORKFLOW_TIPS=0\n</code></pre>\n<p><strong>Turning tips off is respected permanently.</strong> A tool that keeps helpfully reminding you of something you dismissed is a tool people come to resent.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The `--no-verify` policy question</h4><p>Sooner or later someone will propose blocking <code>--no-verify</code> at the organizational level. Usually after an incident.</p>\n<p><strong>Push back on this</strong>, and here is the argument.</p>\n<p><code>--no-verify</code> is a git flag. You cannot remove it; you can only make it costly. Attempts to block it universally produce one of two outcomes: developers stop using the hooks entirely, or they build a shadow workflow you cannot see. Both leave you with less visibility than you started with.</p>\n<p>The better design is what the chain layer does: <strong>let the bypass happen, and record it.</strong> A force-push that rewrites history is not blocked outright \u2014 it emits a receipt marking the bypass, and policy can require a joint-approval marker. Now the bypass is a visible, attributable event rather than an invisible one.</p>\n<p>Visible bypasses are governance. Blocked bypasses are theater with a shadow IT chaser.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 34\u201337</div>\n  <h1>THE CHAIN OF CUSTODY</h1>\n  <div class=\"deck\">This is the layer that turns git from a record of assertions into a record of evidence. It is also the layer that requires real infrastructure, so read the cost section before you plan around it.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Mechanism 04</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span> (core) \u00b7 <span class=\"badge designed\">DESIGNED</span> (offline + batch lookup)</p>\n<h2>Receipts, tiers, and the push gate</h2>\n<hr>\n<h3>The core idea</h3>\n<p>Every operation that crosses a trust boundary produces a <strong>receipt</strong>: a signed, timestamped record linking an action to a verified identity and to the exact content involved.</p>\n<p>Receipts are cryptographically linked to each other, so the chain is tamper-evident. Modifying a past receipt breaks the links after it.</p>\n<p><strong>What a receipt binds together:</strong></p>\n<ul>\n<li><strong>What</strong> \u2014 a content hash. For a commit, the commit SHA. For a clone, the remote URL plus resolved HEAD. For a merge, the merge SHA composed with both parent SHAs.</li>\n<li><strong>Who</strong> \u2014 a verified identity, not a git author field. Git author is self-asserted and trivially forged; the receipt identity is signed.</li>\n<li><strong>When</strong> \u2014 a timestamp from the signing daemon, not from the local machine.</li>\n<li><strong>Which operation</strong> \u2014 clone, push, merge, blame, scan, and so on.</li>\n</ul>\n<h3>The signing model</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> <strong>The signing key never touches SecureGit.</strong></p>\n<p>It lives in a separate signing daemon. In production the key is hardware-rooted; in local development a software key is used. SecureGit is stateless with respect to key material \u2014 it composes an envelope and posts it to the daemon's signing endpoint. It cannot leak a key it never holds.</p>\n<p><strong>The cost of this design, stated up front:</strong> you need the daemon reachable to produce first-class receipts. That is real infrastructure. It is why the chain layer is Layer 3 in the adoption model on page 8 and not Layer 1.</p>\n<p><strong>Offline mode</strong> <span class=\"badge designed\">DESIGNED</span> \u2014 receipts written to a local store and synchronized when the daemon is next reachable. Air-gapped deployment is on this path.</p>\n<h3>Two tiers of evidence, and why the distinction is the point</h3>\n<p><strong>Tier A \u2014 forward-attested.</strong> The daemon witnessed the operation as it happened. Strongest evidence.</p>\n<p><strong>Tier B \u2014 retro-attested.</strong> The receipt was added after the fact. Real, useful, and weaker \u2014 it proves someone asserted something later, not that the daemon observed it at the time.</p>\n<p><strong>The system records which tier you have and never conflates them.</strong> This is the single most important honesty property in the design. A governance system that lets weak evidence masquerade as strong evidence is worse than no governance system, because it produces confident wrong answers in exactly the situations where being wrong is expensive.</p>\n<hr>\n<div class=\"pullquote\">A system that lets retroactive evidence masquerade as witnessed evidence is worse than no system. It produces confident wrong answers precisely when being wrong is expensive.</div>\n<hr>\n<h3>Where the receipt lives \u2014 belt and braces</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Commit receipts are stored in <strong>two places at once</strong>, deliberately:</p>\n<p><strong>1 \u00b7 In the commit message as a trailer.</strong></p>\n<pre><code>X-ContextOS-Receipt: <span class=\"placeholder\">&lt;receipt-id&gt;</span>\n</code></pre>\n<p>Travels with the repository. Survives clone. Readable from <code>git log</code> with no daemon and no network. Tamper-evident, because changing it changes the commit SHA.</p>\n<p><strong>2 \u00b7 In the daemon's receipt store</strong>, indexed by content hash for fast lookup.</p>\n<p><strong>Neither is the single source of truth.</strong> They cross-validate. A trailer with no matching daemon record is suspicious. A daemon record with no trailer is suspicious. Agreement is evidence.</p>\n<p>This is a good pattern generally, and worth stealing even if you never use SecureGit: <strong>when you need durable evidence, write it to two stores with different failure modes and treat disagreement as signal.</strong></p>\n<h3>The push gate</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> (core) \u2014 This is the enforcement point.</p>\n<p>Before a push executes, SecureGit walks every commit in the push range and checks whether each has a receipt. If any commit does not, the push is blocked:</p>\n<pre><code>error: push rejected \u2014 commit <span class=\"placeholder\">&lt;sha&gt;</span> has no chain receipt.\nRun `securegit attest <span class=\"placeholder\">&lt;sha&gt;</span>` to add one, or set\nchain.fail_on_unattested=warn to push with a marker receipt.\n</code></pre>\n<p><strong>Note the shape of that error message.</strong> It says what is wrong, gives the exact command to fix it, and names the configuration key to change the policy. That is the standard every error message in a security tool should meet, because a blocking error without a remedy is just an obstacle.</p>\n<p><strong>Adopting on an existing repository:</strong> your history predates the chain, so nothing has receipts, so your first push is blocked. This is the single most likely moment for someone to give up on the chain layer.</p>\n<p>Two ways through:</p>\n<ul>\n<li><code>securegit attest &lt;sha&gt;</code> \u2014 add retro-attested (Tier B) receipts. A <code>--since</code> flag supports partial attestation from a starting point.</li>\n<li>Set <code>chain.fail_on_unattested=warn</code> \u2014 push proceeds and un-receipted commits are marked as pre-chain in the record.</li>\n</ul>\n<p><strong>Recommendation:</strong> for an existing repository, set the policy to <code>warn</code>, adopt the chain from today forward, and accept that history before adoption is Tier B or unmarked. Retro-attesting years of history produces a lot of weak evidence and obscures where the strong evidence starts. A clean boundary \u2014 \"the chain begins here\" \u2014 is more useful than a uniform blanket of weak claims.</p>\n<h3>Force push</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> If a push would rewrite published history, a <strong>bypass receipt</strong> is emitted before the push, and policy can require a joint-approval marker in the commit message.</p>\n<p>The default is to require it. The pattern mirrors a physical two-person rule for high-stakes operations.</p>\n<p><strong>The philosophy again:</strong> force-push is not forbidden. Sometimes it is genuinely correct. It is <em>recorded</em>, and it requires a second signal. The bypass becomes an attributable event.</p>\n<h3>Blame with provenance</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> \u2014 exposed in the agent tool surface as well as the CLI.</p>\n<pre><code>securegit blame <span class=\"placeholder\">&lt;file&gt;</span> [--commit <span class=\"placeholder\">&lt;sha&gt;</span>] [--format=human|json]\n</code></pre>\n<p>Standard blame output, augmented per line with the receipt tier and operation type:</p>\n<pre><code><span class=\"placeholder\">&lt;sha&gt;</span>  <span class=\"placeholder\">&lt;identity&gt;</span>  A  human-edit   fn handle_request() {\n<span class=\"placeholder\">&lt;sha&gt;</span>  <span class=\"placeholder\">&lt;identity&gt;</span>  A  agent-exec       let body = req.body();\n<span class=\"placeholder\">&lt;no-receipt&gt;</span>       ?  (unattested) }\n</code></pre>\n<p><strong>That third column is the one that matters, and it is why this feature exists.</strong> In a codebase where humans and agents both commit, \"which lines did an agent write\" stops being an archaeology exercise and becomes a query.</p>\n<p><strong>Blame degrades gracefully by default.</strong> Unattested lines show <code>?</code> rather than erroring. <code>--strict</code> errors instead.</p>\n<p>The principle: <strong>visibility tools never block; only gate operations block.</strong> Blame is read-only. If it refused to run on partially-attested history, nobody would ever run it on a real repository.</p>\n<h3>Merge</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> A merge joins two receipt chains, so its receipt encodes both parent SHAs in its content hash \u2014 preserving the history graph inside the evidence chain.</p>\n<p>Multi-author merges \u2014 detected from author/committer mismatch or co-author trailers \u2014 can require a joint-approval marker. Without it: a warning, and a receipt marked unverified rather than clean.</p>\n<h3>Policy and configuration</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Policy resolves in a fixed order: <strong>git config \u2192 environment variables \u2192 built-in defaults.</strong></p>\n<p>Git config first is a deliberate operational choice: it means managed-workstation tooling can push policy through standard git configuration, which every organization already has a mechanism for. Environment variables override, which is what CI needs.</p>\n<table>\n<thead>\n<tr>\n<th>Key</th>\n<th>Default</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>chain.daemon_url</code></td>\n<td>local endpoint</td>\n<td>Where the signing daemon lives</td>\n</tr>\n<tr>\n<td><code>chain.fail_on_daemon_error</code></td>\n<td><code>warn</code></td>\n<td><code>block</code>, <code>warn</code>, or <code>ignore</code></td>\n</tr>\n<tr>\n<td><code>chain.fail_on_unattested</code></td>\n<td><code>block</code></td>\n<td>Policy for un-receipted commits at push</td>\n</tr>\n<tr>\n<td><code>chain.require_joint_approval_on_force_push</code></td>\n<td><code>true</code></td>\n<td>Require a second signal for history rewrites</td>\n</tr>\n<tr>\n<td><code>chain.offline_store</code></td>\n<td>local path</td>\n<td>Where offline receipts queue</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Hard defaults are always enforced</strong>: block on unattested, require joint approval on force-push and multi-author merge. You can loosen them explicitly. You cannot accidentally end up without them.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The honest cost of the chain layer</h4><p>Before you plan around this, know what it asks of you.</p>\n<p><strong>You need a signing daemon.</strong> That is infrastructure to deploy, operate, and monitor. For a solo developer this is probably not worth it. For a team with a compliance requirement it usually is. Decide honestly which you are.</p>\n<p><strong>First push on an existing repository will be blocked.</strong> Mitigated by <code>attest</code> and the <code>warn</code> policy, but it will surprise someone. Warn your team before you enable it, not after.</p>\n<p><strong>Large pushes add latency.</strong> The gate looks up every commit in the push range. On a wide range this is noticeable. A batch lookup endpoint is <span class=\"badge designed\">DESIGNED</span> and not yet shipped; today, expect a per-commit cost.</p>\n<p><strong>The daemon is a dependency.</strong> Read operations warn, write operations block, and that is configurable \u2014 but a daemon outage will interrupt pushes if you have configured it to. Decide your failure policy deliberately, in advance, and write it down.</p></aside></div>\n</section>\n\n\n<section class=\"section-opener\">\n  <div class=\"section-opener-part\">PART THREE</div>\n  <div class=\"section-opener-name\">SUPPLY<br>CHAIN</div>\n  <div class=\"opener-line\">Ninety percent of your application\nis code nobody on your team wrote.</div><div class=\"opener-line\">This part is about proving\nwhat you knew about it, and when.</div>\n  <div class=\"section-opener-foot\">39 \u2192 45</div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 39\u201341</div>\n  <h1>SBOM, OSV, AND THE NUMBER THAT MATTERS MORE THAN ZERO</h1>\n  <div class=\"deck\">An architectural decision worth understanding, because it tells you exactly what this tool will and will not do for your dependencies \u2014 and because the boundary moved once, deliberately, and the reasoning is instructive.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Supply chain</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span> (anchoring) \u00b7 <span class=\"badge designed\">DESIGNED</span> (parts)</p>\n<h2>Anchoring, not scanning</h2>\n<hr>\n<h3>The gap</h3>\n<p>The chain of custody in Part II proves what humans and agents contributed to your repository. It says nothing about the overwhelming majority of a modern application, which is open-source dependencies you did not write and mostly have not read.</p>\n<p>Federal procurement, defense customers, and enterprise diligence all ask the same four questions:</p>\n<ol>\n<li>What is in your software? (SBOM)</li>\n<li>What is its license posture?</li>\n<li>What was its known-vulnerability state at a given moment?</li>\n<li>Can you prove how it was built?</li>\n</ol>\n<p>Git history answers none of these.</p>\n<h3>The decision: anchor, don't bundle</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> <strong>SecureGit records the canonical hash of each supply-chain tool's output at the moment it ran, and anchors that hash in the chain.</strong></p>\n<p>You keep your existing tooling \u2014 whatever vulnerability scanner, SBOM generator, and signing tool you already run. SecureGit is the anchor, not the scanner.</p>\n<p><strong>The reasoning</strong>, which was contested internally and decided in a strategic review:</p>\n<p><strong>Bundling scanners expands the maintenance surface without bound.</strong> Every ecosystem needs its own. They change constantly. You are now maintaining a scanner fleet instead of a chain.</p>\n<p><strong>Fork drift.</strong> A bundled copy of somebody else's scanner falls behind, and the gap between your version and theirs is invisible to your customer.</p>\n<p><strong>License coverage narrows.</strong> Bundling constrains what you can ship and to whom.</p>\n<p><strong>And customers already run these tools.</strong> Telling a security team to replace a scanner they have tuned for three years is how you lose the deal.</p>\n<hr>\n<div class=\"pullquote\">We are the chain anchor, not the scanner. That single sentence decides what this product is and \u2014 more usefully \u2014 what it will never become.</div>\n<hr>\n<h3>The one exception, and why it moved</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> There is exactly one place the anchor-don't-bundle rule was amended, and documenting the amendment precisely is how you keep it from becoming a pattern.</p>\n<p><strong>The problem with the pure position:</strong> most enterprise customers assume \"SBOM provenance\" means the system takes an SBOM, looks up vulnerabilities, and anchors the result. They do not want to install another tool. They want the join to happen inside the system.</p>\n<p><strong>The amendment:</strong> SecureGit bundles a <strong>vulnerability-database lookup client</strong>, not a scanner.</p>\n<p>What it does: given a package and a version from an existing SBOM, query a public vulnerability database and return known advisories. Stateless. No local database. No manifest parsing. No dependency-tree resolution. One API, one schema.</p>\n<p>What it explicitly does not do: manifest discovery, transitive dependency resolution, license analysis, SBOM generation, or wrapping any of the ecosystem-specific audit tools. Those all go through the normal ingest path.</p>\n<p><strong>The distinction that justifies the amendment:</strong> a database lookup against a pre-existing SBOM is qualitatively different from running a scanner. The customer already did the hard part \u2014 dependency resolution \u2014 when they generated the SBOM. This adds a search step, not an analysis step.</p>\n<p><strong>Naming the boundary this precisely is the point.</strong> A vague boundary erodes. A boundary stated as \"lookup against an existing SBOM, never resolution or discovery\" is one where future scope creep is detectable by anyone reading the design record.</p>\n<h3>Four safety properties worth stealing</h3>\n<p>Whether or not you use SecureGit, these four decisions are good engineering and transfer to any system that anchors third-party output.</p>\n<p><strong>1 \u00b7 Opt-in, never default.</strong> <span class=\"badge shipped\">SHIPPED</span> The lookup is an explicit flag. It is never triggered automatically. <strong>Regulated environments must know when an outbound network call happens</strong> \u2014 a security tool making a surprise outbound request is a finding, not a feature.</p>\n<p><strong>2 \u00b7 The anchor never waits on the scan.</strong> <span class=\"badge shipped\">SHIPPED</span> The SBOM receipt is emitted unconditionally. If the vulnerability lookup fails \u2014 network error, API down, partial batch failure \u2014 the SBOM anchor still succeeds and the scan is recorded as degraded. The auditor learns \"SBOM anchored, scan was partially complete,\" which is true and useful, instead of nothing at all.</p>\n<p><strong>3 \u00b7 <code>scan_completeness</code> is a first-class field.</strong> <span class=\"badge shipped\">SHIPPED</span> Every vulnerability-state receipt carries a completeness value from 0 to 1.</p>\n<p><strong>This is the number that matters more than the CVE count.</strong></p>\n<blockquote>\n<p><strong>\"0 CVEs\" without \"completeness = 1.0\" is not a clean bill of health.</strong>\nIt might mean the scan covered 12% of your components and found nothing in those. Those are wildly different facts and most tooling renders them identically.</p>\n</blockquote>\n<p><strong>4 \u00b7 Absence is not evidence of absence.</strong> <span class=\"badge shipped\">SHIPPED</span> If no vulnerability receipt exists for a commit, the correct reading is <strong>\"no scan was run,\"</strong> not \"no vulnerabilities.\" Any report generated from the chain must surface that distinction explicitly.</p>\n<p>That fourth one is the most commonly violated principle in the entire security-tooling industry. Dashboards render \"no findings\" and \"not scanned\" the same way constantly, and executives read both as green.</p>\n<hr>\n<h3>Air-gapped environments</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Customers who cannot make outbound calls point the tool at a local mirror of the vulnerability database. The mirror's snapshot date is recorded in the receipt, so an auditor can see how current the vulnerability data was at scan time.</p>\n<p><strong>We document the download; we do not ship the data.</strong> That keeps licensing clean and keeps the tool from carrying a stale copy of somebody else's dataset.</p>\n<h3>Rescan, because advisories keep arriving</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> The same dependency set scanned in April may have more known vulnerabilities in May. Nothing about your code changed; the world's knowledge did.</p>\n<pre><code>securegit sbom rescan <span class=\"placeholder\">&lt;receipt-id&gt;</span>\n</code></pre>\n<p>Runs a fresh lookup, bypassing cache, and emits a new vulnerability-state receipt linked to the same SBOM.</p>\n<p><strong>The chain accumulates a timeline</strong>, and the timeline is the actual product here. \"What did we know, and when did we know it\" is answerable as a sequence of signed records rather than a reconstruction from memory.</p>\n<p><strong>This is what makes monthly compliance refresh cheap.</strong> You do not re-anchor the SBOM. You add a new point to the vulnerability timeline against the same anchor.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The single-command flow</h4><p>The whole supply-chain story, once configured, is one pipe:</p>\n<pre><code><span class=\"placeholder\">&lt;your-sbom-generator&gt;</span> | securegit sbom emit --with-osv-scan\n</code></pre>\n<p>Two receipts land in the chain \u2014 the SBOM anchor and the vulnerability state \u2014 cryptographically linked to each other.</p>\n<p><strong>The link key is a content hash, not an identifier.</strong> This is a small design decision with a large consequence: a content hash is reproducible across serializations, while a generated identifier changes if a record is ever re-signed. Choosing the reproducible key means the join still works years later, after re-signings, migrations, and format changes.</p>\n<p>If you build anything that joins records across systems and time, choose the content hash.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 42\u201343</div>\n  <h1>LOCK FILES AND LICENSES</h1>\n  <div class=\"deck\">Two quieter capabilities that answer two questions auditors ask early: when did this dependency arrive, and are we permitted to ship it.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Supply chain</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span> (core) \u00b7 <span class=\"badge designed\">DESIGNED</span> (coverage)</p>\n<h2>What changed, and what you are allowed to ship</h2>\n<hr>\n<h3>Lock-file change receipts</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> When a dependency lock file changes, a receipt records the change: the file's content hash before and after, and where format support exists, a count of dependencies added, removed, and version-bumped.</p>\n<p><strong>Format coverage is explicitly tiered, and the tiering is honest:</strong></p>\n<p><strong>Full parsing</strong> <span class=\"badge shipped\">SHIPPED</span> \u2014 the major lock formats for the ecosystems most represented in the codebase get real dependency-count diffs.</p>\n<p><strong>Change-only</strong> <span class=\"badge shipped\">SHIPPED</span> \u2014 every other lock format records <strong>that</strong> the file changed and hashes it, but reports zero counts.</p>\n<p><strong>This distinction is deliberate and it is surfaced, not hidden.</strong> A record saying \"changed, counts unavailable for this format\" is materially different from \"changed, zero dependencies affected.\" Reporting the second when you mean the first is how audit records become misleading.</p>\n<p><strong>When more formats get full parsing:</strong> <span class=\"badge designed\">DESIGNED</span>. Check your build.</p>\n<h3>License detection</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> License detection runs at acquisition time using file heuristics and a standard license identifier database.</p>\n<p><strong>Three properties, each of which is a deliberate choice:</strong></p>\n<p><strong><code>UNKNOWN</code> is a valid recorded value.</strong> Not an error, not a blank. Recording \"we looked and could not determine this\" is more useful than recording nothing, because it distinguishes \"unlicensed\" from \"unexamined.\"</p>\n<p><strong>It is best-effort and says so.</strong> License detection from files is genuinely hard. Dual licensing, per-directory exceptions, vendored code with different terms, and modified license text all defeat heuristics. The tool does not pretend otherwise.</p>\n<p><strong>It never blocks the clone.</strong> <span class=\"badge shipped\">SHIPPED</span> License detection at acquisition is <strong>visibility, not enforcement</strong>.</p>\n<p>That last one deserves defending, because it looks like a gap.</p>\n<p>If license detection blocked acquisition, you could not obtain a repository in order to <em>examine</em> its license \u2014 which is the most common reason to obtain an unfamiliar repository in the first place. Blocking would make the tool actively counterproductive for the exact workflow it was built to support. Enforcement belongs at a later gate, where you have information and intent.</p>\n<hr>\n<div class=\"pullquote\">UNKNOWN is a valid value. \"We looked and could not determine this\" is a materially different fact from \"we did not look,\" and the record should be able to tell them apart.</div>\n<hr>\n<h3>What this gives you in practice</h3>\n<p>Three answers you can produce from the chain that you probably cannot produce today:</p>\n<p><strong>\"When did this dependency enter our tree?\"</strong> \u2014 the lock-file receipt timeline, with a signed timestamp and an attributed actor.</p>\n<p><strong>\"What was our license posture at release 4.2?\"</strong> \u2014 the license records anchored around that release point.</p>\n<p><strong>\"Did anyone review this dependency addition?\"</strong> \u2014 the lock-file change receipt is linked to a commit, which is linked to an identity, which may be linked to an approval.</p>\n<p>None of those are exotic questions. All of them currently require an engineer to spend an afternoon in <code>git log</code> and produce an answer hedged with \"as far as I can tell.\"</p>\n<hr>\n<aside class=\"sidebar\"><h4>The trap in dependency counts</h4><p>A tempting metric: \"dependencies added this quarter.\" Easy to compute from lock-file receipts, easy to chart, and nearly meaningless.</p>\n<p>A lock file update that bumps four hundred transitive versions after a single direct dependency change looks enormous and is routine. A single added direct dependency that pulls in an unmaintained package with network access looks trivial and is the actual risk.</p>\n<p><strong>Use the receipts for provenance, not for scoring.</strong> The value is answering \"when and by whom,\" not producing a number that goes on a slide. Numbers that go on slides get optimized, and a team optimizing for fewer lock-file changes is a team batching risky changes into larger, less reviewable updates.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 44\u201345</div>\n  <h1>REPORTS THAT LAND WHERE REVIEWERS ALREADY LIVE</h1>\n  <div class=\"deck\">Everything in Parts II and III exists to make one specific conversation go differently. The way it lands in that conversation is: as a report your reviewer's tools already read.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Supply chain &amp; governance</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span> (SARIF, GitLab SAST, baselines, audit log, compliance report) / <span class=\"badge designed\">DESIGNED</span> (integrated chain audit)</p>\n<h2>SARIF, GitLab SAST, baselines, audit</h2>\n<hr>\n<h3>SARIF 2.1.0, and GitLab SAST v15</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> One flag on the scan command; the report uploads directly into whatever dashboard your reviewers already open.</p>\n<pre><code>securegit scan . --format sarif  --report-output securegit.sarif\nsecuregit scan . --format gitlab --report-output gl-sast-report.json\n</code></pre>\n<p>SARIF 2.1.0 is the OASIS standard for static-analysis output. It carries CWE mappings, stable fingerprints (line-drift resistant), severity, and location, and is understood by <strong>GitHub Advanced Security</strong>, <strong>Azure DevOps</strong>, <strong>SonarQube</strong>, and <strong>DefectDojo</strong>. GitLab SAST v15 is GitLab's native format and lights up the security dashboard and MR widget without additional plumbing.</p>\n<p><code>--report-output</code> writes the file even when <code>--fail-on</code> gates the exit code, so a single CI step can both <em>enforce</em> a bar and <em>publish</em> findings to the reviewer's dashboard. That was the single most common failure mode we saw in early pilots: teams that gated hard but had nowhere for developers to see findings ended up disabling the gate.</p>\n<h3>Baselines: adopting on legacy code without failing every build</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> The pattern that unblocks adoption on a mature codebase.</p>\n<pre><code>securegit scan . --write-baseline &quot;Legacy findings accepted 2026-08-03; expires 2026-11-03&quot; \\\n  --expires 2026-11-03\ngit add .securegit/baseline.json &amp;&amp; git commit -m &quot;chore(security): baseline v1&quot;\n\n# thereafter, every scan and every CI run\nsecuregit scan . --fail-on high --baseline .securegit/baseline.json\n</code></pre>\n<p>Baselines suppress known findings by a <strong>stable fingerprint</strong> that includes rule, file, snippet, and CWE \u2014 not by line number. That means shuffling code up or down a file does not invalidate a suppression, and does not re-fail a build. Every suppression carries <code>reason</code>, <code>created_at</code>, <code>created_by</code>, and optionally <code>expires</code> (RFC3339). Expired entries automatically re-fire; the log records who added them and why. <code>--no-baseline</code> re-runs without suppressions for review.</p>\n<p><strong>Adopt in this order:</strong> run once; commit a baseline with an expiry three months out; gate PRs on <code>--fail-on high</code>; use the three months to burn down the baseline; renew what you cannot fix with a shorter fresh expiry. The teams that succeed do exactly this. The teams that try to reach zero before turning on gating do not adopt.</p>\n<h3>The tamper-evident audit log</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> A separate, always-on log \u2014 independent of the chain \u2014 that captures every security-relevant event: scan runs, scan blocks, hook execution, baseline writes, credential store operations, policy denials, tool updates.</p>\n<p>Every entry is JSONL with a <code>prev_hash</code> and <code>hash</code> field. Break the chain \u2014 edit any entry, delete any entry, reorder entries \u2014 and <code>securegit audit verify</code> fails with the exact index of the break.</p>\n<pre><code>securegit audit show --last 100\nsecuregit audit verify\nsecuregit audit export --format cef &gt; /var/log/securegit.cef   # ArcSight/Sentinel/QRadar\nsecuregit audit export --format jsonl --output audit.jsonl      # Splunk/Datadog/Loki\n</code></pre>\n<p>The log lives under <code>~/.local/share/securegit/audit/</code> and rotates automatically. Two of its three consumers \u2014 SIEM ingest and internal review \u2014 work without any signing infrastructure at all, which is why we count the audit log as an on-ramp to Layer 3 that requires no daemon.</p>\n<h3>The compliance report</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> A single command that produces a review packet against two frameworks security teams actually cite.</p>\n<pre><code>securegit compliance report --format markdown --output compliance.md\nsecuregit compliance report --format json     --output compliance.json\n</code></pre>\n<p>Findings are grouped by <strong>OWASP Top 10 (2021)</strong> via CWE mapping, and by <strong>NIST SSDF v1.1</strong> practice (PS.1\u2013PS.3, PW.1\u2013PW.9, PO.1\u2013PO.5, RV.1\u2013RV.3). The markdown output is intended for direct inclusion in review packets; the JSON output is a starting point for a GRC integration.</p>\n<p>It is not a substitute for an audit. It is a substitute for two engineer-days of copy-and-paste when the vendor questionnaire arrives, and it is the artifact that most reliably ends the \u201cbut can you prove any of it?\u201d exchange described on the next page.</p>\n<h3>What the chain adds on top</h3>\n<p><span class=\"badge designed\">DESIGNED</span> A report generated directly from chain data \u2014 no engineer reconstruction \u2014 covering human contribution (who committed what, at which evidence tier), supply-chain state as anchored at a point in time, build provenance, <strong>and the gaps</strong>. Explicitly. Commits without receipts. Scans that did not complete. Periods with no vulnerability data. <strong>The report names its own holes</strong>, which is the property that makes it credible rather than merely impressive.</p>\n<h3>Why gap-reporting is the credibility feature</h3>\n<p>A report with no gaps is not trustworthy. Every real system has gaps \u2014 a daemon outage, a repository adopted late, a scan that timed out, a format without full parsing support.</p>\n<p>A report that shows none is either describing a system nobody uses or hiding something. An auditor who has read more than a few of these knows that immediately, and a clean report makes them look harder rather than less hard.</p>\n<p><strong>A report that says \"these forty commits are attested, these three are not, and here is why\" is stronger than one that claims forty-three.</strong> It is stronger because it demonstrates that the system is capable of noticing a problem \u2014 which is the only real evidence that its clean findings mean anything.</p>\n<hr>\n<div class=\"pullquote\">A report with no gaps is not trustworthy. Every real system has gaps. Showing yours is what makes the rest of it believable.</div>\n<hr>\n<h3>Verification without access</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> A design property worth understanding, because it comes up in every enterprise conversation.</p>\n<p>Some anchored payloads live in customer-controlled storage. A verifier without access to that storage <strong>can still verify the hash chain</strong> \u2014 the anchoring, the timestamps, the identities, and the integrity of the sequence \u2014 without reading the payload contents.</p>\n<p>This means an auditor can confirm that a record is authentic, unmodified, and correctly sequenced without you handing over your source code, your SBOM contents, or your scan output.</p>\n<p><strong>That separation \u2014 verify the chain, gate the contents \u2014 is what makes third-party audit possible without disclosure.</strong> It is a genuinely useful property and it is not obvious until you need it.</p>\n<h3>A caveat that must travel with the numbers</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Vulnerability counts drift. The same dependency set, unchanged, will show more known vulnerabilities next quarter, because advisories are published continuously.</p>\n<p><strong>This is correct behavior and it confuses people constantly.</strong> A customer sees \"3 CVEs in March, 11 in June, nothing changed in our code\" and reasonably asks what went wrong.</p>\n<p>Nothing went wrong. The world learned more.</p>\n<p>Any report generated from this data must explain that, in the report, near the numbers \u2014 not in a footnote, and not left to a support conversation. <strong>The rescan timeline is the explanation</strong>: it shows the same anchor with a sequence of point-in-time observations, which makes the drift legible as knowledge accumulation rather than as decay.</p>\n<hr></div>\n</section>\n\n\n<section class=\"section-opener\">\n  <div class=\"section-opener-part\">PART FOUR</div>\n  <div class=\"section-opener-name\">SECRETS<br>&amp; AGENTS</div>\n  <div class=\"opener-line\">Your agent needs credentials.\nYour agent must never hold them.</div><div class=\"opener-line\">Both of those are true at once,\nand the resolution is a handle.</div>\n  <div class=\"section-opener-foot\">47 \u2192 51</div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 47\u201348</div>\n  <h1>HANDLES, NOT VALUES</h1>\n  <div class=\"deck\">A narrow idea with a wide consequence: the thing doing the work gets the ability to act without ever receiving the value it acts with.</div>\n  <div class=\"two-col\"><p><strong>Standfirst:</strong> <em>Mechanism 05</em> \u00b7 <span class=\"badge shipped\">SHIPPED</span> (core) \u00b7 <span class=\"badge designed\">DESIGNED</span> (most of the surface \u2014 markers throughout)</p>\n<h2>The secret broker</h2>\n<hr>\n<h3>The problem, sharpened</h3>\n<p>An agent needs to push to a repository. Pushing requires a token. Therefore the agent needs the token.</p>\n<p>That reasoning is wrong, and finding where it is wrong is the whole design.</p>\n<p><strong>The agent needs the <em>capability</em> to push. It does not need the <em>value</em> of the token.</strong> Those are separable, and separating them is the entire product.</p>\n<p>Why it matters more with agents than with humans: a human who sees a token generally does not write it down. An agent operates in a context window that may be logged, summarized into memory, included in a trace, sent to a model provider, or echoed into terminal output that ends up in a transcript. <strong>A secret that enters an agent's context has entered every downstream system that touches that context.</strong> Not maybe \u2014 by construction.</p>\n<h3>The mechanism</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> <strong>Late binding at the execution boundary.</strong></p>\n<ol>\n<li>A user or agent requests an action, referring to a secret by a <strong>handle</strong> \u2014 a stable name, never a value.</li>\n<li>SecureGit validates the requested action against the handle's profile. Is this handle allowed to be used by this command, against this host?</li>\n<li>Only then does SecureGit resolve the actual value from the backing store.</li>\n<li>The value is injected directly into the child process, HTTP client, or credential callback.</li>\n<li>The value is <strong>redacted from all observed output</strong> \u2014 stdout, stderr, logs, error messages, and structured responses.</li>\n<li>A metadata-only audit event is recorded: which handle, which provider, which host, which command family, when, and what the result was. <strong>Never the value.</strong></li>\n</ol>\n<p><strong>The value exists for the duration of one operation, inside one process boundary, and is never rendered anywhere a human or a model can read it.</strong></p>\n<pre><code>securegit run --with-secret OPENAI_API_KEY=openai-dev -- npm test\n</code></pre>\n<p>The agent typed <code>openai-dev</code>. The agent never saw a key.</p>\n<hr>\n<div class=\"pullquote\">The agent needs the capability to push. It does not need the value of the token. Everything here follows from noticing that those are different.</div>\n<hr>\n<h3>The safety defaults</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Six rules, enforced rather than recommended:</p>\n<ol>\n<li><strong>No command prints a secret value by default.</strong></li>\n<li><strong>Structured and agent-facing responses return handles, provider metadata, and status only</strong> \u2014 never values.</li>\n<li><strong>Known values are redacted</strong> from stdout, stderr, logs, responses, and error messages.</li>\n<li><strong>Use is audited as metadata</strong>: provider, handle, target host, command family, timestamp, result.</li>\n<li><strong>Handles can be constrained</strong> by provider, host, repository, command family, expiration, and permitted environment variable names.</li>\n<li><strong>Raw reveal, export, or copy requires an explicit human-only path</strong> and is disabled for agent interfaces by default.</li>\n</ol>\n<p>Rule 6 is the one that makes the rest coherent. Without it, an agent asks for the value and gets it, and every other rule is decoration.</p>\n<h3>Writes never pass through shell history</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Storing a secret reads the value from an environment variable rather than an argument:</p>\n<pre><code>OPENAI_API_KEY_VALUE=... securegit secret set openai-dev \\\n  --value-env OPENAI_API_KEY_VALUE --yes\n</code></pre>\n<p><strong>Never <code>--value &lt;the-actual-secret&gt;</code>.</strong> Command-line arguments are visible in process listings and durable in shell history \u2014 two places a secret is very hard to remove from once it lands. This is a small ergonomic cost and it removes an entire class of leak.</p>\n<p>Production writes and deletes require both explicit confirmation and an explicit production flag. <strong>Production targets are visually distinct in prompts and logs.</strong> Making the dangerous environment <em>look</em> different is a cheap and effective control.</p>\n<h3>Provider profiles, not key-value sprawl</h3>\n<p><span class=\"badge designed\">DESIGNED</span> The intended model is that secrets are <strong>provider access profiles</strong>, not arbitrary key-value pairs.</p>\n<p>A profile records the handle, the provider, the credential kind, the host, the capabilities it grants, the commands it is permitted for, and where the value actually lives. <strong>The value stays in the backing store.</strong></p>\n<pre><code>handle:            gitlab-main\nprovider:          gitlab\nkind:              pat\nhost:              gitlab.example.com\ncapabilities:      vcs.read_repo, vcs.write_repo, vcs.create_repo\nallowed_commands:  acquire, clone, fetch, push, server\nbackend:           <span class=\"placeholder\">&lt;secrets-manager&gt;</span>\nbackend_ref:       <span class=\"placeholder\">&lt;path-in-that-manager&gt;</span>\n</code></pre>\n<p><strong>Why profiles rather than key-value:</strong> a profile carries enough metadata for the tool to apply safe defaults and to refuse obviously wrong uses. A key-value pair carries none, so every safety decision has to be made by the caller, which means it is made inconsistently or not at all.</p>\n<h3>Backends</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Metadata discovery, execution-time value resolution, guarded writes and deletes, and a local store (\u201ccredential store v2\u201d) adequate for single-user work.</p>\n<p><strong>Credential store v2, in detail.</strong> Encryption is <strong>ChaCha20-Poly1305</strong> AEAD, keyed to the machine (macOS ioreg UUID, Linux <code>/etc/machine-id</code> / DMI product UUID, Windows machine GUID). Every write is atomic \u2014 temp file, fsync, rename \u2014 with an integrity check on read; a tampered file fails closed rather than silently returning junk. An optional environment variable <code>SECUREGIT_CREDSTORE_PASSPHRASE</code> layers a passphrase-derived key on top of the machine key. The store lives at <code>~/.securegit/credentials.encrypted</code> and is not portable between machines by design; migration is deliberate, one credential at a time.</p>\n<p><span class=\"badge designed\">DESIGNED</span> The intended backend order:</p>\n<p><strong>1 \u00b7 Your existing secrets manager.</strong> For teams that already have one, this is the source of truth. SecureGit stores <strong>bindings</strong> \u2014 handle to location \u2014 not copies. Nobody should duplicate hundreds of secrets into a new tool's config directory to adopt it.</p>\n<p><strong>2 \u00b7 OS keychain.</strong> For local development. Uses the platform's native credential store.</p>\n<p><strong>3 \u00b7 The built-in encrypted store (v2, above).</strong> Compatibility and single-user work. Fine for a laptop; <strong>explicitly not the high-assurance layer</strong> and documented as such.</p>\n<p><strong>Backend endpoints are always user-configurable and never hardcoded.</strong> Cloud, local, LAN, or an internal hostname \u2014 the tool must not assume.</p>\n<h3>Discovery without disclosure</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> The most useful single property for agent workflows:</p>\n<pre><code>securegit secret discover --target dev --keys-only\n</code></pre>\n<p>Returns names, paths, comments, tags, versions, and metadata. <strong>Returns no values.</strong></p>\n<p>This gives an agent enough context to bind the right profile \u2014 to know that a key called <code>PAYMENTS_API_KEY</code> exists in the production path \u2014 without ever exposing what it is. The agent can reason about the shape of your secret inventory while being structurally incapable of reading it.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The open questions, published</h4><p>These are genuinely undecided, and printing them is how you find out what people actually need before you build the wrong thing.</p>\n<p><strong>Should local development default to the OS keychain, even before a full secrets-manager integration ships?</strong></p>\n<p><strong>Should agents be allowed to create secrets, or only bind handles a human created?</strong> The safe answer is bind-only. The safe answer is also annoying, and annoying controls get bypassed.</p>\n<p><strong>What is the minimum useful audit format before secret usage gets full chain receipts?</strong></p>\n<p><strong>Which providers ship in the first catalog?</strong> Version control, cloud, and model APIs are the obvious three. Order within them is not obvious.</p>\n<p>If you have an opinion on any of these, it will change what gets built. That is not a courtesy \u2014 it is the fastest available way to avoid building the wrong thing.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 49</div>\n  <h1>FINDING REPOSITORIES WITHOUT HANDLING TOKENS</h1>\n  <div class=\"deck\">The same handle discipline, applied to the everyday problem of \"which repository was that.\"* \u00b7 <span class=\"badge shipped\">SHIPPED</span></div>\n  <div class=\"\"><hr>\n<h3>Registering servers</h3>\n<pre><code>securegit server add github-main --platform github --api-url https://api.github.com\nsecuregit server add gitlab-main --platform gitlab --api-url https://gitlab.example.com/api/v4\n</code></pre>\n<p>Credentials are stored keyed to the server name and resolved through the normal credential chain. <strong>The token is never typed into a search command.</strong></p>\n<h3>Searching</h3>\n<pre><code>securegit server search <span class=\"placeholder\">&lt;query&gt;</span>                        # all registered servers\nsecuregit server search <span class=\"placeholder\">&lt;query&gt;</span> --server gitlab-main   # one server\nsecuregit server search <span class=\"placeholder\">&lt;query&gt;</span> --json                 # machine-readable\nsecuregit server search <span class=\"placeholder\">&lt;query&gt;</span> --limit 10             # cap per provider\nsecuregit server search <span class=\"placeholder\">&lt;query&gt;</span> --include-groups       # orgs and groups too\n</code></pre>\n<p>One command across GitHub and GitLab. Results normalize across providers: server name, provider, repository path, description, web URL, clone URLs, visibility, default branch, and last activity where the provider supplies it.</p>\n<p><code>--include-groups</code> returns organizations from GitHub and groups from GitLab, normalized into the same record shape.</p>\n<h3>The credential boundary</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Resolution order:</p>\n<ol>\n<li>A per-server environment variable</li>\n<li>The encrypted stored credential, keyed to the server name</li>\n<li>Host-based fallback from existing stored auth</li>\n</ol>\n<p><strong>The rule that matters:</strong> an agent should call <code>securegit server search</code> \u2014 or the equivalent agent-facing tool \u2014 rather than reading <code>.env</code> files or handling personal access tokens directly.</p>\n<p>This is a small thing that eliminates a large and extremely common leak path. Agents reading <code>.env</code> files to find credentials is not a hypothetical; it is a default behavior of many agent setups, and the resulting token ends up in a context window and then in a log.</p>\n<hr>\n<aside class=\"sidebar\"><h4>Compatibility surface</h4><p>Testing confirmed that GitLab instances expose GitHub-compatible API endpoints for discovery, authentication, and repository listing.</p>\n<p>Practical consequence: the same code path works against GitHub, GitHub Enterprise, GitLab, and the smaller self-hosted git servers that implement the same surface.</p>\n<p>If you run self-hosted git, this is worth ten minutes of testing before you assume you need something custom.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 50\u201351</div>\n  <h1>THE MCP SURFACE</h1>\n  <div class=\"deck\">The same tool, exposed to an agent, with the safety properties preserved rather than bolted on.* \u00b7 <span class=\"badge shipped\">SHIPPED</span></div>\n  <div class=\"two-col\"><hr>\n<h3>Why this belongs in this magazine</h3>\n<p>Every discipline in the AI-engineering transition converges here. Your agents commit code. They pull dependencies. They need credentials. They push.</p>\n<p><strong>Every one of those is a trust boundary crossing performed by something that is not a person, at a volume no person could match, with an audit trail that does not currently exist.</strong></p>\n<p>An agent doing git operations through raw shell commands has your full permissions, your tokens in its context, and no record beyond whatever the shell happened to log. An agent doing git operations through a brokered tool surface has scoped capabilities, handles instead of values, and a receipt for every crossing.</p>\n<p>That is the entire argument for this page.</p>\n<h3>The exposed surface</h3>\n<p><span class=\"badge shipped\">SHIPPED</span> Agents get a structured tool surface covering the same operations humans use \u2014 <strong>32 tools</strong> across ten families \u2014 delivered as <code>securegit-mcp</code>, a standard MCP server:</p>\n<p><strong>Repository state</strong> \u2014 status, log, show, diff, blame\n<strong>Change</strong> \u2014 add, commit, safe-commit, undo, stash, snapshot\n<strong>Branching</strong> \u2014 branch create / list / delete, checkout, merge, worktrees, stack\n<strong>Remote</strong> \u2014 push, remote list, server add / list / push, repository create\n<strong>Security</strong> \u2014 scan, scan staged, findings, posture, review, baseline, audit\n<strong>Discovery</strong> \u2014 search repositories across registered servers (<code>securegit_search_repos</code>)\n<strong>Release</strong> \u2014 tag create / list, release list, CI status, pull request list\n<strong>Backup &amp; recovery</strong> \u2014 backup add / list / push, snapshot list / restore\n<strong>Meta</strong> \u2014 update-check (throttled daily, on startup), version, health\n<strong>Escape hatch</strong> \u2014 raw git passthrough (guarded)</p>\n<h3>The four properties that make this safe</h3>\n<p><strong>1 \u00b7 Handles, not values.</strong> Agent-facing tools accept secret handles. They do not accept, return, or display credential values. An agent asks to push using <code>gitlab-main</code>; it never learns what <code>gitlab-main</code> is.</p>\n<p><strong>2 \u00b7 Guarded destructive operations.</strong> Operations that destroy or overwrite require explicit confirmation. Some are unavailable to agents entirely, by policy, regardless of what the agent believes it has been authorized to do.</p>\n<p><strong>3 \u00b7 Everything is scanned and anchored.</strong> An agent commit passes the same gates a human commit does. An agent push emits the same receipt. <strong>The chain does not distinguish between human and agent for the purpose of requiring evidence \u2014 it distinguishes for the purpose of recording which one it was.</strong></p>\n<p><strong>4 \u00b7 Provenance survives.</strong> <span class=\"badge shipped\">SHIPPED</span> Blame with chain overlay means \"which lines did an agent write\" is a query, not an investigation. In a codebase where agents contribute meaningfully, this is the difference between reviewable and unreviewable.</p>\n<hr>\n<div class=\"pullquote\">An agent with a raw shell has your permissions and your tokens. An agent with a brokered tool surface has scoped capabilities and a receipt. The difference is not a policy. It is an architecture.</div>\n<hr>\n<h3>Graph integration</h3>\n<p><span class=\"badge designed\">DESIGNED</span> Repository operations can update a knowledge graph after acquire, fetch, pull, and push \u2014 building a queryable model of code, contributors, and change over time.</p>\n<pre><code>GRAPHRAG_ENABLED=1 GRAPHRAG_API_URL=<span class=\"placeholder\">&lt;your-endpoint&gt;</span> securegit push\n</code></pre>\n<p>Off by default, and it should stay off until you have a reason.</p>\n<p><strong>The connection to the wider argument:</strong> an agent that can traverse a repository graph can answer questions that no amount of file reading answers \u2014 which commits touched the module that broke, who reviewed them, what the dependency posture was at the time. That is retrieval over structure rather than over text, and it is a different capability class.</p>\n<h3>The honest warning</h3>\n<p><strong>Do not give an agent write access to a repository you cannot afford to have rewritten, on your first day.</strong></p>\n<p>Start read-only. Let it scan, search, review, and report. Watch what it does for a week. Then grant commit access on a branch. Then, much later, push.</p>\n<p><strong>The receipts are what make that progression safe rather than a leap of faith.</strong> You are not trusting the agent. You are building a record that lets you check, and expanding scope as the record earns it.</p>\n<hr></div>\n</section>\n\n\n<section class=\"section-opener\">\n  <div class=\"section-opener-part\">PART FIVE</div>\n  <div class=\"section-opener-name\">ADOPTION</div>\n  <div class=\"opener-line\">The engineering was the easy part.</div><div class=\"opener-line\">Thirty days, twelve objections,\nand the failure modes nobody\nwrites down.</div>\n  <div class=\"section-opener-foot\">53 \u2192 61</div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 53\u201355</div>\n  <h1>SOLO, THEN TEAM, THEN ORG</h1>\n  <div class=\"deck\">A sequence that has survived contact with real teams. Each phase is independently valuable, so stopping early is a success rather than a failure.</div>\n  <div class=\"two-col\"><hr>\n<h2>DAYS 1\u20133 \u00b7 YOURSELF</h2>\n<p><strong>Goal:</strong> you are using it daily without thinking about it.</p>\n<ul>\n<li>Install. Alias it. Set up completion.</li>\n<li>Acquire three real repositories with <code>acquire</code> instead of <code>clone</code>.</li>\n<li>Scan two repositories you actually work in. Fix or rotate anything critical.</li>\n<li>Configure <code>skip_paths</code> properly.</li>\n<li>Install a pre-commit hook in one repository.</li>\n</ul>\n<p><strong>Exit criteria:</strong> you have used it five days running without consulting documentation, and the pre-commit hook has passed at least twenty times and blocked you at most once.</p>\n<p><strong>If you do not hit that:</strong> stop. Do not bring a team into a tool you are not yet fluent in. Every question they ask, you will be answering by reading docs in front of them, which is the fastest way to lose a rollout.</p>\n<h2>DAYS 4\u201310 \u00b7 ONE REPOSITORY, ONE TEAM</h2>\n<p><strong>Goal:</strong> a shared gate in one place, with the team's consent.</p>\n<ul>\n<li>Pick <strong>one</strong> repository. Not the most critical. Not a toy. Something real that a few people touch.</li>\n<li>Move hooks into version control: commit a hook directory and set <code>core.hooksPath</code>.</li>\n<li>Add scanning to CI as <strong>advisory</strong> \u2014 reporting, not blocking. (Page 58.)</li>\n<li>Run for a week. Collect false positives. Tune <code>skip_paths</code> and scanner selection.</li>\n<li><strong>Then</strong> flip CI to blocking at <code>--fail-on high</code>.</li>\n</ul>\n<p><strong>Exit criteria:</strong> one week of CI runs with a false-positive rate low enough that nobody has asked to disable it.</p>\n<p><strong>The critical sequencing rule: advisory first, blocking second.</strong> A gate that blocks before it is tuned generates an incident, a meeting, and a permanent reputation as the thing that broke the build. That reputation outlives every subsequent improvement.</p>\n<h2>DAYS 11\u201320 \u00b7 THE SECOND AND THIRD REPOSITORY</h2>\n<p><strong>Goal:</strong> prove it generalizes, and find out where it does not.</p>\n<ul>\n<li>Add two more repositories, ideally in different languages or ecosystems.</li>\n<li>Notice what breaks. Different stacks produce different false-positive profiles, and the tuning you did on a Python service will not transfer cleanly to a frontend monorepo.</li>\n<li>Write down your organization's <code>skip_paths</code> baseline and share it.</li>\n<li>Add one external plugin matched to your stack, on a <strong>schedule</strong>, not in the hot path.</li>\n</ul>\n<p><strong>Exit criteria:</strong> three repositories, three teams, no open complaints about noise.</p>\n<h2>DAYS 21\u201330 \u00b7 POLICY AND SCALE</h2>\n<p><strong>Goal:</strong> it is infrastructure, not a personal preference.</p>\n<ul>\n<li>Distribute configuration through whatever manages developer workstations. Git config is the intended channel because every organization already has a mechanism for it.</li>\n<li>Decide your chain-layer position. <strong>Be honest about whether you need it</strong> \u2014 it requires a signing daemon, and if you have no compliance driver, Layers 1 and 2 may be your correct final state.</li>\n<li>If you do need it: stand up the daemon, set <code>chain.fail_on_unattested=warn</code> initially, and communicate before enabling \u2014 not after.</li>\n<li>Add supply-chain anchoring if procurement or audit is driving.</li>\n</ul>\n<p><strong>Exit criteria:</strong> a new engineer joining gets SecureGit configured by your standard onboarding, without anyone explaining what it is.</p>\n<hr>\n<div class=\"pullquote\">Advisory first. Blocking second. A gate that blocks before it is tuned earns a reputation that outlives every improvement you make to it afterward.</div>\n<hr>\n<h2>THE FOUR WAYS ROLLOUTS DIE</h2>\n<p>Each of these has killed a real adoption. Each has a specific counter.</p>\n<p><strong>1 \u00b7 The noisy first scan.</strong> Someone runs it on a mature repository, gets four hundred findings, concludes the tool is useless, and tells everyone.\n<strong>Counter:</strong> never let the first team-visible scan be untuned. Configure <code>skip_paths</code> and <code>--fail-on high</code> before anyone else sees output. Gate on new findings, not total findings.</p>\n<p><strong>2 \u00b7 The blocking gate nobody agreed to.</strong> Enabled on a Friday. Blocks a release. The release goes out with the gate disabled and it never comes back.\n<strong>Counter:</strong> advisory for a full week, minimum. Announce the switch to blocking with a date. Let people object beforehand \u2014 the objections are usually right and always cheaper before than after.</p>\n<p><strong>3 \u00b7 The champion leaves.</strong> One enthusiastic engineer set it all up. They change teams. Configuration rots, nobody knows how it works, it gets removed in a cleanup.\n<strong>Counter:</strong> version-controlled hooks, documented <code>skip_paths</code> baseline, CI configuration in the repository. If it only lives in one person's shell, it dies with their tenure.</p>\n<p><strong>4 \u00b7 The chain layer adopted too early.</strong> A team with no compliance requirement stands up a signing daemon, hits the first-push block, spends a week on it, and concludes the whole product is heavy.\n<strong>Counter:</strong> Layers 1 and 2 are a complete adoption. <strong>Do not adopt Layer 3 without a specific question you need to answer for someone outside your team.</strong> If you cannot name the person who will ask, you are not ready and you do not need it.</p>\n<hr>\n<aside class=\"sidebar\"><h4>What to say in the first team meeting</h4><p>Keep it to four sentences. Longer pitches invite longer arguments.</p>\n<p><em>\"We're adding a scanner to CI. For the first week it reports and doesn't block, so we can see what it finds and tune out the noise. After that it'll fail the build on high-severity findings \u2014 mostly credentials. If it's noisy, tell me and I'll fix the configuration rather than lowering the bar.\"</em></p>\n<p>Note what that does not contain: zero-trust, supply chain, chain of custody, provenance, attestation. <strong>Those are real and they are not why an engineer will accept a new gate in their build.</strong> They accept it because it catches credentials and because you promised to fix the noise.</p>\n<p>Save the architecture for the people who ask.</p></aside></div>\n</section>\n\n\n<section class=\"objections-page\">\n  <div class=\"small caps signal\">Pages 56\u201357 \u00b7 Twelve Objections</div>\n  <h1>ANSWERED HONESTLY</h1>\n  <div class=\"deck\">Including three where the honest answer is that you are right.</div>\n  <div class=\"objections-grid\">\n    <div class=\"objection\">  <div class=\"objection-num\">1</div>  <div class=\"objection-heading\">We already have a secrets scanner in CI.</div>    <div class=\"objection-body\"><p>Then you have covered one of four layers, at the latest possible moment. CI catches secrets after they are committed and pushed \u2014 the credential is already in history and already on the remote. A pre-commit gate catches it before it exists anywhere durable. And CI covers none of the acquisition problem. Keep your CI scanner; add the gate that runs earlier.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">2</div>  <div class=\"objection-heading\">This will slow down our builds.</div>    <div class=\"objection-body\"><p>Built-in scanners run in the tens of milliseconds per file and scan a large repository in about half a minute. External plugins are ten to a hundred times slower. Use built-in scanners in the hot path and external plugins on a schedule, and the build cost is negligible. If you enable ten external plugins on every commit, your build will get slower and that will be a configuration decision, not a tool property. Page 20 has the numbers.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">3</div>  <div class=\"objection-heading\">Developers will just use `--no-verify`.</div>    <div class=\"objection-body\"><p>Some will, sometimes, and that is by design \u2014 the override is printed in the error message. A gate with no visible escape is removed entirely rather than bypassed occasionally, and then you have no gate and no signal. The goal is not zero bypasses. It is that bypasses are deliberate, rare, and visible.</p></div></div>\n<div class=\"objection conceded\">  <div class=\"objection-num\">4</div>  <div class=\"objection-heading\">We don't clone untrusted repositories.</div>  <div class=\"objection-concession\">You may be right.</div>  <div class=\"objection-body\"><p>If your engineers only ever work in repositories your organization owns, on infrastructure you control, the acquisition layer is not for you. Adopt scanning and skip acquisition. But check the actual behavior first \u2014 including what your CI pulls, what your build tooling fetches, and what your agents clone. The answer is often different from the policy.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">5</div>  <div class=\"objection-heading\">This is another tool to maintain.</div>    <div class=\"objection-body\"><p>It is one binary and one config directory. No daemon, no service, no background process, for Layers 1 and 2. Uninstall is deleting two things. The chain layer does require infrastructure, and that is precisely why it is Layer 3 and optional.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">6</div>  <div class=\"objection-heading\">Our security team will need to approve it.</div>    <div class=\"objection-body\"><p>They should. Hand them page 8 (what it is and is not), page 30 (the plugin trust problem, stated against our own interest), page 44 (SARIF/GitLab SAST reports, the audit log, the compliance report against OWASP Top 10 and NIST SSDF v1.1), and page 63 (the maturity table). The single artifact that most reliably ends the \u201ccan you prove any of it?\u201d exchange is <code>securegit compliance report</code>, and it takes one command to produce. A tool that publishes its own weak points evaluates faster than one that does not, because the reviewer's first job is finding what you did not tell them.</p></div></div>\n<div class=\"objection conceded\">  <div class=\"objection-num\">7</div>  <div class=\"objection-heading\">The false positives will bury us.</div>  <div class=\"objection-concession\">Partly right.</div>  <div class=\"objection-body\"><p>The first scan of a mature repository will be noisy. Every scanner is. The entropy scanner in particular trades false positives for catching credential formats no pattern list knows yet. The fix is tuning, not tolerance \u2014 page 60 is a full playbook. But if you adopt this without tuning it, you will be buried, and that outcome is on the adoption, not on you.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">8</div>  <div class=\"objection-heading\">We're a small team, this is enterprise stuff.</div>    <div class=\"objection-body\"><p>Layers 1 and 2 are a solo-developer adoption and cost you ten minutes. Layers 3 and 4 are enterprise and you should skip them until someone external asks a question you cannot answer. Nothing about the first two layers requires a team, a process, or a budget.</p></div></div>\n<div class=\"objection conceded\">  <div class=\"objection-num\">9</div>  <div class=\"objection-heading\">What happens when this project is abandoned?</div>  <div class=\"objection-concession\">Legitimate, and worth answering directly.</div>  <div class=\"objection-body\"><p>Your repositories remain normal git repositories. Acquisition produces standard git. Receipts live in commit trailers, which are plain text in your history and readable with <code>git log</code> forever, daemon or no daemon. Scan output is JSON. <strong>There is no proprietary format and no lock-in anywhere in the design</strong> \u2014 which is a deliberate property precisely because this objection is correct to raise about any tool.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">10</div>  <div class=\"objection-heading\">We need this to work air-gapped.</div>    <div class=\"objection-body\"><p>Scanning, acquisition, credential store v2, the audit log, and the compliance report all work offline today. Vulnerability lookups support a local mirror, with the mirror's snapshot date recorded in the receipt. <code>securegit update-check</code> is throttled and offline-tolerant \u2014 it warns, it does not phone home to gate. Chain offline mode (fully offline receipt anchoring) is <span class=\"badge designed\">DESIGNED</span> and not yet shipped \u2014 if air-gap chain-of-custody is a hard requirement, that is the one real gap today and you should plan around it rather than around a roadmap.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">11</div>  <div class=\"objection-heading\">Can it handle our monorepo?</div>    <div class=\"objection-body\"><p>Partly. Scanning is fast per file and the walker parallelizes across cores, but there is no incremental cache \u2014 <span class=\"badge planned\">PLANNED</span>. On a very large monorepo, scan the diff rather than the tree, gate on <code>--baseline</code> for existing findings, and use <code>skip_paths</code> aggressively. If you need full-tree scans of a multi-gigabyte repository on every commit, this is not there yet and pretending otherwise would waste your time.</p></div></div>\n<div class=\"objection\">  <div class=\"objection-num\">12</div>  <div class=\"objection-heading\">Why not just use gitleaks and cosign?</div>    <div class=\"objection-body\"><p>Do, if that is what you need. Gitleaks is excellent at secrets. Signing tools are excellent at signing \u2014 in fact SecureGit uses cosign itself for its own release signatures. SecureGit's contribution is the acquisition boundary (which neither addresses), the eleven scanners that are not <code>secrets</code>, the SARIF/GitLab SAST/audit/compliance pipeline in one binary, and the chain that links commit, scan, dependency, and push into one queryable evidence trail rather than four disconnected outputs. <strong>If you do not need the chain and you have secrets covered, you may only want the acquisition layer.</strong> That is a legitimate outcome and we would rather you adopt one layer than reject four.</p>\n<hr></div></div>\n  </div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 58\u201359</div>\n  <h1>GATES PEOPLE DO NOT DISABLE</h1>\n  <div class=\"deck\">CI is where security tooling goes to be turned off. The difference is entirely in how you introduce it.</div>\n  <div class=\"two-col\"><hr>\n<h3>The basic integration</h3>\n<pre><code>name: Security Scan\non: [push, pull_request]\n\njobs:\n  scan:\n    runs-on: ubuntu-latest\n    permissions:\n      security-events: write   # for SARIF upload\n      contents: read\n    steps:\n      - uses: actions/checkout@v4\n      - name: Install SecureGit\n        run: curl -fsSL https://<span class=\"placeholder\">&lt;release-host&gt;</span>/securegit/install.sh | sh\n      - name: Scan (report + gate in one step)\n        run: |\n          securegit scan . \\\n            --format sarif --report-output securegit.sarif \\\n            --baseline .securegit/baseline.json \\\n            --fail-on high\n      - name: Upload to GitHub Advanced Security\n        if: always()\n        uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: securegit.sarif\n</code></pre>\n<p><strong>In production, do not curl-pipe-shell in CI.</strong> Pin a version, verify a checksum, or use a container image. A security scan step that installs itself from an unpinned URL is a supply-chain finding inside your supply-chain control, and a reviewer will find it.</p>\n<h3>GitLab equivalent</h3>\n<pre><code>security-scan:\n  image: registry.example.com/securegit:0.12\n  script:\n    - securegit scan . --format gitlab --report-output gl-sast-report.json\n        --baseline .securegit/baseline.json --fail-on high\n  artifacts:\n    reports:\n      sast: gl-sast-report.json\n</code></pre>\n<p>The <code>sast</code> artifact lights up GitLab's security dashboard and MR widget with zero further plumbing.</p>\n<h3>The four-stage introduction</h3>\n<p><strong>Stage 1 \u00b7 Advisory, informational.</strong> Scan runs, prints findings, always exits zero. One week minimum. You are collecting the false-positive profile of your actual codebase, which you cannot predict.</p>\n<p><strong>Stage 2 \u00b7 Advisory, annotated.</strong> Findings appear as PR annotations. Still non-blocking. People start fixing things voluntarily because the finding is in front of them at the moment they are looking at the code.</p>\n<p><strong>Stage 3 \u00b7 Blocking on new findings only.</strong> Scan the diff, not the tree. New high-severity findings fail; existing ones do not. <strong>This is the sustainable steady state for most teams</strong>, and many should stop here permanently.</p>\n<p><strong>Stage 4 \u00b7 Blocking on total.</strong> Only after the backlog is burned down. Many teams never reach this and should not feel bad about it.</p>\n<h3>Scan the diff, not the tree</h3>\n<p>The single most important CI configuration decision:</p>\n<pre><code>git diff origin/main --name-only | xargs securegit scan --fail-on high\n</code></pre>\n<p><strong>Three reasons this is right, and they compound:</strong></p>\n<p><strong>Speed.</strong> Seconds instead of minutes.\n<strong>Relevance.</strong> The author can act on it. A finding in code they did not touch is somebody else's problem, delivered at the worst possible moment.\n<strong>Fairness.</strong> Nobody should be blocked by a finding that predates their branch. This is the one that determines whether the gate is perceived as reasonable, and perception determines survival.</p>\n<h3>Where each gate belongs</h3>\n<table>\n<thead>\n<tr>\n<th>Gate</th>\n<th>Scope</th>\n<th>Threshold</th>\n<th>Speed</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Pre-commit</td>\n<td>staged only</td>\n<td><code>high</code></td>\n<td>under a second</td>\n</tr>\n<tr>\n<td>Pre-push</td>\n<td>push range</td>\n<td><code>critical</code></td>\n<td>seconds</td>\n</tr>\n<tr>\n<td>PR / CI</td>\n<td>diff vs. main</td>\n<td><code>high</code></td>\n<td>seconds</td>\n</tr>\n<tr>\n<td>Nightly</td>\n<td>full tree, all plugins</td>\n<td>report only</td>\n<td>minutes</td>\n</tr>\n<tr>\n<td>Release</td>\n<td>full tree + SBOM + CVE</td>\n<td><code>critical</code></td>\n<td>minutes</td>\n</tr>\n</tbody>\n</table>\n<p><strong>Note the nightly row reports rather than blocks.</strong> Nightly is where you run everything expensive and let it be noisy, because nobody is waiting on it. That is the correct home for the deep plugin set.</p>\n<hr>\n<aside class=\"sidebar\"><h4>The metric to watch</h4><p>Do not track findings. Track <strong>the disable rate</strong>.</p>\n<p>Count how often people use <code>--no-verify</code>, skip the CI step, or add a suppression. That number is the real health of your rollout, and it moves before anything else does.</p>\n<p>A rising disable rate means the gate is producing more friction than perceived value, and it means it <em>now</em> \u2014 weeks before anyone raises it in a meeting, and months before someone removes the gate in a cleanup PR.</p>\n<p>Findings count measures your codebase. Disable rate measures your adoption. Only one of them tells you whether the tool will still be here next quarter.</p></aside></div>\n</section>\n\n\n<section class=\"page-break feature-page\">\n  <div class=\"small caps graphite\">Page 60\u201361</div>\n  <h1>MAKING IT QUIET</h1>\n  <div class=\"deck\">A tool that cries wolf gets disabled. Driving noise down is not maintenance \u2014 it is the work that determines whether any of this survives.</div>\n  <div class=\"two-col\"><hr>\n<h2>THE FALSE-POSITIVE PLAYBOOK</h2>\n<h3>Step 1 \u00b7 Classify before you suppress</h3>\n<p>Every finding you are about to dismiss is one of four things. <strong>Naming which one changes what you do about it.</strong></p>\n<p><strong>A \u00b7 Wrong path.</strong> Vendor code, dependencies, build output, fixtures. Not your code, not your problem. \u2192 <code>skip_paths</code>.</p>\n<p><strong>B \u00b7 Wrong scanner for this stack.</strong> A Python security scanner on a Go repository. \u2192 Disable the plugin for this repository.</p>\n<p><strong>C \u00b7 Genuinely a pattern, deliberately used.</strong> Dynamic execution you meant to write. \u2192 Suppress narrowly, at that location, with a comment explaining why. <strong>Not repository-wide.</strong></p>\n<p><strong>D \u00b7 High entropy, not a secret.</strong> Hashes, test vectors, base64 assets, minified bundles. \u2192 <code>skip_paths</code> for asset directories; narrow suppression for individual cases.</p>\n<p><strong>Never suppress category (C) globally.</strong> \"We use dynamic execution in one place\" becomes \"we no longer detect dynamic execution anywhere,\" and the next occurrence is invisible.</p>\n<h3>Step 2 \u00b7 Fix the paths first</h3>\n<p>Most first-week noise is path noise. This one setting resolves the majority of it:</p>\n<pre><code>export SECUREGIT_SKIP_PATHS=&quot;**/node_modules/**:**/vendor/**:**/target/**:**/dist/**:**/build/**:**/.venv/**:**/testdata/**&quot;\n</code></pre>\n<p>Or per-invocation:</p>\n<pre><code>securegit scan . --skip-paths &quot;**/node_modules/**,**/vendor/**&quot;\n</code></pre>\n<p><strong>Write your organization's baseline down and share it.</strong> Every team rediscovering this independently is wasted effort and inconsistent results.</p>\n<h3>Step 3 \u00b7 Right-size the scanner set</h3>\n<pre><code>securegit scan . --plugins secrets,patterns\n</code></pre>\n<p>For a commit gate, <code>secrets</code> and <code>patterns</code> are usually the right pair. <strong><code>entropy</code> belongs in scheduled scans</strong>, not in the path where someone is trying to commit a fix at 6pm.</p>\n<h3>Step 4 \u00b7 Tune the threshold, not the coverage</h3>\n<p>Prefer <code>--fail-on high</code> over disabling scanners. Keep the coverage and raise the bar. You still see the medium findings; they just do not block. Disabling a scanner loses information permanently; raising a threshold loses only the interruption.</p>\n<h3>Step 5 \u00b7 Re-measure</h3>\n<pre><code>securegit scan . --format json | jq '[.findings[] | .severity] | group_by(.) | map({severity: .[0], count: length})'\n</code></pre>\n<p><strong>Target: under five findings per hundred files at <code>high</code> or above on a tuned repository.</strong> Above that, keep tuning. Below that, you are in the range where people read findings instead of dismissing them.</p>\n<hr>\n<div class=\"pullquote\">Under five findings per hundred files is where people read them. Above that, they dismiss them. There is no third behavior.</div>\n<hr>\n<h2>COMMON PROBLEMS</h2>\n<p><strong>Scan is slow.</strong>\nScanning the tree instead of the diff. Or scanning a dependency directory. Or too many external plugins in the hot path. In that order of likelihood.</p>\n<p><strong>Acquire fails on a private repository.</strong>\nCredentials not registered. <code>securegit server add</code>, then retry. Page 49.</p>\n<p><strong>Pre-commit hook does not run.</strong>\nNot executable (<code>chmod +x</code>), or <code>core.hooksPath</code> points elsewhere, or you are committing from a GUI that bypasses hooks \u2014 which many do, silently.</p>\n<p><strong>Push blocked with \"no chain receipt.\"</strong>\nExpected on a repository adopted after its history began. Either <code>securegit attest</code> the range, or set <code>chain.fail_on_unattested=warn</code>. Page 36.</p>\n<p><strong>Plugin does not run.</strong>\nNot executable, not in <code>~/.config/securegit/plugins/</code>, or its output is not valid JSON. Test it standalone first: run it against one file and check that it prints parseable JSON.</p>\n<p><strong>Findings differ between local and CI.</strong>\nDifferent scanner sets, different <code>skip_paths</code>, or different versions. <strong>Pin the version in CI and share the configuration.</strong> Divergence between local and CI results destroys trust in both.</p>\n<p><strong>Scan finds nothing on a repository you know has issues.</strong>\nCheck <code>--include-git</code>. Check that your <code>skip_paths</code> is not excluding the code. Check the version. In that order.</p>\n<hr>\n<aside class=\"sidebar\"><h4>When to give up on a repository</h4><p>Some repositories are not worth gating, and admitting that is better than a permanently ignored gate.</p>\n<p>A twelve-year-old codebase with vendored dependencies, generated files checked in, and test fixtures full of realistic-looking fake credentials may produce noise you cannot tune below the usable threshold in reasonable time.</p>\n<p><strong>For those: scan on a schedule, report, do not block.</strong> Put the gate on new repositories and on the parts of the old one that are actively developed.</p>\n<p>A gate that is always red is not a gate. It is a broken light that everyone has learned to walk past, and it makes every other gate you install less credible.</p></aside></div>\n</section>\n\n\n<section class=\"command-card\">\n  <div class=\"small caps signal\">Page 62 \u00b7 Command Reference Card</div>\n  <h1>QUICK REFERENCE</h1>\n  <div class=\"command-card-body\"><pre class=\"cmd-group\"><code>ACQUISITION\n  securegit acquire <span class=\"placeholder\">&lt;url&gt;</span> <span class=\"placeholder\">&lt;path&gt;</span>          acquire safely (zip+history)\n  securegit acquire <span class=\"placeholder\">&lt;url&gt;</span> .               acquire here</code></pre>\n<pre class=\"cmd-group\"><code>SCANNING  \u00b7  12 built-in scanners\n  securegit scan <span class=\"placeholder\">&lt;path&gt;</span>                   scan a path\n  securegit scan --staged                 scan staged changes\n  securegit scan . --fail-on high         non-zero exit at high+\n  securegit scan . --min-severity high    display high+ only\n  securegit scan . --include-git          include .git directory\n  securegit scan . --format json          machine-readable\n  securegit scan . --format sarif  --report-output f.sarif   GHAS/Azure/Sonar\n  securegit scan . --format gitlab --report-output gl.json   GitLab SAST\n  securegit scan . --skip-paths \"<span class=\"placeholder\">&lt;glob&gt;</span>\"  exclude paths\n  securegit scan . --plugins a,b          named scanners only\n  securegit scan . --write-baseline \"<span class=\"placeholder\">&lt;reason&gt;</span>\" --expires <span class=\"placeholder\">&lt;RFC3339&gt;</span>\n  securegit scan . --baseline .securegit/baseline.json</code></pre>\n<pre class=\"cmd-group\"><code>EVERYDAY\n  securegit status / add / diff / log / blame\n  securegit safe-commit -m \"<span class=\"placeholder\">&lt;msg&gt;</span>\"        scan, then commit\n  securegit commit -m \"<span class=\"placeholder\">&lt;msg&gt;</span>\"             commit with receipt\n  securegit commit --ai                   AI-suggested commit message\n  securegit findings / review / posture\n  securegit undo                          universal undo (18 mutating cmds)\n  securegit snapshot                      commit-independent restore points\n  securegit snapshot list / restore <span class=\"placeholder\">&lt;id&gt;</span></code></pre>\n<pre class=\"cmd-group\"><code>BRANCHING &amp; STACKS\n  securegit branch-create / branch-list / checkout / merge\n  securegit worktree-add <span class=\"placeholder\">&lt;path&gt;</span>\n  securegit conflicts / resolve            conflict inspection &amp; resolution\n  securegit stack                          stacked-diff workflow\n  securegit absorb                         git-absorb port (auto-fixup)</code></pre>\n<pre class=\"cmd-group\"><code>REMOTE\n  securegit push\n  securegit server add <span class=\"placeholder\">&lt;name&gt;</span> --platform <span class=\"placeholder\">&lt;p&gt;</span> --api-url <span class=\"placeholder\">&lt;url&gt;</span>\n  securegit server list / search <span class=\"placeholder\">&lt;query&gt;</span>\n  securegit repo-create <span class=\"placeholder\">&lt;name&gt;</span></code></pre>\n<pre class=\"cmd-group\"><code>SETTINGS  \u00b7  layered: /etc \u2192 user \u2192 repo \u2192 env\n  securegit settings show / path / init\n  /etc/securegit/config.toml               org policy (min_fail_on, locked_keys, audit_required)\n  ~/.config/securegit/config.toml          user\n  <span class=\"placeholder\">&lt;repo&gt;</span>/.securegit/config.toml            repo</code></pre>\n<pre class=\"cmd-group\"><code>AUDIT  \u00b7  hash-chained, tamper-evident\n  securegit audit show --last <span class=\"placeholder\">&lt;N&gt;</span>\n  securegit audit verify\n  securegit audit export --format jsonl --output audit.jsonl\n  securegit audit export --format cef  &gt; securegit.cef</code></pre>\n<pre class=\"cmd-group\"><code>COMPLIANCE  \u00b7  OWASP Top 10 (2021) via CWE  \u00b7  NIST SSDF v1.1\n  securegit compliance report --format markdown --output compliance.md\n  securegit compliance report --format json     --output compliance.json</code></pre>\n<pre class=\"cmd-group\"><code>PLUGINS &amp; UPDATES\n  securegit plugin list / install <span class=\"placeholder\">&lt;name&gt;</span> / info <span class=\"placeholder\">&lt;name&gt;</span>\n  securegit plugin check-updates\n  securegit plugin update --all\n  securegit update-check                   throttled daily on startup</code></pre>\n<pre class=\"cmd-group\"><code>WORKFLOWS  \u00b7  20 shipped scripts, 4-level config override\n  securegit workflow list / info <span class=\"placeholder\">&lt;name&gt;</span> / install\n  securegit workflow run <span class=\"placeholder\">&lt;name&gt;</span> --dry-run</code></pre>\n<pre class=\"cmd-group\"><code>SECRETS  \u00b7  handles, never values  \u00b7  ChaCha20-Poly1305 store\n  securegit secret add <span class=\"placeholder\">&lt;handle&gt;</span> --provider <span class=\"placeholder\">&lt;p&gt;</span>\n  securegit secret list / info / test <span class=\"placeholder\">&lt;handle&gt;</span>\n  securegit secret discover --target <span class=\"placeholder\">&lt;t&gt;</span> --keys-only\n  securegit secret rotate <span class=\"placeholder\">&lt;handle&gt;</span>\n  securegit run --with-secret NAME=<span class=\"placeholder\">&lt;handle&gt;</span> -- <span class=\"placeholder\">&lt;cmd&gt;</span></code></pre>\n<pre class=\"cmd-group\"><code>CHAIN &amp; SUPPLY\n  securegit attest <span class=\"placeholder\">&lt;sha&gt;</span>  |  --since <span class=\"placeholder\">&lt;sha&gt;</span>\n  securegit sbom emit [--with-osv-scan]\n  securegit sbom rescan <span class=\"placeholder\">&lt;receipt-id&gt;</span></code></pre>\n<pre class=\"cmd-group\"><code>ANALYTICS &amp; AI CONTEXT\n  securegit gain                           workflow adoption analytics\n  securegit <span class=\"placeholder\">&lt;cmd&gt;</span> --compact                60\u201390% token reduction for LLM contexts</code></pre>\n<pre class=\"cmd-group\"><code>ESCAPE HATCH &amp; HELP\n  securegit git-raw -- <span class=\"placeholder\">&lt;any git command&gt;</span>\n  securegit --help / <span class=\"placeholder\">&lt;command&gt;</span> --help / --version</code></pre>\n<pre class=\"cmd-group\"><code>CONFIG PATHS\n  /etc/securegit/config.toml               org policy (fleet)\n  ~/.config/securegit/config.toml          user\n  ~/.config/securegit/plugins/             external plugins\n  ~/.config/securegit/workflows/           workflow overrides\n  ~/.securegit/credentials.encrypted       credential store v2\n  ~/.local/share/securegit/audit/          audit log</code></pre>\n<pre class=\"cmd-group\"><code>ENVIRONMENT\n  SECUREGIT_FAIL_ON=high\n  SECUREGIT_SKIP_PATHS=\"<span class=\"placeholder\">&lt;glob&gt;</span>:<span class=\"placeholder\">&lt;glob&gt;</span>\"\n  SECUREGIT_CA_BUNDLE=/etc/ssl/corp-bundle.pem\n  SECUREGIT_PROXY=http://proxy.corp.example.com:3128\n  SECUREGIT_CREDSTORE_PASSPHRASE=<span class=\"placeholder\">&lt;passphrase&gt;</span>\n  HTTP_PROXY / HTTPS_PROXY / NO_PROXY\n  SECUREGIT_VERBOSE=1  \u00b7  SECUREGIT_WORKFLOW_TIPS=0</code></pre></div>\n</section>\n\n\n<section class=\"maturity-page\">\n  <div class=\"small caps signal\">Page 63 \u00b7 Maturity Table &amp; Glossary</div>\n  <h1>WHAT IS REAL TODAY</h1>\n  <div class=\"deck\">Every capability in this issue, in one table. This is the page to hand your security reviewer.</div>\n  <div class=\"maturity-body\"><p><strong>AT A GLANCE:</strong> As of v0.12.16, every capability that was <span class=\"badge designed\">DESIGNED</span> in the first two categories has shipped except two (offline chain, batch receipt lookup). The maturity table is denser at the top than at the bottom, which is the direction any honest table should move.</p>\n<table>\n<thead>\n<tr>\n<th>Capability</th>\n<th>State</th>\n</tr>\n</thead>\n<tbody>\n<tr class=\"row-shipped\">\n<td>Archive-first acquisition (zip+history / zip-only / bare), hooks stripped</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Sanitization report on acquire</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Twelve</strong> built-in scanners (<code>secrets</code>, <code>patterns</code>, <code>entropy</code>, <code>binary</code>, <code>encoding</code> / Trojan Source, <code>supply-chain</code>, <code>ci-cd</code>, <code>container</code>, <code>iac</code>, <code>deserialization</code>, <code>dangerous-files</code>, <code>git-internals</code>)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>External plugin system (any language) + managed update manifest</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Staged / diff / tree scanning, severity thresholds</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Output formats: pretty, JSON, <strong>SARIF 2.1.0</strong>, <strong>GitLab SAST v15</strong></td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><code>--report-output</code> (report even when gate fails)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Baselines</strong> with stable fingerprints, RFC3339 <code>expires</code>, <code>--no-baseline</code></td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Pre-commit and pre-push hook patterns</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Workflow scripts with dry-run \u2014 <strong>20 shipped</strong>, 4-level config override, language auto-detection</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Server registration and cross-provider search</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Credential resolution chain</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Agent tool surface (<strong>32 MCP tools</strong>, <code>securegit-mcp</code>)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Secret handles, execution-boundary resolution, redaction</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Metadata-only secret audit events</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Guarded production writes and deletes</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Credential store v2</strong> \u2014 ChaCha20-Poly1305, machine-bound, atomic, fails closed</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Optional passphrase overlay</strong> (<code>SECUREGIT_CREDSTORE_PASSPHRASE</code>)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Chain receipts: acquire, commit, push, merge, scan</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Receipt storage in commit trailers + daemon store</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Tier A / Tier B evidence distinction</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Push gate on unattested commits</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Force-push bypass receipts + joint approval</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Layered configuration</strong> (<code>/etc</code> \u2192 user \u2192 repo \u2192 env)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Org policy</strong> (<code>min_fail_on</code>, <code>locked_keys</code>, <code>audit_required</code>)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit settings</code> command</strong> (<code>show</code> / <code>path</code> / <code>init</code>)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Hash-chained audit log</strong> with <code>audit verify</code></td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Audit export</strong> \u2014 JSONL (SIEM), <strong>CEF</strong> (ArcSight/Sentinel/QRadar)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Compliance report</strong> \u2014 OWASP Top 10 (2021) via CWE + NIST SSDF v1.1</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>SBOM anchoring</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Vulnerability-database lookup (opt-in)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><code>scan_completeness</code> field</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Offline vulnerability mirror</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>SBOM rescan timeline</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Lock-file change receipts (major formats)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>License detection (best-effort, non-blocking)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Blame with per-line chain overlay</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Diamond merge receipts + multi-author approval</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Signed releases</strong> \u2014 CycloneDX SBOM + <code>SHA256SUMS</code> + Sigstore keyless (cosign)</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong>Corporate networks</strong> \u2014 OS trust store, <code>SECUREGIT_CA_BUNDLE</code>, standard proxy env</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit undo</code></strong> \u2014 universal, 18 mutating commands journaled</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit snapshot</code></strong> \u2014 continuous, commit-independent restore points</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit commit --ai</code></strong> \u2014 AI-suggested commit messages</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit conflicts</code> / <code>resolve</code></strong> \u2014 first-class conflict UX</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit stack</code></strong> \u2014 stacked-diff workflow</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit absorb</code></strong> \u2014 auto-fixup, port of git-absorb</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>--compact</code> mode</strong> \u2014 60\u201390% token reduction for LLM contexts</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit gain</code></strong> \u2014 workflow adoption analytics</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td><strong><code>securegit update-check</code></strong> \u2014 throttled daily startup check for binary, MCP, plugins</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-shipped\">\n<td>Secrets-manager backend: metadata discovery + value resolution</td>\n<td><span class=\"badge shipped\">SHIPPED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>Chain offline store with sync-on-reconnect</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>Batch receipt lookup for large pushes</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>Integrated chain audit report generator</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>Provider profile catalog</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>OS keychain backend (Keychain / DPAPI / Secret Service)</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>Full lock-file parsing for remaining formats</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-designed\">\n<td>Repository graph integration</td>\n<td><span class=\"badge designed\">DESIGNED</span></td>\n</tr>\n<tr class=\"row-planned\">\n<td>Incremental scan cache</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n<tr class=\"row-planned\">\n<td><code>--jobs</code> parallelism control</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n<tr class=\"row-planned\">\n<td>Native dynamic (FFI) plugins</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n<tr class=\"row-planned\">\n<td>WebAssembly sandboxed plugins</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n<tr class=\"row-planned\">\n<td>Plugin marketplace</td>\n<td><span class=\"badge planned\">PLANNED</span></td>\n</tr>\n</tbody>\n</table>\n<hr>\n<h2>GLOSSARY</h2>\n<p><strong>ACQUIRE</strong> \u2014 Fetch a repository as an archive, strip hooks, scan, then convert to a normal git repository. The ordering is the security property.</p>\n<p><strong>ANCHOR</strong> \u2014 Record the content hash of an external tool's output in the chain, without bundling that tool.</p>\n<p><strong>ATTEST</strong> \u2014 Add a receipt to a commit after the fact. Produces Tier B evidence.</p>\n<p><strong>AUDIT LOG</strong> \u2014 A tamper-evident JSONL log, independent of the chain, capturing every security-relevant event with <code>prev_hash</code> linkage. Verified by <code>securegit audit verify</code>; exports to JSONL or CEF.</p>\n<p><strong>BASELINE</strong> \u2014 A file (<code>.securegit/baseline.json</code>) suppressing a known set of findings by stable fingerprint. Every entry carries reason, creator, timestamp, and optional RFC3339 expiry.</p>\n<p><strong>BUILT-IN SCANNER</strong> \u2014 One of the <strong>twelve</strong> scanners compiled into the binary. Zero startup cost.</p>\n<p><strong>CEF</strong> \u2014 Common Event Format. The audit-log export format understood by ArcSight, Microsoft Sentinel, and QRadar.</p>\n<p><strong>CHACHA20-POLY1305</strong> \u2014 The AEAD used by credential store v2. Machine-keyed by default; optionally passphrase-augmented.</p>\n<p><strong>CHAIN OF CUSTODY</strong> \u2014 A tamper-evident sequence of signed receipts linking actions to identities and content.</p>\n<p><strong>COMPLIANCE REPORT</strong> \u2014 <code>securegit compliance report</code>. Findings grouped by OWASP Top 10 (2021) via CWE and by NIST SSDF v1.1 practice.</p>\n<p><strong>COSIGN / SIGSTORE KEYLESS</strong> \u2014 The signing model used for SecureGit releases. Signatures anchor to short-lived OIDC identities rather than long-lived keys.</p>\n<p><strong>DIAMOND RECEIPT</strong> \u2014 A merge receipt encoding both parent commits, preserving the history graph inside the evidence chain.</p>\n<p><strong>EXECUTION BOUNDARY</strong> \u2014 The last moment before an operation runs; where a secret value is resolved and injected.</p>\n<p><strong>EXTERNAL PLUGIN</strong> \u2014 An executable that takes a file path and prints JSON findings. Any language. Subprocess cost.</p>\n<p><strong>FORWARD-ATTESTED (TIER A)</strong> \u2014 The signing daemon witnessed the operation as it happened.</p>\n<p><strong>HANDLE</strong> \u2014 A stable name referring to a secret. Used by humans and agents; never a value.</p>\n<p><strong>JOINT APPROVAL</strong> \u2014 A second signal required for high-stakes operations such as force-push or multi-author merge.</p>\n<p><strong>PUSH GATE</strong> \u2014 Enforcement at push time, checking that every commit in range carries a receipt.</p>\n<p><strong>RECEIPT</strong> \u2014 A signed, timestamped record binding an operation to an identity and a content hash.</p>\n<p><strong>RETRO-ATTESTED (TIER B)</strong> \u2014 A receipt added after the fact. Real evidence, weaker than Tier A, always distinguished from it.</p>\n<p><strong>SANITIZATION REPORT</strong> \u2014 The record written at acquisition describing what was fetched, stripped, and found.</p>\n<p><strong>SCAN COMPLETENESS</strong> \u2014 What fraction of a scan actually finished. Zero findings without full completeness is not a clean result.</p>\n<p><strong>SARIF 2.1.0</strong> \u2014 OASIS static-analysis report format understood by GitHub Advanced Security, Azure DevOps, SonarQube, DefectDojo.</p>\n<p><strong>SKIP PATHS</strong> \u2014 Glob patterns excluded from scanning. The primary false-positive control.</p>\n<p><strong>SNAPSHOT</strong> \u2014 A commit-independent restore point captured by <code>securegit snapshot</code>.</p>\n<p><strong>TRAILER</strong> \u2014 A key-value line in a commit message. Where receipt identifiers live so they survive without a daemon.</p>\n<p><strong>TROJAN SOURCE</strong> \u2014 CVE-2021-42574. The class of source-code attack that hides intent using BiDi override, homoglyph, or zero-width Unicode. Detected by the <code>encoding</code> scanner.</p>\n<p><strong>UNDO</strong> \u2014 <code>securegit undo</code>. Universal reversal for 18 mutating commands, backed by an operation journal separate from git reflog.</p>\n<hr></div>\n</section>\n\n\n<section class=\"back-cover\">\n  <div class=\"back-cover-idea\">You already<br>ran the code.<br>You just<br>called it<br>a clone.</div>\n  <div class=\"back-cover-mid\">BOTTLENECK \u00b7 NO. 01 \u00b7 THE UNTRUSTED CLONE<br>START AT PAGE 11. IT TAKES TEN MINUTES.</div>\n  <div class=\"back-cover-foot\">ARMYKNIFELABS \u00b7 A LIMITED SERIES</div>\n</section>\n"
  },
  "outline": [
    {
      "title": "BOTTLENECK \u2014 Issue 01 (SecureGit Edition)",
      "level": 1,
      "summary": "**Full editorial manuscript & page-by-page layout brief** Prepared for handoff to Claude Design (Adobe InDesign / Express) Companion file: `BOTTLENECK-Issue01-SECUREGIT-ArtDirection.md` ---",
      "quality": 0.105,
      "children": [
        {
          "title": "PUBLICATION FACTS",
          "level": 2,
          "summary": "| Field | Value | |---|---| | Title | **BOTTLENECK** | | Standfirst | *The magazine for engineers becoming AI engineers.* | | Issue | No. 01 \u2014 **THE UNTRUSTED CLONE** | | Cover subject | **SecureGit** \u2014 zero-trust git,",
          "quality": 0.675,
          "children": [
            {
              "title": "The editorial contract for this issue",
              "level": 3,
              "summary": "This is a magazine, not a brochure. The difference is enforced by three rules: 1. **Every feature carries a maturity marker.** `SHIPPED`, `DESIGNED`, or `PLANNED`. No exceptions, no soft language, no \"coming soon\" used to imply \"nearly here.\" A reader",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "House style (enforce throughout)",
              "level": 3,
              "summary": "- Banned words: *unlock, game-changing, revolutionize, seamless, leverage (verb), supercharge, 10x, journey, empower, cutting-edge, effortless, simply.* - \"Simply\" is banned specifically because it is the word that makes a struggling reader feel stupid. If a step is simple, it will",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGE 1 \u2014 COVER",
      "level": 1,
      "summary": "**Masthead** > BOTTLENECK **Issue line (small caps, under masthead rule)** > NO. 01 \u00b7 THE UNTRUSTED CLONE \u00b7 A LIMITED SERIES **Cover line (dominant, stacked, 4 lines, flush left)** > `git clone` > RUNS > SOMEBODY > ELSE'S CODE. **Sub-cover",
      "quality": 1.0,
      "children": []
    },
    {
      "title": "PAGE 2 \u2014 MASTHEAD & HOW TO READ THIS ISSUE",
      "level": 1,
      "summary": "**BOTTLENECK** *The magazine for engineers becoming AI engineers.* Issue 01 \u00b7 The Untrusted Clone Published by ArmyknifeLabs. **About this issue.** Most tooling magazines are written by people who want you to be impressed. This one is written by the people",
      "quality": 1.0,
      "children": []
    },
    {
      "title": "PAGE 3 \u2014 CONTENTS",
      "level": 1,
      "summary": "**ISSUE 01 \u00b7 THE UNTRUSTED CLONE** **FRONT** 04 \u2014 Editor's Letter: *The Repository Is The Attack Surface* 06 \u2014 Ninety Seconds: what actually happens when you clone 08 \u2014 What SecureGit Is, And What It Is Not **PART I \u00b7",
      "quality": 1.0,
      "children": []
    },
    {
      "title": "PAGES 4\u20135 \u2014 EDITOR'S LETTER",
      "level": 1,
      "summary": "",
      "quality": 0.0,
      "children": [
        {
          "title": "The Repository Is The Attack Surface",
          "level": 2,
          "summary": "**Deck:** *You have spent your career securing what you deploy. The thing you never secured is the moment code arrives on your machine \u2014 and that moment now happens dozens of times a week, increasingly without you watching.* --- Here",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 6\u20137 \u2014 NINETY SECONDS (INFOGRAPHIC SPREAD)",
      "level": 1,
      "summary": "**Spread headline (spans gutter):** WHAT ACTUALLY HAPPENS **Deck:** *Two timelines, same repository. The top is `git clone`. The bottom is `securegit acquire`. Read them against each other.*",
      "quality": 0.135,
      "children": [
        {
          "title": "Panel A (left page, upper) \u2014 TIMELINE ONE: `git clone`",
          "level": 2,
          "summary": "**Format:** a horizontal timeline, left to right, with events as ticks above the line and *trust state* as a colored band below it. The band starts neutral and turns signal-orange at the first execution point, staying orange to the end.",
          "quality": 0.995,
          "children": []
        },
        {
          "title": "Panel B (left page, lower) \u2014 TIMELINE TWO: `securegit acquire`",
          "level": 2,
          "summary": "| Moment | What happens | Your exposure | |---|---|---| | `t+0` | **Fetched as an archive, not as a live repo** | Inert bytes | | `t+2s` | Archive extracted to a staging path | Files on disk, no",
          "quality": 0.705,
          "children": []
        },
        {
          "title": "Panel C (right page, full) \u2014 THE THREE THINGS PEOPLE GET WRONG",
          "level": 2,
          "summary": "**Format:** three tall cards, each with a myth in condensed caps, a correction in serif body, and a one-line takeaway in mono. **MYTH 1 \u00b7 \"I only clone repos I trust.\"** You clone repos your dependencies trust, which is a",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 8\u20139 \u2014 WHAT SECUREGIT IS, AND WHAT IT IS NOT",
      "level": 1,
      "summary": "**Headline:** THE ONE-PAGE MENTAL MODEL **Deck:** *Before any command, this. Five minutes here saves an hour of confusion later.* ---",
      "quality": 0.1,
      "children": [
        {
          "title": "The one sentence",
          "level": 3,
          "summary": "**SecureGit is a git wrapper that puts a gate on the two moments code crosses a trust boundary \u2014 arrival and departure \u2014 and writes a signed receipt for each crossing.** Everything else is elaboration. If you remember one thing,",
          "quality": 0.7333333333333333,
          "children": []
        },
        {
          "title": "The four layers",
          "level": 3,
          "summary": "Think of it as four layers stacked, each usable without the ones above it. **You can adopt one layer and stop.** Most people should, at first. **LAYER 1 \u00b7 ACQUISITION** `SHIPPED` Fetch untrusted code without executing it. `securegit acquire` replaces",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "What it is not",
          "level": 3,
          "summary": "This section exists because mis-set expectations are the most common cause of abandonment, and every item below has caused a real \"wait, I thought it did X\" moment. **It is not a replacement for git.** It wraps git. Your repositories",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGE 10 \u2014 PART I: SECTION OPENER",
      "level": 1,
      "summary": "**Full-bleed. Reversed: paper type on ink ground. Type only.** > **PART ONE > DAY ONE** > Ten minutes to your first scan. > An evening to a changed workflow. > Nothing on these pages requires > a decision from anyone",
      "quality": 0.25,
      "children": []
    },
    {
      "title": "PAGE 11 \u2014 THE TEN-MINUTE QUICKSTART",
      "level": 1,
      "summary": "**Headline:** TEN MINUTES **Deck:** *Five commands. No configuration. No account. Nothing to uninstall afterward except one binary.* **DESIGN NOTE:** This page is the single most important page in the issue and should be designed as a self-contained card that survives",
      "quality": 0.34,
      "children": [
        {
          "title": "1 \u00b7 Install",
          "level": 3,
          "summary": "``` curl -fsSL https://<release-host>/securegit/install.sh | sh ``` **Before you paste that:** you are about to pipe a script from the internet into a shell, in an issue about not trusting code from the internet. We are aware of the irony",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "2 \u00b7 Acquire something real",
          "level": 3,
          "summary": "Pick a public repository you have never inspected. Small is better for a first run. ``` securegit acquire https://github.com/toml-lang/toml /tmp/first-acquire ```",
          "quality": 0.35,
          "children": []
        },
        {
          "title": "3 \u00b7 Confirm the hooks are gone",
          "level": 3,
          "summary": "``` ls -la /tmp/first-acquire/.git/hooks ``` **This should be empty.** That is the whole product in one directory listing. A normal clone of the same repository will have sample hooks in there and, from a hostile source, could have live ones.",
          "quality": 0.6666666666666666,
          "children": []
        },
        {
          "title": "4 \u00b7 Read the report",
          "level": 3,
          "summary": "``` cat /tmp/first-acquire/.securegit-report.json ```",
          "quality": 0.06666666666666667,
          "children": []
        },
        {
          "title": "5 \u00b7 Scan something you care about",
          "level": 3,
          "summary": "``` securegit scan . --fail-on high ``` Run this in a repository you actually work in. It will finish in seconds on a normal project. --- **FOOT BLOCK (reversed, full width):** > **You are done.** That is the tool. Everything",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 12\u201313 \u2014 INSTALL, AND YOUR FIRST ACQUIRE",
      "level": 1,
      "summary": "**Headline:** GETTING IT ON YOUR MACHINE **Deck:** *Longer than the quickstart, with the parts that go wrong.* ---",
      "quality": 0.09,
      "children": [
        {
          "title": "Platforms",
          "level": 3,
          "summary": "`SHIPPED` on Linux and macOS, both Intel and Apple Silicon. Windows support is **experimental** \u2014 it works, and it is not yet held to the same bar, and you should expect rough edges around path handling and shell integration. If",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The three install paths",
          "level": 3,
          "summary": "**Path A \u2014 release binary.** Download, verify, place on `PATH`. Most auditable. Recommended for anyone who reads the rest of this magazine and takes it seriously. Every release ships with a **CycloneDX SBOM**, `SHA256SUMS`, and a **Sigstore keyless signature (cosign)**:",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "What gets created",
          "level": 3,
          "summary": "``` ~/.config/securegit/config.toml ~/.config/securegit/plugins/ ~/.local/share/securegit/audit/ # tamper-evident audit log ``` No daemon, no service, no background process, nothing in your login items. Uninstall is deleting the binary and those directories.",
          "quality": 0.48333333333333334,
          "children": []
        },
        {
          "title": "Corporate networks",
          "level": 3,
          "summary": "SecureGit reads the OS trust store by default, so a TLS-inspecting corporate proxy with a properly installed root CA \u201cjust works.\u201d For private CAs held in a file rather than the trust store, point `SECUREGIT_CA_BUNDLE=/etc/ssl/corp-bundle.pem` at a PEM bundle (mirroring",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Your first acquire, in detail",
          "level": 3,
          "summary": "``` securegit acquire <url> <destination> ``` You can also acquire into the current directory: ``` securegit acquire <url> . ``` **What happens, in order:** 1. The remote is resolved and the content is fetched **as an archive**, not as a",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "What you can do with the result",
          "level": 3,
          "summary": "Everything. It is a normal repository. ``` cd /tmp/first-acquire git log git diff git checkout -b my-branch ``` There is no lock-in, no proprietary format, no wrapper you have to keep using. If you delete SecureGit tomorrow, the repository you",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 14\u201315 \u2014 YOUR FIRST SCAN, AND HOW TO READ A FINDING",
      "level": 1,
      "summary": "**Headline:** WHAT THE SCANNER SEES **Deck:** *Findings are only useful if you can act on them within about ten seconds of reading them. Here is how to get to that.* ---",
      "quality": 0.155,
      "children": [
        {
          "title": "The command surface",
          "level": 3,
          "summary": "``` securegit scan <path> securegit scan . securegit scan . --fail-on high securegit scan . --min-severity high securegit scan . --include-git securegit scan . --format json securegit scan . --format sarif --report-output f.sarif securegit scan . --format gitlab --report-output gl.json",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The twelve built-in scanners",
          "level": 3,
          "summary": "All `SHIPPED`, all compiled into the binary, all sub-millisecond to low-millisecond per file. Load automatically; no configuration required. | Scanner | Looks for | Typical severity | |---|---|---| | `secrets` | AWS keys, GitHub/Slack tokens, private keys, DB passwords, OpenAI",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Anatomy of a finding",
          "level": 3,
          "summary": "**DESIGN NOTE:** render as an annotated specimen, similar to a museum label. A single finding, blown up, with callout lines to marginal annotations. ``` CRITICAL secrets src/config/settings.py:14 AWS Access Key AKIA\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 \u2192 Rotate this credential, then remove from history. ```",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The ten-second triage",
          "level": 3,
          "summary": "For each finding, in order: 1. **Is it in a test fixture or an example?** Very common, usually benign, and the reason `--skip-paths` exists. But check that the example key is actually fake \u2014 real keys get pasted into examples",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 16\u201317 \u2014 MAKING IT FEEL LIKE GIT",
      "level": 1,
      "summary": "**Headline:** MUSCLE MEMORY **Deck:** *The single biggest predictor of whether you are still using this in a month is whether typing it costs you anything. Spend thirty seconds now.* --- The command is called `securegit` because the name should be",
      "quality": 0.425,
      "children": [
        {
          "title": "Bash / Zsh",
          "level": 3,
          "summary": "Add to `~/.bashrc` or `~/.zshrc`: ``` alias sgit='securegit' alias sg='securegit' ```",
          "quality": 0.18333333333333332,
          "children": []
        },
        {
          "title": "Fish",
          "level": 3,
          "summary": "Add to `~/.config/fish/config.fish`: ``` alias sgit='securegit' alias sg='securegit' ```",
          "quality": 0.15,
          "children": []
        },
        {
          "title": "PowerShell",
          "level": 3,
          "summary": "Add to your profile: ``` Set-Alias -Name sgit -Value securegit Set-Alias -Name sg -Value securegit ```",
          "quality": 0.26666666666666666,
          "children": []
        },
        {
          "title": "Tab completion follows the alias",
          "level": 3,
          "summary": "```",
          "quality": 0.016666666666666666,
          "children": []
        }
      ]
    },
    {
      "title": "bash",
      "level": 1,
      "summary": "complete -F _securegit sgit complete -F _securegit sg",
      "quality": 0.04,
      "children": []
    },
    {
      "title": "zsh",
      "level": 1,
      "summary": "compdef sgit=securegit compdef sg=securegit ``` Do this. An alias without completion is worse than no alias, because you lose discoverability and you will forget the flags. ---",
      "quality": 0.135,
      "children": [
        {
          "title": "The workflow alias set",
          "level": 3,
          "summary": "This is the part that actually changes behavior. Create `~/.securegit_aliases`: ```",
          "quality": 0.18333333333333332,
          "children": []
        }
      ]
    },
    {
      "title": "Acquire instead of clone, with a name that reminds you why",
      "level": 1,
      "summary": "alias safe-clone='securegit acquire'",
      "quality": 0.015,
      "children": []
    },
    {
      "title": "Scan right here",
      "level": 1,
      "summary": "alias scan-here='securegit scan .'",
      "quality": 0.02,
      "children": []
    },
    {
      "title": "Scan including repository metadata \u2014 for unfamiliar code",
      "level": 1,
      "summary": "alias scan-deep='securegit scan . --include-git'",
      "quality": 0.025,
      "children": []
    },
    {
      "title": "Scan only what you are about to commit",
      "level": 1,
      "summary": "alias scan-staged='securegit scan --staged --fail-on high'",
      "quality": 0.03,
      "children": []
    },
    {
      "title": "Scan only what changed against your main branch",
      "level": 1,
      "summary": "alias scan-diff='git diff main --name-only | xargs securegit scan'",
      "quality": 0.045,
      "children": []
    },
    {
      "title": "Skip the usual noise",
      "level": 1,
      "summary": "alias scan-clean='securegit scan . --skip-paths \"**/node_modules/**,**/vendor/**\"'",
      "quality": 0.03,
      "children": []
    },
    {
      "title": "Plugin housekeeping",
      "level": 1,
      "summary": "alias plugin-status='securegit plugin list && securegit plugin check-updates' ``` Source it: ``` echo \"source ~/.securegit_aliases\" >> ~/.bashrc ``` **`scan-diff` is the one you will use most** and it is the one people discover last. Scanning only what changed against your",
      "quality": 0.295,
      "children": [
        {
          "title": "Environment defaults",
          "level": 3,
          "summary": "Set these once and stop passing flags: ``` export SECUREGIT_FAIL_ON=high export SECUREGIT_SKIP_PATHS=\"**/node_modules/**:**/vendor/**:**/target/**:**/dist/**\" ``` --- **SIDEBAR (page 17, boxed): On the name** > We have been asked, repeatedly, whether the binary should be called `sgit` or `safegit` or something shorter. >",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 18\u201319 \u2014 THE PRE-COMMIT HOOK",
      "level": 1,
      "summary": "**Headline:** YOUR FIRST GUARDRAIL **Deck:** *One file, six lines. This is the point where SecureGit stops being a thing you remember to run and starts being a thing that protects you when you forget.* --- Everything up to this page",
      "quality": 0.36,
      "children": [
        {
          "title": "The hook",
          "level": 3,
          "summary": "Create `.git/hooks/pre-commit`: ``` #!/bin/bash securegit scan --staged --fail-on high || { echo \"SecureGit: staged changes contain high-severity findings. Commit blocked.\" echo \"Review with: securegit scan --staged\" echo \"Override once with: git commit --no-verify\" exit 1 } ``` Make it executable:",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Distributing hooks to a team",
          "level": 3,
          "summary": "`.git/hooks` is not version controlled, which is a genuine problem and not one SecureGit invented. Three options, in increasing order of robustness: **A \u00b7 A setup script in the repository.** `scripts/setup-hooks.sh`, run once by each developer, documented in the README.",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The pre-push hook",
          "level": 3,
          "summary": "Same idea, wider net, runs less often: ``` #!/bin/bash securegit scan --fail-on critical || exit 1 ``` Push happens less frequently than commit, so you can afford a broader scan. Note the threshold is `critical` here rather than `high` \u2014",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 20\u201321 \u2014 WILL THIS SLOW ME DOWN?",
      "level": 1,
      "summary": "**Headline:** THE HONEST PERFORMANCE PAGE **Deck:** *Yes, somewhat, in specific places. Here is exactly where, with numbers, so you can decide rather than find out.* --- Performance is where security tooling adoption actually dies. Not in the evaluation, where everyone",
      "quality": 0.315,
      "children": [
        {
          "title": "Single file",
          "level": 3,
          "summary": "A small source file, all twelve built-in scanners plus one external plugin: | Scanner | Findings | Duration | |---|---|---| | `secrets` / `patterns` / `entropy` (built-in) | 6 | ~5 ms combined | | `encoding` / `supply-chain` / `ci-cd`",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Throughput",
          "level": 3,
          "summary": "| Plugin type | Files per second | |---|---| | Built-in (Rust, in-process) | ~1,000\u20135,000 | | External (subprocess) | ~50\u2013200 | **Built-in scanners are between ten and a hundred times faster than external ones**, because an external plugin pays",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Large repository",
          "level": 3,
          "summary": "A substantial open-source repository, several hundred megabytes, five thousand-plus files: | Phase | Time | |---|---| | Acquisition | ~2.5 s | | Extraction | ~1.8 s | | Scan, all 12 built-in scanners | ~35 s | | Scan,",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Resource use",
          "level": 3,
          "summary": "| Scale | CPU | Memory | |---|---|---| | Under 100 files | 1 core, under 10% | under 50 MB | | 5,000+ files | 1\u20134 cores, 40\u201380% | 100\u2013500 MB | | Per external plugin | process overhead",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The five rules that keep it fast",
          "level": 3,
          "summary": "**1 \u00b7 Scan the diff, not the tree.** In daily work you almost never need a full scan. ``` git diff main --name-only | xargs securegit scan securegit scan --staged ``` This is the single highest-leverage habit in this issue.",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "What is not fast yet, and is known",
          "level": 3,
          "summary": "Stated so you can plan rather than discover: - **File walking is sequential.** Plugins run concurrently per file, but the walker itself is single-threaded. `PLANNED` \u2014 parallel file processing, expected to be a multiple-times speedup on large trees. - **There",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 22\u201323 \u2014 DAY ONE CHECKLIST",
      "level": 1,
      "summary": "**Headline:** WHERE YOU SHOULD BE **Deck:** *If you have worked through Part I, this is your state. Tick it off honestly \u2014 the gaps are where you will fail next week.* --- **DESIGN NOTE:** Render as a genuine checklist with",
      "quality": 0.27,
      "children": [
        {
          "title": "Installed and verified",
          "level": 3,
          "summary": "- [ ] `securegit --version` returns a version - [ ] `~/.config/securegit/` exists - [ ] I know how to uninstall it (delete binary + that directory)",
          "quality": 0.45,
          "children": []
        },
        {
          "title": "Acquisition",
          "level": 3,
          "summary": "- [ ] I have acquired at least one repository - [ ] I checked `.git/hooks` and confirmed it was empty - [ ] I read a `.securegit-report.json` - [ ] `safe-clone` is aliased and I have used it once",
          "quality": 0.7333333333333333,
          "children": []
        },
        {
          "title": "Scanning",
          "level": 3,
          "summary": "- [ ] I have scanned a repository I actually work in - [ ] I looked at every `critical` finding - [ ] I rotated anything real (or confirmed there was nothing real) - [ ] `skip_paths` is configured",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Muscle memory",
          "level": 3,
          "summary": "- [ ] `sgit` (or equivalent) is aliased - [ ] Tab completion works on the alias - [ ] `~/.securegit_aliases` exists and is sourced - [ ] `SECUREGIT_FAIL_ON` and `SECUREGIT_SKIP_PATHS` are set",
          "quality": 0.55,
          "children": []
        },
        {
          "title": "The guardrail",
          "level": 3,
          "summary": "- [ ] A pre-commit hook is installed in at least one repository - [ ] I have seen it pass - [ ] I know how to override it (`--no-verify`) and why I should not do so reflexively ---",
          "quality": 0.6666666666666666,
          "children": []
        },
        {
          "title": "WHEN IT GOES WRONG",
          "level": 2,
          "summary": "**Deck:** *The six failures that account for most first-week abandonment, and the fix for each.* **\"It found 400 things and I stopped reading.\"** You scanned a dependency directory. Configure `skip_paths`, rescan, and look at `critical` only. Then gate on new",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGE 24 \u2014 PART II: SECTION OPENER",
      "level": 1,
      "summary": "**Full-bleed. Reversed: paper type on ink ground.** > **PART TWO > THE MENTAL > MODEL** > Four mechanisms. > Why each one is shaped the way it is, > and what it costs. *(Foot line, mono, orange:)* `25 \u2192 37`",
      "quality": 0.205,
      "children": []
    },
    {
      "title": "PAGES 25\u201327 \u2014 ACQUIRE, NOT CLONE",
      "level": 1,
      "summary": "**Standfirst:** *Mechanism 01* \u00b7 `SHIPPED`",
      "quality": 0.025,
      "children": [
        {
          "title": "Why archive-first defeats hooks",
          "level": 2,
          "summary": "**Deck:** *The entire security argument rests on one ordering decision. Understanding it takes about four minutes and makes everything else in the tool obvious.* ---",
          "quality": 0.125,
          "children": [
            {
              "title": "The problem with clone, precisely",
              "level": 3,
              "summary": "`git clone` does several things that are individually reasonable and collectively a problem. It negotiates with a remote and transfers objects. Fine. It writes those objects into a `.git` directory. Fine. It **materializes the repository's configuration and hook directory as",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The reordering",
              "level": 3,
              "summary": "SecureGit changes one thing: **the order of operations.** ``` git clone: fetch \u2192 materialize repo \u2192 (inspect never) securegit acquire: fetch archive \u2192 strip \u2192 inspect \u2192 materialize repo ``` That is the whole mechanism. Everything else follows from it.",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "What this does not protect you from",
              "level": 3,
              "summary": "Stated clearly, because overclaiming here would be dishonest and would eventually be found out. **It does not make the code safe to run.** If you acquire a repository, read the report, and then run `npm install && npm start`, you",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The report",
              "level": 3,
              "summary": "Every acquisition writes `.securegit-report.json` into the destination. ``` .securegit-report.json ``` It records what was fetched, what was stripped, what the scanners found, and when. **Read it the first ten times.** After that you will have calibrated intuition for what a",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Acquisition and the chain",
              "level": 3,
              "summary": "`SHIPPED` Acquisition emits a chain receipt recording the remote URL and the resolved `HEAD` commit at the moment of acquisition. **Acquisition never blocks on the chain.** If the signing daemon is unreachable, you get a warning and your repository. This",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGES 28\u201330 \u2014 SCAN",
      "level": 1,
      "summary": "**Standfirst:** *Mechanism 02* \u00b7 `SHIPPED`",
      "quality": 0.025,
      "children": [
        {
          "title": "Twelve scanners, and a ladder you climb slowly",
          "level": 2,
          "summary": "**Deck:** *The architecture is a deliberate two-tier split: a fast core of twelve scanners that always runs, and a slow periphery you opt into. Understanding which tier you are in explains every performance question you will have.* ---",
          "quality": 0.19,
          "children": [
            {
              "title": "The two tiers",
              "level": 3,
              "summary": "**Built-in (Rust, in-process).** Twelve scanners compiled into the binary \u2014 `secrets`, `patterns`, `entropy`, `binary`, `encoding` (Trojan Source), `supply-chain`, `ci-cd`, `container`, `iac`, `deserialization`, `dangerous-files`, `git-internals`. Zero startup cost, shared runtime, direct memory access to file contents, sub-millisecond to low-millisecond per file.",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The plugin ladder",
              "level": 3,
              "summary": "Climb this slowly. Each rung is optional and each adds time. **RUNG 0 \u00b7 Built-in only.** `SHIPPED` Twelve scanners, no configuration. This is where you start and where most teams correctly stop. Fast enough for pre-commit hooks and CI gates.",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Writing a plugin",
              "level": 3,
              "summary": "The protocol is deliberately trivial: **a plugin is an executable that takes a file path and prints JSON to stdout.** Python: ``` #!/usr/bin/env python3 import json, sys file_path = sys.argv[1] findings = []",
              "quality": 0.55,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "your logic here",
      "level": 1,
      "summary": "print(json.dumps({ \"plugin_name\": \"my-scanner\", \"findings\": findings, \"scanned_files\": 1 })) ``` Bash: ``` #!/bin/bash FILE=\"$1\" echo '{\"plugin_name\":\"my-scanner\",\"findings\":[],\"scanned_files\":1}' ``` Install: ``` cp my-plugin ~/.config/securegit/plugins/ chmod +x ~/.config/securegit/plugins/my-plugin securegit scan /path/to/test/file ``` **That is the entire interface.** No SDK, no registration, no manifest, no",
      "quality": 0.44,
      "children": [
        {
          "title": "Plugin types, current and future",
          "level": 3,
          "summary": "| Type | Location | Performance | Status | |---|---|---|---| | Built-in (Rust) | compiled in | 0 ms startup | `SHIPPED` | | External (any language) | `~/.config/securegit/plugins/` | 20\u201350 ms startup | `SHIPPED` | | Native dynamic (Rust",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The plugin trust problem",
          "level": 3,
          "summary": "We are going to state this directly rather than bury it, because it is the honest weak point of any plugin architecture. An external plugin runs as a subprocess with your user's permissions. It can read your filesystem. Depending on",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 31\u201333 \u2014 THE GUARDED COMMIT",
      "level": 1,
      "summary": "**Standfirst:** *Mechanism 03* \u00b7 `SHIPPED`",
      "quality": 0.025,
      "children": [
        {
          "title": "A gate that survives contact with a deadline",
          "level": 2,
          "summary": "**Deck:** *Any gate can stop a bad commit. The engineering problem is stopping it without teaching people to route around the gate.* ---",
          "quality": 0.115,
          "children": [
            {
              "title": "The commands",
              "level": 3,
              "summary": "``` securegit status # working tree state securegit scan --staged # scan what is staged securegit safe-commit -m \"message\" # scan, then commit if clean securegit commit -m \"message\" # commit with chain receipt securegit findings # review current findings",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Why the gate is at commit and not earlier",
              "level": 3,
              "summary": "You could gate at `add`. It would be worse. Staging is exploratory. People stage, unstage, restage, and split changes across several commits. A gate at `add` fires constantly during normal work, most firings are irrelevant because the content changes again",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Why the *enforcement* gate is at push",
              "level": 3,
              "summary": "There is a second gate, and it is not at commit. It is at push. This is worth its own explanation because it is counterintuitive and it is deliberate. **Commits are local and revisable.** You can rewrite them, amend them,",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The full working surface",
              "level": 3,
              "summary": "SecureGit wraps the git operations you use daily. All `SHIPPED`: **Everyday** \u2014 `status`, `add`, `commit`, `safe-commit`, `diff`, `log`, `show`, `blame` **Branching** \u2014 `branch_create`, `branch_list`, `branch_delete`, `checkout`, `merge` **Remote** \u2014 `push`, `remote_list`, `server_add`, `server_list`, `server_push` **History** \u2014 `stash_save`, `stash_pop`, `stash_list`, `undo`,",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Which operations emit receipts",
              "level": 3,
              "summary": "Not everything does, and the boundary is principled. | Operation | Receipt | Why | |---|---|---| | `acquire` / `clone` | **Yes** | Code arrives from outside | | `commit` | **Yes** | Content declared finished | | `push` |",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Workflow scripts",
              "level": 3,
              "summary": "`SHIPPED` Guided scripts for common operations, exposed so you do not need to know where they live: ``` securegit workflow list securegit workflow info dev-flow securegit workflow install securegit workflow run dev-flow --dry-run securegit workflow run commit-craft ``` Bundled workflows",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGES 34\u201337 \u2014 THE CHAIN OF CUSTODY",
      "level": 1,
      "summary": "**Standfirst:** *Mechanism 04* \u00b7 `SHIPPED` (core) \u00b7 `DESIGNED` (offline + batch lookup)",
      "quality": 0.06,
      "children": [
        {
          "title": "Receipts, tiers, and the push gate",
          "level": 2,
          "summary": "**Deck:** *This is the layer that turns git from a record of assertions into a record of evidence. It is also the layer that requires real infrastructure, so read the cost section before you plan around it.* ---",
          "quality": 0.19,
          "children": [
            {
              "title": "The core idea",
              "level": 3,
              "summary": "Every operation that crosses a trust boundary produces a **receipt**: a signed, timestamped record linking an action to a verified identity and to the exact content involved. Receipts are cryptographically linked to each other, so the chain is tamper-evident. Modifying",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The signing model",
              "level": 3,
              "summary": "`SHIPPED` **The signing key never touches SecureGit.** It lives in a separate signing daemon. In production the key is hardware-rooted; in local development a software key is used. SecureGit is stateless with respect to key material \u2014 it composes an",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Two tiers of evidence, and why the distinction is the point",
              "level": 3,
              "summary": "**Tier A \u2014 forward-attested.** The daemon witnessed the operation as it happened. Strongest evidence. **Tier B \u2014 retro-attested.** The receipt was added after the fact. Real, useful, and weaker \u2014 it proves someone asserted something later, not that the daemon",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Where the receipt lives \u2014 belt and braces",
              "level": 3,
              "summary": "`SHIPPED` Commit receipts are stored in **two places at once**, deliberately: **1 \u00b7 In the commit message as a trailer.** ``` X-ContextOS-Receipt: <receipt-id> ``` Travels with the repository. Survives clone. Readable from `git log` with no daemon and no network.",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The push gate",
              "level": 3,
              "summary": "`SHIPPED` (core) \u2014 This is the enforcement point. Before a push executes, SecureGit walks every commit in the push range and checks whether each has a receipt. If any commit does not, the push is blocked: ``` error: push rejected",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Force push",
              "level": 3,
              "summary": "`SHIPPED` If a push would rewrite published history, a **bypass receipt** is emitted before the push, and policy can require a joint-approval marker in the commit message. The default is to require it. The pattern mirrors a physical two-person rule",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Blame with provenance",
              "level": 3,
              "summary": "`SHIPPED` \u2014 exposed in the agent tool surface as well as the CLI. ``` securegit blame <file> [--commit <sha>] [--format=human|json] ``` Standard blame output, augmented per line with the receipt tier and operation type: ``` <sha> <identity> A human-edit fn",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Merge",
              "level": 3,
              "summary": "`SHIPPED` A merge joins two receipt chains, so its receipt encodes both parent SHAs in its content hash \u2014 preserving the history graph inside the evidence chain. Multi-author merges \u2014 detected from author/committer mismatch or co-author trailers \u2014 can require",
              "quality": 0.9166666666666666,
              "children": []
            },
            {
              "title": "Policy and configuration",
              "level": 3,
              "summary": "`SHIPPED` Policy resolves in a fixed order: **git config \u2192 environment variables \u2192 built-in defaults.** Git config first is a deliberate operational choice: it means managed-workstation tooling can push policy through standard git configuration, which every organization already has a",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGE 38 \u2014 PART III: SECTION OPENER",
      "level": 1,
      "summary": "**Full-bleed. Reversed.** > **PART THREE > SUPPLY > CHAIN** > Ninety percent of your application > is code nobody on your team wrote. > This part is about proving > what you knew about it, and when. *(Foot line, mono,",
      "quality": 0.225,
      "children": []
    },
    {
      "title": "PAGES 39\u201341 \u2014 SBOM, OSV, AND THE NUMBER THAT MATTERS MORE THAN ZERO",
      "level": 1,
      "summary": "**Standfirst:** *Supply chain* \u00b7 `SHIPPED` (anchoring) \u00b7 `DESIGNED` (parts)",
      "quality": 0.045,
      "children": [
        {
          "title": "Anchoring, not scanning",
          "level": 2,
          "summary": "**Deck:** *An architectural decision worth understanding, because it tells you exactly what this tool will and will not do for your dependencies \u2014 and because the boundary moved once, deliberately, and the reasoning is instructive.* ---",
          "quality": 0.18,
          "children": [
            {
              "title": "The gap",
              "level": 3,
              "summary": "The chain of custody in Part II proves what humans and agents contributed to your repository. It says nothing about the overwhelming majority of a modern application, which is open-source dependencies you did not write and mostly have not read.",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The decision: anchor, don't bundle",
              "level": 3,
              "summary": "`SHIPPED` **SecureGit records the canonical hash of each supply-chain tool's output at the moment it ran, and anchors that hash in the chain.** You keep your existing tooling \u2014 whatever vulnerability scanner, SBOM generator, and signing tool you already run.",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The one exception, and why it moved",
              "level": 3,
              "summary": "`SHIPPED` There is exactly one place the anchor-don't-bundle rule was amended, and documenting the amendment precisely is how you keep it from becoming a pattern. **The problem with the pure position:** most enterprise customers assume \"SBOM provenance\" means the system",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Four safety properties worth stealing",
              "level": 3,
              "summary": "Whether or not you use SecureGit, these four decisions are good engineering and transfer to any system that anchors third-party output. **1 \u00b7 Opt-in, never default.** `SHIPPED` The lookup is an explicit flag. It is never triggered automatically. **Regulated environments",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Air-gapped environments",
              "level": 3,
              "summary": "`SHIPPED` Customers who cannot make outbound calls point the tool at a local mirror of the vulnerability database. The mirror's snapshot date is recorded in the receipt, so an auditor can see how current the vulnerability data was at scan",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Rescan, because advisories keep arriving",
              "level": 3,
              "summary": "`SHIPPED` The same dependency set scanned in April may have more known vulnerabilities in May. Nothing about your code changed; the world's knowledge did. ``` securegit sbom rescan <receipt-id> ``` Runs a fresh lookup, bypassing cache, and emits a new",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGES 42\u201343 \u2014 LOCK FILES AND LICENSES",
      "level": 1,
      "summary": "**Standfirst:** *Supply chain* \u00b7 `SHIPPED` (core) \u00b7 `DESIGNED` (coverage)",
      "quality": 0.045,
      "children": [
        {
          "title": "What changed, and what you are allowed to ship",
          "level": 2,
          "summary": "**Deck:** *Two quieter capabilities that answer two questions auditors ask early: when did this dependency arrive, and are we permitted to ship it.* ---",
          "quality": 0.12,
          "children": [
            {
              "title": "Lock-file change receipts",
              "level": 3,
              "summary": "`SHIPPED` When a dependency lock file changes, a receipt records the change: the file's content hash before and after, and where format support exists, a count of dependencies added, removed, and version-bumped. **Format coverage is explicitly tiered, and the tiering",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "License detection",
              "level": 3,
              "summary": "`SHIPPED` License detection runs at acquisition time using file heuristics and a standard license identifier database. **Three properties, each of which is a deliberate choice:** **`UNKNOWN` is a valid recorded value.** Not an error, not a blank. Recording \"we looked",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "What this gives you in practice",
              "level": 3,
              "summary": "Three answers you can produce from the chain that you probably cannot produce today: **\"When did this dependency enter our tree?\"** \u2014 the lock-file receipt timeline, with a signed timestamp and an attributed actor. **\"What was our license posture at",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGES 44\u201345 \u2014 REPORTS THAT LAND WHERE REVIEWERS ALREADY LIVE",
      "level": 1,
      "summary": "**Standfirst:** *Supply chain & governance* \u00b7 `SHIPPED` (SARIF, GitLab SAST, baselines, audit log, compliance report) / `DESIGNED` (integrated chain audit)",
      "quality": 0.1,
      "children": [
        {
          "title": "SARIF, GitLab SAST, baselines, audit",
          "level": 2,
          "summary": "**Deck:** *Everything in Parts II and III exists to make one specific conversation go differently. The way it lands in that conversation is: as a report your reviewer's tools already read.* ---",
          "quality": 0.16,
          "children": [
            {
              "title": "SARIF 2.1.0, and GitLab SAST v15",
              "level": 3,
              "summary": "`SHIPPED` One flag on the scan command; the report uploads directly into whatever dashboard your reviewers already open. ``` securegit scan . --format sarif --report-output securegit.sarif securegit scan . --format gitlab --report-output gl-sast-report.json ``` SARIF 2.1.0 is the OASIS standard",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Baselines: adopting on legacy code without failing every build",
              "level": 3,
              "summary": "`SHIPPED` The pattern that unblocks adoption on a mature codebase. ``` securegit scan . --write-baseline \"Legacy findings accepted 2026-08-03; expires 2026-11-03\" \\ --expires 2026-11-03 git add .securegit/baseline.json && git commit -m \"chore(security): baseline v1\"",
              "quality": 0.5666666666666667,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "thereafter, every scan and every CI run",
      "level": 1,
      "summary": "securegit scan . --fail-on high --baseline .securegit/baseline.json ``` Baselines suppress known findings by a **stable fingerprint** that includes rule, file, snippet, and CWE \u2014 not by line number. That means shuffling code up or down a file does not invalidate",
      "quality": 0.68,
      "children": [
        {
          "title": "The tamper-evident audit log",
          "level": 3,
          "summary": "`SHIPPED` A separate, always-on log \u2014 independent of the chain \u2014 that captures every security-relevant event: scan runs, scan blocks, hook execution, baseline writes, credential store operations, policy denials, tool updates. Every entry is JSONL with a `prev_hash` and `hash`",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The compliance report",
          "level": 3,
          "summary": "`SHIPPED` A single command that produces a review packet against two frameworks security teams actually cite. ``` securegit compliance report --format markdown --output compliance.md securegit compliance report --format json --output compliance.json ``` Findings are grouped by **OWASP Top 10 (2021)**",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "What the chain adds on top",
          "level": 3,
          "summary": "`DESIGNED` A report generated directly from chain data \u2014 no engineer reconstruction \u2014 covering human contribution (who committed what, at which evidence tier), supply-chain state as anchored at a point in time, build provenance, **and the gaps**. Explicitly. Commits without",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Why gap-reporting is the credibility feature",
          "level": 3,
          "summary": "A report with no gaps is not trustworthy. Every real system has gaps \u2014 a daemon outage, a repository adopted late, a scan that timed out, a format without full parsing support. A report that shows none is either describing",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Verification without access",
          "level": 3,
          "summary": "`SHIPPED` A design property worth understanding, because it comes up in every enterprise conversation. Some anchored payloads live in customer-controlled storage. A verifier without access to that storage **can still verify the hash chain** \u2014 the anchoring, the timestamps, the",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "A caveat that must travel with the numbers",
          "level": 3,
          "summary": "`SHIPPED` Vulnerability counts drift. The same dependency set, unchanged, will show more known vulnerabilities next quarter, because advisories are published continuously. **This is correct behavior and it confuses people constantly.** A customer sees \"3 CVEs in March, 11 in June,",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGE 46 \u2014 PART IV: SECTION OPENER",
      "level": 1,
      "summary": "**Full-bleed. Reversed.** > **PART FOUR > SECRETS > & AGENTS** > Your agent needs credentials. > Your agent must never hold them. > Both of those are true at once, > and the resolution is a handle. *(Foot line, mono,",
      "quality": 0.225,
      "children": []
    },
    {
      "title": "PAGES 47\u201348 \u2014 HANDLES, NOT VALUES",
      "level": 1,
      "summary": "**Standfirst:** *Mechanism 05* \u00b7 `SHIPPED` (core) \u00b7 `DESIGNED` (most of the surface \u2014 markers throughout)",
      "quality": 0.075,
      "children": [
        {
          "title": "The secret broker",
          "level": 2,
          "summary": "**Deck:** *A narrow idea with a wide consequence: the thing doing the work gets the ability to act without ever receiving the value it acts with.* ---",
          "quality": 0.135,
          "children": [
            {
              "title": "The problem, sharpened",
              "level": 3,
              "summary": "An agent needs to push to a repository. Pushing requires a token. Therefore the agent needs the token. That reasoning is wrong, and finding where it is wrong is the whole design. **The agent needs the *capability* to push. It",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The mechanism",
              "level": 3,
              "summary": "`SHIPPED` **Late binding at the execution boundary.** 1. A user or agent requests an action, referring to a secret by a **handle** \u2014 a stable name, never a value. 2. SecureGit validates the requested action against the handle's profile. Is",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "The safety defaults",
              "level": 3,
              "summary": "`SHIPPED` Six rules, enforced rather than recommended: 1. **No command prints a secret value by default.** 2. **Structured and agent-facing responses return handles, provider metadata, and status only** \u2014 never values. 3. **Known values are redacted** from stdout, stderr, logs,",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Writes never pass through shell history",
              "level": 3,
              "summary": "`SHIPPED` Storing a secret reads the value from an environment variable rather than an argument: ``` OPENAI_API_KEY_VALUE=... securegit secret set openai-dev \\ --value-env OPENAI_API_KEY_VALUE --yes ``` **Never `--value <the-actual-secret>`.** Command-line arguments are visible in process listings and durable in shell",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Provider profiles, not key-value sprawl",
              "level": 3,
              "summary": "`DESIGNED` The intended model is that secrets are **provider access profiles**, not arbitrary key-value pairs. A profile records the handle, the provider, the credential kind, the host, the capabilities it grants, the commands it is permitted for, and where the",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Backends",
              "level": 3,
              "summary": "`SHIPPED` Metadata discovery, execution-time value resolution, guarded writes and deletes, and a local store (\u201ccredential store v2\u201d) adequate for single-user work. **Credential store v2, in detail.** Encryption is **ChaCha20-Poly1305** AEAD, keyed to the machine (macOS ioreg UUID, Linux `/etc/machine-id` /",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Discovery without disclosure",
              "level": 3,
              "summary": "`SHIPPED` The most useful single property for agent workflows: ``` securegit secret discover --target dev --keys-only ``` Returns names, paths, comments, tags, versions, and metadata. **Returns no values.** This gives an agent enough context to bind the right profile \u2014",
              "quality": 1.0,
              "children": []
            }
          ]
        }
      ]
    },
    {
      "title": "PAGE 49 \u2014 SERVER DISCOVERY AND THE CREDENTIAL BOUNDARY",
      "level": 1,
      "summary": "**Headline:** FINDING REPOSITORIES WITHOUT HANDLING TOKENS **Deck:** *The same handle discipline, applied to the everyday problem of \"which repository was that.\"* \u00b7 `SHIPPED` ---",
      "quality": 0.12,
      "children": [
        {
          "title": "Registering servers",
          "level": 3,
          "summary": "``` securegit server add github-main --platform github --api-url https://api.github.com securegit server add gitlab-main --platform gitlab --api-url https://gitlab.example.com/api/v4 ``` Credentials are stored keyed to the server name and resolved through the normal credential chain. **The token is never typed into a",
          "quality": 0.7,
          "children": []
        },
        {
          "title": "Searching",
          "level": 3,
          "summary": "``` securegit server search <query> # all registered servers securegit server search <query> --server gitlab-main # one server securegit server search <query> --json # machine-readable securegit server search <query> --limit 10 # cap per provider securegit server search <query> --include-groups",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The credential boundary",
          "level": 3,
          "summary": "`SHIPPED` Resolution order: 1. A per-server environment variable 2. The encrypted stored credential, keyed to the server name 3. Host-based fallback from existing stored auth **The rule that matters:** an agent should call `securegit server search` \u2014 or the equivalent",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 50\u201351 \u2014 SECUREGIT FOR AGENTS",
      "level": 1,
      "summary": "**Headline:** THE MCP SURFACE **Deck:** *The same tool, exposed to an agent, with the safety properties preserved rather than bolted on.* \u00b7 `SHIPPED` ---",
      "quality": 0.12,
      "children": [
        {
          "title": "Why this belongs in this magazine",
          "level": 3,
          "summary": "Every discipline in the AI-engineering transition converges here. Your agents commit code. They pull dependencies. They need credentials. They push. **Every one of those is a trust boundary crossing performed by something that is not a person, at a volume",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The exposed surface",
          "level": 3,
          "summary": "`SHIPPED` Agents get a structured tool surface covering the same operations humans use \u2014 **32 tools** across ten families \u2014 delivered as `securegit-mcp`, a standard MCP server: **Repository state** \u2014 status, log, show, diff, blame **Change** \u2014 add, commit, safe-commit,",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The four properties that make this safe",
          "level": 3,
          "summary": "**1 \u00b7 Handles, not values.** Agent-facing tools accept secret handles. They do not accept, return, or display credential values. An agent asks to push using `gitlab-main`; it never learns what `gitlab-main` is. **2 \u00b7 Guarded destructive operations.** Operations that destroy",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Graph integration",
          "level": 3,
          "summary": "`DESIGNED` Repository operations can update a knowledge graph after acquire, fetch, pull, and push \u2014 building a queryable model of code, contributors, and change over time. ``` GRAPHRAG_ENABLED=1 GRAPHRAG_API_URL=<your-endpoint> securegit push ``` Off by default, and it should stay off",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "The honest warning",
          "level": 3,
          "summary": "**Do not give an agent write access to a repository you cannot afford to have rewritten, on your first day.** Start read-only. Let it scan, search, review, and report. Watch what it does for a week. Then grant commit access",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGE 52 \u2014 PART V: SECTION OPENER",
      "level": 1,
      "summary": "**Full-bleed. Reversed.** > **PART FIVE > ADOPTION** > The engineering was the easy part. > Thirty days, twelve objections, > and the failure modes nobody > writes down. *(Foot line, mono, orange:)* `53 \u2192 61` ---",
      "quality": 0.18,
      "children": []
    },
    {
      "title": "PAGES 53\u201355 \u2014 THE 30-DAY ROLLOUT",
      "level": 1,
      "summary": "**Headline:** SOLO, THEN TEAM, THEN ORG **Deck:** *A sequence that has survived contact with real teams. Each phase is independently valuable, so stopping early is a success rather than a failure.* --- **DESIGN NOTE:** Render as a horizontal timeline running",
      "quality": 0.29,
      "children": [
        {
          "title": "DAYS 1\u20133 \u00b7 YOURSELF",
          "level": 2,
          "summary": "**Goal:** you are using it daily without thinking about it. - Install. Alias it. Set up completion. - Acquire three real repositories with `acquire` instead of `clone`. - Scan two repositories you actually work in. Fix or rotate anything critical.",
          "quality": 0.625,
          "children": []
        },
        {
          "title": "DAYS 4\u201310 \u00b7 ONE REPOSITORY, ONE TEAM",
          "level": 2,
          "summary": "**Goal:** a shared gate in one place, with the team's consent. - Pick **one** repository. Not the most critical. Not a toy. Something real that a few people touch. - Move hooks into version control: commit a hook directory and",
          "quality": 0.675,
          "children": []
        },
        {
          "title": "DAYS 11\u201320 \u00b7 THE SECOND AND THIRD REPOSITORY",
          "level": 2,
          "summary": "**Goal:** prove it generalizes, and find out where it does not. - Add two more repositories, ideally in different languages or ecosystems. - Notice what breaks. Different stacks produce different false-positive profiles, and the tuning you did on a Python",
          "quality": 0.435,
          "children": []
        },
        {
          "title": "DAYS 21\u201330 \u00b7 POLICY AND SCALE",
          "level": 2,
          "summary": "**Goal:** it is infrastructure, not a personal preference. - Distribute configuration through whatever manages developer workstations. Git config is the intended channel because every organization already has a mechanism for it. - Decide your chain-layer position. **Be honest about whether",
          "quality": 0.73,
          "children": []
        },
        {
          "title": "THE FOUR WAYS ROLLOUTS DIE",
          "level": 2,
          "summary": "Each of these has killed a real adoption. Each has a specific counter. **1 \u00b7 The noisy first scan.** Someone runs it on a mature repository, gets four hundred findings, concludes the tool is useless, and tells everyone. **Counter:** never",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 56\u201357 \u2014 TWELVE OBJECTIONS",
      "level": 1,
      "summary": "**Headline:** ANSWERED HONESTLY **Deck:** *Including three where the honest answer is that you are right.* --- **DESIGN NOTE:** Two columns, six per page. Objection in condensed caps, response in serif. Where the answer concedes, mark it with a signal-colored rule",
      "quality": 1.0,
      "children": []
    },
    {
      "title": "PAGES 58\u201359 \u2014 CI/CD INTEGRATION",
      "level": 1,
      "summary": "**Headline:** GATES PEOPLE DO NOT DISABLE **Deck:** *CI is where security tooling goes to be turned off. The difference is entirely in how you introduce it.* ---",
      "quality": 0.135,
      "children": [
        {
          "title": "The basic integration",
          "level": 3,
          "summary": "``` name: Security Scan on: [push, pull_request] jobs: scan: runs-on: ubuntu-latest permissions: security-events: write # for SARIF upload contents: read steps: - uses: actions/checkout@v4 - name: Install SecureGit run: curl -fsSL https://<release-host>/securegit/install.sh | sh - name: Scan (report + gate",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "GitLab equivalent",
          "level": 3,
          "summary": "``` security-scan: image: registry.example.com/securegit:0.12 script: - securegit scan . --format gitlab --report-output gl-sast-report.json --baseline .securegit/baseline.json --fail-on high artifacts: reports: sast: gl-sast-report.json ``` The `sast` artifact lights up GitLab's security dashboard and MR widget with zero further plumbing.",
          "quality": 0.6166666666666667,
          "children": []
        },
        {
          "title": "The four-stage introduction",
          "level": 3,
          "summary": "**Stage 1 \u00b7 Advisory, informational.** Scan runs, prints findings, always exits zero. One week minimum. You are collecting the false-positive profile of your actual codebase, which you cannot predict. **Stage 2 \u00b7 Advisory, annotated.** Findings appear as PR annotations. Still",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Scan the diff, not the tree",
          "level": 3,
          "summary": "The single most important CI configuration decision: ``` git diff origin/main --name-only | xargs securegit scan --fail-on high ``` **Three reasons this is right, and they compound:** **Speed.** Seconds instead of minutes. **Relevance.** The author can act on it. A",
          "quality": 1.0,
          "children": []
        },
        {
          "title": "Where each gate belongs",
          "level": 3,
          "summary": "| Gate | Scope | Threshold | Speed | |---|---|---|---| | Pre-commit | staged only | `high` | under a second | | Pre-push | push range | `critical` | seconds | | PR / CI | diff vs. main",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGES 60\u201361 \u2014 TROUBLESHOOTING AND THE FALSE-POSITIVE PLAYBOOK",
      "level": 1,
      "summary": "**Headline:** MAKING IT QUIET **Deck:** *A tool that cries wolf gets disabled. Driving noise down is not maintenance \u2014 it is the work that determines whether any of this survives.* ---",
      "quality": 0.155,
      "children": [
        {
          "title": "THE FALSE-POSITIVE PLAYBOOK",
          "level": 2,
          "summary": "",
          "quality": 0.0,
          "children": [
            {
              "title": "Step 1 \u00b7 Classify before you suppress",
              "level": 3,
              "summary": "Every finding you are about to dismiss is one of four things. **Naming which one changes what you do about it.** **A \u00b7 Wrong path.** Vendor code, dependencies, build output, fixtures. Not your code, not your problem. \u2192 `skip_paths`. **B",
              "quality": 1.0,
              "children": []
            },
            {
              "title": "Step 2 \u00b7 Fix the paths first",
              "level": 3,
              "summary": "Most first-week noise is path noise. This one setting resolves the majority of it: ``` export SECUREGIT_SKIP_PATHS=\"**/node_modules/**:**/vendor/**:**/target/**:**/dist/**:**/build/**:**/.venv/**:**/testdata/**\" ``` Or per-invocation: ``` securegit scan . --skip-paths \"**/node_modules/**,**/vendor/**\" ``` **Write your organization's baseline down and share it.** Every team rediscovering this independently",
              "quality": 0.7666666666666667,
              "children": []
            },
            {
              "title": "Step 3 \u00b7 Right-size the scanner set",
              "level": 3,
              "summary": "``` securegit scan . --plugins secrets,patterns ``` For a commit gate, `secrets` and `patterns` are usually the right pair. **`entropy` belongs in scheduled scans**, not in the path where someone is trying to commit a fix at 6pm.",
              "quality": 0.6333333333333333,
              "children": []
            },
            {
              "title": "Step 4 \u00b7 Tune the threshold, not the coverage",
              "level": 3,
              "summary": "Prefer `--fail-on high` over disabling scanners. Keep the coverage and raise the bar. You still see the medium findings; they just do not block. Disabling a scanner loses information permanently; raising a threshold loses only the interruption.",
              "quality": 0.6166666666666667,
              "children": []
            },
            {
              "title": "Step 5 \u00b7 Re-measure",
              "level": 3,
              "summary": "``` securegit scan . --format json | jq '[.findings[] | .severity] | group_by(.) | map({severity: .[0], count: length})' ``` **Target: under five findings per hundred files at `high` or above on a tuned repository.** Above that, keep tuning. Below that,",
              "quality": 1.0,
              "children": []
            }
          ]
        },
        {
          "title": "COMMON PROBLEMS",
          "level": 2,
          "summary": "**Scan is slow.** Scanning the tree instead of the diff. Or scanning a dependency directory. Or too many external plugins in the hot path. In that order of likelihood. **Acquire fails on a private repository.** Credentials not registered. `securegit server",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGE 62 \u2014 COMMAND REFERENCE CARD",
      "level": 1,
      "summary": "**DESIGN NOTE:** Design this page to be printed, cut out, and kept. Dense mono, tight grouping, clear section rules. This is the page people will pin up. **Headline:** QUICK REFERENCE ``` ACQUISITION securegit acquire <url> <path> acquire safely (zip+history) securegit",
      "quality": 1.0,
      "children": []
    },
    {
      "title": "PAGE 63 \u2014 THE MATURITY TABLE \u00b7 GLOSSARY",
      "level": 1,
      "summary": "**Headline:** WHAT IS REAL TODAY **Deck:** *Every capability in this issue, in one table. This is the page to hand your security reviewer.* **DESIGN NOTE:** Three-state visual coding, weight declining down the table. `SHIPPED` in ink bold. `DESIGNED` in graphite.",
      "quality": 1.0,
      "children": [
        {
          "title": "GLOSSARY",
          "level": 2,
          "summary": "**ACQUIRE** \u2014 Fetch a repository as an archive, strip hooks, scan, then convert to a normal git repository. The ordering is the security property. **ANCHOR** \u2014 Record the content hash of an external tool's output in the chain, without bundling",
          "quality": 1.0,
          "children": []
        }
      ]
    },
    {
      "title": "PAGE 64 \u2014 BACK COVER",
      "level": 1,
      "summary": "**Full-bleed ink ground. Reversed type. One idea.** **Centered, large:** > You already > ran the code. > You just > called it > a clone. **Lower third, small, mono, orange:** > BOTTLENECK \u00b7 NO. 01 \u00b7 THE UNTRUSTED CLONE >",
      "quality": 0.315,
      "children": []
    }
  ],
  "assembled_markdown": "# BOTTLENECK \u2014 Issue 01 (SecureGit Edition)\n\n**Full editorial manuscript & page-by-page layout brief**\nPrepared for handoff to Claude Design (Adobe InDesign / Express)\n\nCompanion file: `BOTTLENECK-Issue01-SECUREGIT-ArtDirection.md`\n\n---\n\n## PUBLICATION FACTS\n\n| Field | Value |\n|---|---|\n| Title | **BOTTLENECK** |\n| Standfirst | *The magazine for engineers becoming AI engineers.* |\n| Issue | No. 01 \u2014 **THE UNTRUSTED CLONE** |\n| Cover subject | **SecureGit** \u2014 zero-trust git, guarded commits, chain-of-custody evidence |\n| Series | A limited series. One issue per system on the board. |\n| Publisher | ArmyknifeLabs |\n| Extent | **64 pages**, self-cover |\n| Trim | 8.5 \u00d7 11 in (US Letter), digital-first, print-safe |\n| Voice | Senior engineer, plainspoken. Declarative. Opinionated. No hype vocabulary. |\n| Primary job | **Adoption.** A reader should finish page 23 already using it, and finish page 61 able to roll it out to a team. |\n| Disclosure | Sanitized architecture + anonymized figures. Safe for public distribution. |\n\n### The editorial contract for this issue\n\nThis is a magazine, not a brochure. The difference is enforced by three rules:\n\n1. **Every feature carries a maturity marker.** `SHIPPED`, `DESIGNED`, or `PLANNED`. No exceptions, no soft language, no \"coming soon\" used to imply \"nearly here.\" A reader who installs the tool must find exactly what this issue promised.\n2. **Every friction point is named before the reader hits it.** Adoption dies on unpleasant surprises, not on missing features. We publish the surprises.\n3. **The tool is described as it behaves, not as it is positioned.** Where SecureGit is worse than an alternative, we say so and say when to use the alternative.\n\n### House style (enforce throughout)\n\n- Banned words: *unlock, game-changing, revolutionize, seamless, leverage (verb), supercharge, 10x, journey, empower, cutting-edge, effortless, simply.*\n- \"Simply\" is banned specifically because it is the word that makes a struggling reader feel stupid. If a step is simple, it will read as simple without being told.\n- Sentences average under 18 words. Paragraphs cap at four sentences.\n- Second person throughout Parts I and V (the reader is doing something). Third person in Parts II\u2013IV (the reader is understanding something).\n- Every command shown is copy-pasteable and complete. No `...` inside a command a reader is meant to run.\n- Anonymized values only. No real hostnames, IPs, addresses, filing numbers, or credential-shaped strings.\n\n---\n\n# PAGE 1 \u2014 COVER\n\n**Masthead**\n\n> BOTTLENECK\n\n**Issue line (small caps, under masthead rule)**\n\n> NO. 01 \u00b7 THE UNTRUSTED CLONE \u00b7 A LIMITED SERIES\n\n**Cover line (dominant, stacked, 4 lines, flush left)**\n\n> `git clone`\n> RUNS\n> SOMEBODY\n> ELSE'S CODE.\n\n**Sub-cover line**\n\n> You knew that. You did it anyway, this week, probably twice.\n> A field guide to SecureGit \u2014 and to git that can prove what happened.\n\n**Cover teasers (lower third, hairline rules between)**\n\n> **DAY ONE** \u2014 installed and scanning in ten minutes\n> **ACQUIRE, NOT CLONE** \u2014 why archive-first defeats hooks\n> **TWELVE SCANNERS** \u2014 the built-in set, and the plugin ladder\n> **THE GUARDED COMMIT** \u2014 a gate that does not slow you down\n> **RECEIPTS** \u2014 cryptographic chain of custody for every push\n> **SARIF + GITLAB SAST** \u2014 reports that upload where your dashboards already live\n> **BASELINES** \u2014 adopting on legacy code without failing every build\n> **AUDIT LOG** \u2014 hash-chained, tamper-evident, SIEM-ready\n> **HANDLES, NOT VALUES** \u2014 giving agents credentials they never see\n> **THE 30-DAY ROLLOUT** \u2014 solo, then team, then org\n> **TWELVE OBJECTIONS** \u2014 answered honestly, including the ones we lose\n\n**Bottom rule (reversed out of signal color)**\n\n> PLUS: THE MATURITY TABLE \u2014 every feature marked SHIPPED, DESIGNED, OR PLANNED \u00b7 CURRENT VERSION 0.12.16\n\n**Cover art direction:** a wide field of fine parallel rules \u2014 the \"flow\" \u2014 running left to right, interrupted at roughly 60% width by a single vertical gate. Rules that pass through the gate continue in ink; a small number are turned back at the gate and rendered in signal orange, curling away. No locks. No shields. No hooded figures. No padlock iconography of any kind. The gate is a hairline, not a wall \u2014 the whole argument of the issue is that the gate is thin.\n\n---\n\n# PAGE 2 \u2014 MASTHEAD & HOW TO READ THIS ISSUE\n\n**BOTTLENECK**\n*The magazine for engineers becoming AI engineers.*\n\nIssue 01 \u00b7 The Untrusted Clone\n\nPublished by ArmyknifeLabs.\n\n**About this issue.** Most tooling magazines are written by people who want you to be impressed. This one is written by the people who build the tool, for people who are deciding whether to type one command. Being impressed is not the goal. Typing the command is.\n\n**How to read this issue**\n\n**If you have ten minutes:** page 11. That is the quickstart. It ends with you having scanned a real repository. Everything else in this issue is optional after that.\n\n**If you have an evening:** Part I, pages 10\u201323. That is Day One in full \u2014 install, first acquire, first scan, reading findings, aliases, the pre-commit hook, and a straight answer on speed. You will finish it with SecureGit in your daily workflow.\n\n**If you are evaluating this for a team:** start at page 56 (the twelve objections), then page 53 (the 30-day rollout), then come back to Part II for the mental model. You need the objections first because your team will raise them before you finish your first sentence.\n\n**If you are in a regulated environment:** Parts III and IV, pages 38\u201351. SBOM anchoring, CVE state over time, license posture, and the credential boundary. Then the maturity table on page 63, because some of what you need is `DESIGNED` and not yet `SHIPPED`, and you should know which before you plan around it.\n\n**The maturity markers**\n\nEvery capability in this issue is marked. Here is exactly what the three words mean, and we hold ourselves to them:\n\n**`SHIPPED`** \u2014 in a released build. You can install it today and use it. If you cannot, that is a bug and we want the report.\n\n**`DESIGNED`** \u2014 the architecture is decided and written down in an accepted design record, and implementation is partial or scheduled. It is real engineering intent, not a wish. It is not something you can run.\n\n**`PLANNED`** \u2014 identified, scoped, not yet designed in detail. Directional only. Do not build a procurement plan on a `PLANNED` row.\n\nIf a page describes something and you cannot find the marker, treat it as `PLANNED` and write to us, because we made a mistake.\n\n**Corrections** run at the front of the next issue, at the same size as the original claim.\n\n---\n\n# PAGE 3 \u2014 CONTENTS\n\n**ISSUE 01 \u00b7 THE UNTRUSTED CLONE**\n\n**FRONT**\n04 \u2014 Editor's Letter: *The Repository Is The Attack Surface*\n06 \u2014 Ninety Seconds: what actually happens when you clone\n08 \u2014 What SecureGit Is, And What It Is Not\n\n**PART I \u00b7 DAY ONE**\n10 \u2014 Section opener\n11 \u2014 **The Ten-Minute Quickstart**\n12 \u2014 Install, and your first acquire\n14 \u2014 Your first scan, and how to read a finding\n16 \u2014 Making it feel like git: aliases and muscle memory\n18 \u2014 The pre-commit hook: your first guardrail\n20 \u2014 \"Will this slow me down?\" \u2014 the honest performance page\n22 \u2014 Day One checklist, and what to do when it goes wrong\n\n**PART II \u00b7 THE MENTAL MODEL**\n24 \u2014 Section opener\n25 \u2014 **Acquire, not clone**: why archive-first defeats hooks\n28 \u2014 **Scan**: twelve built-in scanners and the plugin ladder\n31 \u2014 **The guarded commit**: staged scanning without the friction\n34 \u2014 **The chain of custody**: receipts, tiers, and the push gate\n\n**PART III \u00b7 SUPPLY CHAIN & GOVERNANCE**\n38 \u2014 Section opener\n39 \u2014 SBOM, OSV, and the number that matters more than zero\n42 \u2014 Lock files, licenses, and what changed when\n44 \u2014 **SARIF, GitLab SAST, and baselines**: reports that upload where your dashboards live\n45 \u2014 **The audit log & compliance report**: hash-chained evidence, OWASP + NIST SSDF mapping\n\n**PART IV \u00b7 SECRETS & AGENTS**\n46 \u2014 Section opener\n47 \u2014 Handles, not values: the secret broker\n49 \u2014 Server discovery and the credential boundary\n50 \u2014 SecureGit for agents: the MCP surface\n\n**PART V \u00b7 ADOPTION**\n52 \u2014 Section opener\n53 \u2014 **The 30-day rollout**: solo \u2192 team \u2192 org\n56 \u2014 **Twelve objections**, answered honestly\n58 \u2014 CI/CD integration that people do not disable\n60 \u2014 Troubleshooting and the false-positive playbook\n\n**BACK**\n62 \u2014 Command reference card\n63 \u2014 The maturity table \u00b7 Glossary\n64 \u2014 Back cover\n\n---\n\n# PAGES 4\u20135 \u2014 EDITOR'S LETTER\n\n## The Repository Is The Attack Surface\n\n**Deck:** *You have spent your career securing what you deploy. The thing you never secured is the moment code arrives on your machine \u2014 and that moment now happens dozens of times a week, increasingly without you watching.*\n\n---\n\nHere is a thing every engineer knows and almost nobody acts on.\n\n`git clone` is not a download. It is a download plus a filesystem write plus, depending on what is in the archive and what you do next, code execution. Hooks. Submodule configuration. Filter drivers. Path handling quirks that have produced real CVEs in real git releases more than once. The repository is not data that you then choose to run. It is a bundle of data and executable configuration that you have invited onto your machine, inside your home directory, with your permissions.\n\nYou know this. So does everyone. And then a colleague posts a link, and you clone it, because the alternative is not cloning it, and that is not a real alternative.\n\nThe reason this became urgent rather than merely true is that the volume changed.\n\nTwo years ago you cloned a handful of repositories a month, and you had usually heard of them. Now an agent working on your behalf pulls down a dependency you have not evaluated, to check whether its API does what a search result claimed. It does that at three in the morning while you are asleep, using your credentials, on your machine. The judgment step that used to sit between \"I found this repository\" and \"it is now on my disk\" has been compressed to nothing, and in many workflows removed entirely.\n\nThat is the bottleneck this issue is about. Not \"can I get the code\" \u2014 that was solved decades ago. **Can I get the code without the act of getting it being the vulnerability, and can I prove afterward what arrived and what I did with it.**\n\n---\n\n**PULL QUOTE (page 4, outer column, signal)**\n\n> `git clone` is not a download. It is a download that has been granted permission to run.\n\n---\n\nThere is a second half to this, and it is the half that turns a security tool into an engineering tool.\n\nOnce you accept that code arrival is an event worth controlling, you notice that code *departure* is too. A push is the first moment your work crosses onto shared infrastructure. It is the moment where \"I wrote this\" becomes \"we shipped this.\" And in almost every organization, that moment carries no durable proof of anything: not who wrote which lines, not which of those lines came from a human versus an agent, not whether any scanner ever looked at it, not what the dependency posture was at the time.\n\nSix months later somebody asks. They ask because of an audit, or an incident, or a customer questionnaire, or a diligence process. And the honest answer is that you have git history, which tells you what changed and who the commit claims to be from, and nothing else. Git history is a record of assertions, not a record of evidence. It was never designed to be otherwise \u2014 Linus was solving a different problem, and solving it very well.\n\nSecureGit is one answer to both halves. It puts a thin gate on arrival, a thin gate on departure, and a cryptographic receipt on both. That is the whole idea, and the rest of this issue is about whether the gates are thin enough that you will actually keep them.\n\n---\n\n**PULL QUOTE (page 5, inset, boxed)**\n\n> Git history is a record of assertions, not a record of evidence. That was the right design for 2005. It is an expensive gap in 2026.\n\n---\n\nNow the part where I tell you what this is going to cost you, because adoption articles that skip this are the reason engineers distrust adoption articles.\n\n**It will cost you a new verb.** You will type `acquire` instead of `clone` for untrusted repositories. That is a habit change, and habit changes fail unless you make them cheap. Page 16 is entirely about making it cheap.\n\n**It will cost you seconds, and occasionally a minute.** Scanning is not free. On a normal working repository it is fast enough that you will stop noticing. On a very large repository it is not, and page 20 gives you real numbers rather than reassurance.\n\n**It will cost you some false positives.** Every scanner produces them. A scanner that claims otherwise is either lying or not looking hard. Page 60 is a playbook for driving them down, because a tool that cries wolf gets disabled in week two, and a disabled tool has negative value \u2014 it provides the feeling of security with none of it.\n\n**And it will cost you a conversation with your team**, which is the expensive part. Page 56 gives you the twelve objections you will hear, with honest answers, including the three cases where the honest answer is \"you are right, do not use this for that.\"\n\nWhat you get for it: code that arrives without executing, commits that cannot silently ship a secret, pushes that carry proof, and \u2014 the part that surprises people \u2014 a genuine answer to the question \"what did we know about our supply chain in March,\" which is a question that currently ends most enterprise sales conversations in an uncomfortable silence.\n\nStart at page 11. It takes ten minutes and it ends with you having scanned something real.\n\n\u2014 *The Editors*\n\n---\n\n# PAGES 6\u20137 \u2014 NINETY SECONDS (INFOGRAPHIC SPREAD)\n\n**Spread headline (spans gutter):** WHAT ACTUALLY HAPPENS\n\n**Deck:** *Two timelines, same repository. The top is `git clone`. The bottom is `securegit acquire`. Read them against each other.*\n\n## Panel A (left page, upper) \u2014 TIMELINE ONE: `git clone`\n\n**Format:** a horizontal timeline, left to right, with events as ticks above the line and *trust state* as a colored band below it. The band starts neutral and turns signal-orange at the first execution point, staying orange to the end.\n\n| Moment | What happens | Your exposure |\n|---|---|---|\n| `t+0` | Network fetch begins | None yet |\n| `t+2s` | Objects written into `.git` | Disk write, your permissions |\n| `t+3s` | **Hook scripts land in `.git/hooks`** | **Dormant executables now on disk** |\n| `t+3s` | `.gitmodules`, `.gitattributes`, config land | Filter and submodule directives, unread |\n| `t+4s` | Working tree checkout | Paths written per attacker-influenced names |\n| `t+4s` | Clone reports success | **You believe you have data. You have data and configuration.** |\n| `t+30s` | You `cd` in and run anything git-adjacent | **Hooks may execute. Filters may execute.** |\n| `t+45s` | You open the repo in an editor with extensions | Editor-level execution surface |\n\n**Annotation (signal, pointing at `t+3s`):** *Nothing has run yet. That is the entire window in which a decision is still cheap. `git clone` gives you no place to stand inside it.*\n\n## Panel B (left page, lower) \u2014 TIMELINE TWO: `securegit acquire`\n\n| Moment | What happens | Your exposure |\n|---|---|---|\n| `t+0` | **Fetched as an archive, not as a live repo** | Inert bytes |\n| `t+2s` | Archive extracted to a staging path | Files on disk, no git configuration active |\n| `t+2s` | **Hooks stripped** | Dormant executables removed, not merely ignored |\n| `t+3s` | Scanners run across the tree | Findings collected |\n| `t+4s` | A sanitization report is written alongside | You have something to read |\n| `t+5s` | Converted into a normal git repository | **Now** it is a repo \u2014 after inspection |\n| `t+5s` | `SHIPPED` \u2014 a chain receipt records the acquisition | Provenance from moment zero |\n\n**Annotation:** *The order is the product. Fetch, strip, inspect, then confer git-ness. `git clone` confers git-ness first and inspects never.*\n\n## Panel C (right page, full) \u2014 THE THREE THINGS PEOPLE GET WRONG\n\n**Format:** three tall cards, each with a myth in condensed caps, a correction in serif body, and a one-line takeaway in mono.\n\n**MYTH 1 \u00b7 \"I only clone repos I trust.\"**\nYou clone repos your dependencies trust, which is a different set and a much larger one. You also clone repos an agent selected on your behalf from a search result. The trusted-source model assumes a human evaluated the source. Increasingly, none did.\n`\u2192 The question is not \"do I trust this\" but \"did anyone actually look.\"`\n\n**MYTH 2 \u00b7 \"My scanner catches this.\"**\nMost scanning runs in CI, which is after the clone, on a machine that is not yours, looking at the tree rather than the repository metadata. The hooks that concern us are in `.git`, which most scanners exclude by default and which CI usually does not receive.\n`\u2192 Scan the .git directory. Most tools do not, by default. Check yours.`\n\n**MYTH 3 \u00b7 \"This is theoretical.\"**\nHook execution and path-handling behavior in git have produced real, patched CVEs on multiple occasions. The mitigation is always \"upgrade git,\" which works until the next one, and which does nothing about the window between disclosure and your fleet actually upgrading.\n`\u2192 Defense in depth exists because point fixes arrive late and unevenly.`\n\n**Foot callout (right page, reversed):**\n\n> **The honest framing**\n> SecureGit does not make untrusted code safe. Nothing does. It makes the *acquisition* of untrusted code non-executing, and it gives you a place to stand \u2014 a moment where the code is on your disk, inert, and you can look at it before it becomes a live repository.\n>\n> That moment did not previously exist. That is the entire contribution. Everything else in this issue is built on it.\n\n---\n\n# PAGES 8\u20139 \u2014 WHAT SECUREGIT IS, AND WHAT IT IS NOT\n\n**Headline:** THE ONE-PAGE MENTAL MODEL\n\n**Deck:** *Before any command, this. Five minutes here saves an hour of confusion later.*\n\n---\n\n### The one sentence\n\n**SecureGit is a git wrapper that puts a gate on the two moments code crosses a trust boundary \u2014 arrival and departure \u2014 and writes a signed receipt for each crossing.**\n\nEverything else is elaboration. If you remember one thing, remember: **arrival, departure, receipt.**\n\n### The four layers\n\nThink of it as four layers stacked, each usable without the ones above it. **You can adopt one layer and stop.** Most people should, at first.\n\n**LAYER 1 \u00b7 ACQUISITION** `SHIPPED`\nFetch untrusted code without executing it. `securegit acquire` replaces `git clone` for anything you did not write. Archive-first (ZIP-with-history by default; `zip-only` and `bare-checkout` also available), hooks stripped, twelve scanners run, then converted to a normal git repo.\n*Adopt this alone and you have already gotten most of the day-one value.*\n\n**LAYER 2 \u00b7 SCANNING** `SHIPPED`\nTwelve built-in Rust scanners \u2014 secrets, patterns, entropy, binary, encoding (Trojan Source), supply chain, CI/CD, container, IaC, deserialization, dangerous files, git internals \u2014 plus a plugin system that wraps tools you already use. Run it on a path, on staged changes, on a diff, or in CI. Outputs pretty, JSON, **SARIF 2.1.0**, or **GitLab SAST v15**. `--write-baseline` lets you adopt on a legacy codebase without failing every build.\n*Adopt this second. It is the layer that changes your commit habits.*\n\n**LAYER 3 \u00b7 CHAIN OF CUSTODY & GOVERNANCE** `SHIPPED` (core, audit log, layered policy) / `DESIGNED` (offline chain, batch lookup)\nEvery trust-boundary operation emits a cryptographically signed receipt. Push can be gated on whether the commits in range have receipts. Blame can show provenance per line. A separate **hash-chained audit log** captures every security-relevant event (scan, block, hook, baseline write, credential change) and exports as JSONL or CEF for SIEM ingest. **Layered configuration** (`/etc/securegit` \u2192 user \u2192 repo \u2192 env) with `[policy] min_fail_on`, `locked_keys`, and `audit_required` gives security teams a governance surface developers can tighten but not weaken.\n*Adopt this when you need to prove things to someone who was not there. Two of its three pieces (audit log, policy) work without any signing infrastructure.*\n\n**LAYER 4 \u00b7 SUPPLY-CHAIN PROVENANCE** `SHIPPED` (anchoring, SBOM, compliance report) / `DESIGNED` (parts)\nSBOM anchoring, CVE state at a point in time, license posture, lock-file change records \u2014 all attached to the same chain. Releases ship a CycloneDX SBOM, `SHA256SUMS`, and a **Sigstore keyless (cosign)** signature. **`securegit compliance report`** maps findings to OWASP Top 10 (2021) via CWE and summarizes NIST SSDF v1.1 practice coverage \u2014 markdown or JSON, straight into a review packet.\n*Adopt this when procurement, audit, or a customer questionnaire forces the question.*\n\n---\n\n**DIAGRAM (page 8, foot):** four horizontal bands stacked, each labeled, each with its maturity marker at the right edge. Bands get narrower toward the top to indicate adoption funnel. An arrow at the left marked *\"start here\"* points at Layer 1.\n\n---\n\n### What it is not\n\nThis section exists because mis-set expectations are the most common cause of abandonment, and every item below has caused a real \"wait, I thought it did X\" moment.\n\n**It is not a replacement for git.** It wraps git. Your repositories stay normal git repositories. Your teammates who do not use SecureGit are unaffected. You can stop using it at any time and lose nothing but the receipts.\n\n**It is not a malware sandbox.** It prevents *acquisition-time* execution and it scans. It does not analyze behavior, emulate, or detonate anything. Code you subsequently choose to build and run is code you chose to build and run.\n\n**It is not a SAST platform.** It is not competing with Semgrep or SonarQube on depth of static analysis. It wraps them. If you need deep dataflow analysis, you need those tools, and SecureGit's job is to run them at the right moment and anchor their output.\n\n**It is not a secrets manager.** It brokers access to secrets that live in a real secrets manager. It is not where your secrets should live. (Page 47.)\n\n**It is not a bundled vulnerability scanner** \u2014 with one deliberate, narrow exception documented on page 40. It anchors the output of the scanners you already run. That boundary was a decision, it was contested internally, and page 40 explains why it moved once and why it will not move again casually.\n\n**It does not make your history trustworthy retroactively.** Commits made before you adopted it have no receipts. There is a path for retro-attestation, and it is honest about being a different tier of evidence. A receipt that says \"we checked afterward\" is worth less than one that says \"we watched it happen,\" and the system records which one you have.\n\n---\n\n**SIDEBAR (page 9, foot, boxed): The three-word test**\n\n> Whenever you are unsure whether SecureGit is involved in something, ask: **is this a boundary crossing?**\n>\n> Code arriving from elsewhere \u2014 yes. Code leaving to shared infrastructure \u2014 yes. Anything purely local, in your working tree, between you and your own disk \u2014 no.\n>\n> `status`, `log`, `diff`, `add`, `checkout`, `branch`, `stash` cross no boundary and emit no receipts. That is deliberate. A tool that instruments everything gets turned off.\n\n---\n\n# PAGE 10 \u2014 PART I: SECTION OPENER\n\n**Full-bleed. Reversed: paper type on ink ground. Type only.**\n\n> **PART ONE\n> DAY ONE**\n\n> Ten minutes to your first scan.\n> An evening to a changed workflow.\n\n> Nothing on these pages requires\n> a decision from anyone but you.\n\n*(Foot line, mono, orange:)* `11 \u2192 23`\n\n---\n\n# PAGE 11 \u2014 THE TEN-MINUTE QUICKSTART\n\n**Headline:** TEN MINUTES\n\n**Deck:** *Five commands. No configuration. No account. Nothing to uninstall afterward except one binary.*\n\n**DESIGN NOTE:** This page is the single most important page in the issue and should be designed as a self-contained card that survives being screenshotted and shared out of context. Large terminal blocks, generous spacing, numbered steps with hanging mono numerals in signal. Every command on one line, no wrapping.\n\n---\n\n### 1 \u00b7 Install\n\n```\ncurl -fsSL https://<release-host>/securegit/install.sh | sh\n```\n\n**Before you paste that:** you are about to pipe a script from the internet into a shell, in an issue about not trusting code from the internet. We are aware of the irony and you should be too. If you would rather not, download the release binary, verify it with the shipped `SHA256SUMS` + **Sigstore keyless signature (cosign)**, and put it on your `PATH`. Both paths are supported and the second one is the one we would pick. Page 12 has the verify commands.\n\nVerify:\n\n```\nsecuregit --version\n```\n\n### 2 \u00b7 Acquire something real\n\nPick a public repository you have never inspected. Small is better for a first run.\n\n```\nsecuregit acquire https://github.com/toml-lang/toml /tmp/first-acquire\n```\n\n### 3 \u00b7 Confirm the hooks are gone\n\n```\nls -la /tmp/first-acquire/.git/hooks\n```\n\n**This should be empty.** That is the whole product in one directory listing. A normal clone of the same repository will have sample hooks in there and, from a hostile source, could have live ones.\n\n### 4 \u00b7 Read the report\n\n```\ncat /tmp/first-acquire/.securegit-report.json\n```\n\n### 5 \u00b7 Scan something you care about\n\n```\nsecuregit scan . --fail-on high\n```\n\nRun this in a repository you actually work in. It will finish in seconds on a normal project.\n\n---\n\n**FOOT BLOCK (reversed, full width):**\n\n> **You are done.** That is the tool. Everything after this page makes it faster, quieter, and more useful \u2014 but you have already prevented the class of problem this issue is about.\n>\n> If step 5 produced findings you disagree with, that is expected and it is not a failure. Go to page 60.\n\n---\n\n# PAGES 12\u201313 \u2014 INSTALL, AND YOUR FIRST ACQUIRE\n\n**Headline:** GETTING IT ON YOUR MACHINE\n\n**Deck:** *Longer than the quickstart, with the parts that go wrong.*\n\n---\n\n### Platforms\n\n`SHIPPED` on Linux and macOS, both Intel and Apple Silicon. Windows support is **experimental** \u2014 it works, and it is not yet held to the same bar, and you should expect rough edges around path handling and shell integration. If you are on Windows and this is a blocker, tell us; that feedback moves priority.\n\nContainer use is supported and is the recommended way to try it if you do not want a binary on your host.\n\n### The three install paths\n\n**Path A \u2014 release binary.** Download, verify, place on `PATH`. Most auditable. Recommended for anyone who reads the rest of this magazine and takes it seriously. Every release ships with a **CycloneDX SBOM**, `SHA256SUMS`, and a **Sigstore keyless signature (cosign)**:\n\n```\nsha256sum -c SHA256SUMS --ignore-missing\ncosign verify-blob \\\n  --certificate SHA256SUMS.pem --signature SHA256SUMS.sig \\\n  --certificate-identity-regexp 'github.com/armyknifelabs-tools/securegit' \\\n  --certificate-oidc-issuer https://token.actions.githubusercontent.com \\\n  SHA256SUMS\n```\n\n**Path B \u2014 install script.** Fastest. Reasonable for a laptop, not for a fleet.\n\n**Path C \u2014 build from source.** Requires a Rust toolchain. Slowest, most transparent. Use this if your organization requires building security tooling from source, which some do.\n\n**For a fleet:** do not use Path B. Package it, sign it, and distribute it the way you distribute everything else. A security tool installed by curl-pipe-shell across two hundred machines is a supply-chain finding of its own, and somebody will eventually point that out in a review. Distribute a system-wide `/etc/securegit/config.toml` at the same time \u2014 that is where org policy lives (page 45).\n\n### What gets created\n\n```\n~/.config/securegit/config.toml\n~/.config/securegit/plugins/\n~/.local/share/securegit/audit/           # tamper-evident audit log\n```\n\nNo daemon, no service, no background process, nothing in your login items. Uninstall is deleting the binary and those directories.\n\n### Corporate networks\n\nSecureGit reads the OS trust store by default, so a TLS-inspecting corporate proxy with a properly installed root CA \u201cjust works.\u201d For private CAs held in a file rather than the trust store, point `SECUREGIT_CA_BUNDLE=/etc/ssl/corp-bundle.pem` at a PEM bundle (mirroring `curl --cacert`). Explicit proxying uses standard `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`, plus `SECUREGIT_PROXY` (or `[network] proxy`) as an override. These cover the two failure modes we hear about most from enterprise pilots.\n\n**This matters for adoption more than it sounds.** A tool that is trivially reversible gets tried. A tool that installs infrastructure gets a meeting.\n\n---\n\n### Your first acquire, in detail\n\n```\nsecuregit acquire <url> <destination>\n```\n\nYou can also acquire into the current directory:\n\n```\nsecuregit acquire <url> .\n```\n\n**What happens, in order:**\n\n1. The remote is resolved and the content is fetched **as an archive**, not as a live repository. This is the load-bearing step. No git configuration is active at any point during transfer.\n2. The archive is extracted to the destination.\n3. **Hooks are stripped.** Not disabled, not renamed \u2014 removed.\n4. The configured scanners run across the extracted tree.\n5. A report is written to `.securegit-report.json` in the destination.\n6. The tree is converted into a normal git repository, with history.\n7. `SHIPPED` A chain receipt is emitted recording the remote URL and the resolved `HEAD` at acquisition time. Acquisition never blocks on receipt failure \u2014 if the signing daemon is unreachable, you get a warning and your clone.\n\n**Point 7 deserves a note.** The receipt records what you got, from where, at what commit, at what time. Later, if the upstream force-pushes and history changes underneath you, your receipt still says what you actually received. That is a surprisingly practical thing to have and it costs nothing at acquisition time.\n\n### What you can do with the result\n\nEverything. It is a normal repository.\n\n```\ncd /tmp/first-acquire\ngit log\ngit diff\ngit checkout -b my-branch\n```\n\nThere is no lock-in, no proprietary format, no wrapper you have to keep using. If you delete SecureGit tomorrow, the repository you acquired is unaffected.\n\n---\n\n**SIDEBAR (page 13, boxed): The three things that go wrong on first install**\n\n> **\"Command not found\" after the install script.** The binary landed somewhere not on your `PATH`. Check `~/.local/bin` and `/usr/local/bin`, then restart your shell.\n>\n> **Acquire is slow on a very large repository.** Archive-first fetch has different performance characteristics than a shallow clone. For repositories in the hundreds of megabytes, expect seconds, not milliseconds, for acquisition and then a separate scan pass. Page 20 has numbers.\n>\n> **A private repository fails to acquire.** You have not registered credentials yet. Page 49. For a first run, use a public repository \u2014 do not start your evaluation by debugging auth.\n\n---\n\n# PAGES 14\u201315 \u2014 YOUR FIRST SCAN, AND HOW TO READ A FINDING\n\n**Headline:** WHAT THE SCANNER SEES\n\n**Deck:** *Findings are only useful if you can act on them within about ten seconds of reading them. Here is how to get to that.*\n\n---\n\n### The command surface\n\n```\nsecuregit scan <path>\nsecuregit scan .\nsecuregit scan . --fail-on high\nsecuregit scan . --min-severity high\nsecuregit scan . --include-git\nsecuregit scan . --format json\nsecuregit scan . --format sarif  --report-output f.sarif\nsecuregit scan . --format gitlab --report-output gl.json\nsecuregit scan --staged\nsecuregit scan . --write-baseline \"<reason>\"\nsecuregit scan . --baseline .securegit/baseline.json\n```\n\n`--include-git` deserves emphasis. Most scanners in the industry exclude `.git` by default, and the acquisition threat this magazine opens with lives precisely there. When you are evaluating an unfamiliar repository, include it.\n\n`--format sarif` and `--format gitlab` upload directly into the dashboards your reviewers already read \u2014 GitHub Advanced Security, Azure DevOps, SonarQube, DefectDojo on one side; the GitLab security dashboard and MR widget on the other. `--report-output` writes the report even when the scan gates, so one CI step can both report *and* enforce. Page 44 is a full worked example.\n\n### The twelve built-in scanners\n\nAll `SHIPPED`, all compiled into the binary, all sub-millisecond to low-millisecond per file. Load automatically; no configuration required.\n\n| Scanner | Looks for | Typical severity |\n|---|---|---|\n| `secrets` | AWS keys, GitHub/Slack tokens, private keys, DB passwords, OpenAI keys | Critical / High |\n| `patterns` | Dynamic execution, shell injection, reverse shells, persistence indicators | Critical / High |\n| `entropy` | High-entropy strings that look like keys but match no known format | High / Medium |\n| `binary` | Unexpected ELF, PE, Mach-O executables in source trees | Critical / Medium |\n| `encoding` | **Trojan Source (CVE-2021-42574)** \u2014 BiDi overrides, homoglyphs, zero-width chars | Critical / High |\n| `supply-chain` | 36 known typosquatted packages (npm/PyPI/Ruby), malicious lifecycle hooks, dependency confusion | Critical / High |\n| `ci-cd` | `pull_request_target` abuse, unpinned actions, input injection into `run:`, cache poisoning, `curl\\|bash` in pipelines, secret exfiltration, Jenkins `@Grab` | Critical / High |\n| `container` | Privileged pods, Docker socket mounts, `cap_add: ALL`, RBAC wildcards, host namespaces | Critical / Medium |\n| `iac` | Open security groups, public S3 buckets, `local-exec` provisioners, Ansible shell pipes | Critical / Medium |\n| `deserialization` | Python `pickle`/`marshal`, `yaml.load()` without SafeLoader, Java `ObjectInputStream`, XXE | Critical / High |\n| `dangerous-files` | `.gitmodules` path traversal (CVE-2018-17456), fsmonitor hooks, filter drivers | Critical / High |\n| `git-internals` | Unexpected hooks after sanitization, dangerous git config keys | Critical / High |\n\n**`entropy` is the one that produces the most false positives**, by a wide margin, and it is also the one that catches the credentials that no pattern list knows about yet. That is the trade and it is not resolvable \u2014 you tune it, you do not fix it. Page 60.\n\n**`encoding` and `dangerous-files` are the ones that will most surprise you the first time they fire.** Trojan Source findings look wrong until you view the file with an editor that shows invisible codepoints; `.gitmodules` findings are usually benign in your own repos and are the whole reason `--include-git` exists for someone else's.\n\n### Anatomy of a finding\n\n**DESIGN NOTE:** render as an annotated specimen, similar to a museum label. A single finding, blown up, with callout lines to marginal annotations.\n\n```\nCRITICAL  secrets           src/config/settings.py:14\n          AWS Access Key\n          AKIA\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\n          \u2192 Rotate this credential, then remove from history.\n```\n\n**Severity** \u2014 `critical`, `high`, `medium`, `low`. Drives `--fail-on` and `--min-severity`.\n\n**Scanner** \u2014 which plugin found it. Useful because it tells you the false-positive profile immediately. A finding from `secrets` is usually real. A finding from `entropy` needs a human look.\n\n**Location** \u2014 file and line. Always. A finding without a location is a bug.\n\n**The redacted value** \u2014 enough to identify it, never enough to use it. **Findings never print full secret values**, including in JSON output. This matters because scan output ends up in CI logs, which are frequently more readable than the repository was.\n\n**The action** \u2014 what to do. A finding that does not tell you what to do is a notification, not a finding.\n\n---\n\n### The ten-second triage\n\nFor each finding, in order:\n\n1. **Is it in a test fixture or an example?** Very common, usually benign, and the reason `--skip-paths` exists. But check that the example key is actually fake \u2014 real keys get pasted into examples more often than anyone admits.\n2. **Is it a real credential?** If yes, stop reading this magazine. Rotate it. Removing it from the file is not sufficient; it is in history, and if the repository was ever public or ever shared, assume compromise. Rotation is the only remediation.\n3. **Is it high entropy but not a secret?** A hash, a test vector, a base64 asset, a minified bundle. Suppress it by path, not by disabling the scanner.\n4. **Is it a pattern finding you disagree with?** Read the line. `patterns` findings are usually about dynamic execution, and \"I know what I'm doing here\" is sometimes correct and sometimes the exact sentence that precedes an incident.\n\n---\n\n**SIDEBAR (page 15, boxed): The first scan is always the worst one**\n\n> Your first scan of a mature repository will produce more findings than every subsequent scan combined. Years of accumulated fixtures, examples, vendored code, and one or two genuine problems.\n>\n> **Do not try to get to zero on day one.** The correct first move is:\n>\n> 1. Run it. Look at the `critical` findings only. There are usually few.\n> 2. Fix or rotate anything real.\n> 3. Set `--fail-on high` and configure `skip_paths` for vendor and dependency directories.\n> 4. **From this point forward, gate on new findings, not total findings.**\n>\n> Teams that try to reach zero before adopting never adopt. Teams that gate the delta and burn down the backlog over a quarter succeed almost every time.\n\n---\n\n# PAGES 16\u201317 \u2014 MAKING IT FEEL LIKE GIT\n\n**Headline:** MUSCLE MEMORY\n\n**Deck:** *The single biggest predictor of whether you are still using this in a month is whether typing it costs you anything. Spend thirty seconds now.*\n\n---\n\nThe command is called `securegit` because the name should be unambiguous when someone reads it in a script, a runbook, or a CI file six months from now. It is not meant to be typed nine characters at a time, forty times a day.\n\n**Alias it. Immediately. Before you do anything else in this issue.**\n\n### Bash / Zsh\n\nAdd to `~/.bashrc` or `~/.zshrc`:\n\n```\nalias sgit='securegit'\nalias sg='securegit'\n```\n\n### Fish\n\nAdd to `~/.config/fish/config.fish`:\n\n```\nalias sgit='securegit'\nalias sg='securegit'\n```\n\n### PowerShell\n\nAdd to your profile:\n\n```\nSet-Alias -Name sgit -Value securegit\nSet-Alias -Name sg -Value securegit\n```\n\n### Tab completion follows the alias\n\n```\n# bash\ncomplete -F _securegit sgit\ncomplete -F _securegit sg\n\n# zsh\ncompdef sgit=securegit\ncompdef sg=securegit\n```\n\nDo this. An alias without completion is worse than no alias, because you lose discoverability and you will forget the flags.\n\n---\n\n### The workflow alias set\n\nThis is the part that actually changes behavior. Create `~/.securegit_aliases`:\n\n```\n# Acquire instead of clone, with a name that reminds you why\nalias safe-clone='securegit acquire'\n\n# Scan right here\nalias scan-here='securegit scan .'\n\n# Scan including repository metadata \u2014 for unfamiliar code\nalias scan-deep='securegit scan . --include-git'\n\n# Scan only what you are about to commit\nalias scan-staged='securegit scan --staged --fail-on high'\n\n# Scan only what changed against your main branch\nalias scan-diff='git diff main --name-only | xargs securegit scan'\n\n# Skip the usual noise\nalias scan-clean='securegit scan . --skip-paths \"**/node_modules/**,**/vendor/**\"'\n\n# Plugin housekeeping\nalias plugin-status='securegit plugin list && securegit plugin check-updates'\n```\n\nSource it:\n\n```\necho \"source ~/.securegit_aliases\" >> ~/.bashrc\n```\n\n**`scan-diff` is the one you will use most** and it is the one people discover last. Scanning only what changed against your main branch turns a multi-second operation into a sub-second one and makes the whole habit sustainable on large repositories.\n\n### Environment defaults\n\nSet these once and stop passing flags:\n\n```\nexport SECUREGIT_FAIL_ON=high\nexport SECUREGIT_SKIP_PATHS=\"**/node_modules/**:**/vendor/**:**/target/**:**/dist/**\"\n```\n\n---\n\n**SIDEBAR (page 17, boxed): On the name**\n\n> We have been asked, repeatedly, whether the binary should be called `sgit` or `safegit` or something shorter.\n>\n> The current answer is no, and the reasoning is worth stating because it is a general principle. **Optimize the written name for the reader; optimize the typed name for the typist.** A script, a CI file, and a runbook are read far more often than they are written, and by people with less context than the author had. `securegit scan . --fail-on high` is self-explaining to someone who has never heard of the tool. `sg scan . --fail-on high` is not.\n>\n> Your shell is yours. Alias it to a single character if you like. The canonical name stays long on purpose.\n\n---\n\n# PAGES 18\u201319 \u2014 THE PRE-COMMIT HOOK\n\n**Headline:** YOUR FIRST GUARDRAIL\n\n**Deck:** *One file, six lines. This is the point where SecureGit stops being a thing you remember to run and starts being a thing that protects you when you forget.*\n\n---\n\nEverything up to this page has been manual. Manual controls work exactly as long as your attention does, which is to say not on the Friday afternoon when it matters.\n\nThe pre-commit hook is where that changes.\n\n### The hook\n\nCreate `.git/hooks/pre-commit`:\n\n```\n#!/bin/bash\nsecuregit scan --staged --fail-on high || {\n  echo \"SecureGit: staged changes contain high-severity findings. Commit blocked.\"\n  echo \"Review with: securegit scan --staged\"\n  echo \"Override once with: git commit --no-verify\"\n  exit 1\n}\n```\n\nMake it executable:\n\n```\nchmod +x .git/hooks/pre-commit\n```\n\n**Three deliberate choices in those six lines, and they are all about adoption rather than security.**\n\n**It scans only staged changes.** Not the repository. Staged-only keeps it fast \u2014 typically well under a second \u2014 which is the difference between a hook you keep and a hook you delete in week two.\n\n**It fails at `high`, not `medium`.** Start permissive. You can tighten later, and tightening a working gate is easy. Loosening a gate that everyone has learned to bypass is nearly impossible, because you have already taught the team that the gate is noise.\n\n**It tells you how to override.** `--no-verify` is printed right there. This looks like a weakness and it is the opposite. A gate with no visible escape hatch gets bypassed by a *global* mechanism \u2014 someone disables hooks entirely, or removes the file, and now you have no gate at all and no signal. A gate with a visible per-commit override gets bypassed once, deliberately, in a way that is visible in the reflog and in the person's memory.\n\n---\n\n**PULL QUOTE**\n\n> A gate with no visible escape hatch does not get respected. It gets removed. Print the override.\n\n---\n\n### Distributing hooks to a team\n\n`.git/hooks` is not version controlled, which is a genuine problem and not one SecureGit invented.\n\nThree options, in increasing order of robustness:\n\n**A \u00b7 A setup script in the repository.** `scripts/setup-hooks.sh`, run once by each developer, documented in the README. Simple, and it depends on people running it.\n\n**B \u00b7 `core.hooksPath`.** Point git at a version-controlled directory:\n\n```\ngit config core.hooksPath .githooks\n```\n\nCommit `.githooks/pre-commit`. Now the hook travels with the repository. Still requires the one-time config, but it is a single command and it can go in your onboarding script.\n\n**C \u00b7 Managed configuration.** Push `core.hooksPath` and SecureGit policy through whatever manages developer workstations. This is the enterprise answer and it is covered on page 55.\n\n**Do not rely on option A alone past about five engineers.** The failure is silent: someone joins, never runs the script, and is unprotected while believing they are protected. That is worse than no hook, because the team's collective sense of coverage is now wrong.\n\n### The pre-push hook\n\nSame idea, wider net, runs less often:\n\n```\n#!/bin/bash\nsecuregit scan --fail-on critical || exit 1\n```\n\nPush happens less frequently than commit, so you can afford a broader scan. Note the threshold is `critical` here rather than `high` \u2014 a push gate that blocks frequently gets bypassed with `--no-verify` reflexively, and then it protects nothing.\n\n**The general rule: the more disruptive the gate, the higher the bar for tripping it.**\n\n---\n\n**SIDEBAR (page 19, boxed): What to do the first time it blocks you**\n\n> It will block you, and the first time will be at a bad moment. That is when adoption is decided.\n>\n> **Do not reach for `--no-verify` reflexively.** Take ninety seconds:\n>\n> `securegit scan --staged` \u2014 read the actual finding.\n>\n> **If it is real:** you were about to commit a credential. The hook just did the single most valuable thing it will ever do for you. Rotate and move on.\n>\n> **If it is a false positive:** add a `skip_paths` entry or narrow the scanner set. Fix it *now*, in that moment, while it is annoying \u2014 because the alternative is that you `--no-verify` past it every day for a month and eventually delete the hook.\n>\n> The failure mode of security tooling is never a dramatic bypass. It is quiet, incremental erosion by people who were busy.\n\n---\n\n# PAGES 20\u201321 \u2014 WILL THIS SLOW ME DOWN?\n\n**Headline:** THE HONEST PERFORMANCE PAGE\n\n**Deck:** *Yes, somewhat, in specific places. Here is exactly where, with numbers, so you can decide rather than find out.*\n\n---\n\nPerformance is where security tooling adoption actually dies. Not in the evaluation, where everyone is patient. In week three, when a scan adds nine seconds to a loop somebody runs forty times a day.\n\nSo: the numbers.\n\n### Single file\n\nA small source file, all twelve built-in scanners plus one external plugin:\n\n| Scanner | Findings | Duration |\n|---|---|---|\n| `secrets` / `patterns` / `entropy` (built-in) | 6 | ~5 ms combined |\n| `encoding` / `supply-chain` / `ci-cd` / `container` / `iac` (built-in) | 0 | ~4 ms combined |\n| `deserialization` / `dangerous-files` / `git-internals` / `binary` (built-in) | 0 | ~3 ms combined |\n| External plugin (Python) | 3 | ~23 ms |\n\n**Total: about 35 ms**, of which the external plugin is roughly two-thirds.\n\nThat ratio is the whole performance story of this tool, and it repeats at every scale.\n\n### Throughput\n\n| Plugin type | Files per second |\n|---|---|\n| Built-in (Rust, in-process) | ~1,000\u20135,000 |\n| External (subprocess) | ~50\u2013200 |\n\n**Built-in scanners are between ten and a hundred times faster than external ones**, because an external plugin pays process-spawn cost \u2014 roughly 20\u201325 ms for an interpreted tool, 5\u201310 ms for a compiled one \u2014 on every invocation.\n\n### Large repository\n\nA substantial open-source repository, several hundred megabytes, five thousand-plus files:\n\n| Phase | Time |\n|---|---|\n| Acquisition | ~2.5 s |\n| Extraction | ~1.8 s |\n| Scan, all 12 built-in scanners | ~35 s |\n| Scan, with ~10 external plugins | ~2\u20133 min |\n\n**Read those last two rows carefully.** Built-in scanners scan a large repository in about half a minute. Adding ten external plugins makes it four to six times slower. That is the trade, stated plainly.\n\n### Resource use\n\n| Scale | CPU | Memory |\n|---|---|---|\n| Under 100 files | 1 core, under 10% | under 50 MB |\n| 5,000+ files | 1\u20134 cores, 40\u201380% | 100\u2013500 MB |\n| Per external plugin | process overhead | +10\u201350 MB each |\n\n---\n\n**PULL QUOTE**\n\n> Built-in scanners scan a large repository in about half a minute. Ten external plugins make it four to six times slower. Choose your plugins like you choose dependencies.\n\n---\n\n### The five rules that keep it fast\n\n\n**1 \u00b7 Scan the diff, not the tree.** In daily work you almost never need a full scan.\n\n```\ngit diff main --name-only | xargs securegit scan\nsecuregit scan --staged\n```\n\nThis is the single highest-leverage habit in this issue.\n\n**2 \u00b7 Configure `skip_paths` once, properly.** `node_modules`, `vendor`, `target`, `dist`, `build`, `.venv`, and whatever your ecosystem's equivalent is. Most first-time \"this is slow\" reports are a scan of a dependency directory.\n\n**3 \u00b7 Built-in scanners for the hot path.** Pre-commit hooks and CI gates should run built-in scanners only. Save the external plugins for scheduled deep scans.\n\n**4 \u00b7 Match the plugin to the stack.** A Python security scanner on a Rust repository costs you process-spawn time on every file and finds nothing. Enable only what applies.\n\n**5 \u00b7 Deep scan on a schedule, not on every commit.** Nightly or per-PR full scans with the whole plugin set; fast built-in scans inline.\n\n### What is not fast yet, and is known\n\nStated so you can plan rather than discover:\n\n- **File walking is sequential.** Plugins run concurrently per file, but the walker itself is single-threaded. `PLANNED` \u2014 parallel file processing, expected to be a multiple-times speedup on large trees.\n- **There is no incremental cache.** Every scan rescans everything in scope. `PLANNED` \u2014 hash-based incremental scanning, which would be a large win on repeat scans.\n- **No `--jobs` flag yet** to control parallelism explicitly. `PLANNED`.\n- **Very large repositories (over a gigabyte) are slow.** Known. Use `--skip-paths` aggressively and scan the diff.\n- **First run of an external plugin may fetch a binary.** One-time delay, surprising if unexpected.\n\n**None of that is hidden and none of it is fixed today.** If your evaluation depends on incremental scanning, it is `PLANNED`, and you should plan for the current behavior.\n\n---\n\n**SIDEBAR (page 21, boxed): How this compares**\n\n> Honest positioning against tools you may already run:\n>\n> **Gitleaks** \u2014 very fast, secrets only. SecureGit's built-in `secrets` scanner is in the same performance class and the same scope. If secrets are all you need and you already run gitleaks, you do not need SecureGit for that. You might still want it for acquisition.\n>\n> **Semgrep** \u2014 moderate speed, much deeper pattern analysis. **Not a competitor.** Wrap it as an external plugin. SecureGit runs it at the right moment and anchors its output.\n>\n> **SonarQube** \u2014 slow, comprehensive, a different category entirely. Complementary. SonarQube analyzes code quality and security in depth; SecureGit secures the acquisition boundary and the commit gate.\n>\n> **The general rule:** SecureGit is an acquisition and boundary layer that happens to include fast scanning. It is not trying to win a static-analysis depth comparison, and a vendor who tells you their tool wins every comparison is telling you something about the vendor.\n\n---\n\n# PAGES 22\u201323 \u2014 DAY ONE CHECKLIST\n\n**Headline:** WHERE YOU SHOULD BE\n\n**Deck:** *If you have worked through Part I, this is your state. Tick it off honestly \u2014 the gaps are where you will fail next week.*\n\n---\n\n**DESIGN NOTE:** Render as a genuine checklist with real checkboxes, in three grouped bands. The reader should want to fill it in.\n\n### Installed and verified\n\n- [ ] `securegit --version` returns a version\n- [ ] `~/.config/securegit/` exists\n- [ ] I know how to uninstall it (delete binary + that directory)\n\n### Acquisition\n\n- [ ] I have acquired at least one repository\n- [ ] I checked `.git/hooks` and confirmed it was empty\n- [ ] I read a `.securegit-report.json`\n- [ ] `safe-clone` is aliased and I have used it once instead of `git clone`\n\n### Scanning\n\n- [ ] I have scanned a repository I actually work in\n- [ ] I looked at every `critical` finding\n- [ ] I rotated anything real (or confirmed there was nothing real)\n- [ ] `skip_paths` is configured for my dependency directories\n- [ ] I have run `scan-diff` at least once and seen how fast it is\n\n### Muscle memory\n\n- [ ] `sgit` (or equivalent) is aliased\n- [ ] Tab completion works on the alias\n- [ ] `~/.securegit_aliases` exists and is sourced\n- [ ] `SECUREGIT_FAIL_ON` and `SECUREGIT_SKIP_PATHS` are set\n\n### The guardrail\n\n- [ ] A pre-commit hook is installed in at least one repository\n- [ ] I have seen it pass\n- [ ] I know how to override it (`--no-verify`) and why I should not do so reflexively\n\n---\n\n## WHEN IT GOES WRONG\n\n**Deck:** *The six failures that account for most first-week abandonment, and the fix for each.*\n\n**\"It found 400 things and I stopped reading.\"**\nYou scanned a dependency directory. Configure `skip_paths`, rescan, and look at `critical` only. Then gate on new findings rather than total findings. Page 15.\n\n**\"The scan takes too long.\"**\nYou are scanning the tree when you should be scanning the diff. `securegit scan --staged` or `git diff main --name-only | xargs securegit scan`. Page 20.\n\n**\"The hook blocks me constantly.\"**\nYour threshold is too low or your `skip_paths` is wrong. Move to `--fail-on high`, fix the paths, and \u2014 importantly \u2014 fix it the first time it annoys you rather than the tenth. Page 19.\n\n**\"It won't acquire my private repository.\"**\nCredentials are not registered. Page 49. Do not debug this during your first hour; use public repositories to evaluate.\n\n**\"My teammate doesn't have the hook.\"**\n`.git/hooks` is not version controlled. Use `core.hooksPath` with a committed hook directory. Page 18.\n\n**\"I forgot to use it.\"**\nExpected, and the actual reason most adoptions fail. The fix is not discipline. The fix is the pre-commit hook, which runs whether you remember or not, plus a shell alias short enough that `acquire` is not more expensive to type than `clone`.\n\n---\n\n**FOOT BLOCK (page 23, reversed, full width):**\n\n> **You can stop here.**\n>\n> Part I is a complete, coherent adoption. Acquisition plus scanning plus a pre-commit hook is a real improvement in your security posture and it costs you almost nothing ongoing.\n>\n> Parts II through V are for when you want to understand *why* it works the way it does, prove things to other people, or bring a team along. None of it is required. A reader who stops at this page and keeps the habit has gotten more value than a reader who finishes the issue and installs nothing.\n\n---\n\n# PAGE 24 \u2014 PART II: SECTION OPENER\n\n**Full-bleed. Reversed: paper type on ink ground.**\n\n> **PART TWO\n> THE MENTAL\n> MODEL**\n\n> Four mechanisms.\n> Why each one is shaped the way it is,\n> and what it costs.\n\n*(Foot line, mono, orange:)* `25 \u2192 37`\n\n---\n\n# PAGES 25\u201327 \u2014 ACQUIRE, NOT CLONE\n\n**Standfirst:** *Mechanism 01* \u00b7 `SHIPPED`\n\n## Why archive-first defeats hooks\n\n**Deck:** *The entire security argument rests on one ordering decision. Understanding it takes about four minutes and makes everything else in the tool obvious.*\n\n---\n\n### The problem with clone, precisely\n\n`git clone` does several things that are individually reasonable and collectively a problem.\n\nIt negotiates with a remote and transfers objects. Fine. It writes those objects into a `.git` directory. Fine. It **materializes the repository's configuration and hook directory as live, on-disk state**. And it checks out a working tree using path names that the remote controls.\n\nThe trouble is that steps three and four happen before you have looked at anything. By the time `clone` returns success, executable configuration is resident on your filesystem, under your user, and the next git-adjacent command you run \u2014 or the next editor you open, or the next build you kick off \u2014 may execute it.\n\nThere are mitigations. Modern git does not run hooks on clone by default; protocol restrictions exist; `core.hooksPath` can be constrained. All of that is good and none of it is a substitute for not having the executable content on disk in the first place, because mitigations are point fixes against known techniques and your fleet is always some distance behind the current release.\n\n### The reordering\n\nSecureGit changes one thing: **the order of operations.**\n\n```\ngit clone:                fetch \u2192 materialize repo \u2192 (inspect never)\nsecuregit acquire:        fetch archive \u2192 strip \u2192 inspect \u2192 materialize repo\n```\n\nThat is the whole mechanism. Everything else follows from it.\n\n**Fetch as an archive.** The content arrives as inert bytes. There is no live repository during transfer, so there is nothing for repository configuration to act on.\n\n**Strip.** Hooks are removed, not disabled. Removal is stronger than disabling because disabling is a configuration state and configuration states can be changed, inherited, or overridden.\n\n**Inspect.** Scanners run on a tree that is not yet a repository. This is the moment that does not exist in the normal flow \u2014 code on your disk, inert, before it has been granted repository status.\n\n**Materialize.** Now it becomes a real git repository. From this point it is indistinguishable from a cloned one, which is exactly what you want: no lock-in, no wrapper, no special format.\n\n---\n\n**PULL QUOTE**\n\n> The order is the product. Fetch, strip, inspect, then confer git-ness. Clone confers git-ness first and inspects never.\n\n---\n\n### What this does not protect you from\n\nStated clearly, because overclaiming here would be dishonest and would eventually be found out.\n\n**It does not make the code safe to run.** If you acquire a repository, read the report, and then run `npm install && npm start`, you have executed the code. That was your decision and it was outside the tool's scope.\n\n**It does not detect novel malware.** The scanners look for known patterns, credentials, dangerous constructs, and anomalous entropy. A sophisticated, targeted implant designed to look like ordinary code will not be caught by pattern matching, and no tool in this category will catch it.\n\n**It does not protect against a compromised upstream you already trust.** If a maintainer account is taken over and a malicious commit is published, acquisition works exactly as designed and delivers you the malicious commit \u2014 inertly, scanned, with a receipt. The receipt is actually useful afterward. The acquisition did not prevent it.\n\n**What it does do** is close the window in which merely *obtaining* code can compromise you. That window is narrow, it is real, it is exploited, and it was previously unaddressed by anything in your workflow.\n\n### The report\n\nEvery acquisition writes `.securegit-report.json` into the destination.\n\n```\n.securegit-report.json\n```\n\nIt records what was fetched, what was stripped, what the scanners found, and when. **Read it the first ten times.** After that you will have calibrated intuition for what a normal report looks like, and the abnormal one will stand out. That calibration is the actual skill; the file is just how you acquire it.\n\n### Acquisition and the chain\n\n`SHIPPED` Acquisition emits a chain receipt recording the remote URL and the resolved `HEAD` commit at the moment of acquisition.\n\n**Acquisition never blocks on the chain.** If the signing daemon is unreachable, you get a warning and your repository. This is a deliberate policy choice: read-side operations warn, write-side operations block. A security tool that prevents you from obtaining code because a daemon is down will be uninstalled by lunchtime, and rightly.\n\nThe receipt matters later. If upstream force-pushes and rewrites history, your receipt still records what you actually received, at what SHA, at what time. That is the difference between \"I think I cloned it in March\" and a signed statement about what arrived.\n\n---\n\n**SIDEBAR (page 27, boxed): When to use plain `git clone`**\n\n> There are cases where `acquire` is the wrong tool and you should say so out loud:\n>\n> **Your own repositories, on infrastructure you control.** You wrote it. The acquisition threat model does not apply. Use `clone`.\n>\n> **Extremely large repositories where you need a shallow or partial clone.** Archive-first fetch has different characteristics. If you need `--depth 1` on a multi-gigabyte monorepo, use git and scan afterward.\n>\n> **Anything inside a build system that expects `git clone` semantics.** Do not fight your toolchain. Scan the result instead.\n>\n> A tool that claims to be correct in every situation is a tool whose recommendations you should discount. `acquire` is for code you did not write, from sources you have not audited. That is a large and growing category, and it is not everything.\n\n---\n\n# PAGES 28\u201330 \u2014 SCAN\n\n**Standfirst:** *Mechanism 02* \u00b7 `SHIPPED`\n\n## Twelve scanners, and a ladder you climb slowly\n\n**Deck:** *The architecture is a deliberate two-tier split: a fast core of twelve scanners that always runs, and a slow periphery you opt into. Understanding which tier you are in explains every performance question you will have.*\n\n---\n\n### The two tiers\n\n**Built-in (Rust, in-process).** Twelve scanners compiled into the binary \u2014 `secrets`, `patterns`, `entropy`, `binary`, `encoding` (Trojan Source), `supply-chain`, `ci-cd`, `container`, `iac`, `deserialization`, `dangerous-files`, `git-internals`. Zero startup cost, shared runtime, direct memory access to file contents, sub-millisecond to low-millisecond per file. These always run.\n\n**External (any language, subprocess).** Wraps existing tools \u2014 the mature security tooling ecosystem that already exists and that you may already run. Language-agnostic, community-maintainable without Rust knowledge, isolated by OS process boundary. Costs 20\u201350 ms of startup per plugin invocation. Managed by `securegit plugin list|check-updates|update` with a manifest-driven update source; CI can gate on stale plugins with `--fail-on-outdated` and `--fail-on-security`.\n\n**The design principle:** do not rebuild the security ecosystem in Rust. The tools that exist are good, well-maintained, and specialized. Build a fast core for the things that must run on every commit, and wrap everything else.\n\n### The plugin ladder\n\nClimb this slowly. Each rung is optional and each adds time.\n\n**RUNG 0 \u00b7 Built-in only.** `SHIPPED` Twelve scanners, no configuration. This is where you start and where most teams correctly stop. Fast enough for pre-commit hooks and CI gates. Between them they cover code (`secrets`, `patterns`, `entropy`, `encoding`), dependencies (`supply-chain`, `dangerous-files`), pipelines (`ci-cd`), infrastructure (`container`, `iac`), runtime execution paths (`deserialization`), binaries (`binary`), and repository metadata (`git-internals`).\n\n**RUNG 1 \u00b7 One external plugin that matches your stack.** A Python security scanner on a Python codebase; a Go one on Go. One plugin, chosen deliberately. Scheduled scans, not the hot path.\n\n**RUNG 2 \u00b7 A secrets specialist.** Wrap a dedicated secrets tool alongside the built-in `secrets` scanner. Different pattern databases catch different things and the overlap is not total.\n\n**RUNG 3 \u00b7 A SAST engine.** Semgrep or similar, with rules tuned for your codebase. This is where scan times get long and where the value gets deep. Nightly or per-PR.\n\n**RUNG 4 \u00b7 Specialized and compliance.** Container linting, IaC scanning, license detection, malware signatures. Enable per-repository based on what that repository actually contains.\n\n**Most teams should live at rung 0 for the hot path and rung 2 or 3 on a schedule.** Running rung 4 on every commit is how you end up with a two-minute pre-commit hook and a team that has learned to use `--no-verify`.\n\n---\n\n### Writing a plugin\n\nThe protocol is deliberately trivial: **a plugin is an executable that takes a file path and prints JSON to stdout.**\n\nPython:\n\n```\n#!/usr/bin/env python3\nimport json, sys\n\nfile_path = sys.argv[1]\nfindings = []\n\n# your logic here\n\nprint(json.dumps({\n    \"plugin_name\": \"my-scanner\",\n    \"findings\": findings,\n    \"scanned_files\": 1\n}))\n```\n\nBash:\n\n```\n#!/bin/bash\nFILE=\"$1\"\necho '{\"plugin_name\":\"my-scanner\",\"findings\":[],\"scanned_files\":1}'\n```\n\nInstall:\n\n```\ncp my-plugin ~/.config/securegit/plugins/\nchmod +x ~/.config/securegit/plugins/my-plugin\nsecuregit scan /path/to/test/file\n```\n\n**That is the entire interface.** No SDK, no registration, no manifest, no compilation. If you can write a script that prints JSON, you can write a plugin. This was chosen over a richer interface specifically because organizational security policies are idiosyncratic, and the tool that gets custom policies written for it is the one where writing them takes an afternoon.\n\n### Plugin types, current and future\n\n| Type | Location | Performance | Status |\n|---|---|---|---|\n| Built-in (Rust) | compiled in | 0 ms startup | `SHIPPED` |\n| External (any language) | `~/.config/securegit/plugins/` | 20\u201350 ms startup | `SHIPPED` |\n| Native dynamic (Rust + FFI) | `plugins/*.so` | near-native | `PLANNED` |\n| WebAssembly | `plugins/*.wasm` | fast, sandboxed | `PLANNED` |\n\n**WASM is the interesting future one** and it is `PLANNED`, not `DESIGNED`. The appeal is capability-based sandboxing: a plugin with no filesystem or network access unless explicitly granted. Today, external plugins run as subprocesses and inherit your permissions \u2014 which is worth stating plainly, because **installing a plugin is installing software with your privileges.**\n\n### The plugin trust problem\n\nWe are going to state this directly rather than bury it, because it is the honest weak point of any plugin architecture.\n\nAn external plugin runs as a subprocess with your user's permissions. It can read your filesystem. Depending on the tool, it can make network calls. **A malicious plugin in a security tool is an excellent attack.**\n\nMitigations available today, all of them procedural rather than technical:\n\n1. Read the plugin source. They are small by design \u2014 that is a security property, not just a convenience.\n2. Prefer plugins that wrap well-known tools, and verify the wrapped binary independently.\n3. Test in a container before putting one on your working machine.\n4. Do not install a plugin because a search result recommended it.\n\n`PLANNED` WASM sandboxing addresses this properly. Until it lands, **plugin installation deserves the same scrutiny as adding a dependency**, and we would rather say so than let you discover it.\n\n---\n\n**SIDEBAR (page 30, boxed): What the built-in scanners actually catch**\n\n> From a deliberately seeded test file of about twenty lines, the built-in set found nine issues in roughly four milliseconds:\n>\n> **2 critical** \u2014 a cloud access key, a hardcoded password\n> **3 high** \u2014 a database password, a dynamic execution call, a hardcoded secret\n> **1 medium** \u2014 a high-entropy string\n> **3 low** \u2014 development comments flagged by an external plugin\n>\n> That is the shape of a normal result: the built-in scanners handle credentials and dangerous constructs quickly and well, and external plugins add breadth at a cost in time.\n>\n> Note the low-severity items came from the external plugin. That is typical, and it is why external plugins belong on a schedule rather than in your commit path.\n\n---\n\n# PAGES 31\u201333 \u2014 THE GUARDED COMMIT\n\n**Standfirst:** *Mechanism 03* \u00b7 `SHIPPED`\n\n## A gate that survives contact with a deadline\n\n**Deck:** *Any gate can stop a bad commit. The engineering problem is stopping it without teaching people to route around the gate.*\n\n---\n\n### The commands\n\n```\nsecuregit status                     # working tree state\nsecuregit scan --staged              # scan what is staged\nsecuregit safe-commit -m \"message\"   # scan, then commit if clean\nsecuregit commit -m \"message\"        # commit with chain receipt\nsecuregit findings                   # review current findings\nsecuregit review                     # guided review of changes\nsecuregit diff                       # inspect changes\n```\n\n`safe-commit` is the one to build a habit around. It scans staged changes and commits only if they pass, in a single step, which removes the \"I meant to scan first\" failure entirely.\n\n### Why the gate is at commit and not earlier\n\nYou could gate at `add`. It would be worse.\n\nStaging is exploratory. People stage, unstage, restage, and split changes across several commits. A gate at `add` fires constantly during normal work, most firings are irrelevant because the content changes again before it is committed, and the annoyance-to-value ratio is terrible.\n\n**Commit is the first moment the content is declared finished.** That is where a gate belongs \u2014 at a natural boundary the developer has already decided to stop at, rather than in the middle of their thinking.\n\n### Why the *enforcement* gate is at push\n\nThere is a second gate, and it is not at commit. It is at push. This is worth its own explanation because it is counterintuitive and it is deliberate.\n\n**Commits are local and revisable.** You can rewrite them, amend them, squash them, and \u2014 importantly for the chain \u2014 attest them retroactively. A commit that is missing a receipt today can have one added tomorrow with no loss of meaning, because the commit has not gone anywhere.\n\n**Push is the first crossing onto shared infrastructure.** It is the moment your work becomes other people's problem. It is the last point at which stopping is cheap and the first point at which not stopping is expensive.\n\nSo the policy is: **commit-time scanning is advisory and fast; push-time chain enforcement is blocking.** Different gates, different jobs, different costs.\n\n---\n\n**PULL QUOTE**\n\n> Commits are local and revisable. Push is the first crossing onto shared infrastructure. Gate where stopping is still cheap.\n\n---\n\n### The full working surface\n\nSecureGit wraps the git operations you use daily. All `SHIPPED`:\n\n**Everyday** \u2014 `status`, `add`, `commit`, `safe-commit`, `diff`, `log`, `show`, `blame`\n**Branching** \u2014 `branch_create`, `branch_list`, `branch_delete`, `checkout`, `merge`\n**Remote** \u2014 `push`, `remote_list`, `server_add`, `server_list`, `server_push`\n**History** \u2014 `stash_save`, `stash_pop`, `stash_list`, `undo`, `tag_create`, `tag_list`\n**Security** \u2014 `scan`, `scan_staged`, `findings`, `posture`, `review`\n**Repository** \u2014 `repo_create`, `worktree_add`, `worktree_list`, `worktree_lock`\n**Backup** \u2014 `backup_add`, `backup_list`, `backup_push`\n**Escape hatch** \u2014 `git_raw`\n\n**`git_raw` matters more than its placement in that list suggests.** It passes a command straight through to git. It exists because no wrapper covers everything, and a wrapper that traps you when it hits its limits is a wrapper you will abandon at the first sharp edge. The escape hatch is a feature, and it is documented, and using it is not a failure.\n\n### Which operations emit receipts\n\nNot everything does, and the boundary is principled.\n\n| Operation | Receipt | Why |\n|---|---|---|\n| `acquire` / `clone` | **Yes** | Code arrives from outside |\n| `commit` | **Yes** | Content declared finished |\n| `push` | **Yes** | Crosses to shared infrastructure |\n| `merge` | **Yes** | Joins two histories |\n| `scan` | **Yes** | Findings become evidence |\n| `fetch` / `pull` | **Yes** | Content arrives from outside |\n| `blame` | **Yes** | Read-only provenance query |\n| `status`, `log`, `diff`, `add`, `checkout`, `branch`, `tag`, `stash`, `config` | **No** | No boundary crossed |\n\nThat last row is the one that keeps the tool usable. **Instrumenting everything is how you build something people turn off.**\n\n### Workflow scripts\n\n`SHIPPED` Guided scripts for common operations, exposed so you do not need to know where they live:\n\n```\nsecuregit workflow list\nsecuregit workflow info dev-flow\nsecuregit workflow install\nsecuregit workflow run dev-flow --dry-run\nsecuregit workflow run commit-craft\n```\n\nBundled workflows install to `~/.config/securegit/workflows`. Every workflow supports `--dry-run` at the wrapper level, which prints what would happen and executes nothing. Some workflows have their own internal dry-run, passed through after `--`:\n\n```\nsecuregit workflow run pr-prepare -- --dry-run\n```\n\n**`--dry-run` before every unfamiliar workflow.** It costs one flag and it is the difference between learning a tool and being surprised by it.\n\nSecureGit also shows contextual tips after some interactive commands \u2014 after branch operations it may suggest `dev-flow`; after staged commit work, `commit-craft`. Tips are local, throttled, and suppressed for `--json`, `--quiet`, and `--compact`. Turn them off entirely:\n\n```\nSECUREGIT_WORKFLOW_TIPS=0\n```\n\n**Turning tips off is respected permanently.** A tool that keeps helpfully reminding you of something you dismissed is a tool people come to resent.\n\n---\n\n**SIDEBAR (page 33, boxed): The `--no-verify` policy question**\n\n> Sooner or later someone will propose blocking `--no-verify` at the organizational level. Usually after an incident.\n>\n> **Push back on this**, and here is the argument.\n>\n> `--no-verify` is a git flag. You cannot remove it; you can only make it costly. Attempts to block it universally produce one of two outcomes: developers stop using the hooks entirely, or they build a shadow workflow you cannot see. Both leave you with less visibility than you started with.\n>\n> The better design is what the chain layer does: **let the bypass happen, and record it.** A force-push that rewrites history is not blocked outright \u2014 it emits a receipt marking the bypass, and policy can require a joint-approval marker. Now the bypass is a visible, attributable event rather than an invisible one.\n>\n> Visible bypasses are governance. Blocked bypasses are theater with a shadow IT chaser.\n\n---\n\n# PAGES 34\u201337 \u2014 THE CHAIN OF CUSTODY\n\n**Standfirst:** *Mechanism 04* \u00b7 `SHIPPED` (core) \u00b7 `DESIGNED` (offline + batch lookup)\n\n## Receipts, tiers, and the push gate\n\n**Deck:** *This is the layer that turns git from a record of assertions into a record of evidence. It is also the layer that requires real infrastructure, so read the cost section before you plan around it.*\n\n---\n\n### The core idea\n\nEvery operation that crosses a trust boundary produces a **receipt**: a signed, timestamped record linking an action to a verified identity and to the exact content involved.\n\nReceipts are cryptographically linked to each other, so the chain is tamper-evident. Modifying a past receipt breaks the links after it.\n\n**What a receipt binds together:**\n\n- **What** \u2014 a content hash. For a commit, the commit SHA. For a clone, the remote URL plus resolved HEAD. For a merge, the merge SHA composed with both parent SHAs.\n- **Who** \u2014 a verified identity, not a git author field. Git author is self-asserted and trivially forged; the receipt identity is signed.\n- **When** \u2014 a timestamp from the signing daemon, not from the local machine.\n- **Which operation** \u2014 clone, push, merge, blame, scan, and so on.\n\n### The signing model\n\n`SHIPPED` **The signing key never touches SecureGit.**\n\nIt lives in a separate signing daemon. In production the key is hardware-rooted; in local development a software key is used. SecureGit is stateless with respect to key material \u2014 it composes an envelope and posts it to the daemon's signing endpoint. It cannot leak a key it never holds.\n\n**The cost of this design, stated up front:** you need the daemon reachable to produce first-class receipts. That is real infrastructure. It is why the chain layer is Layer 3 in the adoption model on page 8 and not Layer 1.\n\n**Offline mode** `DESIGNED` \u2014 receipts written to a local store and synchronized when the daemon is next reachable. Air-gapped deployment is on this path.\n\n### Two tiers of evidence, and why the distinction is the point\n\n**Tier A \u2014 forward-attested.** The daemon witnessed the operation as it happened. Strongest evidence.\n\n**Tier B \u2014 retro-attested.** The receipt was added after the fact. Real, useful, and weaker \u2014 it proves someone asserted something later, not that the daemon observed it at the time.\n\n**The system records which tier you have and never conflates them.** This is the single most important honesty property in the design. A governance system that lets weak evidence masquerade as strong evidence is worse than no governance system, because it produces confident wrong answers in exactly the situations where being wrong is expensive.\n\n---\n\n**PULL QUOTE**\n\n> A system that lets retroactive evidence masquerade as witnessed evidence is worse than no system. It produces confident wrong answers precisely when being wrong is expensive.\n\n---\n\n### Where the receipt lives \u2014 belt and braces\n\n`SHIPPED` Commit receipts are stored in **two places at once**, deliberately:\n\n**1 \u00b7 In the commit message as a trailer.**\n\n```\nX-ContextOS-Receipt: <receipt-id>\n```\n\nTravels with the repository. Survives clone. Readable from `git log` with no daemon and no network. Tamper-evident, because changing it changes the commit SHA.\n\n**2 \u00b7 In the daemon's receipt store**, indexed by content hash for fast lookup.\n\n**Neither is the single source of truth.** They cross-validate. A trailer with no matching daemon record is suspicious. A daemon record with no trailer is suspicious. Agreement is evidence.\n\nThis is a good pattern generally, and worth stealing even if you never use SecureGit: **when you need durable evidence, write it to two stores with different failure modes and treat disagreement as signal.**\n\n### The push gate\n\n`SHIPPED` (core) \u2014 This is the enforcement point.\n\nBefore a push executes, SecureGit walks every commit in the push range and checks whether each has a receipt. If any commit does not, the push is blocked:\n\n```\nerror: push rejected \u2014 commit <sha> has no chain receipt.\nRun `securegit attest <sha>` to add one, or set\nchain.fail_on_unattested=warn to push with a marker receipt.\n```\n\n**Note the shape of that error message.** It says what is wrong, gives the exact command to fix it, and names the configuration key to change the policy. That is the standard every error message in a security tool should meet, because a blocking error without a remedy is just an obstacle.\n\n**Adopting on an existing repository:** your history predates the chain, so nothing has receipts, so your first push is blocked. This is the single most likely moment for someone to give up on the chain layer.\n\nTwo ways through:\n\n- `securegit attest <sha>` \u2014 add retro-attested (Tier B) receipts. A `--since` flag supports partial attestation from a starting point.\n- Set `chain.fail_on_unattested=warn` \u2014 push proceeds and un-receipted commits are marked as pre-chain in the record.\n\n**Recommendation:** for an existing repository, set the policy to `warn`, adopt the chain from today forward, and accept that history before adoption is Tier B or unmarked. Retro-attesting years of history produces a lot of weak evidence and obscures where the strong evidence starts. A clean boundary \u2014 \"the chain begins here\" \u2014 is more useful than a uniform blanket of weak claims.\n\n### Force push\n\n`SHIPPED` If a push would rewrite published history, a **bypass receipt** is emitted before the push, and policy can require a joint-approval marker in the commit message.\n\nThe default is to require it. The pattern mirrors a physical two-person rule for high-stakes operations.\n\n**The philosophy again:** force-push is not forbidden. Sometimes it is genuinely correct. It is *recorded*, and it requires a second signal. The bypass becomes an attributable event.\n\n### Blame with provenance\n\n`SHIPPED` \u2014 exposed in the agent tool surface as well as the CLI.\n\n```\nsecuregit blame <file> [--commit <sha>] [--format=human|json]\n```\n\nStandard blame output, augmented per line with the receipt tier and operation type:\n\n```\n<sha>  <identity>  A  human-edit   fn handle_request() {\n<sha>  <identity>  A  agent-exec       let body = req.body();\n<no-receipt>       ?  (unattested) }\n```\n\n**That third column is the one that matters, and it is why this feature exists.** In a codebase where humans and agents both commit, \"which lines did an agent write\" stops being an archaeology exercise and becomes a query.\n\n**Blame degrades gracefully by default.** Unattested lines show `?` rather than erroring. `--strict` errors instead.\n\nThe principle: **visibility tools never block; only gate operations block.** Blame is read-only. If it refused to run on partially-attested history, nobody would ever run it on a real repository.\n\n### Merge\n\n`SHIPPED` A merge joins two receipt chains, so its receipt encodes both parent SHAs in its content hash \u2014 preserving the history graph inside the evidence chain.\n\nMulti-author merges \u2014 detected from author/committer mismatch or co-author trailers \u2014 can require a joint-approval marker. Without it: a warning, and a receipt marked unverified rather than clean.\n\n### Policy and configuration\n\n`SHIPPED` Policy resolves in a fixed order: **git config \u2192 environment variables \u2192 built-in defaults.**\n\nGit config first is a deliberate operational choice: it means managed-workstation tooling can push policy through standard git configuration, which every organization already has a mechanism for. Environment variables override, which is what CI needs.\n\n| Key | Default | Meaning |\n|---|---|---|\n| `chain.daemon_url` | local endpoint | Where the signing daemon lives |\n| `chain.fail_on_daemon_error` | `warn` | `block`, `warn`, or `ignore` |\n| `chain.fail_on_unattested` | `block` | Policy for un-receipted commits at push |\n| `chain.require_joint_approval_on_force_push` | `true` | Require a second signal for history rewrites |\n| `chain.offline_store` | local path | Where offline receipts queue |\n\n**Hard defaults are always enforced**: block on unattested, require joint approval on force-push and multi-author merge. You can loosen them explicitly. You cannot accidentally end up without them.\n\n---\n\n**SIDEBAR (page 37, boxed): The honest cost of the chain layer**\n\n> Before you plan around this, know what it asks of you.\n>\n> **You need a signing daemon.** That is infrastructure to deploy, operate, and monitor. For a solo developer this is probably not worth it. For a team with a compliance requirement it usually is. Decide honestly which you are.\n>\n> **First push on an existing repository will be blocked.** Mitigated by `attest` and the `warn` policy, but it will surprise someone. Warn your team before you enable it, not after.\n>\n> **Large pushes add latency.** The gate looks up every commit in the push range. On a wide range this is noticeable. A batch lookup endpoint is `DESIGNED` and not yet shipped; today, expect a per-commit cost.\n>\n> **The daemon is a dependency.** Read operations warn, write operations block, and that is configurable \u2014 but a daemon outage will interrupt pushes if you have configured it to. Decide your failure policy deliberately, in advance, and write it down.\n\n---\n\n# PAGE 38 \u2014 PART III: SECTION OPENER\n\n**Full-bleed. Reversed.**\n\n> **PART THREE\n> SUPPLY\n> CHAIN**\n\n> Ninety percent of your application\n> is code nobody on your team wrote.\n\n> This part is about proving\n> what you knew about it, and when.\n\n*(Foot line, mono, orange:)* `39 \u2192 45`\n\n---\n\n# PAGES 39\u201341 \u2014 SBOM, OSV, AND THE NUMBER THAT MATTERS MORE THAN ZERO\n\n**Standfirst:** *Supply chain* \u00b7 `SHIPPED` (anchoring) \u00b7 `DESIGNED` (parts)\n\n## Anchoring, not scanning\n\n**Deck:** *An architectural decision worth understanding, because it tells you exactly what this tool will and will not do for your dependencies \u2014 and because the boundary moved once, deliberately, and the reasoning is instructive.*\n\n---\n\n### The gap\n\nThe chain of custody in Part II proves what humans and agents contributed to your repository. It says nothing about the overwhelming majority of a modern application, which is open-source dependencies you did not write and mostly have not read.\n\nFederal procurement, defense customers, and enterprise diligence all ask the same four questions:\n\n1. What is in your software? (SBOM)\n2. What is its license posture?\n3. What was its known-vulnerability state at a given moment?\n4. Can you prove how it was built?\n\nGit history answers none of these.\n\n### The decision: anchor, don't bundle\n\n`SHIPPED` **SecureGit records the canonical hash of each supply-chain tool's output at the moment it ran, and anchors that hash in the chain.**\n\nYou keep your existing tooling \u2014 whatever vulnerability scanner, SBOM generator, and signing tool you already run. SecureGit is the anchor, not the scanner.\n\n**The reasoning**, which was contested internally and decided in a strategic review:\n\n**Bundling scanners expands the maintenance surface without bound.** Every ecosystem needs its own. They change constantly. You are now maintaining a scanner fleet instead of a chain.\n\n**Fork drift.** A bundled copy of somebody else's scanner falls behind, and the gap between your version and theirs is invisible to your customer.\n\n**License coverage narrows.** Bundling constrains what you can ship and to whom.\n\n**And customers already run these tools.** Telling a security team to replace a scanner they have tuned for three years is how you lose the deal.\n\n---\n\n**PULL QUOTE**\n\n> We are the chain anchor, not the scanner. That single sentence decides what this product is and \u2014 more usefully \u2014 what it will never become.\n\n---\n\n### The one exception, and why it moved\n\n`SHIPPED` There is exactly one place the anchor-don't-bundle rule was amended, and documenting the amendment precisely is how you keep it from becoming a pattern.\n\n**The problem with the pure position:** most enterprise customers assume \"SBOM provenance\" means the system takes an SBOM, looks up vulnerabilities, and anchors the result. They do not want to install another tool. They want the join to happen inside the system.\n\n**The amendment:** SecureGit bundles a **vulnerability-database lookup client**, not a scanner.\n\nWhat it does: given a package and a version from an existing SBOM, query a public vulnerability database and return known advisories. Stateless. No local database. No manifest parsing. No dependency-tree resolution. One API, one schema.\n\nWhat it explicitly does not do: manifest discovery, transitive dependency resolution, license analysis, SBOM generation, or wrapping any of the ecosystem-specific audit tools. Those all go through the normal ingest path.\n\n**The distinction that justifies the amendment:** a database lookup against a pre-existing SBOM is qualitatively different from running a scanner. The customer already did the hard part \u2014 dependency resolution \u2014 when they generated the SBOM. This adds a search step, not an analysis step.\n\n**Naming the boundary this precisely is the point.** A vague boundary erodes. A boundary stated as \"lookup against an existing SBOM, never resolution or discovery\" is one where future scope creep is detectable by anyone reading the design record.\n\n### Four safety properties worth stealing\n\nWhether or not you use SecureGit, these four decisions are good engineering and transfer to any system that anchors third-party output.\n\n**1 \u00b7 Opt-in, never default.** `SHIPPED` The lookup is an explicit flag. It is never triggered automatically. **Regulated environments must know when an outbound network call happens** \u2014 a security tool making a surprise outbound request is a finding, not a feature.\n\n**2 \u00b7 The anchor never waits on the scan.** `SHIPPED` The SBOM receipt is emitted unconditionally. If the vulnerability lookup fails \u2014 network error, API down, partial batch failure \u2014 the SBOM anchor still succeeds and the scan is recorded as degraded. The auditor learns \"SBOM anchored, scan was partially complete,\" which is true and useful, instead of nothing at all.\n\n**3 \u00b7 `scan_completeness` is a first-class field.** `SHIPPED` Every vulnerability-state receipt carries a completeness value from 0 to 1.\n\n**This is the number that matters more than the CVE count.**\n\n> **\"0 CVEs\" without \"completeness = 1.0\" is not a clean bill of health.**\n> It might mean the scan covered 12% of your components and found nothing in those. Those are wildly different facts and most tooling renders them identically.\n\n**4 \u00b7 Absence is not evidence of absence.** `SHIPPED` If no vulnerability receipt exists for a commit, the correct reading is **\"no scan was run,\"** not \"no vulnerabilities.\" Any report generated from the chain must surface that distinction explicitly.\n\nThat fourth one is the most commonly violated principle in the entire security-tooling industry. Dashboards render \"no findings\" and \"not scanned\" the same way constantly, and executives read both as green.\n\n---\n\n### Air-gapped environments\n\n`SHIPPED` Customers who cannot make outbound calls point the tool at a local mirror of the vulnerability database. The mirror's snapshot date is recorded in the receipt, so an auditor can see how current the vulnerability data was at scan time.\n\n**We document the download; we do not ship the data.** That keeps licensing clean and keeps the tool from carrying a stale copy of somebody else's dataset.\n\n### Rescan, because advisories keep arriving\n\n`SHIPPED` The same dependency set scanned in April may have more known vulnerabilities in May. Nothing about your code changed; the world's knowledge did.\n\n```\nsecuregit sbom rescan <receipt-id>\n```\n\nRuns a fresh lookup, bypassing cache, and emits a new vulnerability-state receipt linked to the same SBOM.\n\n**The chain accumulates a timeline**, and the timeline is the actual product here. \"What did we know, and when did we know it\" is answerable as a sequence of signed records rather than a reconstruction from memory.\n\n**This is what makes monthly compliance refresh cheap.** You do not re-anchor the SBOM. You add a new point to the vulnerability timeline against the same anchor.\n\n---\n\n**SIDEBAR (page 41, boxed): The single-command flow**\n\n> The whole supply-chain story, once configured, is one pipe:\n>\n> ```\n> <your-sbom-generator> | securegit sbom emit --with-osv-scan\n> ```\n>\n> Two receipts land in the chain \u2014 the SBOM anchor and the vulnerability state \u2014 cryptographically linked to each other.\n>\n> **The link key is a content hash, not an identifier.** This is a small design decision with a large consequence: a content hash is reproducible across serializations, while a generated identifier changes if a record is ever re-signed. Choosing the reproducible key means the join still works years later, after re-signings, migrations, and format changes.\n>\n> If you build anything that joins records across systems and time, choose the content hash.\n\n---\n\n# PAGES 42\u201343 \u2014 LOCK FILES AND LICENSES\n\n**Standfirst:** *Supply chain* \u00b7 `SHIPPED` (core) \u00b7 `DESIGNED` (coverage)\n\n## What changed, and what you are allowed to ship\n\n**Deck:** *Two quieter capabilities that answer two questions auditors ask early: when did this dependency arrive, and are we permitted to ship it.*\n\n---\n\n### Lock-file change receipts\n\n`SHIPPED` When a dependency lock file changes, a receipt records the change: the file's content hash before and after, and where format support exists, a count of dependencies added, removed, and version-bumped.\n\n**Format coverage is explicitly tiered, and the tiering is honest:**\n\n**Full parsing** `SHIPPED` \u2014 the major lock formats for the ecosystems most represented in the codebase get real dependency-count diffs.\n\n**Change-only** `SHIPPED` \u2014 every other lock format records **that** the file changed and hashes it, but reports zero counts.\n\n**This distinction is deliberate and it is surfaced, not hidden.** A record saying \"changed, counts unavailable for this format\" is materially different from \"changed, zero dependencies affected.\" Reporting the second when you mean the first is how audit records become misleading.\n\n**When more formats get full parsing:** `DESIGNED`. Check your build.\n\n### License detection\n\n`SHIPPED` License detection runs at acquisition time using file heuristics and a standard license identifier database.\n\n**Three properties, each of which is a deliberate choice:**\n\n**`UNKNOWN` is a valid recorded value.** Not an error, not a blank. Recording \"we looked and could not determine this\" is more useful than recording nothing, because it distinguishes \"unlicensed\" from \"unexamined.\"\n\n**It is best-effort and says so.** License detection from files is genuinely hard. Dual licensing, per-directory exceptions, vendored code with different terms, and modified license text all defeat heuristics. The tool does not pretend otherwise.\n\n**It never blocks the clone.** `SHIPPED` License detection at acquisition is **visibility, not enforcement**.\n\nThat last one deserves defending, because it looks like a gap.\n\nIf license detection blocked acquisition, you could not obtain a repository in order to *examine* its license \u2014 which is the most common reason to obtain an unfamiliar repository in the first place. Blocking would make the tool actively counterproductive for the exact workflow it was built to support. Enforcement belongs at a later gate, where you have information and intent.\n\n---\n\n**PULL QUOTE**\n\n> `UNKNOWN` is a valid value. \"We looked and could not determine this\" is a materially different fact from \"we did not look,\" and the record should be able to tell them apart.\n\n---\n\n### What this gives you in practice\n\nThree answers you can produce from the chain that you probably cannot produce today:\n\n**\"When did this dependency enter our tree?\"** \u2014 the lock-file receipt timeline, with a signed timestamp and an attributed actor.\n\n**\"What was our license posture at release 4.2?\"** \u2014 the license records anchored around that release point.\n\n**\"Did anyone review this dependency addition?\"** \u2014 the lock-file change receipt is linked to a commit, which is linked to an identity, which may be linked to an approval.\n\nNone of those are exotic questions. All of them currently require an engineer to spend an afternoon in `git log` and produce an answer hedged with \"as far as I can tell.\"\n\n---\n\n**SIDEBAR (page 43, boxed): The trap in dependency counts**\n\n> A tempting metric: \"dependencies added this quarter.\" Easy to compute from lock-file receipts, easy to chart, and nearly meaningless.\n>\n> A lock file update that bumps four hundred transitive versions after a single direct dependency change looks enormous and is routine. A single added direct dependency that pulls in an unmaintained package with network access looks trivial and is the actual risk.\n>\n> **Use the receipts for provenance, not for scoring.** The value is answering \"when and by whom,\" not producing a number that goes on a slide. Numbers that go on slides get optimized, and a team optimizing for fewer lock-file changes is a team batching risky changes into larger, less reviewable updates.\n\n---\n\n# PAGES 44\u201345 \u2014 REPORTS THAT LAND WHERE REVIEWERS ALREADY LIVE\n\n**Standfirst:** *Supply chain & governance* \u00b7 `SHIPPED` (SARIF, GitLab SAST, baselines, audit log, compliance report) / `DESIGNED` (integrated chain audit)\n\n## SARIF, GitLab SAST, baselines, audit\n\n**Deck:** *Everything in Parts II and III exists to make one specific conversation go differently. The way it lands in that conversation is: as a report your reviewer's tools already read.*\n\n---\n\n### SARIF 2.1.0, and GitLab SAST v15\n\n`SHIPPED` One flag on the scan command; the report uploads directly into whatever dashboard your reviewers already open.\n\n```\nsecuregit scan . --format sarif  --report-output securegit.sarif\nsecuregit scan . --format gitlab --report-output gl-sast-report.json\n```\n\nSARIF 2.1.0 is the OASIS standard for static-analysis output. It carries CWE mappings, stable fingerprints (line-drift resistant), severity, and location, and is understood by **GitHub Advanced Security**, **Azure DevOps**, **SonarQube**, and **DefectDojo**. GitLab SAST v15 is GitLab's native format and lights up the security dashboard and MR widget without additional plumbing.\n\n`--report-output` writes the file even when `--fail-on` gates the exit code, so a single CI step can both *enforce* a bar and *publish* findings to the reviewer's dashboard. That was the single most common failure mode we saw in early pilots: teams that gated hard but had nowhere for developers to see findings ended up disabling the gate.\n\n### Baselines: adopting on legacy code without failing every build\n\n`SHIPPED` The pattern that unblocks adoption on a mature codebase.\n\n```\nsecuregit scan . --write-baseline \"Legacy findings accepted 2026-08-03; expires 2026-11-03\" \\\n  --expires 2026-11-03\ngit add .securegit/baseline.json && git commit -m \"chore(security): baseline v1\"\n\n# thereafter, every scan and every CI run\nsecuregit scan . --fail-on high --baseline .securegit/baseline.json\n```\n\nBaselines suppress known findings by a **stable fingerprint** that includes rule, file, snippet, and CWE \u2014 not by line number. That means shuffling code up or down a file does not invalidate a suppression, and does not re-fail a build. Every suppression carries `reason`, `created_at`, `created_by`, and optionally `expires` (RFC3339). Expired entries automatically re-fire; the log records who added them and why. `--no-baseline` re-runs without suppressions for review.\n\n**Adopt in this order:** run once; commit a baseline with an expiry three months out; gate PRs on `--fail-on high`; use the three months to burn down the baseline; renew what you cannot fix with a shorter fresh expiry. The teams that succeed do exactly this. The teams that try to reach zero before turning on gating do not adopt.\n\n### The tamper-evident audit log\n\n`SHIPPED` A separate, always-on log \u2014 independent of the chain \u2014 that captures every security-relevant event: scan runs, scan blocks, hook execution, baseline writes, credential store operations, policy denials, tool updates.\n\nEvery entry is JSONL with a `prev_hash` and `hash` field. Break the chain \u2014 edit any entry, delete any entry, reorder entries \u2014 and `securegit audit verify` fails with the exact index of the break.\n\n```\nsecuregit audit show --last 100\nsecuregit audit verify\nsecuregit audit export --format cef > /var/log/securegit.cef   # ArcSight/Sentinel/QRadar\nsecuregit audit export --format jsonl --output audit.jsonl      # Splunk/Datadog/Loki\n```\n\nThe log lives under `~/.local/share/securegit/audit/` and rotates automatically. Two of its three consumers \u2014 SIEM ingest and internal review \u2014 work without any signing infrastructure at all, which is why we count the audit log as an on-ramp to Layer 3 that requires no daemon.\n\n### The compliance report\n\n`SHIPPED` A single command that produces a review packet against two frameworks security teams actually cite.\n\n```\nsecuregit compliance report --format markdown --output compliance.md\nsecuregit compliance report --format json     --output compliance.json\n```\n\nFindings are grouped by **OWASP Top 10 (2021)** via CWE mapping, and by **NIST SSDF v1.1** practice (PS.1\u2013PS.3, PW.1\u2013PW.9, PO.1\u2013PO.5, RV.1\u2013RV.3). The markdown output is intended for direct inclusion in review packets; the JSON output is a starting point for a GRC integration.\n\nIt is not a substitute for an audit. It is a substitute for two engineer-days of copy-and-paste when the vendor questionnaire arrives, and it is the artifact that most reliably ends the \u201cbut can you prove any of it?\u201d exchange described on the next page.\n\n### What the chain adds on top\n\n`DESIGNED` A report generated directly from chain data \u2014 no engineer reconstruction \u2014 covering human contribution (who committed what, at which evidence tier), supply-chain state as anchored at a point in time, build provenance, **and the gaps**. Explicitly. Commits without receipts. Scans that did not complete. Periods with no vulnerability data. **The report names its own holes**, which is the property that makes it credible rather than merely impressive.\n\n### Why gap-reporting is the credibility feature\n\nA report with no gaps is not trustworthy. Every real system has gaps \u2014 a daemon outage, a repository adopted late, a scan that timed out, a format without full parsing support.\n\nA report that shows none is either describing a system nobody uses or hiding something. An auditor who has read more than a few of these knows that immediately, and a clean report makes them look harder rather than less hard.\n\n**A report that says \"these forty commits are attested, these three are not, and here is why\" is stronger than one that claims forty-three.** It is stronger because it demonstrates that the system is capable of noticing a problem \u2014 which is the only real evidence that its clean findings mean anything.\n\n---\n\n**PULL QUOTE**\n\n> A report with no gaps is not trustworthy. Every real system has gaps. Showing yours is what makes the rest of it believable.\n\n---\n\n### Verification without access\n\n`SHIPPED` A design property worth understanding, because it comes up in every enterprise conversation.\n\nSome anchored payloads live in customer-controlled storage. A verifier without access to that storage **can still verify the hash chain** \u2014 the anchoring, the timestamps, the identities, and the integrity of the sequence \u2014 without reading the payload contents.\n\nThis means an auditor can confirm that a record is authentic, unmodified, and correctly sequenced without you handing over your source code, your SBOM contents, or your scan output.\n\n**That separation \u2014 verify the chain, gate the contents \u2014 is what makes third-party audit possible without disclosure.** It is a genuinely useful property and it is not obvious until you need it.\n\n### A caveat that must travel with the numbers\n\n`SHIPPED` Vulnerability counts drift. The same dependency set, unchanged, will show more known vulnerabilities next quarter, because advisories are published continuously.\n\n**This is correct behavior and it confuses people constantly.** A customer sees \"3 CVEs in March, 11 in June, nothing changed in our code\" and reasonably asks what went wrong.\n\nNothing went wrong. The world learned more.\n\nAny report generated from this data must explain that, in the report, near the numbers \u2014 not in a footnote, and not left to a support conversation. **The rescan timeline is the explanation**: it shows the same anchor with a sequence of point-in-time observations, which makes the drift legible as knowledge accumulation rather than as decay.\n\n---\n\n# PAGE 46 \u2014 PART IV: SECTION OPENER\n\n**Full-bleed. Reversed.**\n\n> **PART FOUR\n> SECRETS\n> & AGENTS**\n\n> Your agent needs credentials.\n> Your agent must never hold them.\n\n> Both of those are true at once,\n> and the resolution is a handle.\n\n*(Foot line, mono, orange:)* `47 \u2192 51`\n\n---\n\n# PAGES 47\u201348 \u2014 HANDLES, NOT VALUES\n\n**Standfirst:** *Mechanism 05* \u00b7 `SHIPPED` (core) \u00b7 `DESIGNED` (most of the surface \u2014 markers throughout)\n\n## The secret broker\n\n**Deck:** *A narrow idea with a wide consequence: the thing doing the work gets the ability to act without ever receiving the value it acts with.*\n\n---\n\n### The problem, sharpened\n\nAn agent needs to push to a repository. Pushing requires a token. Therefore the agent needs the token.\n\nThat reasoning is wrong, and finding where it is wrong is the whole design.\n\n**The agent needs the *capability* to push. It does not need the *value* of the token.** Those are separable, and separating them is the entire product.\n\nWhy it matters more with agents than with humans: a human who sees a token generally does not write it down. An agent operates in a context window that may be logged, summarized into memory, included in a trace, sent to a model provider, or echoed into terminal output that ends up in a transcript. **A secret that enters an agent's context has entered every downstream system that touches that context.** Not maybe \u2014 by construction.\n\n### The mechanism\n\n`SHIPPED` **Late binding at the execution boundary.**\n\n1. A user or agent requests an action, referring to a secret by a **handle** \u2014 a stable name, never a value.\n2. SecureGit validates the requested action against the handle's profile. Is this handle allowed to be used by this command, against this host?\n3. Only then does SecureGit resolve the actual value from the backing store.\n4. The value is injected directly into the child process, HTTP client, or credential callback.\n5. The value is **redacted from all observed output** \u2014 stdout, stderr, logs, error messages, and structured responses.\n6. A metadata-only audit event is recorded: which handle, which provider, which host, which command family, when, and what the result was. **Never the value.**\n\n**The value exists for the duration of one operation, inside one process boundary, and is never rendered anywhere a human or a model can read it.**\n\n```\nsecuregit run --with-secret OPENAI_API_KEY=openai-dev -- npm test\n```\n\nThe agent typed `openai-dev`. The agent never saw a key.\n\n---\n\n**PULL QUOTE**\n\n> The agent needs the capability to push. It does not need the value of the token. Everything here follows from noticing that those are different.\n\n---\n\n### The safety defaults\n\n`SHIPPED` Six rules, enforced rather than recommended:\n\n1. **No command prints a secret value by default.**\n2. **Structured and agent-facing responses return handles, provider metadata, and status only** \u2014 never values.\n3. **Known values are redacted** from stdout, stderr, logs, responses, and error messages.\n4. **Use is audited as metadata**: provider, handle, target host, command family, timestamp, result.\n5. **Handles can be constrained** by provider, host, repository, command family, expiration, and permitted environment variable names.\n6. **Raw reveal, export, or copy requires an explicit human-only path** and is disabled for agent interfaces by default.\n\nRule 6 is the one that makes the rest coherent. Without it, an agent asks for the value and gets it, and every other rule is decoration.\n\n### Writes never pass through shell history\n\n`SHIPPED` Storing a secret reads the value from an environment variable rather than an argument:\n\n```\nOPENAI_API_KEY_VALUE=... securegit secret set openai-dev \\\n  --value-env OPENAI_API_KEY_VALUE --yes\n```\n\n**Never `--value <the-actual-secret>`.** Command-line arguments are visible in process listings and durable in shell history \u2014 two places a secret is very hard to remove from once it lands. This is a small ergonomic cost and it removes an entire class of leak.\n\nProduction writes and deletes require both explicit confirmation and an explicit production flag. **Production targets are visually distinct in prompts and logs.** Making the dangerous environment *look* different is a cheap and effective control.\n\n### Provider profiles, not key-value sprawl\n\n`DESIGNED` The intended model is that secrets are **provider access profiles**, not arbitrary key-value pairs.\n\nA profile records the handle, the provider, the credential kind, the host, the capabilities it grants, the commands it is permitted for, and where the value actually lives. **The value stays in the backing store.**\n\n```\nhandle:            gitlab-main\nprovider:          gitlab\nkind:              pat\nhost:              gitlab.example.com\ncapabilities:      vcs.read_repo, vcs.write_repo, vcs.create_repo\nallowed_commands:  acquire, clone, fetch, push, server\nbackend:           <secrets-manager>\nbackend_ref:       <path-in-that-manager>\n```\n\n**Why profiles rather than key-value:** a profile carries enough metadata for the tool to apply safe defaults and to refuse obviously wrong uses. A key-value pair carries none, so every safety decision has to be made by the caller, which means it is made inconsistently or not at all.\n\n### Backends\n\n`SHIPPED` Metadata discovery, execution-time value resolution, guarded writes and deletes, and a local store (\u201ccredential store v2\u201d) adequate for single-user work.\n\n**Credential store v2, in detail.** Encryption is **ChaCha20-Poly1305** AEAD, keyed to the machine (macOS ioreg UUID, Linux `/etc/machine-id` / DMI product UUID, Windows machine GUID). Every write is atomic \u2014 temp file, fsync, rename \u2014 with an integrity check on read; a tampered file fails closed rather than silently returning junk. An optional environment variable `SECUREGIT_CREDSTORE_PASSPHRASE` layers a passphrase-derived key on top of the machine key. The store lives at `~/.securegit/credentials.encrypted` and is not portable between machines by design; migration is deliberate, one credential at a time.\n\n`DESIGNED` The intended backend order:\n\n**1 \u00b7 Your existing secrets manager.** For teams that already have one, this is the source of truth. SecureGit stores **bindings** \u2014 handle to location \u2014 not copies. Nobody should duplicate hundreds of secrets into a new tool's config directory to adopt it.\n\n**2 \u00b7 OS keychain.** For local development. Uses the platform's native credential store.\n\n**3 \u00b7 The built-in encrypted store (v2, above).** Compatibility and single-user work. Fine for a laptop; **explicitly not the high-assurance layer** and documented as such.\n\n**Backend endpoints are always user-configurable and never hardcoded.** Cloud, local, LAN, or an internal hostname \u2014 the tool must not assume.\n\n### Discovery without disclosure\n\n`SHIPPED` The most useful single property for agent workflows:\n\n```\nsecuregit secret discover --target dev --keys-only\n```\n\nReturns names, paths, comments, tags, versions, and metadata. **Returns no values.**\n\nThis gives an agent enough context to bind the right profile \u2014 to know that a key called `PAYMENTS_API_KEY` exists in the production path \u2014 without ever exposing what it is. The agent can reason about the shape of your secret inventory while being structurally incapable of reading it.\n\n---\n\n**SIDEBAR (page 48, boxed): The open questions, published**\n\n> These are genuinely undecided, and printing them is how you find out what people actually need before you build the wrong thing.\n>\n> **Should local development default to the OS keychain, even before a full secrets-manager integration ships?**\n>\n> **Should agents be allowed to create secrets, or only bind handles a human created?** The safe answer is bind-only. The safe answer is also annoying, and annoying controls get bypassed.\n>\n> **What is the minimum useful audit format before secret usage gets full chain receipts?**\n>\n> **Which providers ship in the first catalog?** Version control, cloud, and model APIs are the obvious three. Order within them is not obvious.\n>\n> If you have an opinion on any of these, it will change what gets built. That is not a courtesy \u2014 it is the fastest available way to avoid building the wrong thing.\n\n---\n\n# PAGE 49 \u2014 SERVER DISCOVERY AND THE CREDENTIAL BOUNDARY\n\n**Headline:** FINDING REPOSITORIES WITHOUT HANDLING TOKENS\n\n**Deck:** *The same handle discipline, applied to the everyday problem of \"which repository was that.\"* \u00b7 `SHIPPED`\n\n---\n\n### Registering servers\n\n```\nsecuregit server add github-main --platform github --api-url https://api.github.com\nsecuregit server add gitlab-main --platform gitlab --api-url https://gitlab.example.com/api/v4\n```\n\nCredentials are stored keyed to the server name and resolved through the normal credential chain. **The token is never typed into a search command.**\n\n### Searching\n\n```\nsecuregit server search <query>                        # all registered servers\nsecuregit server search <query> --server gitlab-main   # one server\nsecuregit server search <query> --json                 # machine-readable\nsecuregit server search <query> --limit 10             # cap per provider\nsecuregit server search <query> --include-groups       # orgs and groups too\n```\n\nOne command across GitHub and GitLab. Results normalize across providers: server name, provider, repository path, description, web URL, clone URLs, visibility, default branch, and last activity where the provider supplies it.\n\n`--include-groups` returns organizations from GitHub and groups from GitLab, normalized into the same record shape.\n\n### The credential boundary\n\n`SHIPPED` Resolution order:\n\n1. A per-server environment variable\n2. The encrypted stored credential, keyed to the server name\n3. Host-based fallback from existing stored auth\n\n**The rule that matters:** an agent should call `securegit server search` \u2014 or the equivalent agent-facing tool \u2014 rather than reading `.env` files or handling personal access tokens directly.\n\nThis is a small thing that eliminates a large and extremely common leak path. Agents reading `.env` files to find credentials is not a hypothetical; it is a default behavior of many agent setups, and the resulting token ends up in a context window and then in a log.\n\n---\n\n**SIDEBAR (foot, boxed): Compatibility surface**\n\n> Testing confirmed that GitLab instances expose GitHub-compatible API endpoints for discovery, authentication, and repository listing.\n>\n> Practical consequence: the same code path works against GitHub, GitHub Enterprise, GitLab, and the smaller self-hosted git servers that implement the same surface.\n>\n> If you run self-hosted git, this is worth ten minutes of testing before you assume you need something custom.\n\n---\n\n# PAGES 50\u201351 \u2014 SECUREGIT FOR AGENTS\n\n**Headline:** THE MCP SURFACE\n\n**Deck:** *The same tool, exposed to an agent, with the safety properties preserved rather than bolted on.* \u00b7 `SHIPPED`\n\n---\n\n### Why this belongs in this magazine\n\nEvery discipline in the AI-engineering transition converges here. Your agents commit code. They pull dependencies. They need credentials. They push.\n\n**Every one of those is a trust boundary crossing performed by something that is not a person, at a volume no person could match, with an audit trail that does not currently exist.**\n\nAn agent doing git operations through raw shell commands has your full permissions, your tokens in its context, and no record beyond whatever the shell happened to log. An agent doing git operations through a brokered tool surface has scoped capabilities, handles instead of values, and a receipt for every crossing.\n\nThat is the entire argument for this page.\n\n### The exposed surface\n\n`SHIPPED` Agents get a structured tool surface covering the same operations humans use \u2014 **32 tools** across ten families \u2014 delivered as `securegit-mcp`, a standard MCP server:\n\n**Repository state** \u2014 status, log, show, diff, blame\n**Change** \u2014 add, commit, safe-commit, undo, stash, snapshot\n**Branching** \u2014 branch create / list / delete, checkout, merge, worktrees, stack\n**Remote** \u2014 push, remote list, server add / list / push, repository create\n**Security** \u2014 scan, scan staged, findings, posture, review, baseline, audit\n**Discovery** \u2014 search repositories across registered servers (`securegit_search_repos`)\n**Release** \u2014 tag create / list, release list, CI status, pull request list\n**Backup & recovery** \u2014 backup add / list / push, snapshot list / restore\n**Meta** \u2014 update-check (throttled daily, on startup), version, health\n**Escape hatch** \u2014 raw git passthrough (guarded)\n\n### The four properties that make this safe\n\n**1 \u00b7 Handles, not values.** Agent-facing tools accept secret handles. They do not accept, return, or display credential values. An agent asks to push using `gitlab-main`; it never learns what `gitlab-main` is.\n\n**2 \u00b7 Guarded destructive operations.** Operations that destroy or overwrite require explicit confirmation. Some are unavailable to agents entirely, by policy, regardless of what the agent believes it has been authorized to do.\n\n**3 \u00b7 Everything is scanned and anchored.** An agent commit passes the same gates a human commit does. An agent push emits the same receipt. **The chain does not distinguish between human and agent for the purpose of requiring evidence \u2014 it distinguishes for the purpose of recording which one it was.**\n\n**4 \u00b7 Provenance survives.** `SHIPPED` Blame with chain overlay means \"which lines did an agent write\" is a query, not an investigation. In a codebase where agents contribute meaningfully, this is the difference between reviewable and unreviewable.\n\n---\n\n**PULL QUOTE**\n\n> An agent with a raw shell has your permissions and your tokens. An agent with a brokered tool surface has scoped capabilities and a receipt. The difference is not a policy. It is an architecture.\n\n---\n\n### Graph integration\n\n`DESIGNED` Repository operations can update a knowledge graph after acquire, fetch, pull, and push \u2014 building a queryable model of code, contributors, and change over time.\n\n```\nGRAPHRAG_ENABLED=1 GRAPHRAG_API_URL=<your-endpoint> securegit push\n```\n\nOff by default, and it should stay off until you have a reason.\n\n**The connection to the wider argument:** an agent that can traverse a repository graph can answer questions that no amount of file reading answers \u2014 which commits touched the module that broke, who reviewed them, what the dependency posture was at the time. That is retrieval over structure rather than over text, and it is a different capability class.\n\n### The honest warning\n\n**Do not give an agent write access to a repository you cannot afford to have rewritten, on your first day.**\n\nStart read-only. Let it scan, search, review, and report. Watch what it does for a week. Then grant commit access on a branch. Then, much later, push.\n\n**The receipts are what make that progression safe rather than a leap of faith.** You are not trusting the agent. You are building a record that lets you check, and expanding scope as the record earns it.\n\n---\n\n# PAGE 52 \u2014 PART V: SECTION OPENER\n\n**Full-bleed. Reversed.**\n\n> **PART FIVE\n> ADOPTION**\n\n> The engineering was the easy part.\n\n> Thirty days, twelve objections,\n> and the failure modes nobody\n> writes down.\n\n*(Foot line, mono, orange:)* `53 \u2192 61`\n\n---\n\n# PAGES 53\u201355 \u2014 THE 30-DAY ROLLOUT\n\n**Headline:** SOLO, THEN TEAM, THEN ORG\n\n**Deck:** *A sequence that has survived contact with real teams. Each phase is independently valuable, so stopping early is a success rather than a failure.*\n\n---\n\n**DESIGN NOTE:** Render as a horizontal timeline running across the spread with four phase blocks. Each block: days, headline, actions, exit criteria. Exit criteria in signal.\n\n## DAYS 1\u20133 \u00b7 YOURSELF\n\n**Goal:** you are using it daily without thinking about it.\n\n- Install. Alias it. Set up completion.\n- Acquire three real repositories with `acquire` instead of `clone`.\n- Scan two repositories you actually work in. Fix or rotate anything critical.\n- Configure `skip_paths` properly.\n- Install a pre-commit hook in one repository.\n\n**Exit criteria:** you have used it five days running without consulting documentation, and the pre-commit hook has passed at least twenty times and blocked you at most once.\n\n**If you do not hit that:** stop. Do not bring a team into a tool you are not yet fluent in. Every question they ask, you will be answering by reading docs in front of them, which is the fastest way to lose a rollout.\n\n## DAYS 4\u201310 \u00b7 ONE REPOSITORY, ONE TEAM\n\n**Goal:** a shared gate in one place, with the team's consent.\n\n- Pick **one** repository. Not the most critical. Not a toy. Something real that a few people touch.\n- Move hooks into version control: commit a hook directory and set `core.hooksPath`.\n- Add scanning to CI as **advisory** \u2014 reporting, not blocking. (Page 58.)\n- Run for a week. Collect false positives. Tune `skip_paths` and scanner selection.\n- **Then** flip CI to blocking at `--fail-on high`.\n\n**Exit criteria:** one week of CI runs with a false-positive rate low enough that nobody has asked to disable it.\n\n**The critical sequencing rule: advisory first, blocking second.** A gate that blocks before it is tuned generates an incident, a meeting, and a permanent reputation as the thing that broke the build. That reputation outlives every subsequent improvement.\n\n## DAYS 11\u201320 \u00b7 THE SECOND AND THIRD REPOSITORY\n\n**Goal:** prove it generalizes, and find out where it does not.\n\n- Add two more repositories, ideally in different languages or ecosystems.\n- Notice what breaks. Different stacks produce different false-positive profiles, and the tuning you did on a Python service will not transfer cleanly to a frontend monorepo.\n- Write down your organization's `skip_paths` baseline and share it.\n- Add one external plugin matched to your stack, on a **schedule**, not in the hot path.\n\n**Exit criteria:** three repositories, three teams, no open complaints about noise.\n\n## DAYS 21\u201330 \u00b7 POLICY AND SCALE\n\n**Goal:** it is infrastructure, not a personal preference.\n\n- Distribute configuration through whatever manages developer workstations. Git config is the intended channel because every organization already has a mechanism for it.\n- Decide your chain-layer position. **Be honest about whether you need it** \u2014 it requires a signing daemon, and if you have no compliance driver, Layers 1 and 2 may be your correct final state.\n- If you do need it: stand up the daemon, set `chain.fail_on_unattested=warn` initially, and communicate before enabling \u2014 not after.\n- Add supply-chain anchoring if procurement or audit is driving.\n\n**Exit criteria:** a new engineer joining gets SecureGit configured by your standard onboarding, without anyone explaining what it is.\n\n---\n\n**PULL QUOTE (spread, spanning)**\n\n> Advisory first. Blocking second. A gate that blocks before it is tuned earns a reputation that outlives every improvement you make to it afterward.\n\n---\n\n## THE FOUR WAYS ROLLOUTS DIE\n\nEach of these has killed a real adoption. Each has a specific counter.\n\n**1 \u00b7 The noisy first scan.** Someone runs it on a mature repository, gets four hundred findings, concludes the tool is useless, and tells everyone.\n**Counter:** never let the first team-visible scan be untuned. Configure `skip_paths` and `--fail-on high` before anyone else sees output. Gate on new findings, not total findings.\n\n**2 \u00b7 The blocking gate nobody agreed to.** Enabled on a Friday. Blocks a release. The release goes out with the gate disabled and it never comes back.\n**Counter:** advisory for a full week, minimum. Announce the switch to blocking with a date. Let people object beforehand \u2014 the objections are usually right and always cheaper before than after.\n\n**3 \u00b7 The champion leaves.** One enthusiastic engineer set it all up. They change teams. Configuration rots, nobody knows how it works, it gets removed in a cleanup.\n**Counter:** version-controlled hooks, documented `skip_paths` baseline, CI configuration in the repository. If it only lives in one person's shell, it dies with their tenure.\n\n**4 \u00b7 The chain layer adopted too early.** A team with no compliance requirement stands up a signing daemon, hits the first-push block, spends a week on it, and concludes the whole product is heavy.\n**Counter:** Layers 1 and 2 are a complete adoption. **Do not adopt Layer 3 without a specific question you need to answer for someone outside your team.** If you cannot name the person who will ask, you are not ready and you do not need it.\n\n---\n\n**SIDEBAR (page 55, boxed): What to say in the first team meeting**\n\n> Keep it to four sentences. Longer pitches invite longer arguments.\n>\n> *\"We're adding a scanner to CI. For the first week it reports and doesn't block, so we can see what it finds and tune out the noise. After that it'll fail the build on high-severity findings \u2014 mostly credentials. If it's noisy, tell me and I'll fix the configuration rather than lowering the bar.\"*\n>\n> Note what that does not contain: zero-trust, supply chain, chain of custody, provenance, attestation. **Those are real and they are not why an engineer will accept a new gate in their build.** They accept it because it catches credentials and because you promised to fix the noise.\n>\n> Save the architecture for the people who ask.\n\n---\n\n# PAGES 56\u201357 \u2014 TWELVE OBJECTIONS\n\n**Headline:** ANSWERED HONESTLY\n\n**Deck:** *Including three where the honest answer is that you are right.*\n\n---\n\n**DESIGN NOTE:** Two columns, six per page. Objection in condensed caps, response in serif. Where the answer concedes, mark it with a signal-colored rule at the left edge.\n\n**1 \u00b7 \"We already have a secrets scanner in CI.\"**\nThen you have covered one of four layers, at the latest possible moment. CI catches secrets after they are committed and pushed \u2014 the credential is already in history and already on the remote. A pre-commit gate catches it before it exists anywhere durable. And CI covers none of the acquisition problem. Keep your CI scanner; add the gate that runs earlier.\n\n**2 \u00b7 \"This will slow down our builds.\"**\nBuilt-in scanners run in the tens of milliseconds per file and scan a large repository in about half a minute. External plugins are ten to a hundred times slower. Use built-in scanners in the hot path and external plugins on a schedule, and the build cost is negligible. If you enable ten external plugins on every commit, your build will get slower and that will be a configuration decision, not a tool property. Page 20 has the numbers.\n\n**3 \u00b7 \"Developers will just use `--no-verify`.\"**\nSome will, sometimes, and that is by design \u2014 the override is printed in the error message. A gate with no visible escape is removed entirely rather than bypassed occasionally, and then you have no gate and no signal. The goal is not zero bypasses. It is that bypasses are deliberate, rare, and visible.\n\n**4 \u00b7 \"We don't clone untrusted repositories.\"** \u258c*You may be right.*\nIf your engineers only ever work in repositories your organization owns, on infrastructure you control, the acquisition layer is not for you. Adopt scanning and skip acquisition. But check the actual behavior first \u2014 including what your CI pulls, what your build tooling fetches, and what your agents clone. The answer is often different from the policy.\n\n**5 \u00b7 \"This is another tool to maintain.\"**\nIt is one binary and one config directory. No daemon, no service, no background process, for Layers 1 and 2. Uninstall is deleting two things. The chain layer does require infrastructure, and that is precisely why it is Layer 3 and optional.\n\n**6 \u00b7 \"Our security team will need to approve it.\"**\nThey should. Hand them page 8 (what it is and is not), page 30 (the plugin trust problem, stated against our own interest), page 44 (SARIF/GitLab SAST reports, the audit log, the compliance report against OWASP Top 10 and NIST SSDF v1.1), and page 63 (the maturity table). The single artifact that most reliably ends the \u201ccan you prove any of it?\u201d exchange is `securegit compliance report`, and it takes one command to produce. A tool that publishes its own weak points evaluates faster than one that does not, because the reviewer's first job is finding what you did not tell them.\n\n**7 \u00b7 \"The false positives will bury us.\"** \u258c*Partly right.*\nThe first scan of a mature repository will be noisy. Every scanner is. The entropy scanner in particular trades false positives for catching credential formats no pattern list knows yet. The fix is tuning, not tolerance \u2014 page 60 is a full playbook. But if you adopt this without tuning it, you will be buried, and that outcome is on the adoption, not on you.\n\n**8 \u00b7 \"We're a small team, this is enterprise stuff.\"**\nLayers 1 and 2 are a solo-developer adoption and cost you ten minutes. Layers 3 and 4 are enterprise and you should skip them until someone external asks a question you cannot answer. Nothing about the first two layers requires a team, a process, or a budget.\n\n**9 \u00b7 \"What happens when this project is abandoned?\"** \u258c*Legitimate, and worth answering directly.*\nYour repositories remain normal git repositories. Acquisition produces standard git. Receipts live in commit trailers, which are plain text in your history and readable with `git log` forever, daemon or no daemon. Scan output is JSON. **There is no proprietary format and no lock-in anywhere in the design** \u2014 which is a deliberate property precisely because this objection is correct to raise about any tool.\n\n**10 \u00b7 \"We need this to work air-gapped.\"**\nScanning, acquisition, credential store v2, the audit log, and the compliance report all work offline today. Vulnerability lookups support a local mirror, with the mirror's snapshot date recorded in the receipt. `securegit update-check` is throttled and offline-tolerant \u2014 it warns, it does not phone home to gate. Chain offline mode (fully offline receipt anchoring) is `DESIGNED` and not yet shipped \u2014 if air-gap chain-of-custody is a hard requirement, that is the one real gap today and you should plan around it rather than around a roadmap.\n\n**11 \u00b7 \"Can it handle our monorepo?\"**\nPartly. Scanning is fast per file and the walker parallelizes across cores, but there is no incremental cache \u2014 `PLANNED`. On a very large monorepo, scan the diff rather than the tree, gate on `--baseline` for existing findings, and use `skip_paths` aggressively. If you need full-tree scans of a multi-gigabyte repository on every commit, this is not there yet and pretending otherwise would waste your time.\n\n**12 \u00b7 \"Why not just use gitleaks and cosign?\"**\nDo, if that is what you need. Gitleaks is excellent at secrets. Signing tools are excellent at signing \u2014 in fact SecureGit uses cosign itself for its own release signatures. SecureGit's contribution is the acquisition boundary (which neither addresses), the eleven scanners that are not `secrets`, the SARIF/GitLab SAST/audit/compliance pipeline in one binary, and the chain that links commit, scan, dependency, and push into one queryable evidence trail rather than four disconnected outputs. **If you do not need the chain and you have secrets covered, you may only want the acquisition layer.** That is a legitimate outcome and we would rather you adopt one layer than reject four.\n\n---\n\n# PAGES 58\u201359 \u2014 CI/CD INTEGRATION\n\n**Headline:** GATES PEOPLE DO NOT DISABLE\n\n**Deck:** *CI is where security tooling goes to be turned off. The difference is entirely in how you introduce it.*\n\n---\n\n### The basic integration\n\n```\nname: Security Scan\non: [push, pull_request]\n\njobs:\n  scan:\n    runs-on: ubuntu-latest\n    permissions:\n      security-events: write   # for SARIF upload\n      contents: read\n    steps:\n      - uses: actions/checkout@v4\n      - name: Install SecureGit\n        run: curl -fsSL https://<release-host>/securegit/install.sh | sh\n      - name: Scan (report + gate in one step)\n        run: |\n          securegit scan . \\\n            --format sarif --report-output securegit.sarif \\\n            --baseline .securegit/baseline.json \\\n            --fail-on high\n      - name: Upload to GitHub Advanced Security\n        if: always()\n        uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: securegit.sarif\n```\n\n**In production, do not curl-pipe-shell in CI.** Pin a version, verify a checksum, or use a container image. A security scan step that installs itself from an unpinned URL is a supply-chain finding inside your supply-chain control, and a reviewer will find it.\n\n### GitLab equivalent\n\n```\nsecurity-scan:\n  image: registry.example.com/securegit:0.12\n  script:\n    - securegit scan . --format gitlab --report-output gl-sast-report.json\n        --baseline .securegit/baseline.json --fail-on high\n  artifacts:\n    reports:\n      sast: gl-sast-report.json\n```\n\nThe `sast` artifact lights up GitLab's security dashboard and MR widget with zero further plumbing.\n\n### The four-stage introduction\n\n**Stage 1 \u00b7 Advisory, informational.** Scan runs, prints findings, always exits zero. One week minimum. You are collecting the false-positive profile of your actual codebase, which you cannot predict.\n\n**Stage 2 \u00b7 Advisory, annotated.** Findings appear as PR annotations. Still non-blocking. People start fixing things voluntarily because the finding is in front of them at the moment they are looking at the code.\n\n**Stage 3 \u00b7 Blocking on new findings only.** Scan the diff, not the tree. New high-severity findings fail; existing ones do not. **This is the sustainable steady state for most teams**, and many should stop here permanently.\n\n**Stage 4 \u00b7 Blocking on total.** Only after the backlog is burned down. Many teams never reach this and should not feel bad about it.\n\n### Scan the diff, not the tree\n\nThe single most important CI configuration decision:\n\n```\ngit diff origin/main --name-only | xargs securegit scan --fail-on high\n```\n\n**Three reasons this is right, and they compound:**\n\n**Speed.** Seconds instead of minutes.\n**Relevance.** The author can act on it. A finding in code they did not touch is somebody else's problem, delivered at the worst possible moment.\n**Fairness.** Nobody should be blocked by a finding that predates their branch. This is the one that determines whether the gate is perceived as reasonable, and perception determines survival.\n\n### Where each gate belongs\n\n| Gate | Scope | Threshold | Speed |\n|---|---|---|---|\n| Pre-commit | staged only | `high` | under a second |\n| Pre-push | push range | `critical` | seconds |\n| PR / CI | diff vs. main | `high` | seconds |\n| Nightly | full tree, all plugins | report only | minutes |\n| Release | full tree + SBOM + CVE | `critical` | minutes |\n\n**Note the nightly row reports rather than blocks.** Nightly is where you run everything expensive and let it be noisy, because nobody is waiting on it. That is the correct home for the deep plugin set.\n\n---\n\n**SIDEBAR (page 59, boxed): The metric to watch**\n\n> Do not track findings. Track **the disable rate**.\n>\n> Count how often people use `--no-verify`, skip the CI step, or add a suppression. That number is the real health of your rollout, and it moves before anything else does.\n>\n> A rising disable rate means the gate is producing more friction than perceived value, and it means it *now* \u2014 weeks before anyone raises it in a meeting, and months before someone removes the gate in a cleanup PR.\n>\n> Findings count measures your codebase. Disable rate measures your adoption. Only one of them tells you whether the tool will still be here next quarter.\n\n---\n\n# PAGES 60\u201361 \u2014 TROUBLESHOOTING AND THE FALSE-POSITIVE PLAYBOOK\n\n**Headline:** MAKING IT QUIET\n\n**Deck:** *A tool that cries wolf gets disabled. Driving noise down is not maintenance \u2014 it is the work that determines whether any of this survives.*\n\n---\n\n## THE FALSE-POSITIVE PLAYBOOK\n\n### Step 1 \u00b7 Classify before you suppress\n\nEvery finding you are about to dismiss is one of four things. **Naming which one changes what you do about it.**\n\n**A \u00b7 Wrong path.** Vendor code, dependencies, build output, fixtures. Not your code, not your problem. \u2192 `skip_paths`.\n\n**B \u00b7 Wrong scanner for this stack.** A Python security scanner on a Go repository. \u2192 Disable the plugin for this repository.\n\n**C \u00b7 Genuinely a pattern, deliberately used.** Dynamic execution you meant to write. \u2192 Suppress narrowly, at that location, with a comment explaining why. **Not repository-wide.**\n\n**D \u00b7 High entropy, not a secret.** Hashes, test vectors, base64 assets, minified bundles. \u2192 `skip_paths` for asset directories; narrow suppression for individual cases.\n\n**Never suppress category (C) globally.** \"We use dynamic execution in one place\" becomes \"we no longer detect dynamic execution anywhere,\" and the next occurrence is invisible.\n\n### Step 2 \u00b7 Fix the paths first\n\nMost first-week noise is path noise. This one setting resolves the majority of it:\n\n```\nexport SECUREGIT_SKIP_PATHS=\"**/node_modules/**:**/vendor/**:**/target/**:**/dist/**:**/build/**:**/.venv/**:**/testdata/**\"\n```\n\nOr per-invocation:\n\n```\nsecuregit scan . --skip-paths \"**/node_modules/**,**/vendor/**\"\n```\n\n**Write your organization's baseline down and share it.** Every team rediscovering this independently is wasted effort and inconsistent results.\n\n### Step 3 \u00b7 Right-size the scanner set\n\n```\nsecuregit scan . --plugins secrets,patterns\n```\n\nFor a commit gate, `secrets` and `patterns` are usually the right pair. **`entropy` belongs in scheduled scans**, not in the path where someone is trying to commit a fix at 6pm.\n\n### Step 4 \u00b7 Tune the threshold, not the coverage\n\nPrefer `--fail-on high` over disabling scanners. Keep the coverage and raise the bar. You still see the medium findings; they just do not block. Disabling a scanner loses information permanently; raising a threshold loses only the interruption.\n\n### Step 5 \u00b7 Re-measure\n\n```\nsecuregit scan . --format json | jq '[.findings[] | .severity] | group_by(.) | map({severity: .[0], count: length})'\n```\n\n**Target: under five findings per hundred files at `high` or above on a tuned repository.** Above that, keep tuning. Below that, you are in the range where people read findings instead of dismissing them.\n\n---\n\n**PULL QUOTE**\n\n> Under five findings per hundred files is where people read them. Above that, they dismiss them. There is no third behavior.\n\n---\n\n## COMMON PROBLEMS\n\n**Scan is slow.**\nScanning the tree instead of the diff. Or scanning a dependency directory. Or too many external plugins in the hot path. In that order of likelihood.\n\n**Acquire fails on a private repository.**\nCredentials not registered. `securegit server add`, then retry. Page 49.\n\n**Pre-commit hook does not run.**\nNot executable (`chmod +x`), or `core.hooksPath` points elsewhere, or you are committing from a GUI that bypasses hooks \u2014 which many do, silently.\n\n**Push blocked with \"no chain receipt.\"**\nExpected on a repository adopted after its history began. Either `securegit attest` the range, or set `chain.fail_on_unattested=warn`. Page 36.\n\n**Plugin does not run.**\nNot executable, not in `~/.config/securegit/plugins/`, or its output is not valid JSON. Test it standalone first: run it against one file and check that it prints parseable JSON.\n\n**Findings differ between local and CI.**\nDifferent scanner sets, different `skip_paths`, or different versions. **Pin the version in CI and share the configuration.** Divergence between local and CI results destroys trust in both.\n\n**Scan finds nothing on a repository you know has issues.**\nCheck `--include-git`. Check that your `skip_paths` is not excluding the code. Check the version. In that order.\n\n---\n\n**SIDEBAR (page 61, boxed): When to give up on a repository**\n\n> Some repositories are not worth gating, and admitting that is better than a permanently ignored gate.\n>\n> A twelve-year-old codebase with vendored dependencies, generated files checked in, and test fixtures full of realistic-looking fake credentials may produce noise you cannot tune below the usable threshold in reasonable time.\n>\n> **For those: scan on a schedule, report, do not block.** Put the gate on new repositories and on the parts of the old one that are actively developed.\n>\n> A gate that is always red is not a gate. It is a broken light that everyone has learned to walk past, and it makes every other gate you install less credible.\n\n---\n\n# PAGE 62 \u2014 COMMAND REFERENCE CARD\n\n**DESIGN NOTE:** Design this page to be printed, cut out, and kept. Dense mono, tight grouping, clear section rules. This is the page people will pin up.\n\n**Headline:** QUICK REFERENCE\n\n```\nACQUISITION\n  securegit acquire <url> <path>          acquire safely (zip+history)\n  securegit acquire <url> .               acquire here\n\nSCANNING  \u00b7  12 built-in scanners\n  securegit scan <path>                   scan a path\n  securegit scan --staged                 scan staged changes\n  securegit scan . --fail-on high         non-zero exit at high+\n  securegit scan . --min-severity high    display high+ only\n  securegit scan . --include-git          include .git directory\n  securegit scan . --format json          machine-readable\n  securegit scan . --format sarif  --report-output f.sarif   GHAS/Azure/Sonar\n  securegit scan . --format gitlab --report-output gl.json   GitLab SAST\n  securegit scan . --skip-paths \"<glob>\"  exclude paths\n  securegit scan . --plugins a,b          named scanners only\n  securegit scan . --write-baseline \"<reason>\" --expires <RFC3339>\n  securegit scan . --baseline .securegit/baseline.json\n\nEVERYDAY\n  securegit status / add / diff / log / blame\n  securegit safe-commit -m \"<msg>\"        scan, then commit\n  securegit commit -m \"<msg>\"             commit with receipt\n  securegit commit --ai                   AI-suggested commit message\n  securegit findings / review / posture\n  securegit undo                          universal undo (18 mutating cmds)\n  securegit snapshot                      commit-independent restore points\n  securegit snapshot list / restore <id>\n\nBRANCHING & STACKS\n  securegit branch-create / branch-list / checkout / merge\n  securegit worktree-add <path>\n  securegit conflicts / resolve            conflict inspection & resolution\n  securegit stack                          stacked-diff workflow\n  securegit absorb                         git-absorb port (auto-fixup)\n\nREMOTE\n  securegit push\n  securegit server add <name> --platform <p> --api-url <url>\n  securegit server list / search <query>\n  securegit repo-create <name>\n\nSETTINGS  \u00b7  layered: /etc \u2192 user \u2192 repo \u2192 env\n  securegit settings show / path / init\n  /etc/securegit/config.toml               org policy (min_fail_on, locked_keys, audit_required)\n  ~/.config/securegit/config.toml          user\n  <repo>/.securegit/config.toml            repo\n\nAUDIT  \u00b7  hash-chained, tamper-evident\n  securegit audit show --last <N>\n  securegit audit verify\n  securegit audit export --format jsonl --output audit.jsonl\n  securegit audit export --format cef  > securegit.cef\n\nCOMPLIANCE  \u00b7  OWASP Top 10 (2021) via CWE  \u00b7  NIST SSDF v1.1\n  securegit compliance report --format markdown --output compliance.md\n  securegit compliance report --format json     --output compliance.json\n\nPLUGINS & UPDATES\n  securegit plugin list / install <name> / info <name>\n  securegit plugin check-updates\n  securegit plugin update --all\n  securegit update-check                   throttled daily on startup\n\nWORKFLOWS  \u00b7  20 shipped scripts, 4-level config override\n  securegit workflow list / info <name> / install\n  securegit workflow run <name> --dry-run\n\nSECRETS  \u00b7  handles, never values  \u00b7  ChaCha20-Poly1305 store\n  securegit secret add <handle> --provider <p>\n  securegit secret list / info / test <handle>\n  securegit secret discover --target <t> --keys-only\n  securegit secret rotate <handle>\n  securegit run --with-secret NAME=<handle> -- <cmd>\n\nCHAIN & SUPPLY\n  securegit attest <sha>  |  --since <sha>\n  securegit sbom emit [--with-osv-scan]\n  securegit sbom rescan <receipt-id>\n\nANALYTICS & AI CONTEXT\n  securegit gain                           workflow adoption analytics\n  securegit <cmd> --compact                60\u201390% token reduction for LLM contexts\n\nESCAPE HATCH & HELP\n  securegit git-raw -- <any git command>\n  securegit --help / <command> --help / --version\n\nCONFIG PATHS\n  /etc/securegit/config.toml               org policy (fleet)\n  ~/.config/securegit/config.toml          user\n  ~/.config/securegit/plugins/             external plugins\n  ~/.config/securegit/workflows/           workflow overrides\n  ~/.securegit/credentials.encrypted       credential store v2\n  ~/.local/share/securegit/audit/          audit log\n\nENVIRONMENT\n  SECUREGIT_FAIL_ON=high\n  SECUREGIT_SKIP_PATHS=\"<glob>:<glob>\"\n  SECUREGIT_CA_BUNDLE=/etc/ssl/corp-bundle.pem\n  SECUREGIT_PROXY=http://proxy.corp.example.com:3128\n  SECUREGIT_CREDSTORE_PASSPHRASE=<passphrase>\n  HTTP_PROXY / HTTPS_PROXY / NO_PROXY\n  SECUREGIT_VERBOSE=1  \u00b7  SECUREGIT_WORKFLOW_TIPS=0\n```\n\n---\n\n# PAGE 63 \u2014 THE MATURITY TABLE \u00b7 GLOSSARY\n\n**Headline:** WHAT IS REAL TODAY\n\n**Deck:** *Every capability in this issue, in one table. This is the page to hand your security reviewer.*\n\n**DESIGN NOTE:** Three-state visual coding, weight declining down the table. `SHIPPED` in ink bold. `DESIGNED` in graphite. `PLANNED` in ghosted outline at reduced opacity. A reader scanning from across a room should see solid at the top, faint at the bottom.\n\n**AT A GLANCE:** As of v0.12.16, every capability that was `DESIGNED` in the first two categories has shipped except two (offline chain, batch receipt lookup). The maturity table is denser at the top than at the bottom, which is the direction any honest table should move.\n\n| Capability | State |\n|---|---|\n| Archive-first acquisition (zip+history / zip-only / bare), hooks stripped | **SHIPPED** |\n| Sanitization report on acquire | **SHIPPED** |\n| **Twelve** built-in scanners (`secrets`, `patterns`, `entropy`, `binary`, `encoding` / Trojan Source, `supply-chain`, `ci-cd`, `container`, `iac`, `deserialization`, `dangerous-files`, `git-internals`) | **SHIPPED** |\n| External plugin system (any language) + managed update manifest | **SHIPPED** |\n| Staged / diff / tree scanning, severity thresholds | **SHIPPED** |\n| Output formats: pretty, JSON, **SARIF 2.1.0**, **GitLab SAST v15** | **SHIPPED** |\n| `--report-output` (report even when gate fails) | **SHIPPED** |\n| **Baselines** with stable fingerprints, RFC3339 `expires`, `--no-baseline` | **SHIPPED** |\n| Pre-commit and pre-push hook patterns | **SHIPPED** |\n| Workflow scripts with dry-run \u2014 **20 shipped**, 4-level config override, language auto-detection | **SHIPPED** |\n| Server registration and cross-provider search | **SHIPPED** |\n| Credential resolution chain | **SHIPPED** |\n| Agent tool surface (**32 MCP tools**, `securegit-mcp`) | **SHIPPED** |\n| Secret handles, execution-boundary resolution, redaction | **SHIPPED** |\n| Metadata-only secret audit events | **SHIPPED** |\n| Guarded production writes and deletes | **SHIPPED** |\n| **Credential store v2** \u2014 ChaCha20-Poly1305, machine-bound, atomic, fails closed | **SHIPPED** |\n| **Optional passphrase overlay** (`SECUREGIT_CREDSTORE_PASSPHRASE`) | **SHIPPED** |\n| Chain receipts: acquire, commit, push, merge, scan | **SHIPPED** |\n| Receipt storage in commit trailers + daemon store | **SHIPPED** |\n| Tier A / Tier B evidence distinction | **SHIPPED** |\n| Push gate on unattested commits | **SHIPPED** |\n| Force-push bypass receipts + joint approval | **SHIPPED** |\n| **Layered configuration** (`/etc` \u2192 user \u2192 repo \u2192 env) | **SHIPPED** |\n| **Org policy** (`min_fail_on`, `locked_keys`, `audit_required`) | **SHIPPED** |\n| **`securegit settings` command** (`show` / `path` / `init`) | **SHIPPED** |\n| **Hash-chained audit log** with `audit verify` | **SHIPPED** |\n| **Audit export** \u2014 JSONL (SIEM), **CEF** (ArcSight/Sentinel/QRadar) | **SHIPPED** |\n| **Compliance report** \u2014 OWASP Top 10 (2021) via CWE + NIST SSDF v1.1 | **SHIPPED** |\n| SBOM anchoring | **SHIPPED** |\n| Vulnerability-database lookup (opt-in) | **SHIPPED** |\n| `scan_completeness` field | **SHIPPED** |\n| Offline vulnerability mirror | **SHIPPED** |\n| SBOM rescan timeline | **SHIPPED** |\n| Lock-file change receipts (major formats) | **SHIPPED** |\n| License detection (best-effort, non-blocking) | **SHIPPED** |\n| Blame with per-line chain overlay | **SHIPPED** |\n| Diamond merge receipts + multi-author approval | **SHIPPED** |\n| **Signed releases** \u2014 CycloneDX SBOM + `SHA256SUMS` + Sigstore keyless (cosign) | **SHIPPED** |\n| **Corporate networks** \u2014 OS trust store, `SECUREGIT_CA_BUNDLE`, standard proxy env | **SHIPPED** |\n| **`securegit undo`** \u2014 universal, 18 mutating commands journaled | **SHIPPED** |\n| **`securegit snapshot`** \u2014 continuous, commit-independent restore points | **SHIPPED** |\n| **`securegit commit --ai`** \u2014 AI-suggested commit messages | **SHIPPED** |\n| **`securegit conflicts` / `resolve`** \u2014 first-class conflict UX | **SHIPPED** |\n| **`securegit stack`** \u2014 stacked-diff workflow | **SHIPPED** |\n| **`securegit absorb`** \u2014 auto-fixup, port of git-absorb | **SHIPPED** |\n| **`--compact` mode** \u2014 60\u201390% token reduction for LLM contexts | **SHIPPED** |\n| **`securegit gain`** \u2014 workflow adoption analytics | **SHIPPED** |\n| **`securegit update-check`** \u2014 throttled daily startup check for binary, MCP, plugins | **SHIPPED** |\n| Secrets-manager backend: metadata discovery + value resolution | **SHIPPED** |\n| Chain offline store with sync-on-reconnect | *DESIGNED* |\n| Batch receipt lookup for large pushes | *DESIGNED* |\n| Integrated chain audit report generator | *DESIGNED* |\n| Provider profile catalog | *DESIGNED* |\n| OS keychain backend (Keychain / DPAPI / Secret Service) | *DESIGNED* |\n| Full lock-file parsing for remaining formats | *DESIGNED* |\n| Repository graph integration | *DESIGNED* |\n| Incremental scan cache | \u00b7PLANNED\u00b7 |\n| `--jobs` parallelism control | \u00b7PLANNED\u00b7 |\n| Native dynamic (FFI) plugins | \u00b7PLANNED\u00b7 |\n| WebAssembly sandboxed plugins | \u00b7PLANNED\u00b7 |\n| Plugin marketplace | \u00b7PLANNED\u00b7 |\n\n---\n\n## GLOSSARY\n\n**ACQUIRE** \u2014 Fetch a repository as an archive, strip hooks, scan, then convert to a normal git repository. The ordering is the security property.\n\n**ANCHOR** \u2014 Record the content hash of an external tool's output in the chain, without bundling that tool.\n\n**ATTEST** \u2014 Add a receipt to a commit after the fact. Produces Tier B evidence.\n\n**AUDIT LOG** \u2014 A tamper-evident JSONL log, independent of the chain, capturing every security-relevant event with `prev_hash` linkage. Verified by `securegit audit verify`; exports to JSONL or CEF.\n\n**BASELINE** \u2014 A file (`.securegit/baseline.json`) suppressing a known set of findings by stable fingerprint. Every entry carries reason, creator, timestamp, and optional RFC3339 expiry.\n\n**BUILT-IN SCANNER** \u2014 One of the **twelve** scanners compiled into the binary. Zero startup cost.\n\n**CEF** \u2014 Common Event Format. The audit-log export format understood by ArcSight, Microsoft Sentinel, and QRadar.\n\n**CHACHA20-POLY1305** \u2014 The AEAD used by credential store v2. Machine-keyed by default; optionally passphrase-augmented.\n\n**CHAIN OF CUSTODY** \u2014 A tamper-evident sequence of signed receipts linking actions to identities and content.\n\n**COMPLIANCE REPORT** \u2014 `securegit compliance report`. Findings grouped by OWASP Top 10 (2021) via CWE and by NIST SSDF v1.1 practice.\n\n**COSIGN / SIGSTORE KEYLESS** \u2014 The signing model used for SecureGit releases. Signatures anchor to short-lived OIDC identities rather than long-lived keys.\n\n**DIAMOND RECEIPT** \u2014 A merge receipt encoding both parent commits, preserving the history graph inside the evidence chain.\n\n**EXECUTION BOUNDARY** \u2014 The last moment before an operation runs; where a secret value is resolved and injected.\n\n**EXTERNAL PLUGIN** \u2014 An executable that takes a file path and prints JSON findings. Any language. Subprocess cost.\n\n**FORWARD-ATTESTED (TIER A)** \u2014 The signing daemon witnessed the operation as it happened.\n\n**HANDLE** \u2014 A stable name referring to a secret. Used by humans and agents; never a value.\n\n**JOINT APPROVAL** \u2014 A second signal required for high-stakes operations such as force-push or multi-author merge.\n\n**PUSH GATE** \u2014 Enforcement at push time, checking that every commit in range carries a receipt.\n\n**RECEIPT** \u2014 A signed, timestamped record binding an operation to an identity and a content hash.\n\n**RETRO-ATTESTED (TIER B)** \u2014 A receipt added after the fact. Real evidence, weaker than Tier A, always distinguished from it.\n\n**SANITIZATION REPORT** \u2014 The record written at acquisition describing what was fetched, stripped, and found.\n\n**SCAN COMPLETENESS** \u2014 What fraction of a scan actually finished. Zero findings without full completeness is not a clean result.\n\n**SARIF 2.1.0** \u2014 OASIS static-analysis report format understood by GitHub Advanced Security, Azure DevOps, SonarQube, DefectDojo.\n\n**SKIP PATHS** \u2014 Glob patterns excluded from scanning. The primary false-positive control.\n\n**SNAPSHOT** \u2014 A commit-independent restore point captured by `securegit snapshot`.\n\n**TRAILER** \u2014 A key-value line in a commit message. Where receipt identifiers live so they survive without a daemon.\n\n**TROJAN SOURCE** \u2014 CVE-2021-42574. The class of source-code attack that hides intent using BiDi override, homoglyph, or zero-width Unicode. Detected by the `encoding` scanner.\n\n**UNDO** \u2014 `securegit undo`. Universal reversal for 18 mutating commands, backed by an operation journal separate from git reflog.\n\n---\n\n# PAGE 64 \u2014 BACK COVER\n\n**Full-bleed ink ground. Reversed type. One idea.**\n\n**Centered, large:**\n\n> You already\n> ran the code.\n> You just\n> called it\n> a clone.\n\n**Lower third, small, mono, orange:**\n\n> BOTTLENECK \u00b7 NO. 01 \u00b7 THE UNTRUSTED CLONE\n> START AT PAGE 11. IT TAKES TEN MINUTES.\n\n**Foot, hairline rule, small caps:**\n\n> ARMYKNIFELABS \u00b7 A LIMITED SERIES\n\n---\n\n*[END OF MANUSCRIPT]*\n"
}