<?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: Anmol Maheshwari</title>
    <description>The latest articles on DEV Community by Anmol Maheshwari (@maheshwarianmol).</description>
    <link>https://dev.to/maheshwarianmol</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%2F4067695%2F763563f8-d805-4bd7-ae23-543660ad96d0.png</url>
      <title>DEV Community: Anmol Maheshwari</title>
      <link>https://dev.to/maheshwarianmol</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maheshwarianmol"/>
    <language>en</language>
    <item>
      <title>We Built a Hackathon Platform Where Every Rule Lives at the Layer That Can't Be Bypassed</title>
      <dc:creator>Anmol Maheshwari</dc:creator>
      <pubDate>Sun, 04 Oct 2026 13:08:26 +0000</pubDate>
      <link>https://dev.to/maheshwarianmol/we-built-a-hackathon-platform-where-every-rule-lives-at-the-layer-that-cant-be-bypassed-4546</link>
      <guid>https://dev.to/maheshwarianmol/we-built-a-hackathon-platform-where-every-rule-lives-at-the-layer-that-cant-be-bypassed-4546</guid>
      <description>&lt;p&gt;Hackathon Raptors gave every team at DOGFOOD 2026 the same brief and 72 hours: build the submission-and-judging platform they would actually run. The winner gets forked into production.&lt;/p&gt;

&lt;p&gt;We're a team of two. Our submission is &lt;strong&gt;Dogfood&lt;/strong&gt; — 9 Spring Boot microservices behind a gateway, a React frontend, PostgreSQL with Row-Level Security, RabbitMQ for the async event mesh, Redis, MinIO, and a judging engine built around z-score normalization with Bayesian shrinkage.&lt;/p&gt;

&lt;p&gt;This is the question we kept returning to while building it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where is this rule actually enforced?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not "does the UI hide the button." Not "does the frontend route guard redirect you." Where in the stack does the rule live, such that bypassing it is architecturally impossible?&lt;/p&gt;

&lt;p&gt;That question shaped every significant decision we made — including which tools we reached for and why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hiding a rule is not the same as enforcing it
&lt;/h2&gt;

&lt;p&gt;The hackathon spec says it plainly: role isolation must be enforced in the backend, and if it only works because the UI hides a button, it does not work.&lt;/p&gt;

&lt;p&gt;The naive version of "judges can't see each other's scores" is a frontend route guard plus a controller that trusts a &lt;code&gt;judge_id&lt;/code&gt; from the request body. Anyone with &lt;code&gt;curl&lt;/code&gt; and a valid token can swap the ID and get another judge's scores back.&lt;/p&gt;

&lt;p&gt;We enforce it twice, and the second layer is the one that matters:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Application:&lt;/strong&gt; every score query filters by &lt;code&gt;judge_id&lt;/code&gt; taken from the verified JWT claim (&lt;code&gt;X-User-Id&lt;/code&gt; injected by the gateway), never from a request parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — Database engine:&lt;/strong&gt; PostgreSQL Row-Level Security keyed on a session variable set from that same claim before every query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;uuid-from-jwt&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;judge_own_scores&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;judging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;judge_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.current_user_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;
    &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.current_role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ORGANIZER'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ADMIN'&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;Even if the application code has a bug, the PostgreSQL kernel silently drops rows that don't belong to the requesting judge. The acceptance suite's T2 check — &lt;code&gt;judge cannot see peer scores&lt;/code&gt; — passes not because we wrote a special case for it, but because the rule lives at a layer that the request can't talk its way around.&lt;/p&gt;




&lt;h2&gt;
  
  
  The deadline is a domain constraint, not a UI feature
&lt;/h2&gt;

&lt;p&gt;A deadline shown in the frontend is a suggestion. A deadline checked against a client-sent timestamp is a suggestion with a race condition.&lt;/p&gt;

&lt;p&gt;The Submission Service enforces the cutoff on the server with a Redis distributed lock (&lt;code&gt;SETNX&lt;/code&gt;) and compares against the event's stored deadline using server time. The client's clock is never trusted. A submission attempted after the deadline is rejected regardless of client clock skew or network latency.&lt;/p&gt;

