<?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: Jay Bamroliya</title>
    <description>The latest articles on DEV Community by Jay Bamroliya (@jay_bamroliya_402b72cf784).</description>
    <link>https://dev.to/jay_bamroliya_402b72cf784</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%2F4016133%2Fbac00b3d-85aa-4759-95d7-0ca27092b377.jpg</url>
      <title>DEV Community: Jay Bamroliya</title>
      <link>https://dev.to/jay_bamroliya_402b72cf784</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jay_bamroliya_402b72cf784"/>
    <language>en</language>
    <item>
      <title>I built an AI agent that fixes incidents and proves it — using SigNoz</title>
      <dc:creator>Jay Bamroliya</dc:creator>
      <pubDate>Sun, 26 Jul 2026 05:52:48 +0000</pubDate>
      <link>https://dev.to/jay_bamroliya_402b72cf784/i-built-an-ai-agent-that-fixes-incidents-and-proves-it-using-signoz-139m</link>
      <guid>https://dev.to/jay_bamroliya_402b72cf784/i-built-an-ai-agent-that-fixes-incidents-and-proves-it-using-signoz-139m</guid>
      <description>&lt;p&gt;&lt;em&gt;How my team built a self-healing SRE agent for the Agents of SigNoz hackathon: it detects a problem in SigNoz, fixes the app, and then uses SigNoz again to prove recovery.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Every observability tool — including SigNoz's own new MCP server — is great at one thing: telling you &lt;strong&gt;what&lt;/strong&gt; broke. "This endpoint is slow, here are the traces." But then a human still has to read the data, guess the cause, apply a fix, and check whether it actually worked.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Agents of SigNoz&lt;/strong&gt; hackathon (Team &lt;strong&gt;TraceBandits&lt;/strong&gt; — Jay Bamroliya &amp;amp; Kaushal Karkar), we asked a different question: what if the agent did that whole loop? Not just &lt;em&gt;explain&lt;/em&gt; the incident, but &lt;strong&gt;fix it — and then prove the fix worked, using observability.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That last part is the whole idea. The result: an endpoint that was taking &lt;strong&gt;4.2 seconds&lt;/strong&gt; drops to &lt;strong&gt;tens of milliseconds&lt;/strong&gt;, detected → fixed → verified, with one click and no human in the loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30kyfqro08hd10n5cxl3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F30kyfqro08hd10n5cxl3.png" alt="SigNoz Self-Healing Agent dashboard showing 4.21s to 36ms recovery" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The agent's dashboard right after a heal: it detected the slow &lt;code&gt;/about&lt;/code&gt; endpoint, disabled the fault, and re-tested it — 4.21 s → 36 ms, with its own incident report.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;The agent runs a closed loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Detect&lt;/strong&gt; — reads service metrics from SigNoz and spots the degraded service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investigate&lt;/strong&gt; — pulls per-operation latency (including internal DB spans) to find &lt;em&gt;where&lt;/em&gt; the time actually goes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix&lt;/strong&gt; — applies a scoped remediation to the running app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; — re-tests the endpoint and confirms recovery, then writes its own incident report.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most "AI + observability" demos stop at step 2. Steps 3 and 4 — &lt;em&gt;acting&lt;/em&gt; and then &lt;em&gt;verifying via observability&lt;/em&gt; — are what make this different, and they're exactly what an on-call engineer actually wants.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj8tzhlh9eqg32v0iqe0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj8tzhlh9eqg32v0iqe0q.png" alt="App degraded while the agent investigates the incident" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One click: I inject a latency fault, the app goes DEGRADED, and the agent starts investigating SigNoz live.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcx3uxdcdwtn2uvj8936i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcx3uxdcdwtn2uvj8936i.png" alt="Architecture: instrumented app streams to SigNoz, agent reads via tools and fixes the app" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The whole system: an OTel-instrumented app streams to SigNoz; the agent reads SigNoz through tools and acts back on the app.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent:&lt;/strong&gt; Spring Boot (Java). Most agents are Python; doing it in Java kept it close to the app we were healing and easy to reason about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brain:&lt;/strong&gt; Llama-3.3-70B on &lt;strong&gt;Groq&lt;/strong&gt; (free tier, OpenAI-compatible function calling). The LLM decides which tools to call and in what order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability:&lt;/strong&gt; &lt;strong&gt;SigNoz&lt;/strong&gt; (Cloud for the demo; the repo ships &lt;code&gt;casting.yaml&lt;/code&gt; so you can self-host it with Foundry).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo target:&lt;/strong&gt; an OpenTelemetry-instrumented Spring Boot app with a runtime fault-injection control plane, so we can stage a real, controllable incident.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the agent uses SigNoz
