<?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: constantine makoid</title>
    <description>The latest articles on DEV Community by constantine makoid (@constantinemakoid).</description>
    <link>https://dev.to/constantinemakoid</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%2F4097657%2F31d09708-af1f-4bba-ba95-562a94980041.png</url>
      <title>DEV Community: constantine makoid</title>
      <link>https://dev.to/constantinemakoid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/constantinemakoid"/>
    <language>en</language>
    <item>
      <title>Introducing Celmis: self-hosted code intelligence over a symbol graph</title>
      <dc:creator>constantine makoid</dc:creator>
      <pubDate>Thu, 27 Aug 2026 16:37:48 +0000</pubDate>
      <link>https://dev.to/constantinemakoid/introducing-celmis-self-hosted-code-intelligence-over-a-symbol-graph-23eb</link>
      <guid>https://dev.to/constantinemakoid/introducing-celmis-self-hosted-code-intelligence-over-a-symbol-graph-23eb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Celmis is self-hosted code intelligence.&lt;/strong&gt; It reads a set of repositories once and&lt;br&gt;
keeps a symbol graph of them; asking questions, reviewing pull requests, auditing&lt;br&gt;
dependencies, routing alerts and serving an MCP endpoint are then all different ways of&lt;br&gt;
reading that one index, rather than five products each holding their own copy of your&lt;br&gt;
code. It runs on one machine under &lt;code&gt;docker compose&lt;/code&gt;, with whichever model provider you&lt;br&gt;
already pay for behind it.&lt;/p&gt;

&lt;p&gt;This is the introduction — what it does, what it refuses to do, and how to run it. The&lt;br&gt;
quickest way to say why it exists is to show the thing a diff-only tool structurally&lt;br&gt;
cannot do.&lt;/p&gt;
&lt;h2&gt;
  
  
  One question, two repositories
&lt;/h2&gt;

&lt;p&gt;I asked a question that spanned two repositories, and the answer quoted both.&lt;/p&gt;

&lt;p&gt;The question was ordinary: &lt;em&gt;"How does the gateway talk to the payments service? Name the&lt;br&gt;
function on each side."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer found a Python publisher:&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;publish&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;batch_id&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="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Emit one settlement event on the published topic.&lt;/span&gt;&lt;span class="sh"&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;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;SETTLEMENT_TOPIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;batch_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;batch_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;encode&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;and a TypeScript listener in a different repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SETTLEMENT_TOPIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settlement&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sockets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&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;They never call each other. They meet on a Kafka topic. Then the answer added something&lt;br&gt;
nobody asked for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Duplicated contract.&lt;/strong&gt; The topic name (&lt;code&gt;payments.settlement.v2&lt;/code&gt;) and the event payload&lt;br&gt;
structure are hardcoded in two separate repositories — &lt;code&gt;src/config.py&lt;/code&gt; in payments and&lt;br&gt;
&lt;code&gt;src/contract.ts&lt;/code&gt; in the gateway. Changing the topic name or the payload schema in one&lt;br&gt;
repository without updating the other will silently break the integration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;A reviewer that reads only the diff cannot say that.&lt;/strong&gt; It never had the other repository&lt;br&gt;
open. That is not a model-quality problem — no amount of reasoning recovers a file that&lt;br&gt;
was never in the context.&lt;/p&gt;
&lt;h2&gt;
  
  
  The shape of the idea
&lt;/h2&gt;

&lt;p&gt;Read the repositories &lt;strong&gt;once&lt;/strong&gt;. Build a symbol graph — deterministically, with tree-sitter,&lt;br&gt;
no model involved. Then everything else is a different way of reading that one index&lt;br&gt;
rather than a separate product with its own copy of your code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ask questions&lt;/strong&gt;, answered with &lt;code&gt;file:line&lt;/code&gt; citations, across repository and language
boundaries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review pull requests&lt;/strong&gt;, with knowledge of who else calls the function in the diff —
including from a repository not in the pull request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit dependencies&lt;/strong&gt;, and produce the artefacts a buyer or an auditor asks for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route what your running services are shouting about&lt;/strong&gt; — alerts arrive, and the index
already knows which repository the failing service is, and who owns it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serve it over MCP&lt;/strong&gt;, so your own editor or agent reads the same index under the same
access rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The index is the product. The rest are surfaces.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the review actually catches
&lt;/h2&gt;

&lt;p&gt;On a real pull request in a demo repository, three findings, all on real lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vals&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="c1"&gt;// vals is {} — undefined, not a TypeError&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// off by one&lt;/span&gt;
&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// never throws, so the catch below is dead&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three inline comments with &lt;code&gt;suggestion&lt;/code&gt; blocks, one summary comment, &lt;strong&gt;zero false&lt;br&gt;
positives on that run&lt;/strong&gt;. I verified all three against the file in the PR branch by hand,&lt;br&gt;
because a review tool that is right four times out of five is a tool you stop reading.&lt;/p&gt;
&lt;h2&gt;
  
  
  The MCP server
