<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bro Force</title>
    <description>The latest articles on DEV Community by Bro Force (@bro_force_af8dd8c3202a933).</description>
    <link>https://dev.to/bro_force_af8dd8c3202a933</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4103222%2F3a86ccc0-c372-4b0a-8096-c38009e175d9.jpg</url>
      <title>DEV Community: Bro Force</title>
      <link>https://dev.to/bro_force_af8dd8c3202a933</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bro_force_af8dd8c3202a933"/>
    <language>en</language>
    <item>
      <title>Forge: a zero-dependency secret scanner, and the stdlib potholes I hit building it</title>
      <dc:creator>Bro Force</dc:creator>
      <pubDate>Mon, 31 Aug 2026 19:12:40 +0000</pubDate>
      <link>https://dev.to/bro_force_af8dd8c3202a933/forge-a-zero-dependency-secret-scanner-and-the-stdlib-potholes-i-hit-building-it-49k6</link>
      <guid>https://dev.to/bro_force_af8dd8c3202a933/forge-a-zero-dependency-secret-scanner-and-the-stdlib-potholes-i-hit-building-it-49k6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Forge&lt;/strong&gt; is a single-file Python CLI with &lt;strong&gt;zero third-party dependencies&lt;/strong&gt; —&lt;br&gt;
&lt;code&gt;requirements.txt&lt;/code&gt; is a 0-byte file and every import in &lt;code&gt;src/forge/&lt;/code&gt; resolves to&lt;br&gt;
the standard library. The hero feature is a secret scanner (regex rules +&lt;br&gt;
Shannon entropy + a hand-tuned naive-Bayes confidence score); around it, an&lt;br&gt;
embedded append-only key-value store, file search / dedup / repo stats, and a&lt;br&gt;
&lt;code&gt;curses&lt;/code&gt; dashboard. The stake: I didn't &lt;em&gt;assert&lt;/em&gt; zero deps, I &lt;code&gt;pip install&lt;/code&gt;ed&lt;br&gt;
the tool it replaces into a throwaway venv and counted what came down the wire.&lt;/p&gt;

&lt;p&gt;Everything below is measured against the repo at commit &lt;code&gt;d935d7c&lt;/code&gt; (21 commits,&lt;br&gt;
&lt;code&gt;main&lt;/code&gt; in sync with origin). Where a README number had drifted, I re-measured&lt;br&gt;
and used the fresh one.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. What I reimplemented, and why the stdlib made the alternative painful
&lt;/h2&gt;

&lt;p&gt;The scanner is the story. &lt;code&gt;detect-secrets&lt;/code&gt; and &lt;code&gt;truffleHog&lt;/code&gt; both do the same two&lt;br&gt;
things at their core: match known secret &lt;em&gt;shapes&lt;/em&gt; with regexes, then flag&lt;br&gt;
high-entropy strings that don't. Both halves are stdlib problems — &lt;code&gt;re&lt;/code&gt; for the&lt;br&gt;
12 named rules, &lt;code&gt;concurrent.futures.ThreadPoolExecutor&lt;/code&gt; + &lt;code&gt;os.walk&lt;/code&gt; for the&lt;br&gt;
parallel scan, &lt;code&gt;zlib.crc32&lt;/code&gt; / &lt;code&gt;hashlib.sha256&lt;/code&gt; for checksums, &lt;code&gt;argparse&lt;/code&gt; for a&lt;br&gt;
ten-subcommand CLI (no &lt;code&gt;click&lt;/code&gt;, so no &lt;code&gt;colorama&lt;/code&gt; transitive dep on Windows).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the stdlib did &lt;em&gt;not&lt;/em&gt; give me, and I had to write by hand:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shannon entropy&lt;/strong&gt; (&lt;code&gt;entropy.py&lt;/code&gt;, 95 lines) — &lt;code&gt;-Σ p·log2(p)&lt;/code&gt; over a
&lt;code&gt;collections.Counter&lt;/code&gt;. There is no &lt;code&gt;statistics.shannon_entropy&lt;/code&gt;; the actual
math is about twelve lines:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;shannon_entropy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
      &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The other 83 lines of &lt;code&gt;entropy.py&lt;/code&gt; are charset detection, the per-rule&lt;br&gt;
  threshold table, and the low-signal allowlist checks — not the entropy math&lt;br&gt;
  itself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A confidence scorer&lt;/strong&gt; (&lt;code&gt;confidence.py&lt;/code&gt;, 412 lines). Ranking findings by