&lt;/h2&gt;

&lt;p&gt;This is the heart of the project, so here are the actual tools the LLM can call — each is a call to the SigNoz Query API (authenticated with a service-account key):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_services&lt;/code&gt; → &lt;code&gt;POST /api/v1/services&lt;/code&gt; — p99 latency, error rate, throughput per service. Finds the degraded service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_top_operations&lt;/code&gt; → &lt;code&gt;POST /api/v1/service/top_operations&lt;/code&gt; — p50/p95/p99 per operation, &lt;strong&gt;including internal spans&lt;/strong&gt; (DB calls, repository methods). This is how the agent pinpoints &lt;em&gt;where&lt;/em&gt; the latency lives, not just that a request is slow.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apply_fix&lt;/code&gt; → calls the app's control plane to disable the fault.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;verify_recovery&lt;/code&gt; → re-tests the endpoint directly and reports the new latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice SigNoz is used &lt;strong&gt;twice&lt;/strong&gt; in the loop: once to &lt;em&gt;detect and diagnose&lt;/em&gt;, and — after the fix — again to confirm recovery. That verification-via-observability step is the part I'm proudest of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxs0oylcl49el2ezpajsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxs0oylcl49el2ezpajsk.png" alt="SigNoz Cloud showing the /about endpoint latency" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SigNoz showing the &lt;code&gt;/about&lt;/code&gt; endpoint's latency — this is what the agent reads to find the bottleneck.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's a real, verbatim conclusion the agent wrote during a run:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Incident summary: the 'smartcontactmanager' service had an endpoint '/about' with abnormally high latency (p99 ≈ 4.2 s). The fix applied was to disable the injected latency fault. After the fix, the endpoint was re-tested and its average response time was 36 ms, confirming recovery."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What broke while building it (the useful part)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Spring bean-name clashes.&lt;/strong&gt; I registered &lt;code&gt;RestClient&lt;/code&gt; beans named &lt;code&gt;groqClient&lt;/code&gt;, &lt;code&gt;demoAppClient&lt;/code&gt; — which collided with the &lt;code&gt;@Component&lt;/code&gt; classes of the same name. Spring refused to start: &lt;em&gt;"a bean with that name has already been defined."&lt;/em&gt; Fix: rename the &lt;code&gt;@Bean&lt;/code&gt; methods (&lt;code&gt;groqRestClient&lt;/code&gt;, &lt;code&gt;demoRestClient&lt;/code&gt;) and match the constructor parameter names, since with multiple &lt;code&gt;RestClient&lt;/code&gt; beans Spring disambiguates by parameter name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Llama stringifies numbers.&lt;/strong&gt; My first tool schema had an integer &lt;code&gt;minutes&lt;/code&gt; parameter. Groq's validation kept rejecting the model's output: &lt;em&gt;"expected integer, but got string"&lt;/em&gt; — Llama emitted &lt;code&gt;{"minutes": "60"}&lt;/code&gt;. Fix: drop flaky numeric params; the tools take only strings and the time window is a server-side default. Simpler schema, zero validation failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Ingestion delay vs. real-time proof.&lt;/strong&gt; SigNoz Cloud has a short ingestion lag, so right after a fix the aggregated p99 hasn't updated yet. For deterministic proof, &lt;code&gt;verify_recovery&lt;/code&gt; re-tests the endpoint &lt;strong&gt;directly&lt;/strong&gt; and reports the measured latency — instant and honest — while SigNoz's dashboards show the same recovery a little later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Observability isn't just for humans anymore.&lt;/strong&gt; The moment you expose telemetry through a clean API (or an MCP server), an agent can reason over it — and &lt;em&gt;act&lt;/em&gt; on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The verify step is underrated.&lt;/strong&gt; Any agent can propose a fix. An agent that checks its own work against real signals is one you might actually trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free tools go far.&lt;/strong&gt; Groq's free Llama + SigNoz + OpenTelemetry — the whole stack cost nothing and runs on a 6 GB laptop.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The code, the &lt;code&gt;casting.yaml&lt;/code&gt; for a reproducible SigNoz deployment, a pitch deck, and a full demo video are all in the repo. Clone it, drop your SigNoz + Groq keys into &lt;code&gt;.env&lt;/code&gt;, run the app and the agent, open the dashboard, and click &lt;strong&gt;Self-Heal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SigNoz's MCP server lets an AI &lt;em&gt;see&lt;/em&gt; your system. This agent lets it &lt;em&gt;heal&lt;/em&gt; your system — and prove it did.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;— Team TraceBandits · Agents of SigNoz (WeMakeDevs × SigNoz)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>signoz</category>
      <category>opentelemetry</category>
      <category>ai</category>
      <category>wemakedevs</category>
    </item>
    <item>
      <title>My registration endpoint took 5.3 seconds. The INSERT took 38 ms.</title>
      <dc:creator>Jay Bamroliya</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:29:26 +0000</pubDate>
      <link>https://dev.to/jay_bamroliya_402b72cf784/my-registration-endpoint-took-53-seconds-the-insert-took-38-ms-3go8</link>
      <guid>https://dev.to/jay_bamroliya_402b72cf784/my-registration-endpoint-took-53-seconds-the-insert-took-38-ms-3go8</guid>
      <description>&lt;p&gt;&lt;em&gt;Instrumenting a five-year-old Spring Boot app with zero code changes, and everything that broke on the way to self-hosting SigNoz on a 6 GB Windows laptop.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;My Spring Boot registration endpoint took &lt;strong&gt;5.3 seconds&lt;/strong&gt;. The SQL &lt;code&gt;INSERT&lt;/code&gt; inside it took &lt;strong&gt;38 milliseconds&lt;/strong&gt;. I only learned that after pointing SigNoz at a five-year-old side project — without changing a single line of its code. This post is the story of that evening: what broke on the way (a lot), and what the traces taught me about my own app.&lt;/p&gt;