&lt;/h2&gt;

&lt;p&gt;Eighteen tools over the same index. Here is a real session, trimmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;initialize&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;serverInfo:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"celmis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.29.1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tools/list&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tools&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;list_projects&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="err"&gt;get_api_surface&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;bootstrap_client&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;search_symbols&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;list_accessible_repos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;start_integration_walk&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;find_consumers&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;get_review&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;route_incident&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;get_owner&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;get_review_policy&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;get_dep_audit&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;get_architecture&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;list_deprecations&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;list_dep_findings&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;--&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tools/call&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;search_symbols&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"project_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"083bd97a-…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SETTLEMENT_TOPIC"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;--&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"repo_slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…celmis-demo-gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"variable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/contract.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"repo_slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…celmis-demo-payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"constant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/config.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One query. Two repositories, two languages, the same contract symbol — from a client that&lt;br&gt;
has never checked either of them out. &lt;code&gt;find_consumers&lt;/code&gt; is the one I use most: it answers&lt;br&gt;
"what breaks if I change this" across the whole set.&lt;/p&gt;
&lt;h2&gt;
  
  
  Alerts, and why they belong on the same index
&lt;/h2&gt;

&lt;p&gt;This is the surface I nearly left out of this article, which was a mistake, because it is&lt;br&gt;
the one that closes the loop.&lt;/p&gt;

&lt;p&gt;Your services are already producing alerts. They land in a channel where somebody has to&lt;br&gt;
work out which repository the failing service actually is, who owns it, and whether the&lt;br&gt;
thing that broke was touched recently. That lookup is the expensive part — not the alert.&lt;/p&gt;

&lt;p&gt;So the same index answers it. An ingest endpoint takes the alert, a binding routes it, and&lt;br&gt;
the card arrives in chat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /webhook/alerts/{token}
  { "severity": "critical",
    "repo_hint": "celmis-codereviewer/celmis-demo-gateway",
    "title": "checkout: unhandled exception in settle()" }

→ notif_delivered event=alert_received
  repo=celmis-codereviewer/celmis-demo-gateway severity=critical
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review results ride the same rails — a finished pull-request review posts its own card:&lt;br&gt;
&lt;code&gt;Review CHANGES · PR #4 — 0 critical · 3 error · 0 warn · 0 info&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two things worth stealing from how this is wired, both of which I got wrong first:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The webhook signature is checked before anything else.&lt;/strong&gt; Wrong signature → &lt;code&gt;401&lt;/code&gt;.&lt;br&gt;
Correct → &lt;code&gt;202&lt;/code&gt;. Replay the exact same delivery → &lt;code&gt;{"status": "duplicate"}&lt;/code&gt; rather than a&lt;br&gt;
second review and a second bill. Delivery IDs are cheap; duplicated model calls are not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A failed channel test must not echo the URL it tested.&lt;/strong&gt; Google Chat webhook URLs carry&lt;br&gt;
&lt;code&gt;key&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt; in the query string — &lt;strong&gt;the URL is a credential&lt;/strong&gt;. &lt;code&gt;httpx&lt;/code&gt; puts the&lt;br&gt;
request URL in the exception text, and an early version of the endpoint returned&lt;br&gt;
&lt;code&gt;str(exc)&lt;/code&gt; verbatim, which meant a failed test handed the caller back the secret it was&lt;br&gt;
testing. If you are building anything that tests a user-supplied webhook, go and check&lt;br&gt;
that path in your own code right now.&lt;/p&gt;
&lt;h2&gt;
  
  
  The surface that quietly became urgent
&lt;/h2&gt;

&lt;p&gt;The dependency audit is the one part that is &lt;strong&gt;deterministic end to end&lt;/strong&gt; — native&lt;br&gt;
auditors and OSV.dev, no LLM in the loop. It produces two things.&lt;/p&gt;

&lt;p&gt;A CycloneDX SBOM, and an evidence pack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;sbom/&amp;lt;repo&amp;gt;.cdx.json&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;CycloneDX,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;per&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;findings.json&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;what&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;found&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;against&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;those&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;components&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;timeline.jsonl&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fact&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;entered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;record&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;MANIFEST.json&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;sha&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;every&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;above&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last file is the point. An archive of files is not evidence — nothing in it stops&lt;br&gt;
the contents from having been edited afterwards. A manifest of digests means &lt;strong&gt;a third&lt;br&gt;
party can verify the pack without trusting the machine that produced it&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"algorithm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"findings.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"921412d4bf97eb32fa4b3e8ad09447dab762f19d31ffec45bddb6d7962bf08e5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sbom/gateway.cdx.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6eadeafad0043b85a51b65ddddba84ef7b43081064ed9e1b320c62838ddc3d8e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timeline.jsonl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b1815a8e3607712aa9332cd1f8d2d0ec6f93cd8dce53dd92844b8edcde790b3d"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"generated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-26T19:09:41Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why now:&lt;/strong&gt; from &lt;strong&gt;11 September 2026&lt;/strong&gt;, under the EU Cyber Resilience Act, manufacturers&lt;br&gt;
must report an actively exploited vulnerability to ENISA and their national CSIRT within&lt;br&gt;
&lt;strong&gt;24 hours&lt;/strong&gt;. The SBOM itself is not mandated until December 2027 — which is the trap,&lt;br&gt;
because on a 24-hour clock the first question is not how to word the notification. It is&lt;br&gt;
whether you ship the component at all, in which service, at which version.&lt;/p&gt;