&lt;p&gt;The rule lives where authoritative time lives.&lt;/p&gt;




&lt;h2&gt;
  
  
  The math problem hiding inside score averaging
&lt;/h2&gt;

&lt;p&gt;Consider three judges scoring the same projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Judge A (Generous):&lt;/strong&gt; scores 8, 9, 10. Mean = 9, StdDev = 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judge B (Harsh):&lt;/strong&gt; scores 2, 3, 4. Mean = 3, StdDev = 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judge C (Neutral):&lt;/strong&gt; scores 4, 6, 8. Mean = 6, StdDev = 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Project X gets a 9 from Judge A, a 4 from Judge B, and an 8 from Judge C. Raw average: &lt;strong&gt;7.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But look at what those scores actually mean relative to each judge's baseline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Judge A's z-score for X: &lt;code&gt;(9 - 9) / 1 = 0&lt;/code&gt; — exactly average for them&lt;/li&gt;
&lt;li&gt;Judge B's z-score for X: &lt;code&gt;(4 - 3) / 1 = +1&lt;/code&gt; — above average for them&lt;/li&gt;
&lt;li&gt;Judge C's z-score for X: &lt;code&gt;(8 - 6) / 2 = +1&lt;/code&gt; — above average for them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normalized average: &lt;strong&gt;+0.67&lt;/strong&gt;. Judge A gave the highest raw score but contributed zero ranking signal — it was just their baseline. Judge B gave a low raw score but it was actually a positive signal relative to how they score everything.&lt;/p&gt;

&lt;p&gt;This is what per-judge, per-criterion z-score normalization does:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;z = (raw_score − judge_mean) / judge_std_dev&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It corrects both a judge's central tendency (hawk vs. dove) and their spread (a judge who clusters scores in a narrow band). Min-max scaling only fixes the range — z-score corrects central tendency too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bayesian shrinkage: what to do when a judge has only reviewed 2 projects
&lt;/h2&gt;

&lt;p&gt;Z-scores have a failure mode: a judge with 1 or 2 reviews gives you noisy estimates of their own mean and standard deviation. If that judge happens to draw a genuinely strong project, their normalized score can swing the leaderboard disproportionately.&lt;/p&gt;

&lt;p&gt;We apply Bayesian shrinkage with k₀ = 5 to handle this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;λ = k / (k + 5)&lt;/p&gt;

&lt;p&gt;z_shrunk = λ × z + (1 − λ) × global_mean&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Judge with &lt;strong&gt;1 review:&lt;/strong&gt; λ = 1/6 ≈ 0.167 — their score is &lt;strong&gt;dampened 83.3%&lt;/strong&gt; toward the global consensus mean.&lt;/li&gt;
&lt;li&gt;Judge with &lt;strong&gt;15 reviews:&lt;/strong&gt; λ = 15/20 = 0.75 — only 25% dampening, their calibration is trusted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One edge case handled explicitly: a judge who gives every project the same score has σ = 0, causing division by zero in the z-score formula. We set σ = 1.0 in that case, which maps all their scores to z = 0 and contributes zero ranking signal. This is a verified invariant in &lt;code&gt;NormalizationEngineTest.java&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The hawk/dove proof falls out cleanly: if Judge A (mean 8, σ 1) and Judge B (mean 4, σ 1) both score a standout project at +2σ above their own baseline (Judge A gives 10, Judge B gives 6), both produce z = +2.0. Their leniency difference becomes mathematically irrelevant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Catching pre-built projects without shelling out
&lt;/h2&gt;

&lt;p&gt;One integrity problem most platforms we studied don't address: a team builds most of the project before the event, pushes to a private repo, then makes one commit during it. The submission looks legitimate.&lt;/p&gt;

