{
    "version": "https://jsonfeed.org/version/1",
    "title": "Tunahan Ali Öztürk Blog",
    "home_page_url": "https://tunahanaliozturk.dev/blog",
    "description": "Tunahan Ali Öztürk Blog",
    "items": [
        {
            "id": "https://tunahanaliozturk.dev/blog/the-orion-family",
            "content_html": "<p>Most .NET backends end up needing the same dozen things: validation, a distributed lock, an audit trail,\nan outbox, encrypted columns, typed IDs, idempotent endpoints, rate limits, retries. Each team builds them\nagain, a little differently, and each version is wrong in its own small way.</p>\n<p>The Orion family is my answer to that: small, focused libraries for those problems, built to one quality\nbar and published on NuGet.</p>\n<p>Today it is 24 libraries in 89 packages, downloaded more than 350,000 times. This post is about why they\nexist, what holds them together, and what each one is for.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"primitives-not-a-framework\">Primitives, not a framework<a href=\"https://tunahanaliozturk.dev/blog/the-orion-family#primitives-not-a-framework\" class=\"hash-link\" aria-label=\"Direct link to Primitives, not a framework\" title=\"Direct link to Primitives, not a framework\" translate=\"no\">​</a></h2>\n<p>Every Orion library solves one problem and stands on its own. You add OrionLock because you need a lock,\nnot because you signed up for a platform. There is no deep dependency web between them; pick only what\nyou need.</p>\n<p>That also means saying no. OrionPatch is a transactional outbox, and its documentation says plainly that\nsagas are out of scope: if you need a process manager, reach for MassTransit or Wolverine instead. A\nlibrary that knows where it stops is easier to trust than one that wants to be everything.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-holds-the-family-together\">What holds the family together<a href=\"https://tunahanaliozturk.dev/blog/the-orion-family#what-holds-the-family-together\" class=\"hash-link\" aria-label=\"Direct link to What holds the family together\" title=\"Direct link to What holds the family together\" translate=\"no\">​</a></h2>\n<p>Building the libraries one after another, I noticed five things being implemented again and again, and\ndrifting apart each time:</p>\n<ul>\n<li class=\"\">calling observers safely, so an observability outage can never break the code that does the real work;</li>\n<li class=\"\">OpenTelemetry naming, so every library's traces and metrics look alike on a dashboard;</li>\n<li class=\"\">a clock that tests can control;</li>\n<li class=\"\">the shape of options and DI registration;</li>\n<li class=\"\">the vocabulary for errors.</li>\n</ul>\n<p>They now live once, in <code>Orion.Abstractions</code>. Its surface is frozen at 1.0: every contract stays source- and\nbinary-compatible across the whole 1.x line, so one library can't break under a sibling's upgrade. The\nrules that go with it are written down and apply to every package in the family.</p>\n<p>Two libraries grew out of that spine. <strong>OrionClock</strong> is a <code>TimeProvider</code>, so it drops into any .NET API that\ntakes one, and it lets a test move time forward instead of waiting. <strong>OrionResult</strong> gives expected failures a\nshape: a zero-allocation <code>Result&lt;T&gt;</code>, an <code>Option&lt;T&gt;</code>, and a structured <code>Error</code> with a code, a kind and field\nerrors.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-is-in-the-family\">What is in the family<a href=\"https://tunahanaliozturk.dev/blog/the-orion-family#what-is-in-the-family\" class=\"hash-link\" aria-label=\"Direct link to What is in the family\" title=\"Direct link to What is in the family\" translate=\"no\">​</a></h2>\n<p><strong>Input and the domain</strong></p>\n<ul>\n<li class=\"\"><strong>OrionGuard</strong>: validation, guard clauses and DDD primitives, with integrations for ASP.NET Core,\nMediatR, Blazor, gRPC, SignalR and OpenTelemetry, source generators, and messages in 14 languages. The\nmost used of the family.</li>\n<li class=\"\"><strong>OrionKey</strong>: strongly-typed IDs from one attribute, with equality, EF Core and JSON converters generated\nfor you.</li>\n</ul>\n<p><strong>Data</strong></p>\n<ul>\n<li class=\"\"><strong>OrionAudit</strong>: an automatic EF Core audit trail with JSON Patch diffs, and time travel to rebuild an\nentity as it was at any moment.</li>\n<li class=\"\"><strong>OrionVault</strong>: column-level encryption at rest for EF Core: AES-256-GCM, key rotation, and a blind\nindex for searching encrypted values.</li>\n<li class=\"\"><strong>OrionPage</strong>: keyset pagination that stays fast on page 10,000, because <code>OFFSET</code> is a table scan.</li>\n</ul>\n<p><strong>Messaging and consistency</strong></p>\n<ul>\n<li class=\"\"><strong>OrionPatch</strong> and <strong>OrionInbox</strong>: the two halves of reliable messaging. Enqueue inside the same\n<code>SaveChanges</code> transaction and dispatch at least once; on the other side, apply each message's effect\nexactly once.</li>\n<li class=\"\"><strong>OrionSaga</strong>: in-process saga orchestration; when a step fails, the completed steps are compensated.</li>\n<li class=\"\"><strong>OrionRelay</strong>: outbound webhooks, signed with HMAC-SHA256, retried with backoff and jitter.</li>\n</ul>\n<p><strong>Coordination</strong></p>\n<ul>\n<li class=\"\"><strong>OrionLock</strong>: distributed locks with lease renewal and fencing tokens, over Redis, Postgres, SQL Server\nor EF Core.</li>\n<li class=\"\"><strong>OrionBeacon</strong>: leader election, so exactly one instance runs the job that must run once.</li>\n</ul>\n<p><strong>HTTP and APIs</strong></p>\n<ul>\n<li class=\"\"><strong>OrionOnce</strong>: idempotency keys, so a retried request gets the stored response instead of running twice.</li>\n<li class=\"\"><strong>OrionEnvelope</strong>: one HTTP contract for the whole API: typed <code>{ data, meta }</code> for success, RFC 9457\nproblem details for failure.</li>\n<li class=\"\"><strong>OrionRate</strong>: token-bucket and sliding-window rate limiting.</li>\n<li class=\"\"><strong>OrionGrant</strong> and <strong>OrionLedger</strong>: permissions and policies, and the full lifecycle of API keys.</li>\n<li class=\"\"><strong>OrionStream</strong>: Server-Sent Events with bounded buffers and heartbeats.</li>\n</ul>\n<p><strong>Cross-cutting</strong></p>\n<ul>\n<li class=\"\"><strong>OrionLens</strong> carries a correlation id through async calls and across HTTP; <strong>OrionShade</strong> masks\nsecrets and personal data before they reach a log; <strong>OrionCache</strong> does cache-aside without a stampede;\n<strong>OrionResilience</strong> retries with jitter over OrionClock, so a test of a retry takes no time.</li>\n</ul>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"seeing-them-together\">Seeing them together<a href=\"https://tunahanaliozturk.dev/blog/the-orion-family#seeing-them-together\" class=\"hash-link\" aria-label=\"Direct link to Seeing them together\" title=\"Direct link to Seeing them together\" translate=\"no\">​</a></h2>\n<p>Package pages show one library at a time. <strong>OrionShowcase</strong> shows how they work together: a\nproduction-shaped banking sample in which one money transfer passes through OrionGuard's validation,\nOrionLock's distributed locks, OrionAudit's change capture, OrionPatch's outbox, OrionKey's typed IDs and\nOrionVault's encrypted personal data. Assembling that story is much harder than any single package looks,\nand that is the point of the sample.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-share-them\">Why share them<a href=\"https://tunahanaliozturk.dev/blog/the-orion-family#why-share-them\" class=\"hash-link\" aria-label=\"Direct link to Why share them\" title=\"Direct link to Why share them\" translate=\"no\">​</a></h2>\n<p>I wrote these because I needed them, and I publish them so that nobody else has to write them again. Every\nlibrary is MIT-licensed, has its documentation on this site, and takes issues on GitHub. The ones that need no database or broker can be tried right here in the\n<a class=\"\" href=\"https://tunahanaliozturk.dev/playground\">playground</a>.</p>\n<p>If you use one of them, or tried one and it didn't fit, I would like to know why. That is the most useful\nthing anyone can tell me.</p>",
            "url": "https://tunahanaliozturk.dev/blog/the-orion-family",
            "title": "Why I built the Orion family",
            "summary": "Most .NET backends end up needing the same dozen things: validation, a distributed lock, an audit trail,",
            "date_modified": "2026-09-30T00:00:00.000Z",
            "author": {
                "name": "Tunahan Ali Öztürk",
                "url": "https://github.com/tunahanaliozturk"
            },
            "tags": [
                "dotnet",
                "orion",
                "open-source"
            ]
        },
        {
            "id": "https://tunahanaliozturk.dev/blog/why-i-built-derbent",
            "content_html": "<p>I use more than one coding agent on the same repositories. Claude Code works on one thing, Codex on\nanother, and now and then I ask Copilot CLI something. Each of them is good at its job, and none of them\nknows what the others did.</p>\n<p>What Claude Code learned an hour ago about why a table looks the way it does, Codex has no idea about.\nThere is no single record of which agent ran which command. Every CLI has its own list of MCP servers, so\nI connect GitHub four times. And if I want an agent to ask me before it runs <code>git push</code>, I have to set\nthat up in four places, four different ways.</p>\n<p>Derbent is the tool I wrote to pull that together. It is free and open source.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-it-does\">What it does<a href=\"https://tunahanaliozturk.dev/blog/why-i-built-derbent#what-it-does\" class=\"hash-link\" aria-label=\"Direct link to What it does\" title=\"Direct link to What it does\" translate=\"no\">​</a></h2>\n<p>Derbent is a gate between coding agents and their tools. Claude Code, Codex, GitHub Copilot CLI and\nAntigravity CLI connect to it as one ordinary MCP server, and your own MCP servers sit behind it. Every\ntool call passes through the gate, and four things happen there.</p>\n<p><strong>Shared memory.</strong> The agents get <code>memory_write</code>, <code>memory_search</code> and <code>memory_read</code>. A note one agent\nwrites in a repository, another agent working in the same repository can find. Search is full-text, and\na git worktree shares the memory of the repository it was added from.</p>\n<p><strong>Rules.</strong> Allow, deny or ask, by agent, tool and argument. Rules are tried in order and the first match\nwins. A tool an agent may not use is not even listed to it, and calling it by name is refused anyway.</p>\n<p><strong>Approvals.</strong> A call that hits an <code>ask</code> rule waits until you decide. Run <code>derbent</code> in a terminal of its\nown and the waiting calls are at the top: approve once, approve for the rest of that agent's session, or\ndeny. The same works from any shell with <code>derbent approve 12</code>. If nobody answers, the call is denied when\nthe timeout runs out, and the agent is told why.</p>\n<p><strong>Receipts.</strong> Every call that passes through the gate leaves a receipt: which agent, which tool, which\narguments, what was decided and what came of it. The receipts form a hash chain, and <code>derbent verify</code>\ntells you whether one was edited or removed. Secrets are masked before anything is stored.</p>\n<p>The agents' own tools, their shell commands and file edits, don't go through MCP. But all four CLIs can\nrun a command of your choice before each tool call. Make that command <code>derbent gate</code>, and a <code>git push</code>\ngoes through the same rules, the same approval and the same receipt chain.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-name\">The name<a href=\"https://tunahanaliozturk.dev/blog/why-i-built-derbent#the-name\" class=\"hash-link\" aria-label=\"Direct link to The name\" title=\"Direct link to The name\" translate=\"no\">​</a></h2>\n<p>In Turkish history, a derbent was a guarded post on a mountain pass or a dangerous road. The men who kept it were\nresponsible for the pass, and in return were spared some taxes. They decided who went through and kept a\nrecord of everyone who did.</p>\n<p>Every English word I tried (gatekeeper, warden, checkpoint, bastion) was either another security\nproduct's name or too general. Derbent says exactly what the tool does, and nobody else uses it.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"how-it-is-built\">How it is built<a href=\"https://tunahanaliozturk.dev/blog/why-i-built-derbent#how-it-is-built\" class=\"hash-link\" aria-label=\"Direct link to How it is built\" title=\"Direct link to How it is built\" translate=\"no\">​</a></h2>\n<p>The most important decision is that there is no daemon. The obvious shape is a hub every agent connects\nto, but that hub has to start before any agent, stay alive, restart after a crash, and guard the port it\nlistens on. In Derbent every agent session starts its own gate process, and they all open the same SQLite\nfile. Nothing has to run first, there is no open port, and a crash takes down one agent's gate, not\neveryone's.</p>\n<p>That has a price, and the documentation says so: every agent session starts its own copy of the servers\nbehind the gate, and approvals are noticed by polling rather than instantly.</p>\n<p>It is written in Go. Claude Code starts <code>derbent gate</code> once for every tool call, so every shell command\nwaits for a process to start, decide and exit. A Go binary starts without loading an interpreter, and it\nis one file for Windows, macOS and Linux that asks for nothing else on the machine.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"what-it-does-not-do\">What it does not do<a href=\"https://tunahanaliozturk.dev/blog/why-i-built-derbent#what-it-does-not-do\" class=\"hash-link\" aria-label=\"Direct link to What it does not do\" title=\"Direct link to What it does not do\" translate=\"no\">​</a></h2>\n<p>Writing down what Derbent doesn't do mattered as much as what it does.</p>\n<ul>\n<li class=\"\"><strong>The agent name is a label, not authentication.</strong> Any process running as you can call itself anything.</li>\n<li class=\"\"><strong>It is not a boundary against an agent that already has a shell as you.</strong> Such an agent could run\n<code>derbent approve</code> itself, or write the database. Approvals guard against mistakes and against prompt\ninjection that stays inside MCP.</li>\n<li class=\"\"><strong>The chain doesn't catch everything on its own.</strong> It finds changes in the middle; a cut-off tail or a\nchain rewritten from the start only shows against a copy of the head hash you keep somewhere else.</li>\n<li class=\"\"><strong>Glob rules don't understand shell syntax.</strong> <code>git push*</code> doesn't match <code>cd repo &amp;&amp; git push</code>.</li>\n</ul>\n<p>The worst thing a security tool can do is promise more than it does. Used knowing its limits, Derbent\nhelps a lot; trusted without knowing them, it would do harm.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"try-it\">Try it<a href=\"https://tunahanaliozturk.dev/blog/why-i-built-derbent#try-it\" class=\"hash-link\" aria-label=\"Direct link to Try it\" title=\"Direct link to Try it\" translate=\"no\">​</a></h2>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#393A34;--prism-background-color:#f6f8fa\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#393A34;background-color:#f6f8fa\"><code class=\"codeBlockLines_e6Vv\"><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">go </span><span class=\"token function\" style=\"color:#d73a49\">install</span><span class=\"token plain\"> github.com/tunahanaliozturk/derbent/cmd/derbent@latest</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">claude mcp </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> derbent -- derbent mcp </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--agent</span><span class=\"token plain\"> claude</span><br></div><div class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">codex mcp </span><span class=\"token function\" style=\"color:#d73a49\">add</span><span class=\"token plain\"> derbent -- derbent mcp </span><span class=\"token parameter variable\" style=\"color:#36acaa\">--agent</span><span class=\"token plain\"> codex</span><br></div></code></pre></div></div>\n<p>With just that, the two agents share one memory and every call is recorded. The\n<a href=\"https://derbent.dev/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">documentation</a> covers rules, approvals and the hooks.</p>\n<p>If you use more than one coding agent, I would like to hear from you, especially about which calls you\nwould want held for approval. Issues and ideas are welcome on\n<a href=\"https://github.com/tunahanaliozturk/derbent\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">GitHub</a>.</p>",
            "url": "https://tunahanaliozturk.dev/blog/why-i-built-derbent",
            "title": "Why I built Derbent",
            "summary": "I use more than one coding agent on the same repositories. Claude Code works on one thing, Codex on",
            "date_modified": "2026-09-29T00:00:00.000Z",
            "author": {
                "name": "Tunahan Ali Öztürk",
                "url": "https://github.com/tunahanaliozturk"
            },
            "tags": [
                "derbent",
                "ai-agents",
                "mcp"
            ]
        }
    ]
}