&lt;p&gt;The document is due in 2027. The visibility it describes is needed fifteen months earlier.&lt;/p&gt;
&lt;h3&gt;
  
  
  The part I am most attached to
&lt;/h3&gt;

&lt;p&gt;The audit reports &lt;strong&gt;what it could not check&lt;/strong&gt;, as prominently as what it found:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Not fully checked (4). Treat a zero here as unknown, not as safe.

- …demo-gateway — npm via npm-audit: no lock file
  (package-lock.json / pnpm-lock.yaml / yarn.lock) — cannot resolve the tree
- …demo-gateway — all via osv-scanner: recognised no manifest or lock file here
- …e2e-probe/requirements.txt — PyPI via pip-audit: dependency resolution failed
  — audited 4 pinned requirements directly, without the transitive tree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;An unchecked ecosystem reports zero vulnerabilities exactly like a clean one.&lt;/strong&gt; If your&lt;br&gt;
tooling cannot tell you which of the two you are looking at, that gap &lt;em&gt;is&lt;/em&gt; the finding.&lt;br&gt;
Steal this behaviour regardless of what you use.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;One machine. Postgres and Qdrant bundled — no external cluster to provision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Celmis-labs/Celmis.git celmis
&lt;span class="nb"&gt;cd &lt;/span&gt;celmis
./scripts/init-env.sh          &lt;span class="c"&gt;# generates .env, every secret in the format it needs&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env up &lt;span class="nt"&gt;-d&lt;/span&gt;
docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a clean server that took &lt;strong&gt;197 seconds&lt;/strong&gt; from &lt;code&gt;git clone&lt;/code&gt; to six healthy services —&lt;br&gt;
measured, not estimated. About 1.1 GB of RAM at peak during indexing, 565 MB at rest.&lt;/p&gt;

&lt;p&gt;Bring your own model key: Gemini, Anthropic, OpenAI, OpenRouter, Groq or Mistral. A free&lt;br&gt;
Gemini key is enough to evaluate it. &lt;strong&gt;No telemetry, no licence check&lt;/strong&gt; — the only&lt;br&gt;
outbound calls are the ones you configure. AGPLv3, the whole thing, not open core.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number I am not hiding
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;17th of 50&lt;/strong&gt; on the Martian Code Review Bench offline set, stable under all three&lt;br&gt;
judges.&lt;/p&gt;

&lt;p&gt;It measures one of the surfaces above — pull-request review on isolated&lt;br&gt;
single-repository PRs. That set has no sibling service for a symbol to have consumers in,&lt;br&gt;
so the cross-repository work this is built around contributes nothing to the score. It is&lt;br&gt;
on the front page of the site with that explanation next to it rather than instead of it.&lt;/p&gt;

&lt;p&gt;I also audited every one of the 79 findings the benchmark counted against us. &lt;strong&gt;33 were&lt;br&gt;
real defects the reference set was silent about.&lt;/strong&gt; That audit is published in full, with&lt;br&gt;
the code for each one — partly because it is the honest thing to do, and partly because&lt;br&gt;
"our precision is actually higher" is a claim nobody should accept without the evidence&lt;br&gt;
attached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Known rough edges
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Semantic search needs a generated vault. Until you make one, the UI says so in an
orange banner rather than quietly returning worse answers.&lt;/li&gt;
&lt;li&gt;The execution sandbox has deliberate internet egress, because &lt;code&gt;pip install&lt;/code&gt; and
&lt;code&gt;npm ci&lt;/code&gt; need it.&lt;/li&gt;
&lt;li&gt;No long-term support branch. Fixes land on the latest release.&lt;/li&gt;
&lt;li&gt;It does not make anyone compliant with anything, and it says so in its own output.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Source, docs and the benchmark audit: &lt;strong&gt;&lt;a href="https://celmis-labs.github.io" rel="noopener noreferrer"&gt;https://celmis-labs.github.io&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer anything — especially about the benchmark methodology, which is the part&lt;br&gt;
that deserves an argument.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>devtools</category>
      <category>selfhosted</category>
    </item>
  </channel>
</rss>