&lt;p&gt;Around 2020, when I was learning Java, I built &lt;strong&gt;Smart Contact Manager&lt;/strong&gt; — a Spring Boot 2.3 app with Spring Security, JPA, and Thymeleaf. I made it purely for my personal learning, and it shows: it has &lt;code&gt;System.out.println&lt;/code&gt; debugging all over it, because that's what I knew back then.&lt;/p&gt;

&lt;p&gt;Full disclosure before we start: I did this whole setup pair-working with an AI coding agent — it drove the terminal while I made the decisions, clicked the UAC prompts, and created the accounts. For a hackathon literally called &lt;em&gt;Agents of SigNoz&lt;/em&gt;, that felt fitting. Every number and error in this post is from my machine, and I watched all of it happen.&lt;/p&gt;

&lt;p&gt;For the Agents of SigNoz pre-event challenge, I asked a different question than "can I instrument a fresh demo app": &lt;strong&gt;can modern observability tooling light up an app I wrote five years ago, without me changing a single line of its code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer turned out to be yes — and the traces immediately showed me two things about my own app that I never knew. Along the way, almost every step of the setup broke in an instructive way, so this post is both a "how to" and an honest list of everything that went wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt; Windows 11 laptop, 6 GB RAM (yes, really), Java 19, no Docker installed, and — as I discovered — a broken WSL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: The setup fought back
&lt;/h2&gt;

&lt;p&gt;SigNoz self-hosting needs Docker. My machine had neither Docker nor a working WSL — &lt;code&gt;wsl --version&lt;/code&gt; died with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class not registered
Error code: Wsl/CallMsi/Install/REGDB_E_CLASSNOTREG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-registering the Store package didn't help. What fixed it was installing the official WSL MSI directly from the &lt;a href="https://github.com/microsoft/WSL/releases" rel="noopener noreferrer"&gt;microsoft/WSL GitHub releases&lt;/a&gt; (&lt;code&gt;wsl.2.7.10.0.x64.msi&lt;/code&gt;), enabling the Virtual Machine Platform feature, and rebooting.&lt;/p&gt;

&lt;p&gt;Then came the first genuinely useful discovery, straight from the &lt;a href="https://signoz.io/docs/install/docker/" rel="noopener noreferrer"&gt;SigNoz Docker install docs&lt;/a&gt;: on Windows, &lt;strong&gt;don't run SigNoz under Docker Desktop&lt;/strong&gt;. ClickHouse Keeper is known to crash in a restart loop with segmentation faults under Docker Desktop's virtualization. The docs recommend native Docker Engine inside WSL 2 instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# inside Ubuntu on WSL2&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://get.docker.com | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With 6 GB of total RAM, I also capped the WSL VM so ClickHouse, the JVM, and my browser could coexist — &lt;code&gt;C:\Users\&amp;lt;me&amp;gt;\.wslconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4GB&lt;/span&gt;
&lt;span class="py"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;6GB&lt;/span&gt;
&lt;span class="py"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked. ClickHouse Keeper reported healthy on the first boot and stayed that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Casting SigNoz with Foundry
&lt;/h2&gt;