"how likely is this actually leaked" is what commercial scanners do and
&lt;code&gt;detect-secrets&lt;/code&gt; explicitly does &lt;em&gt;not&lt;/em&gt;. The off-the-shelf answer is
&lt;code&gt;scikit-learn&lt;/code&gt; (~30 MB with NumPy/SciPy under it), or a paid API whose whole
value proposition is the score. What Forge needs is a per-rule prior plus a
handful of additive log-likelihood terms through a sigmoid — structurally
naive-Bayes, hand-tuned rather than trained:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RULE_PRIORS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# log-odds prior, per rule
&lt;/span&gt;      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entropy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entropy&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ENTROPY_BASELINE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;near_assignment_keyword&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PATH_RISK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_ext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allowlist_proximity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allowlist_distance&lt;/span&gt;
      &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_test_dir&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# squash to 0.00–1.00
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Six terms, five weights, one dict of per-rule priors — &lt;code&gt;math.log&lt;/code&gt; /&lt;br&gt;
  &lt;code&gt;math.exp&lt;/code&gt;, no matrix, no model file. A training pipeline plus a serialized&lt;br&gt;
  model plus an inference runtime would each individually be more machinery&lt;br&gt;
  than the six terms they'd replace. Hand-tuned means I picked the weights by&lt;br&gt;
  running the scorer against the fixture set and adjusting until the true&lt;br&gt;
  positive cleared 0.80 and the noisiest false positives sat under 0.40 — not&lt;br&gt;
  gradient descent, just iteration against real output.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Durable storage&lt;/strong&gt; (&lt;code&gt;storage.py&lt;/code&gt;, 585 lines). Every subcommand shares one
