c5a1b34ebc
PostHog JS snippet injected into _template.html and _index_template.html, pointed at eu.i.posthog.com. All 11 posts + index regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
223 lines
16 KiB
HTML
223 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>A Pre-Commit Agent That Guards Your Secrets for $0.001 — Tinqs Blog</title>
|
|
<meta name="description" content="We built a pre-commit hook that calls DeepSeek V4 Flash to review every commit. It catches leaked secrets, classified terms, broken URLs, and drafts announcements --- for a tenth of a cent per commit.">
|
|
<meta name="robots" content="index, follow">
|
|
<link rel="canonical" href="https://www.tinqs.com/blog/pre-commit-agent">
|
|
|
|
<meta property="og:type" content="article">
|
|
<meta property="og:url" content="https://www.tinqs.com/blog/pre-commit-agent">
|
|
<meta property="og:title" content="A Pre-Commit Agent That Guards Your Secrets for $0.001">
|
|
<meta property="og:description" content="A DeepSeek-powered pre-commit hook that catches leaks for $0.001/commit.">
|
|
<meta property="og:image" content="https://www.tinqs.com/img/og-cover.jpg">
|
|
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="A Pre-Commit Agent That Guards Your Secrets for $0.001">
|
|
<meta name="twitter:description" content="A DeepSeek-powered pre-commit hook that catches leaks for $0.001/commit.">
|
|
<meta name="twitter:image" content="https://www.tinqs.com/img/og-cover.jpg">
|
|
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "BlogPosting",
|
|
"headline": "A Pre-Commit Agent That Guards Your Secrets for $0.001",
|
|
"datePublished": "2026-05-25",
|
|
"author": {
|
|
"@type": "Person",
|
|
"name": "Ozan Bozkurt"
|
|
},
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "Tinqs Limited",
|
|
"url": "https://www.tinqs.com"
|
|
},
|
|
"description": "We built a pre-commit hook that calls DeepSeek V4 Flash to review every commit. It catches leaked secrets, classified terms, broken URLs, and drafts announcements --- for a tenth of a cent per commit."
|
|
}
|
|
</script>
|
|
|
|
<!-- PostHog (EU) -->
|
|
<script>
|
|
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
|
posthog.init('phc_teG6p5oxf6poQHPThq5AGKzWQNhw4bHW9arLwWAVXm3f',{api_host:'https://eu.i.posthog.com',ui_host:'https://eu.posthog.com',person_profiles:'identified_only',defaults:'2026-01-30'})
|
|
</script>
|
|
|
|
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="../style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- NAV -->
|
|
<nav class="nav nav--scrolled" id="nav">
|
|
<a href="/" class="nav__logo" aria-label="Tinqs home">
|
|
<span class="nav__wordmark">TINQS</span>
|
|
</a>
|
|
<div class="nav__links">
|
|
<a href="/#game" class="nav__link">Games</a>
|
|
<a href="/#tech" class="nav__link">Technology</a>
|
|
<a href="/#about" class="nav__link">About</a>
|
|
<a href="/blog/" class="nav__link" style="color: var(--c-accent-l);">Blog</a>
|
|
<a href="/#signup" class="nav__link">Contact</a>
|
|
<a href="/press" class="nav__link">Press</a>
|
|
</div>
|
|
<button class="nav__burger" aria-label="Open menu" id="navBurger">
|
|
<span></span><span></span><span></span>
|
|
</button>
|
|
</nav>
|
|
|
|
<!-- MOBILE MENU -->
|
|
<div class="mobile-menu" id="mobileMenu">
|
|
<a href="/#game" class="mobile-menu__link">Games</a>
|
|
<a href="/#tech" class="mobile-menu__link">Technology</a>
|
|
<a href="/#about" class="mobile-menu__link">About</a>
|
|
<a href="/blog/" class="mobile-menu__link">Blog</a>
|
|
<a href="/#signup" class="mobile-menu__link">Contact</a>
|
|
<a href="/press" class="mobile-menu__link">Press</a>
|
|
</div>
|
|
|
|
<!-- POST -->
|
|
<article class="post">
|
|
<a href="/blog/" class="post__back">← All Posts</a>
|
|
<span class="post__date">25 May 2026</span>
|
|
<h1 class="post__title">A Pre-Commit Agent That Guards Your Secrets for $0.001</h1>
|
|
<p class="post__lead">We have a problem that every small team has: too many things to remember before hitting commit. Don't leak API keys. Don't reference the classified AI codename in public blog posts. Don't link to GitHub repos we deleted six months ago. Don't push a blog post with a 90-character title. We built a pre-commit hook that uses a cheap LLM to check all of this automatically — and it costs less than a tenth of a cent per commit.</p>
|
|
|
|
<div class="post__body">
|
|
<h2>The Problem</h2>
|
|
<p>We maintain a docs repo that serves double duty. Internal files — game design documents, architecture notes, agent configuration — sit alongside a public blog and website. The internal side references classified codenames, machine hostnames, and internal URLs. The public side must never contain any of that.</p>
|
|
<p>We also deleted all our GitHub repos in April 2026 and moved everything to our own Gitea platform. But old links keep creeping back in — someone copies a URL from an old document, a blog post references the wrong remote. These are invisible bugs. The blog looks fine, the build passes, and three weeks later someone notices a dead link.</p>
|
|
<p>A checklist in the README doesn't work. Humans skip checklists. Code review catches some issues but not all — reviewers focus on logic, not whether a URL points to a deleted GitHub org. We needed something automatic, fast, and cheap enough to run on every single commit.</p>
|
|
<h2>Two Layers: Regex + Agent</h2>
|
|
<p>The hook has two layers. The first is instant and free. The second is smart and nearly free.</p>
|
|
<h3>Layer 1: Local Blocklist (0ms, $0.00)</h3>
|
|
<p>A text file of regex patterns, each tagged with a scope and a message:</p>
|
|
<pre><code>public|\bCosmos\b|Classified codename — use "advanced colonist AI"
|
|
all|github\.com/(tinqs-ltd|tinqs)/|GitHub repos deleted — use tinqs.com
|
|
all|sk-[a-zA-Z0-9]{20,}|Possible API key leaked
|
|
all|AKIA[A-Z0-9]{16}|AWS access key leaked
|
|
public|admin\.arikigame\.com|Internal admin URL in public content</code></pre>
|
|
<p>The scope field controls where the pattern is enforced. <code>all</code> means every file. <code>public</code> means only files under <code>website/</code> — our public-facing content. This is critical. We <em>want</em> classified codenames in our internal architecture docs. We just don't want them in blog posts.</p>
|
|
<p>The blocklist runs grep against the staged diff. No network call, no API, no latency. If it finds a match, the commit is blocked immediately with a file path and explanation. This catches 80% of issues before the LLM ever wakes up.</p>
|
|
<h3>Layer 2: DeepSeek V4 Flash Review (~4s, $0.001)</h3>
|
|
<p>If the commit touches public-facing files (<code>website/</code>, blog posts), the hook sends the staged diff to DeepSeek V4 Flash through our inference proxy. The system prompt tells the model exactly what to check:</p>
|
|
<ul>
|
|
<li><strong>Leaked secrets</strong> — API keys, tokens, credentials that the regex might have missed</li>
|
|
<li><strong>Classified terms</strong> — codenames that aren't in the blocklist yet</li>
|
|
<li><strong>Internal URLs</strong> — references to internal services that shouldn't be public</li>
|
|
<li><strong>Blog quality</strong> — title length, meta description, slug consistency, missing fields</li>
|
|
<li><strong>Broken links</strong> — malformed URLs, obvious typos</li>
|
|
<li><strong>Announcements</strong> — if it's a new blog post, draft a one-line summary</li>
|
|
</ul>
|
|
<p>The model responds with structured JSON: errors (block the commit) or warnings (inform but allow). If the API is unreachable or times out, the commit proceeds — the hook never blocks work for infrastructure reasons.</p>
|
|
<h2>Why Not Pi?</h2>
|
|
<p>Our <a href="https://tinqs.com/tinqs/pi" style="color: var(–c-accent-l);">Pi fork</a> is a full coding agent with tool calling, file I/O, and context management. It's what we use for overnight autonomous coding. But for pre-commit review, it's overkill.</p>
|
|
<p>A pre-commit hook needs to finish in under 5 seconds. Pi takes 2–3 seconds just to start the Node.js process and load extensions. The review itself is a single LLM call with a system prompt and a diff — no tools needed, no file reads, no iteration. A direct curl to DeepSeek is faster and simpler.</p>
|
|
<p>That said, the hook is designed as a stepping stone. The blocklist patterns, the review prompt, and the classification logic are all reusable. When we build a Pi-based CI reviewer that runs on pull requests — with tool access to read the full file, check links live, and verify image URLs — it will use the same prompt and the same patterns. The pre-commit hook is the fast, cheap first pass. Pi is the thorough second pass.</p>
|
|
<h2>The Architecture</h2>
|
|
<pre><code>git commit
|
|
↓
|
|
.githooks/pre-commit (bash)
|
|
↓
|
|
Phase 0: Collect staged diff + classify files
|
|
↓
|
|
Phase 1: Regex blocklist scan (instant, free)
|
|
→ Match found → BLOCK (exit 1)
|
|
→ Clean → continue
|
|
↓
|
|
Phase 2: Public files changed?
|
|
→ No → exit 0 (skip AI, no cost)
|
|
→ Yes → send diff to DeepSeek V4 Flash
|
|
↓
|
|
Phase 3: Parse JSON response
|
|
→ Errors → BLOCK (exit 1)
|
|
→ Warnings → print, exit 0
|
|
→ Announcement → print draft
|
|
→ API failure → warn, exit 0</code></pre>
|
|
<p>The hook lives in <code>.githooks/</code> inside the repo — committed, version-controlled, shared by the whole team. A setup script configures <code>git config core.hooksPath</code> to point there. The LFS pre-push hook sits in the same directory.</p>
|
|
<h2>What It Costs</h2>
|
|
<p>The system prompt is ~500 tokens. An average diff is 2,000–4,000 tokens. The response is ~200 tokens. At DeepSeek V4 Flash rates:</p>
|
|
<p>| | Tokens | Cost |</p>
|
|
<p>|–|——–|——|</p>
|
|
<p>| Input (prompt + diff) | ~4,000 | $0.00056 |</p>
|
|
<p>| Output (JSON response) | ~200 | $0.00006 |</p>
|
|
<p>| <strong>Per commit</strong> | | <strong>$0.00062</strong> |</p>
|
|
<p>Call it a tenth of a cent. Twenty commits a day across the team: <strong>$0.012/day</strong>. About <strong>$0.40/month</strong>.</p>
|
|
<p>Commits that only touch internal files (architecture docs, agent config, game design) skip the AI review entirely. Zero cost. The hook only calls DeepSeek when public-facing content changes.</p>
|
|
<h2>What It Catches</h2>
|
|
<p>In the first week:</p>
|
|
<ul>
|
|
<li><strong>2 classified codename leaks</strong> in draft blog posts — caught by the blocklist before the AI even ran</li>
|
|
<li><strong>1 GitHub URL</strong> that crept back in from a copy-paste — caught by the blocklist</li>
|
|
<li><strong>3 blog SEO warnings</strong> — titles over 60 characters, missing og_description — caught by the AI review</li>
|
|
<li><strong>1 announcement draft</strong> generated automatically when a new blog post was committed</li>
|
|
</ul>
|
|
<p>Zero false positives on the blocklist (the patterns are specific enough). Two false positives from the AI (flagged an internal URL in a code example that was clearly illustrative, not a real link). We added a note to the prompt to ignore URLs inside fenced code blocks.</p>
|
|
<h2>Setup</h2>
|
|
<p>One command per machine:</p>
|
|
<pre><code class="language-bash">bash scripts/setup-hooks.sh</code></pre>
|
|
<p>Or on Windows:</p>
|
|
<pre><code class="language-powershell">.\scripts\setup-hooks.ps1</code></pre>
|
|
<p>Set your inference token:</p>
|
|
<pre><code class="language-bash">export TINQS_HOOK_TOKEN=<your-gitea-pat></code></pre>
|
|
<p>That's it. Every <code>git commit</code> now runs the two-layer review. Bypass with <code>git commit –no-verify</code> when you need to (emergencies, known false positives).</p>
|
|
<h2>The Pattern: Guard Rails at the Edge</h2>
|
|
<p>This is the same pattern we apply everywhere: put the guard rail where the action happens. Don't rely on a human checklist. Don't wait for code review. Don't hope someone remembers.</p>
|
|
<p>The pre-commit hook is $0.001 worth of prevention. A leaked API key in a public blog post is hours of rotation, revocation, and audit. A classified codename in a public post is a confidentiality breach. A dead GitHub link is a broken user experience that nobody notices for weeks.</p>
|
|
<p>The tools exist. DeepSeek V4 Flash is cheap enough to call on every commit. The hook is 150 lines of bash. The blocklist is a text file. The total infrastructure cost is zero — it runs on the developer's machine, calls an API we already pay for, and adds 4 seconds to the commit flow.</p>
|
|
<p>The age of agents doesn't just mean agents that write code. It means agents that watch the code you write.</p>
|
|
<hr>
|
|
<p><em>The pre-commit hook is part of <a href="https://tinqs.com" style="color: var(–c-accent-l);">Tinqs Studio</a>, our open platform for game development. The inference proxy, the blocklist patterns, and the review prompt are all open and reusable. We're building <a href="https://arikigame.com" style="color: var(–c-accent-l);">Ariki</a> with these tools — every commit in the game repo runs through the same guard.</em></p>
|
|
|
|
</div>
|
|
|
|
<div class="post__author">
|
|
<div class="post__author-avatar">OB</div>
|
|
<div class="post__author-info">
|
|
<span class="post__author-name">Ozan Bozkurt</span><br>
|
|
CTO & Developer, Tinqs
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
<!-- FOOTER -->
|
|
<footer class="footer">
|
|
<div class="footer__inner">
|
|
<span class="footer__wordmark">TINQS</span>
|
|
<div class="footer__links">
|
|
<a href="/#game">Games</a>
|
|
<a href="/#tech">Technology</a>
|
|
<a href="/#about">About</a>
|
|
<a href="/blog/">Blog</a>
|
|
<a href="mailto:hello@tinqs.com">hello@tinqs.com</a>
|
|
<a href="/press">Press Kit</a>
|
|
</div>
|
|
<p class="footer__copy">Tinqs Limited — London, est. 2020</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
const burger = document.getElementById('navBurger');
|
|
const mobileMenu = document.getElementById('mobileMenu');
|
|
burger.addEventListener('click', () => {
|
|
const open = mobileMenu.classList.toggle('mobile-menu--open');
|
|
burger.classList.toggle('nav__burger--open', open);
|
|
document.body.style.overflow = open ? 'hidden' : '';
|
|
});
|
|
mobileMenu.querySelectorAll('a').forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
mobileMenu.classList.remove('mobile-menu--open');
|
|
burger.classList.remove('nav__burger--open');
|
|
document.body.style.overflow = '';
|
|
});
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|