&lt;p&gt;If you last looked at SigNoz a while ago, the install has changed: the &lt;code&gt;docker-compose&lt;/code&gt; manifests and &lt;code&gt;install.sh&lt;/code&gt; are &lt;strong&gt;deprecated&lt;/strong&gt;. SigNoz now deploys through &lt;strong&gt;Foundry&lt;/strong&gt;, a CLI that treats your observability stack as declarative config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://signoz.io/foundry.sh | bash

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; casting.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
apiVersion: v1alpha1
kind: Installation
metadata:
  name: signoz
spec:
  deployment:
    flavor: compose
    mode: docker
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;foundryctl cast &lt;span class="nt"&gt;-f&lt;/span&gt; casting.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cast&lt;/code&gt; validates Docker, generates Compose files into &lt;code&gt;pours/deployment/&lt;/code&gt;, and starts five containers: ClickHouse, ClickHouse Keeper, Postgres, the SigNoz server, and the OTel collector ("ingester"). UI on &lt;code&gt;:8080&lt;/code&gt;, OTLP ingestion on &lt;code&gt;:4317&lt;/code&gt; (gRPC) and &lt;code&gt;:4318&lt;/code&gt; (HTTP).&lt;/p&gt;

&lt;p&gt;Two real-world notes from my run:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;On a slow connection, the image pull step can get killed mid-way.&lt;/strong&gt; Pre-pulling with &lt;code&gt;docker compose pull&lt;/code&gt; inside &lt;code&gt;pours/deployment/&lt;/code&gt; and re-running &lt;code&gt;foundryctl cast&lt;/code&gt; (it's idempotent) got me through.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A killed first run left a stale migration lock.&lt;/strong&gt; The SigNoz container crash-looped with &lt;code&gt;migrations table is already locked ... duplicate key value violates unique constraint "migration_lock_table_name_key"&lt;/code&gt;. One &lt;code&gt;DELETE FROM migration_lock;&lt;/code&gt; against the bundled Postgres and it healed itself. If your &lt;code&gt;signoz&lt;/code&gt; container restarts endlessly after an interrupted install, check this first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And a genuinely non-obvious gotcha: &lt;strong&gt;telemetry ingestion doesn't start until you create the admin account.&lt;/strong&gt; The collector registers with the SigNoz server via OpAMP, and until the first user/org exists, the server rejects it (&lt;code&gt;cannot create agent without orgId&lt;/code&gt; in the logs) and the collector never opens ports 4317/4318. I spent a while staring at &lt;code&gt;Failed to export&lt;/code&gt; errors from my app before realizing the fix was simply… finishing the signup form at &lt;code&gt;localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Zero-code instrumentation
&lt;/h2&gt;

&lt;p&gt;The app didn't need MySQL for this experiment, so I pointed it at an in-memory H2 database (a dependency swap in &lt;code&gt;pom.xml&lt;/code&gt; plus datasource properties — config, not code). Then the entire instrumentation was one JVM flag and a few environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"smartcontactmanager"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:4318"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http/protobuf"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-javaagent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;opentelemetry-javaagent.jar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nt"&gt;-jar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;smartcontactmanager-0.0.1-SNAPSHOT.jar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The &lt;a href="https://opentelemetry.io/docs/zero-code/java/agent/" rel="noopener noreferrer"&gt;OpenTelemetry Java agent&lt;/a&gt; (v2.29.0) attached to my Spring Boot &lt;strong&gt;2.3.4&lt;/strong&gt; app running on Java 19 — a framework version from 2020 — and instrumented Tomcat, Spring MVC, Spring Data, Hibernate, JDBC, and Spring Security without complaint.&lt;/p&gt;

&lt;p&gt;I generated traffic with a shell script: user registration, failed logins, successful logins, adding contacts, paginating through them, plus some deliberate errors (a bad path variable, 404s, unauthenticated access). Within seconds, 277 spans had landed in ClickHouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk7i0yvmt08k0pkhsq5hi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk7i0yvmt08k0pkhsq5hi.png" alt="Logs Explorer full of Hibernate SQL statements" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My five-year-old app, alive in the APM view.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakdhcd9smb44sqwhb8zz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakdhcd9smb44sqwhb8zz.png" alt="Log detail panel showing trace_id correlation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Latency percentiles, request rate, Apdex — all from one JVM flag.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the traces showed me
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The 5.3-second registration
&lt;/h3&gt;

&lt;p&gt;The Services view showed &lt;code&gt;smartcontactmanager&lt;/code&gt; with a P99 latency of &lt;strong&gt;3.75 seconds&lt;/strong&gt;. Filtering the Traces Explorer by &lt;code&gt;durationNano &amp;gt;= 2000000000&lt;/code&gt; surfaced the culprit: &lt;code&gt;POST /do_register&lt;/code&gt;, &lt;strong&gt;5.29 s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2nmehahjn36105bflu30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2nmehahjn36105bflu30.png" alt="Service overview with latency percentiles, request rate and Apdex charts" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every request slower than 2 seconds — including an 11.5 s cold-start &lt;code&gt;GET /&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The flame graph broke it down mercilessly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Span&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;POST /do_register&lt;/td&gt;
&lt;td&gt;5,292 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;└ UserRepository.save&lt;/td&gt;
&lt;td&gt;2,761 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&amp;nbsp;└ Session.persist&lt;/td&gt;
&lt;td&gt;834 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&amp;nbsp;└ INSERT smartcontact.user&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;38 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fefu4lght73vwz859oktn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fefu4lght73vwz859oktn.png" alt="SigNoz Services page showing smartcontactmanager with P99 latency" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The flame graph: 5.29 s total, and the actual INSERT is the tiny bar at the end.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My first reaction was: &lt;em&gt;wait, 5 seconds just for a registration? Do my other APIs also take this long and I never knew?&lt;/em&gt; That question — which I could never have answered before tonight — is the entire reason observability exists.&lt;/p&gt;

&lt;p&gt;The database was innocent. The actual &lt;code&gt;INSERT&lt;/code&gt; took 38 milliseconds — &lt;strong&gt;0.7% of the request&lt;/strong&gt;. The rest was BCrypt password hashing and Hibernate initializing its machinery on the first write of the session. Without the waterfall I'd have "optimized" the database, which was never the problem.&lt;/p&gt;

&lt;p&gt;(The traces also caught my app restart red-handed: the first &lt;code&gt;GET /&lt;/code&gt; after a restart took &lt;strong&gt;11.5 seconds&lt;/strong&gt; — a JVM cold start on a memory-starved machine, visible as two lonely 11,587 ms rows in the explorer. Every restart of a Java app has a story like this; traces just make it impossible to ignore.)&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The exception my app was swallowing
&lt;/h3&gt;

&lt;p&gt;This one I didn't stage. My traffic script re-registers the same test user every cycle, and the &lt;strong&gt;Exceptions&lt;/strong&gt; tab lit up with &lt;code&gt;JdbcSQLIntegrityConstraintViolationException&lt;/code&gt; → &lt;code&gt;DataIntegrityViolationException&lt;/code&gt; → &lt;code&gt;RollbackException&lt;/code&gt;, three per cycle.&lt;/p&gt;

&lt;p&gt;Here's the thing: the endpoint &lt;strong&gt;returns HTTP 200 for those requests&lt;/strong&gt;. My 2020-era controller catches the exception, prints a stack trace to stdout, and renders the signup page with a friendly error banner. Status-code monitoring would tell you this endpoint never fails. The exceptions tab, populated automatically from span error events, tells you the truth: the database is rolling back a transaction on every duplicate signup, and the "success" metric is lying to you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwj35i0vq62wjiv7qgzgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwj35i0vq62wjiv7qgzgt.png" alt="Traces Explorer filtered to requests slower than 2 seconds" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Constraint violations my app has been silently swallowing — while returning HTTP 200.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That contrast — success metrics green, exceptions tab red — was the single most useful thing SigNoz showed me.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. My logs pillar was empty, and the reason was my own code
&lt;/h3&gt;

&lt;p&gt;Traces and metrics flowed instantly, but the Logs Explorer sat at "No logs yet". The OTel agent captures logs by hooking the logging framework (Logback here) — and my student self logged everything with &lt;code&gt;System.out.println&lt;/code&gt;, which no logging framework ever sees.&lt;/p&gt;

&lt;p&gt;The zero-code fix: turn on framework logging via startup args, no rebuild required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--logging.level.org.hibernate.SQL=DEBUG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly every SQL statement Hibernate executes arrives in SigNoz as a log record, automatically stamped with the &lt;code&gt;trace_id&lt;/code&gt; of the request that caused it — so you can click from a slow span straight to the exact SQL it ran. My own &lt;code&gt;println&lt;/code&gt; "logs" remain invisible forever. Consider this a message from your future self: use a logger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwyw9ta4yc2opg88qrpb2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwyw9ta4yc2opg88qrpb2.png" alt="Flame graph of the 5.29 second POST do_register trace with 38 ms INSERT" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Real Hibernate SQL flowing into the Logs Explorer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn673aikhhz058nibzny7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn673aikhhz058nibzny7.png" alt="Exceptions tab showing swallowed constraint violation exceptions" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every log record carries the &lt;code&gt;trace_id&lt;/code&gt; of the request that produced it — one click from log to trace.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alerts, and an honest note about 6 GB of RAM
&lt;/h2&gt;

&lt;p&gt;To round things out I created an alert rule in the UI: request rate above 0.4 ops/s over the last 5 minutes. The query-builder-with-live-chart flow made this pleasantly concrete — you see the exact line your threshold will cut across before you save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzixau0mw2hdg1bdp2s7e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzixau0mw2hdg1bdp2s7e.png" alt="Alert rule in firing state" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The alert rule, genuinely firing on my traffic generator's waves.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One honest limitation of my potato-spec setup: when I tried to build the alert on &lt;strong&gt;P90 latency&lt;/strong&gt; from the &lt;code&gt;http.server.request.duration&lt;/code&gt; histogram, the percentile query kept failing with an internal error — the ClickHouse window-function query behind percentile aggregation didn't survive my 4 GB WSL memory cap. Switching the alert to the request-count metric worked instantly. On a machine with the recommended RAM I'd expect the percentile alert to work; on 6 GB, know your limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference: every error I hit, and the fix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error / symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Wsl/CallMsi/Install/REGDB_E_CLASSNOTREG&lt;/code&gt; on any &lt;code&gt;wsl&lt;/code&gt; command&lt;/td&gt;
&lt;td&gt;Install the official WSL MSI from &lt;a href="https://github.com/microsoft/WSL/releases" rel="noopener noreferrer"&gt;microsoft/WSL releases&lt;/a&gt;, reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClickHouse Keeper segfault restart-loop on Windows&lt;/td&gt;
&lt;td&gt;Don't use Docker Desktop — install Docker Engine natively inside WSL2 (per SigNoz docs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;foundryctl cast&lt;/code&gt; dies with &lt;code&gt;signal: killed&lt;/code&gt; mid-pull&lt;/td&gt;
&lt;td&gt;Pre-pull with &lt;code&gt;docker compose pull&lt;/code&gt; inside &lt;code&gt;pours/deployment/&lt;/code&gt;, then re-run &lt;code&gt;cast&lt;/code&gt; (it's idempotent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;signoz&lt;/code&gt; container restart-loops: &lt;code&gt;migrations table is already locked ... migration_lock_table_name_key&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Stale lock from an interrupted install: &lt;code&gt;docker exec &amp;lt;postgres-container&amp;gt; psql -U signoz -d signoz -c "DELETE FROM migration_lock;"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App logs &lt;code&gt;Failed to export&lt;/code&gt; forever; ports 4317/4318 closed&lt;/td&gt;
&lt;td&gt;Create the first admin account at &lt;code&gt;localhost:8080&lt;/code&gt; — the collector can't register via OpAMP until an org exists (&lt;code&gt;cannot create agent without orgId&lt;/code&gt; in server logs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SigNoz dies when you close your WSL terminal&lt;/td&gt;
&lt;td&gt;The WSL VM shuts down with its last client — keep a WSL shell open, or configure &lt;code&gt;vmIdleTimeout&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Percentile (P90/P99) alert queries fail with internal error&lt;/td&gt;
&lt;td&gt;ClickHouse ran out of memory under my 4 GB WSL cap — use count/rate metrics, or give the VM more RAM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-code instrumentation is real, even for old apps.&lt;/strong&gt; A 2020 Spring Boot 2.3 app on Java 19 got full traces — HTTP, JPA, JDBC, security filters — from one &lt;code&gt;-javaagent&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traces answer "where did the time go" in a way metrics can't.&lt;/strong&gt; P99 said "slow"; only the waterfall said "BCrypt + Hibernate warm-up, not the database".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exceptions ≠ error responses.&lt;/strong&gt; My app returned 200 while rolling back transactions. Watch both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs are only as good as your logging discipline.&lt;/strong&gt; The agent can ship your logs, but not your &lt;code&gt;println&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the platform-specific docs.&lt;/strong&gt; Native Docker-in-WSL instead of Docker Desktop (ClickHouse Keeper segfaults), the account-creation-before-ingestion dependency, and the Foundry migration were all things I'd never have guessed.&lt;/li&gt;
&lt;li&gt;6 GB of RAM is enough for SigNoz + ClickHouse + a JVM + a browser, if you cap the WSL VM. Tight, but enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won't pretend this was a smooth evening. It took many more hours than I expected, most of them spent on things that had nothing to do with SigNoz itself — a broken WSL, a forced reboot at midnight, a laptop with barely enough RAM. But that's exactly why I'm writing it all down.&lt;/p&gt;

&lt;p&gt;I don't have a grand plan for the hackathon on the 20th yet. What I do have is something most teams won't: a self-hosted SigNoz that already survived everything my machine threw at it, and an app already streaming traces into it. The warm-up did its job.&lt;/p&gt;

&lt;p&gt;If you're sitting on an old side project, don't build a fresh demo to try observability — instrument the old thing. Its skeletons make much better screenshots.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;SigNoz docs: &lt;a href="https://signoz.io/docs" rel="noopener noreferrer"&gt;signoz.io/docs&lt;/a&gt; · OpenTelemetry Java agent: &lt;a href="https://opentelemetry.io/docs/zero-code/java/agent/" rel="noopener noreferrer"&gt;opentelemetry.io/docs/zero-code/java/agent&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>signoz</category>
      <category>opentelemetry</category>
      <category>wemakedevs</category>
      <category>observability</category>
    </item>
    <item>
      <title>I Built an AI That Never Forgets</title>
      <dc:creator>Jay Bamroliya</dc:creator>
      <pubDate>Sun, 05 Jul 2026 12:16:35 +0000</pubDate>
      <link>https://dev.to/jay_bamroliya_402b72cf784/i-built-an-ai-that-never-forgets-1c2a</link>
      <guid>https://dev.to/jay_bamroliya_402b72cf784/i-built-an-ai-that-never-forgets-1c2a</guid>
      <description>&lt;h1&gt;
  
  
  I Built an AI That Never Forgets — for $0 (Cognee Hackathon)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;By Team MindVault — Jay Bamroliya &amp;amp; Kaushal Karkar&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Every AI assistant has the same embarrassing problem.&lt;/p&gt;

&lt;p&gt;You spend 20 minutes explaining your project. You close the tab. You come back tomorrow — and it has no idea who you are.&lt;/p&gt;

&lt;p&gt;Your AI has amnesia. Every. Single. Time.&lt;/p&gt;

&lt;p&gt;For the WeMakeDevs × Cognee Hackathon, we built &lt;strong&gt;MindVault&lt;/strong&gt; to fix that — a personal "living memory" that builds a knowledge graph of your life as you talk to it. And we made it run on a completely free stack. Here's exactly how, including everything that broke along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with Stateless AI
&lt;/h2&gt;

&lt;p&gt;When you call an LLM, every request starts from zero. No memory of your last session, your preferences, your decisions, or your name.&lt;/p&gt;

&lt;p&gt;The usual workarounds all fall short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System prompts&lt;/strong&gt; — token-limited, manually managed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector databases&lt;/strong&gt; — semantic similarity only, no relational context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG pipelines&lt;/strong&gt; — complex to build, no graph awareness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these give you &lt;em&gt;real&lt;/em&gt; persistent memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Cognee
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/topoteretes/cognee" rel="noopener noreferrer"&gt;Cognee&lt;/a&gt; is an open-source memory layer for AI agents. It turns text into a &lt;strong&gt;hybrid graph-vector knowledge store&lt;/strong&gt; — two retrieval systems working together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Vector search&lt;/strong&gt; — "find things semantically similar to this query"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph traversal&lt;/strong&gt; — "follow relationships between concepts"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the difference between a filing cabinet and an actual brain.&lt;/p&gt;

&lt;p&gt;Cognee 1.2's memory API is beautifully simple — four verbs that cover the whole memory lifecycle:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jay is a developer from India building MindVault.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Who is building MindVault?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;improve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                 &lt;span class="c1"&gt;# enrich graph connections
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;everything&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# GDPR-ready erasure
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Built: MindVault
&lt;/h2&gt;

&lt;p&gt;A chat interface where every message becomes structured memory:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;💾 &lt;strong&gt;Remember&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Text → embedded + mined into knowledge-graph entities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔍 &lt;strong&gt;Recall&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Question → hybrid graph+vector search → AI answer from YOUR memories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✨ &lt;strong&gt;Improve&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Re-runs enrichment, strengthening graph connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗑️ &lt;strong&gt;Forget&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Full erasure — complete data lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus the parts we're proud of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A live force-directed knowledge graph&lt;/strong&gt; rendered on Canvas — zero libraries, custom physics (repulsion, springs, gravity). You literally watch your memory grow as you type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice input&lt;/strong&gt; via the Web Speech API — speak your memories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A live LOCAL ↔ CLOUD toggle&lt;/strong&gt; — one click switches between open-source Cognee running on your machine and Cognee Cloud. No restart. Same codebase, &lt;code&gt;memory_engine.py&lt;/code&gt; abstracts both backends behind identical async functions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser (chat · voice · live graph · toggle)
        │
        ▼
FastAPI backend ── /remember /recall /improve /forget
        │
        ▼
memory_engine.py ── one interface, two backends
   ├── LOCAL:  open-source Cognee + Groq + fastembed
   └── CLOUD:  Cognee Cloud REST API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Real Story: Making It Run for $0
&lt;/h2&gt;

&lt;p&gt;This was the hardest and most educational part. We had &lt;strong&gt;no budget for APIs&lt;/strong&gt;. Here's the free stack and every wall we hit:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wall 1: LLM costs.&lt;/strong&gt; Groq's free tier gives you &lt;code&gt;llama-3.3-70b-versatile&lt;/code&gt; at 6,000 tokens/minute. Sounds fine — until you learn Cognee's cognify pipeline makes &lt;em&gt;multiple concurrent LLM calls&lt;/em&gt;. Instant 429 rate-limit errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Cognee ships a built-in rate limiter (backed by &lt;code&gt;aiolimiter&lt;/code&gt;). Three env vars:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LLM_RATE_LIMIT_ENABLED=true
LLM_RATE_LIMIT_REQUESTS=1
LLM_RATE_LIMIT_INTERVAL=15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calls queue and space out automatically. remember() takes ~90 seconds on the free tier — a fair trade for $0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wall 2: Embedding costs.&lt;/strong&gt; Cognee defaults to OpenAI embeddings — which means an OpenAI key and a bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;code&gt;fastembed&lt;/code&gt; runs BAAI/bge-small-en-v1.5 &lt;strong&gt;locally&lt;/strong&gt;. No API key, no network calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EMBEDDING_PROVIDER=fastembed
EMBEDDING_MODEL=BAAI/bge-small-en-v1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Wall 3: Vector dimension mismatch.&lt;/strong&gt; Our LanceDB store had been created with OpenAI's 3072-dim vectors; fastembed produces 384-dim. Schema conflict, cryptic errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; wipe &lt;code&gt;.cognee_system/databases&lt;/code&gt; and let it rebuild with the right schema. Lesson: embedding dimensions are part of your storage schema — changing providers means migrating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wall 4 (Cloud mode): the silent no-op.&lt;/strong&gt; Cognee Cloud's &lt;code&gt;/api/v1/add&lt;/code&gt; accepts &lt;strong&gt;multipart file uploads&lt;/strong&gt;, not JSON. Our JSON POSTs returned plausible status codes while storing &lt;em&gt;nothing&lt;/em&gt;. Recall answers were pure LLM hallucination — confidently wrong, cached per-question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; read the OpenAPI spec (&lt;code&gt;/openapi.json&lt;/code&gt;), switch to multipart:&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="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;datasetName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Debugging lesson: when search says "no data found" but add says "success," &lt;strong&gt;trust the negative signal&lt;/strong&gt; — verify what's actually stored (&lt;code&gt;GET /api/v1/datasets/{id}/data&lt;/code&gt;) instead of trusting status codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Surprised Us
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Graph traversal is genuinely different from vector search.&lt;/strong&gt; We stored "Jay is building MindVault" and "MindVault is powered by Cognee AI" as separate memories, then asked "What is Jay building?" — Cognee connected the dots &lt;em&gt;through the graph&lt;/em&gt;, not by keyword overlap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;improve()&lt;/code&gt; is underrated.&lt;/strong&gt; Most people stop at add-and-search. Re-running enrichment after accumulating memories visibly strengthens the graph — new edges appear between old nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/jaybamroliya/mindvault
&lt;span class="nb"&gt;cd &lt;/span&gt;mindvault
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env   &lt;span class="c"&gt;# add a free Groq key from console.groq.com&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; uvicorn main:app &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total cost: &lt;strong&gt;$0.&lt;/strong&gt; No credit card anywhere in the stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;"Stateless AI" is one of the most annoying unsolved UX problems in AI. Cognee solves it properly — not with a prompt hack, but with a real hybrid memory architecture that you can self-host for free or scale on their cloud.&lt;/p&gt;

&lt;p&gt;If you're building agents, give your AI a memory. It changes everything.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for the WeMakeDevs × Cognee Hackathon by Team MindVault — Jay Bamroliya &amp;amp; Kaushal Karkar. Source: &lt;a href="https://github.com/jaybamroliya/mindvault" rel="noopener noreferrer"&gt;github.com/jaybamroliya/mindvault&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