&lt;p&gt;The Submission Service runs an &lt;strong&gt;in-memory Eclipse JGit scan&lt;/strong&gt; on every submitted repository URL and records a risk score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 commits                           → risk 100  (EMPTY_REPOSITORY)
1 commit                            → risk 80   (SINGLE_COMMIT_DUMP)
&amp;gt;90% commits before event start     → HIGH      (PRE_HACKATHON_CODE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two decisions worth explaining:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pure Java, no shell commands.&lt;/strong&gt; Running &lt;code&gt;git clone&lt;/code&gt; with a user-supplied URL is a command-injection surface. Eclipse JGit in a Java memory buffer removes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A signal, not a verdict.&lt;/strong&gt; The scanner puts anomalies in an organizer review queue and never auto-disqualifies. A one-commit repo might be a legitimate solo build with poor git habits. Detection belongs to the machine; the decision belongs to a human.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not a monolith? Why RabbitMQ? Why Redis?
&lt;/h2&gt;

&lt;p&gt;Fair questions for a 72-hour hackathon, where the simpler choice is usually the right one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On microservices vs. monolith:&lt;/strong&gt; the decision wasn't about scale. At fixture size — 40 projects, 30 judges — a well-structured monolith would have been fine. We split into services because the security boundaries are real and need to be structural, not just logical. The Submission Service must not be able to touch the &lt;code&gt;judging&lt;/code&gt; schema. The Judging Service must not be able to write to &lt;code&gt;submissions&lt;/code&gt;. When those concerns share a codebase and a connection pool, you're trusting developer discipline to maintain the boundary. When they're separate services with separate Flyway-managed schemas and no shared connection, the boundary is physical. You can't accidentally cross it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On RabbitMQ:&lt;/strong&gt; score submission is a write path that needs to stay fast, especially in the final hour of an event when judges are racing the deadline. If submitting a score also triggered synchronous re-normalization across all scores for the event, that write would slow down as the event grew. Instead, the score commits to PostgreSQL and publishes a &lt;code&gt;score.submitted&lt;/code&gt; event to the &lt;code&gt;dogfood.events&lt;/code&gt; topic exchange in under 50ms. Normalization recompute, audit log write, webhook dispatch, and notification all happen asynchronously on their own queues — &lt;code&gt;audit.queue&lt;/code&gt;, &lt;code&gt;certificate.queue&lt;/code&gt;, &lt;code&gt;webhook.queue&lt;/code&gt;, &lt;code&gt;notification.queue&lt;/code&gt;. The judge gets a fast response regardless of what follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Redis:&lt;/strong&gt; two separate jobs. The Voting Service rate-limiter uses a Redis token-bucket per IP/account — Redis's atomic increment operations make this correct under concurrency in a way a database counter isn't, without the lock overhead. The submission deadline lock uses &lt;code&gt;SETNX&lt;/code&gt; because it needs to be atomic and shared across any instance of the Submission Service. Both are cases where Redis does something PostgreSQL can technically do, but with better latency and simpler concurrency guarantees for their specific access patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the architecture diagram doesn't show
&lt;/h2&gt;

&lt;p&gt;Our README has an ASCII diagram of all 9 services. What it doesn't show is that the diagram is the &lt;em&gt;output&lt;/em&gt; of one question asked repeatedly: where does this rule live, and can someone get around that layer from outside?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deadline enforcement → Redis lock on the server, not a client timestamp&lt;/li&gt;
&lt;li&gt;Score isolation → PostgreSQL RLS at the kernel, not a route guard&lt;/li&gt;
&lt;li&gt;Score fairness → Bayesian normalization in the judging engine, not a post-event spreadsheet&lt;/li&gt;
&lt;li&gt;Commit integrity → in-memory JGit at submission, not an organizer eyeballing GitHub profiles&lt;/li&gt;
&lt;li&gt;Webhook trust → HMAC-SHA256 signed payloads, not plain HTTP callbacks&lt;/li&gt;
&lt;li&gt;Concurrency safety → RabbitMQ decoupling, not synchronous blocking writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The microservices weren't the goal. They fell out of keeping each rule in the layer that owns it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The moment the architecture pushed back
&lt;/h2&gt;

&lt;p&gt;One thing no diagram captures: &lt;code&gt;docker compose up&lt;/code&gt; ran out of resources on a local machine.&lt;/p&gt;

&lt;p&gt;By the time all containers were up, the stack was consuming &lt;strong&gt;3.13GB of RAM&lt;/strong&gt; on a 7.4GB host and peaking at &lt;strong&gt;1376% CPU&lt;/strong&gt; across 12 cores — all before a single HTTP request had landed. Nine Spring Boot services each with their own JVM heap, plus PostgreSQL, RabbitMQ, Redis, and MinIO, adds up fast.&lt;/p&gt;

&lt;p&gt;We moved to a cloud shell instance, which was only possible because my teammate had a student subscription. Without it, we would have been cutting services under time pressure.&lt;/p&gt;

&lt;p&gt;That's an honest cost of the microservices decision, and we'd rather say it than hide it. The fix — capping each service's JVM heap with &lt;code&gt;-Xmx&lt;/code&gt; in the Compose file — is straightforward. Most of these services don't need 512MB each at hackathon scale. But we ran out of time before we could tune it, and the spec asks for something that runs on a laptop. Ours runs, but comfortably only on a machine with enough headroom, and that's worth saying plainly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we claimed and what we proved
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.dogfood.toml&lt;/code&gt; claims T1 and T2. The acceptance suite verifies exactly that — 7 of 7 checks passing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tap"&gt;&lt;code&gt;T1  gallery is public ................. PASS
T1  project from fixtures shown ....... PASS
T1  closed event refuses submissions .. PASS
T2  judge sees own scores ............. PASS
T2  judge cannot see peer scores ...... PASS
T2  participant blocked ............... PASS
T2  csv export works .................. PASS

claimed T1 T2, verified T1 T2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quadratic voting, Ed25519-signed certificates, the OpenAPI spec, and bulk export are implemented, but the acceptance suite doesn't cover them, so we don't claim them in &lt;code&gt;.dogfood.toml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A judging platform that is vague about what it can prove is hard to trust. We tried to be exact about our own limits.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/codewisp-ai/DogFood" rel="noopener noreferrer"&gt;github.com/codewisp-ai/DogFood&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Java 21 · Spring Boot 3.3 · PostgreSQL 16 · RabbitMQ 3.13 · Redis 7 · MinIO · React&lt;br&gt;
&lt;strong&gt;License:&lt;/strong&gt; MIT · &lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built for DOGFOOD 2026 by Hackathon Raptors. #Dogfood2026&lt;/em&gt;&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>opensource</category>
      <category>java</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>My "reproducible" build kept returning the same hash after the code changed. That was the bug.</title>
      <dc:creator>Anmol Maheshwari</dc:creator>
      <pubDate>Sat, 05 Sep 2026 15:01:12 +0000</pubDate>
      <link>https://dev.to/maheshwarianmol/my-reproducible-build-kept-returning-the-same-hash-after-the-code-changed-that-was-the-bug-4npj</link>
      <guid>https://dev.to/maheshwarianmol/my-reproducible-build-kept-returning-the-same-hash-after-the-code-changed-that-was-the-bug-4npj</guid>
      <description>&lt;p&gt;TL;DR: I built ZeroTrust — a supply-chain security scanner, zero third-party dependencies, go.mod with no require block — for the Zero Dependency Hackathon (Track E). It catches phantom packages, typosquats, lifecycle-hook backdoors, obfuscated payloads, and eval()-style dynamic execution. The scanner passed every test I wrote for it. Three times, my own verification process tried to hand me a result that looked correct and wasn't. This is what a tool built to catch deception looks like when it nearly deceives its own author.&lt;/p&gt;

&lt;p&gt;The dependency that couldn't be the point&lt;/p&gt;

&lt;p&gt;You already know why this hackathon exists if you've read the brief: chalk and debug, 2.6 billion combined weekly downloads, one phished maintainer account, a crypto-clipper hiding in memory. Shai-Hulud republishing itself into hundreds of packages days later. xz, two years of patient trust-building, one code review away from a backdoor on every Linux server on earth. And now the newer, quieter version of the same problem: AI coding assistants hallucinate package names 19.7% of the time, and attackers have started pre-registering the names the models reliably invent — a supply-chain attack where the victim never even makes a typo, because the machine makes it for them.&lt;/p&gt;

&lt;p&gt;So I built the tool that's supposed to catch exactly that — and I built it with an empty manifest, because a security scanner that asks you to trust more strangers to protect you from strangers has already lost its own argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;go.mod
&lt;span class="go"&gt;module zerotrust
go 1.22

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go list &lt;span class="nt"&gt;-m&lt;/span&gt; all
&lt;span class="go"&gt;zerotrust
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire dependency tree. Everything else — a Bloom filter, a prefix Trie, a TOML tokenizer, a rate limiter, ANSI terminal styling, a SARIF 2.1.0 emitter — is hand-rolled against Go's standard library. Eleven substitutions, each with its own entry in STDLIB.md, because a claim like "zero dependencies" is worthless if it isn't also checkable in under five seconds.&lt;/p&gt;

&lt;p&gt;That instinct — don't just claim something, make it checkable — is what turned this into a much more interesting three days than "write five detectors and call it done."&lt;/p&gt;

&lt;p&gt;Five detectors, one honest question underneath all of them&lt;/p&gt;

&lt;p&gt;ZeroTrust runs five checks, offline by default: phantom-package detection (Bloom filter + Trie against a curated, provenance-documented corpus), typosquat detection (hand-rolled Levenshtein distance), lifecycle-hook scanning (the exact mechanism Shai-Hulud used), Shannon-entropy scanning for obfuscated payloads, and lexical detection of dynamic-execution calls (eval, child_process.exec, os.system).&lt;/p&gt;

&lt;p&gt;Every one of those detectors exists to answer one question: is this thing actually what it claims to be?&lt;/p&gt;

&lt;p&gt;Which made it uncomfortable, in a useful way, when my own build pipeline started asking me the same question back.&lt;/p&gt;

&lt;p&gt;Bug 1: the reproducible build that lied by staying the same&lt;/p&gt;

&lt;p&gt;make verify-reproducible builds the binary twice and diffs the hashes. Simple, and it passed — the same hash, every single time I ran it, across multiple sessions:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;A6DB0B58130997745CFF9798B0FDCF6BBD28DE03910F1F0E34BDB0AB6472E985&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That should have felt like success. It felt wrong instead, for a reason that took me a while to name: real code had changed in between those checks. JSON output landed. SARIF emission landed. Severity-aware exit codes landed. A reproducible build is supposed to mean identical source → identical bytes — it is emphatically not supposed to mean the hash never moves no matter what I do to the source. One of those is determinism. The other is a build that isn't actually rebuilding what I think it's rebuilding, or is embedding something that has nothing to do with my code.&lt;/p&gt;

&lt;p&gt;It was the second one. Since Go 1.18, go build stamps the current git commit hash and a timestamp into the binary by default — vcs.revision, vcs.time — unless you explicitly pass -buildvcs=false. My "stable" hash wasn't proof of a clean build. It was a coincidence of which git commit happened to be checked out at each of my testing sessions.&lt;/p&gt;

&lt;p&gt;The proof that this was really it: I made a doc-only commit — zero code changes, just Markdown — and rebuilt.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;14D9E8B23FF9786D54F7F82764CCC8B587F9E59E97743026DFBD74226FC6714D&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Different hash. From a commit that changed nothing but a README. If a Markdown edit can move your "reproducible" hash, the hash was never measuring what the bonus claim says it measures.&lt;/p&gt;

&lt;p&gt;Fixed with one flag, everywhere the binary gets built:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="err"&gt;makefile&lt;/span&gt;
&lt;span class="err"&gt;go&lt;/span&gt; &lt;span class="err"&gt;build&lt;/span&gt; &lt;span class="err"&gt;-trimpath&lt;/span&gt; &lt;span class="nv"&gt;-buildvcs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-buildid= -s -w"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; ./bin/zerotrust .

&lt;span class="nl"&gt;And a final, actually-trustworthy pair&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

&lt;span class="nl"&gt;Build 1 SHA256 (windows)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;D112B856931D9473E5DF7EC6175E4412FA8D0CE8F76FAF5AE7E6C0438ACB1E9E&lt;/span&gt;
&lt;span class="nl"&gt;Build 2 SHA256 (windows)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;D112B856931D9473E5DF7EC6175E4412FA8D0CE8F76FAF5AE7E6C0438ACB1E9E&lt;/span&gt;
&lt;span class="nl"&gt;REPRODUCIBLE BUILD VERIFICATION&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;PASS&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still identical. But now I'd earned the right to say so, because I'd also watched it correctly fail to be identical when it should have — the doc-only-commit test is what turned "the hash matches" from a hope into an actual falsifiable check. A reproducible-build claim you haven't watched break is a build you haven't actually verified — you've just watched a coincidence hold twice.&lt;/p&gt;

&lt;p&gt;Bug 2: the safety claim I couldn't check on my own machine&lt;/p&gt;

&lt;p&gt;go test -race ./... is how I back up "no data races in the concurrent worker pool." On my own Windows dev machine, it didn't run at all:&lt;/p&gt;

&lt;p&gt;cc1.exe: sorry, unimplemented: 64-bit mode not compiled in&lt;/p&gt;

&lt;p&gt;The race detector needs CGO, CGO needs a working 64-bit C compiler, and my local MinGW toolchain was 32-bit-only. For a real stretch of this build, "the race detector passes" was a claim I genuinely could not verify on the machine I was developing on. Not a cosmetic annoyance — a security tool's own concurrency-safety claim, unverifiable, on the platform I was actually shipping from.&lt;/p&gt;

&lt;p&gt;I didn't fix the toolchain. I changed which environment got to answer the question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:/app &lt;span class="nt"&gt;-w&lt;/span&gt; /app golang:1.22 go &lt;span class="nb"&gt;test&lt;/span&gt; ./... &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-race&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean pass, real Linux, real ELF binary, real 64-bit cc. Then a third, fully independent confirmation once the repo was pushed — GitHub Actions' own ubuntu-latest runners, green on every push since:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ok      zerotrust/tests 5.756s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same source, three separate environments, one of which genuinely couldn't tell me the answer at all. The lesson isn't "Windows is bad" — it's that a claim your own dev machine can't check is a claim you don't actually have yet, no matter how confident the surrounding code looks.&lt;/p&gt;

&lt;p&gt;Bug 3: the validator output that was lying about what it validated&lt;/p&gt;

&lt;p&gt;I claimed a real SARIF 2.1.0 subset, so I ran Microsoft's own sarif-multitool validate against it — the point being that "the JSON parses" and "a schema validator accepts this as real SARIF" are different claims, and only one of them is worth putting in a README.&lt;/p&gt;

&lt;p&gt;The first attempt produced output I couldn't actually trust, and the reason was almost funny: the pasted validator output had ZeroTrust's own startup banner mixed into it — a stray line from the tool being validated, sitting inside what was supposed to be the validator's independent verdict. Contaminated evidence. I genuinely couldn't tell from that run whether sarif-multitool had validated anything or whether I was reading noise.&lt;/p&gt;

&lt;p&gt;I caught it because the contaminating line was visibly out of place — it didn't belong to the tool that was supposed to be running. That's the same instinct the entropy scanner runs on every payload it looks at: something in this stream doesn't match what it claims to be.&lt;/p&gt;

&lt;p&gt;Redo, deliberately isolated — generate the file to disk first, run the validator as a fully separate step, nothing interleaved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash
./bin/zerotrust &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; sarif &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; results.sarif
sarif-multitool validate results.sarif
&lt;span class="c"&gt;# exit code: 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Trustworthy, this time, because I'd removed the thing that made the first result unreadable rather than just re-running it and hoping for a cleaner-looking paste.&lt;/p&gt;

&lt;p&gt;What I'm not going to pretend happened&lt;/p&gt;

&lt;p&gt;Three real bugs is a true story. Five would be padding, and this hackathon specifically rewards naming your limits rather than hiding them — so here's what didn't have drama, stated plainly instead of dressed up:&lt;/p&gt;

&lt;p&gt;The entropy threshold (7.2 bits/byte) was correct on every run I checked. No tuning story, no wrong version I quietly fixed. If there's an origin story for that exact number, I don't have one to tell you.&lt;br&gt;
The tokenizer's blind spot (globalThis&lt;a href=""&gt;'ev'+'al'&lt;/a&gt; defeating lexical pattern matching) is a real, honestly-documented limitation — but I reasoned my way to it, I didn't watch it fail in front of me. Different kind of claim, and I'd rather say so than imply I caught it live.&lt;br&gt;
The Bloom filter's false-positive rate measured 0.0449 against a &amp;lt;0.05 target on the very first check and every check after. No wrong m/k calculation I had to walk back.&lt;br&gt;
Skipping the Single File bonus was a clean, upfront decision — the project's actual shape (four manifest parsers, two hand-rolled data structures, five detectors, a rate limiter, three output renderers) was never going to collapse into one file without sacrificing the test isolation and idiomatic package boundaries a senior Go reviewer would expect. I didn't start down that path and bail. I never started down it.&lt;/p&gt;

&lt;p&gt;A write-up where every single corner has a dragon in it stops being credible around the third dragon. Some corners were just fine. Saying so is worth more than inventing tension that wasn't there.&lt;/p&gt;

&lt;p&gt;By the numbers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; ./... &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-race&lt;/span&gt;
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;--- PASS: TestBloomFilterFalsePositiveBound (0.03s)
    bloom_test.go:52: Actual false positive rate: 0.0449 (target: 0.0500)
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;PASS
ok      zerotrust/tests 5.756s
0 third-party runtime dependencies (go list -m all prints one line: zerotrust)
2,489 lines across 28 Go files

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;15/15 tests passing, race-detector clean, verified independently across Windows-hosted Docker Linux and GitHub Actions' ubuntu-latest&lt;br&gt;
11 documented stdlib-for-package substitutions in STDLIB.md, each on the runtime path — none decorative&lt;br&gt;
6 real attack-pattern findings caught in under a second against a seeded malicious fixture — phantom packages, typosquats, and a curl | sh postinstall hook&lt;br&gt;
1 reproducible-build hash that was wrong for reasons that had nothing to do with the algorithm I thought I was testing&lt;br&gt;
The thing worth taking from this&lt;/p&gt;

&lt;p&gt;A scanner built to catch things pretending to be something they aren't spent its own build process doing exactly that to me, three separate times: a hash that looked stable because it wasn't actually being regenerated against new source, a safety guarantee my own machine was incapable of confirming, and a validation result contaminated by output that didn't belong to the tool producing it.&lt;/p&gt;

&lt;p&gt;None of those were subtle attacks. They were the ordinary, boring failure mode of trusting the first result that looks clean. The entire pitch of this hackathon is that removing dependencies doesn't remove the need for engineering judgment — it just means every piece of that judgment is visibly yours, including the parts where you almost believed something that wasn't true.&lt;/p&gt;

&lt;p&gt;The tool's job is to stop trusting things by default. Turns out that had to start with not trusting my own green checkmarks either.&lt;br&gt;
&lt;a href="https://github.com/codewisp-ai/ZeroTrust.git" rel="noopener noreferrer"&gt;GITHUB REPO&lt;/a&gt;&lt;br&gt;
Built solo for the Zero Dependency Hackathon 2026, Track E (Security &amp;amp; Crypto Utilities). Repo, STDLIB.md, and deps-proof.txt are all public — the reproducible-build command is in the README if you want to try breaking it the way I did.&lt;/p&gt;

&lt;h1&gt;
  
  
  ZeroDependencyHack #Go #Security #SupplyChain #Testing #BuildInPublic
&lt;/h1&gt;

</description>
      <category>go</category>
      <category>security</category>
      <category>showdev</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