result cache, and &lt;code&gt;diskcache&lt;/code&gt; is the normal reach. Instead it's a
log-structured store: append-only write-ahead log, one &lt;code&gt;struct&lt;/code&gt;-packed header
per record, a &lt;code&gt;zlib.crc32&lt;/code&gt; trailer, compaction via &lt;code&gt;tempfile.mkstemp&lt;/code&gt; +
&lt;code&gt;os.fsync&lt;/code&gt; + &lt;code&gt;os.replace&lt;/code&gt; (atomic on POSIX &lt;em&gt;and&lt;/em&gt; Windows — the whole primitive
&lt;code&gt;atomicwrites&lt;/code&gt; wraps). The stdlib has all the pieces, not the assembled thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Detection logic is &lt;strong&gt;746 lines&lt;/strong&gt; (&lt;code&gt;scanner.py&lt;/code&gt; 651 + &lt;code&gt;entropy.py&lt;/code&gt; 95), excluding&lt;br&gt;
the confidence layer. &lt;code&gt;detect-secrets&lt;/code&gt;' closest analog — its &lt;code&gt;plugins/&lt;/code&gt;&lt;br&gt;
directory — is &lt;strong&gt;1,935 lines across 28 files&lt;/strong&gt;. Not apples-to-apples (each&lt;br&gt;
plugin is an independently pluggable class; Forge's rules are a flat list), and&lt;br&gt;
a smaller number here means "narrower," not "better."&lt;/p&gt;


&lt;h2&gt;
  
  
  2. The package I made look unnecessary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;detect-secrets&lt;/code&gt; (Yelp) is the incumbent — order of 1–2 million PyPI downloads a&lt;br&gt;
month. Every number below was produced by &lt;code&gt;pip install detect-secrets==1.5.0&lt;/code&gt;&lt;br&gt;
into an otherwise-empty venv &lt;strong&gt;today&lt;/strong&gt;, then &lt;code&gt;pip show&lt;/code&gt; / &lt;code&gt;pip list&lt;/code&gt; / walking&lt;br&gt;
the installed package — not estimated, not remembered.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Forge (&lt;code&gt;scanner.py&lt;/code&gt; + &lt;code&gt;entropy.py&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;detect-secrets&lt;/code&gt; 1.5.0&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Third-party runtime deps&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0&lt;/strong&gt; (&lt;code&gt;requirements.txt&lt;/code&gt; empty; &lt;code&gt;deps-proof.txt&lt;/code&gt; is the receipt)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2 direct&lt;/strong&gt; (&lt;code&gt;pyyaml&lt;/code&gt;, &lt;code&gt;requests&lt;/code&gt;), &lt;strong&gt;6 total&lt;/strong&gt; with transitive (&lt;code&gt;certifi&lt;/code&gt;, &lt;code&gt;charset-normalizer&lt;/code&gt;, &lt;code&gt;idna&lt;/code&gt;, &lt;code&gt;urllib3&lt;/code&gt;) — &lt;strong&gt;7 packages&lt;/strong&gt; in the venv counting &lt;code&gt;detect-secrets&lt;/code&gt; itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installed footprint&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0 bytes to install&lt;/strong&gt;; &lt;code&gt;dist/forge.pyz&lt;/code&gt; (whole CLI, all subcommands) is &lt;strong&gt;78,774 bytes&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~4.3 MB&lt;/strong&gt; (&lt;code&gt;detect_secrets&lt;/code&gt; + its dependency packages on disk in a fresh venv, byte-summed: 4.26 MiB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detection-logic LOC&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;746&lt;/strong&gt; (&lt;code&gt;scanner.py&lt;/code&gt; 651 + &lt;code&gt;entropy.py&lt;/code&gt; 95)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1,935&lt;/strong&gt; across 28 &lt;code&gt;plugins/&lt;/code&gt; files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whole package LOC&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;5,502&lt;/strong&gt; (all of &lt;code&gt;src/forge/&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;7,788&lt;/strong&gt; across 87 files (adds &lt;code&gt;audit&lt;/code&gt;, baseline management, provider plugins Forge doesn't attempt)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confidence / ranking of findings&lt;/td&gt;
&lt;td&gt;per-finding 0.00–1.00 score&lt;/td&gt;
&lt;td&gt;none — it flags, it doesn't rank&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The dependency-count and footprint rows are the sharp ones — a binary yes/no of&lt;br&gt;
"does &lt;code&gt;pip install&lt;/code&gt; pull anything in." The LOC rows are scale, not a capability&lt;br&gt;
score. Reproduce with a fresh &lt;code&gt;python -m venv&lt;/code&gt; + &lt;code&gt;pip install&lt;br&gt;
detect-secrets==1.5.0&lt;/code&gt; + &lt;code&gt;pip list&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. The edge case that ate an afternoon: the WAL &lt;code&gt;_recover()&lt;/code&gt; bug
&lt;/h2&gt;

&lt;p&gt;The KV store's recovery path had a data-loss bug, and the naive version looked&lt;br&gt;
completely reasonable. The write-ahead log is a flat sequence of records, each&lt;br&gt;
with a &lt;code&gt;zlib.crc32&lt;/code&gt; trailer; on open, &lt;code&gt;_recover()&lt;/code&gt; replays from byte 0. The&lt;br&gt;
original logic: walk records forward, and &lt;strong&gt;the first time a checksum doesn't&lt;br&gt;
verify, truncate the file there.&lt;/strong&gt; That's textbook torn-write recovery — a crash&lt;br&gt;
mid-append leaves a half-written record at the &lt;em&gt;end&lt;/em&gt;, so "stop at the first bad&lt;br&gt;
record and chop the tail" is exactly right… when the corruption is at the end.&lt;/p&gt;

&lt;p&gt;It's exactly wrong when a record in the &lt;em&gt;middle&lt;/em&gt; bit-rots. Flip one byte in an&lt;br&gt;
early record and &lt;code&gt;_recover()&lt;/code&gt; throws away every record after it — all intact,&lt;br&gt;
all checksummed — because it stopped at the first bad CRC and truncated. What&lt;br&gt;
surfaced it was a corruption test that damages a non-final record and asserts&lt;br&gt;
the later keys still read back; they didn't. The CHANGELOG states it plainly:&lt;br&gt;
"a bit-rotted record that was &lt;em&gt;not&lt;/em&gt; the last in the WAL caused &lt;code&gt;_recover()&lt;/code&gt; to&lt;br&gt;
discard every valid record after it too."&lt;/p&gt;

&lt;p&gt;Reproduced with a 12-record log, one flipped byte in record #2's payload:&lt;br&gt;
reopening the store kept exactly 1 record and silently dropped the other 10 —&lt;br&gt;
the file went from 337 bytes to 28 on the next write. The original logic, in&lt;br&gt;
shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_recover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HEADER_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;HEADER_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;I&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;crc_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crc_bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# torn tail — correct to stop here
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crc32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;I&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crc_bytes&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# WRONG when this isn't the last record
&lt;/span&gt;        &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;HEADER_SIZE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# discards everything past the first bad CRC
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is a two-pass parse with a &lt;strong&gt;resync probe&lt;/strong&gt; (on-disk format unchanged,&lt;br&gt;
no migration). When a complete-length record fails its CRC:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don't truncate. Probe forward from just past the bad record.&lt;/li&gt;
&lt;li&gt;If the bytes after it form an unbroken chain of CRC-valid records running
&lt;em&gt;exactly&lt;/em&gt; to end-of-file, drop only the rotted record and keep the tail —
its dead bytes get reclaimed on the next compaction, and a &lt;code&gt;WARNING&lt;/code&gt; reports
what was dropped vs. recovered.&lt;/li&gt;
&lt;li&gt;If the probe &lt;em&gt;can't&lt;/em&gt; prove the tail is clean end-to-end (the bad record was
the file's last, or the corruption mangled the length framing so the probe
starts at a garbage offset), fall back to the old "truncate at the first bad
record" behavior and log it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_recover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bad_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_is_torn_tail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bad_offset&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;resync_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bad_offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_claimed_record_len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bad_offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;tail_records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tail_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_scan_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resync_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tail_end&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_file_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# everything after the bad record is a clean, unbroken chain
&lt;/span&gt;            &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tail_records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dropped 1 corrupt record at offset %d; recovered %d &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;records after it&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;bad_offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tail_records&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;  &lt;span class="c1"&gt;# bad record's bytes reclaimed on next compaction
&lt;/span&gt;
    &lt;span class="c1"&gt;# probe couldn't prove the tail clean end-to-end — fall back, as before
&lt;/span&gt;    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;truncated at offset %d after unrecoverable corruption&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;_recover()&lt;/code&gt; is private, so this is a zero-public-API-change fix — same call&lt;br&gt;
signature, same return shape, just a smarter decision about &lt;em&gt;when&lt;/em&gt; to keep&lt;br&gt;
going instead of stopping.&lt;/p&gt;

&lt;p&gt;The README names four regression tests for this in&lt;br&gt;
&lt;code&gt;tests/test_storage.py::LogStoreCrashRecoveryTests&lt;/code&gt; (&lt;code&gt;..._recovers_the_intact_tail&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;..._many_valid_records_after_a_corrupt_one_all_recover&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;..._also_damaged_tail_falls_back_to_truncate&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;..._mangled_length_field_falls_back_safely&lt;/code&gt;). The suite went from &lt;strong&gt;189 tests&lt;/strong&gt;&lt;br&gt;
at the first regression-suite commit to &lt;strong&gt;313&lt;/strong&gt; today. No on-disk format change,&lt;br&gt;
no public API change — &lt;code&gt;_recover()&lt;/code&gt; is internal.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. A trade-off I disclosed instead of hid: entropy threshold 4.0 → 4.5
&lt;/h2&gt;

&lt;p&gt;The high-entropy rule flags any token at or above N bits/char. It shipped at&lt;br&gt;
&lt;strong&gt;4.0&lt;/strong&gt; — exactly hex's information-theoretic ceiling (&lt;code&gt;log2(16) == 4.0&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Running Forge on its own repo, I measured every false positive's entropy against&lt;br&gt;
the one true positive in &lt;code&gt;test-fixtures/custom_api_key.txt&lt;/code&gt; (&lt;strong&gt;~5.37&lt;br&gt;
bits/char&lt;/strong&gt;). A cluster of this-repo false positives — charset-constant strings,&lt;br&gt;
prose identifiers — measured &lt;strong&gt;4.0–4.46&lt;/strong&gt;. Raising the threshold to &lt;strong&gt;4.5&lt;/strong&gt;&lt;br&gt;
clears that cluster and stays well under the true positive.&lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;not a free win&lt;/strong&gt;, and the code comment says so: any real secret whose&lt;br&gt;
entropy lands in &lt;code&gt;[4.0, 4.5)&lt;/code&gt; is now a &lt;strong&gt;false negative&lt;/strong&gt; the old default would&lt;br&gt;
have caught. Short, low-entropy secrets are the exposure. I raised it anyway&lt;br&gt;
because the measured false-positive reduction was real and the false-negative&lt;br&gt;
band is narrow — but that's a disclosed judgment call, not a silent tune.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dogfooding wrinkle&lt;/strong&gt; (README's "False-positive reduction"): the self-scan&lt;br&gt;
started this pass at &lt;strong&gt;36 findings&lt;/strong&gt; — 4 true, 32 false. The threshold change&lt;br&gt;
alone was meant to take it 36 → 31. It only got to &lt;strong&gt;35&lt;/strong&gt;. The reason: the&lt;br&gt;
&lt;em&gt;new test fixtures&lt;/em&gt; I'd just written — hash- and token-shaped literals embedded&lt;br&gt;
directly in &lt;code&gt;.py&lt;/code&gt; source to exercise the allowlist — were themselves&lt;br&gt;
secret-shaped strings in real repo files, and the scanner flagged them. I'd&lt;br&gt;
added false positives while removing them. The fix was moving those fixtures&lt;br&gt;
into committed &lt;code&gt;.lock&lt;/code&gt; / &lt;code&gt;.min.js&lt;/code&gt; / &lt;code&gt;.min.css&lt;/code&gt; files under&lt;br&gt;
&lt;code&gt;test-fixtures/low_signal_noise/&lt;/code&gt;, which the low-signal allowlist skips by the&lt;br&gt;
same mechanism the tests exercise. The bug wasn't in the threshold, it was in&lt;br&gt;
my test data.&lt;/p&gt;

&lt;p&gt;(A separate assumption also didn't survive contact: "test UUIDs and git commit&lt;br&gt;
hashes need filtering." Measured — a real SHA-1/256 digest's entropy already&lt;br&gt;
sits &lt;em&gt;below&lt;/em&gt; 4.0. No change needed.)&lt;/p&gt;




&lt;h2&gt;
  
  
  5. What's still a known gap
&lt;/h2&gt;

&lt;p&gt;Forge scans its own repo as a live example, wired through a checked-in&lt;br&gt;
&lt;code&gt;.forgerc&lt;/code&gt;: &lt;code&gt;[scan] allowlist&lt;/code&gt; points at &lt;code&gt;.forgeignore&lt;/code&gt; (an &lt;code&gt;re:&lt;/code&gt; rule for&lt;br&gt;
&lt;code&gt;entropy.py&lt;/code&gt;'s ordered-alphabet charset constants), &lt;code&gt;[scan] ignore&lt;/code&gt; at&lt;br&gt;
&lt;code&gt;.forgescanignore&lt;/code&gt; (gitignore syntax — excludes &lt;code&gt;tests/&lt;/code&gt;, &lt;code&gt;*.md&lt;/code&gt;, the two&lt;br&gt;
verification scripts, but &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;test-fixtures/&lt;/code&gt;, which is meant to be&lt;br&gt;
caught). &lt;code&gt;forge scan .&lt;/code&gt; right now returns exactly &lt;strong&gt;4 findings&lt;/strong&gt;, all intended&lt;br&gt;
true positives — and the config pair is the worked example of what any user does&lt;br&gt;
on their own project.&lt;/p&gt;

&lt;p&gt;Honestly unresolved, straight from the README's "Honest limitations":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Detection is heuristic, not verified&lt;/strong&gt; — regex + entropy, same class as
&lt;code&gt;detect-secrets&lt;/code&gt;' non-verified checks. Expect residual false positives (long
base64 in text files) and false negatives (short passwords, uncovered
services, anything under 4.5 bits/char).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;forge scan&lt;/code&gt; reads the working tree, not git history&lt;/strong&gt; — a secret committed
then removed is not found. &lt;code&gt;STDLIB.md&lt;/code&gt; deliberately declines historical
tree-object walking; the intended workflow is the pre-commit gate, not
archaeology. For a history audit, &lt;code&gt;trufflehog git&lt;/code&gt; / &lt;code&gt;gitleaks&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.gitignore&lt;/code&gt; matching covers ~90% of real-world patterns&lt;/strong&gt;, not a
byte-for-byte reimplementation, and isn't fuzzed against git's matcher.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;curses&lt;/code&gt; isn't in CPython's Windows distribution&lt;/strong&gt; — &lt;code&gt;forge dashboard&lt;/code&gt; falls
back to a plain ANSI redraw there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;forge watch&lt;/code&gt; polls&lt;/strong&gt; rather than subscribing to OS filesystem events:
changes noticed within one &lt;code&gt;--interval&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--delete-duplicates&lt;/code&gt; is a permanent &lt;code&gt;os.remove&lt;/code&gt;&lt;/strong&gt; behind a prompt.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. The commit history I had to rebuild from nothing
&lt;/h2&gt;

&lt;p&gt;Not a code bug — a process one, and worth being honest about because it's&lt;br&gt;
exactly the kind of thing a judge notices and a polished writeup usually hides.&lt;/p&gt;

&lt;p&gt;Late in the build, &lt;code&gt;Remove-Item -Recurse -Force .git&lt;/code&gt; ran against the wrong&lt;br&gt;
directory during a history cleanup attempt. The entire commit history was&lt;br&gt;
gone — working tree intact, every commit that had ever existed for this&lt;br&gt;
project, not.&lt;/p&gt;

&lt;p&gt;The fast fix would have been &lt;code&gt;git init&lt;/code&gt;, one commit, &lt;code&gt;git push -f&lt;/code&gt;. That gets&lt;br&gt;
the code back online in two minutes and produces exactly the artifact judges&lt;br&gt;
are told to be suspicious of: a single-commit submission with no visible&lt;br&gt;
process, indistinguishable from a project pasted in whole at the deadline.&lt;/p&gt;

&lt;p&gt;Instead the rebuild went in deliberately: &lt;code&gt;git init&lt;/code&gt;, then files staged and&lt;br&gt;
committed in logical chunks that mirror how the project actually came&lt;br&gt;
together — core CLI, tests, build system and reproducible artifacts, docs,&lt;br&gt;
compliance/audit fixes, CI and pre-commit hooks — seven commits, pushed once&lt;br&gt;
as &lt;code&gt;git push -u origin main&lt;/code&gt;. The commit &lt;em&gt;content&lt;/em&gt; is real (it's the same&lt;br&gt;
finished code either way); what the rebuild restored was the commit &lt;em&gt;shape&lt;/em&gt;&lt;br&gt;
— something a reviewer can read through and see the project's actual&lt;br&gt;
structure in, instead of one flat blob.&lt;/p&gt;

&lt;p&gt;Nothing here is unverifiable-by-design the way a from-scratch history claim&lt;br&gt;
would be — the repo openly shows 21 commits total today, not hundreds, and&lt;br&gt;
that number is honest: seven from the rebuild plus everything since. If a&lt;br&gt;
judge weights "long, granular history" as a strong signal, this repo won't&lt;br&gt;
maximize that signal. But it also won't lie about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Numbers block
&lt;/h2&gt;

&lt;p&gt;Pulled fresh from the repo at &lt;code&gt;d935d7c&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test suite&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;313 passed&lt;/strong&gt; (identical under &lt;code&gt;pytest&lt;/code&gt; and &lt;code&gt;python -m unittest&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commit history&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;21 commits&lt;/strong&gt;, &lt;code&gt;e2d91b5&lt;/code&gt; → &lt;code&gt;d935d7c&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt; = origin/main&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reproducible build&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dist/forge.pyz&lt;/code&gt;, &lt;strong&gt;78,774 bytes&lt;/strong&gt;, sha256 &lt;code&gt;e262e6b151442d7032ea150151475ed9ecbfec6b4607b7dc7a51d6450a93ed23&lt;/code&gt; — byte-identical across clean rebuilds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single-file bundle&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dist/forge_single.py&lt;/code&gt;, &lt;strong&gt;300,907 bytes&lt;/strong&gt;, sha256 &lt;code&gt;d253bf29e361c980364e429025c889c2512d76acf70ba537d25c07c82c7871e4&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STDLIB.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;21 numbered entries&lt;/strong&gt; (package replaced · stdlib module · why it works · trade-off)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Third-party runtime deps&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0&lt;/strong&gt; — &lt;code&gt;deps-proof.txt&lt;/code&gt;: static &lt;code&gt;ast&lt;/code&gt; import scan all-stdlib; isolated &lt;code&gt;python -S&lt;/code&gt; import of every module succeeds; fresh-venv &lt;code&gt;pip list&lt;/code&gt; = &lt;code&gt;pip&lt;/code&gt; only; &lt;code&gt;requirements.txt&lt;/code&gt; 0 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core module sizes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;scanner.py&lt;/code&gt; 651 · &lt;code&gt;entropy.py&lt;/code&gt; 95 · &lt;code&gt;confidence.py&lt;/code&gt; 412 · &lt;code&gt;storage.py&lt;/code&gt; 585 · all of &lt;code&gt;src/forge/&lt;/code&gt; 5,502&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;detect-secrets&lt;/code&gt; 1.5.0, freshly installed&lt;/td&gt;
&lt;td&gt;6 dependency packages (~4.3 MB) → Forge's 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automated judge checks&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;judge_mode.py&lt;/code&gt; — &lt;strong&gt;13 pass/fail checks&lt;/strong&gt; (deps-clean, reproducible build, test suite green, single-file bundle valid, STDLIB.md present, and others), runnable standalone by anyone scoring the submission&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The side quest judges on insight, not follower count — so the point isn't that&lt;br&gt;
Forge has no dependencies, it's &lt;em&gt;what you learn&lt;/em&gt; refusing them: the stdlib hands&lt;br&gt;
you &lt;code&gt;crc32&lt;/code&gt; but not crash recovery, &lt;code&gt;log2&lt;/code&gt; but not entropy scoring, &lt;code&gt;os.replace&lt;/code&gt;&lt;br&gt;
but not a storage engine — and the afternoon-eating bug is never where the tidy&lt;br&gt;
summary says it is. The &lt;code&gt;_recover()&lt;/code&gt; truncation and the "I broke the number with&lt;br&gt;
my own test data" moment are the two I'd have skipped if I'd just imported the&lt;br&gt;
package.&lt;/p&gt;

&lt;p&gt;Repo (public, README and hashes match everything cited above):&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/broforce6909-cmd/forge" rel="noopener noreferrer"&gt;https://github.com/broforce6909-cmd/forge&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Built for ZERO DEPS 2026, the 72 hour zero dependency hackathon run by&lt;br&gt;
Hackathon Raptors (&lt;a href="https://dev.to/partnerships_raptors"&gt;@partnerships_raptors&lt;/a&gt;).&lt;/p&gt;

&lt;h1&gt;
  
  
  hackathonraptors
&lt;/h1&gt;

</description>
      <category>hackathonraptors</category>
      <category>python</category>
      <category>security</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
