<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Resonator LLC</title>
    <link>https://resonator.am/</link>
    <description>Resonator LLC -- a technology company building peer-to-peer applications and solutions.</description>
    <language>en</language>
    <atom:link href="https://resonator.am/feed.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Sat, 12 Sep 2026 17:33:01 +0200</lastBuildDate>
    
    <item>
      <title>Agents need something to check against</title>
      <link>https://resonator.am/2026/08/26/agents-need-something-to-check-against.html</link>
      <guid isPermaLink="true">https://resonator.am/2026/08/26/agents-need-something-to-check-against.html</guid>
      <pubDate>Wed, 26 Aug 2026 08:00:00 +0200</pubDate>
      <description>A talk from Frank Coyle argues that agentic systems need something formal to check against. We put RDF on the wire for our own reasons, and the two lines of thinking meet in the same place.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Frank Coyle, who teaches at Berkeley, gave a talk at the AI Engineer conference
called <em>Why Agentic Systems Need Ontologies</em>. It is twenty minutes, and it names
precisely the problem we designed the network around.</p>

<div class="video">
  <iframe src="https://www.youtube.com/embed/Sir59K8ZDPU" title="Why Agentic Systems Need Ontologies — Frank Coyle, UC Berkeley" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" loading="lazy" allowfullscreen=""></iframe>
  <p class="video-cap">
    Frank Coyle, UC Berkeley &mdash; <em>Why Agentic Systems Need Ontologies</em>,
    at the AI Engineer conference.
    <a href="https://www.youtube.com/watch?v=Sir59K8ZDPU">Watch on YouTube</a>.
  </p>
</div>

<p>His starting point is that a language model is probabilistic by construction.
Hallucination is not a defect waiting to be trained away, it is what the
machinery does. So you do not correct it from inside the model. You put
something formal outside the model, and you check against it.</p>

<h2 id="where-this-lines-up-with-what-we-build">Where this lines up with what we build</h2>

<p>Two of his points are load-bearing for us, and we arrived at them from a
different direction.</p>

<p>The first is why a graph rather than a table. He puts it plainly: adding
something to a relational schema means adding a column and restructuring around
it, while adding something to a graph means attaching one more edge. That is the
same property we rely on for a different reason — <a href="https://resonator.am/2026/08/18/what-rdf-is-and-why-we-use-it.html">merging two RDF graphs is
concatenation</a>, so two peers can
combine what they know without negotiating a schema first. He wants it for
evolution over time. We want it for evolution across machines. Same property,
two payoffs.</p>

<p>The second is reusing vocabularies that already exist rather than inventing
private ones — schema.org, FOAF, Dublin Core. On a peer-to-peer network that
stops being good manners and becomes the mechanism. Predicates are URIs, so two
nodes that have never met can mean the same thing without ever having
coordinated. There is no registry to agree on, because the names are already
global.</p>

<p>Then there is a structural fit that falls straight out of the design. In <a href="https://resonator.am/2026/08/22/sqlite-databases-talking-to-each-other.html">our
envelope</a>, a query is
itself an RDF object — it arrives as a statement carrying its engine-specific
text as a literal. Which means a request can be inspected as data <em>before</em> it is
executed, by exactly the same machinery that inspects everything else on the
wire. Coyle argues that an agent should stay free of side effects until its
result has been validated. On this network, the place to put that check already
exists: every message is a statement, and statements can be checked.</p>

<p>That is the whole reason the wire language is RDF and not a packed binary
struct. A message that describes itself can be examined by something other than
the code that produced it — which is what you need when the thing on the other
end is probabilistic, and what you need when it is simply a peer running
software you did not write.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>SQLite databases talking to each other</title>
      <link>https://resonator.am/2026/08/22/sqlite-databases-talking-to-each-other.html</link>
      <guid isPermaLink="true">https://resonator.am/2026/08/22/sqlite-databases-talking-to-each-other.html</guid>
      <pubDate>Sat, 22 Aug 2026 08:00:00 +0200</pubDate>
      <description>A Resonator node is a SQLite database, a peer-to-peer endpoint, and a SPARQL engine in one process. The interesting part is not the database — it is what the databases say to each other.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Strip away the vocabulary and a Resonator node is three things running in one
process: a <strong>SQLite database</strong>, an <strong>iroh peer-to-peer endpoint</strong>, and a
<strong>SPARQL engine that executes over that SQLite</strong>. There is no server in the
picture, and no account. A node’s identity is an ed25519 keypair, and two nodes
that have each other’s public key can talk.</p>

<p>That much is unremarkable. Plenty of things embed SQLite. What decides whether a
network is worth building is the part in the middle: <span class="hl">what
exactly goes over the wire</span>.</p>

<h2 id="the-first-draft-and-why-it-was-wrong">The first draft, and why it was wrong</h2>

<p>The original design put SQL text inside a Rust struct and sent that. It worked,
and it was a dead end, because it quietly bound the whole network to three
accidents at once:</p>

<ul>
  <li>SQLite’s five storage classes became the network’s value model.</li>
  <li>SQLite’s dialect of SQL became the only payload anyone could parse.</li>
  <li>Rust’s serde became the de facto schema language.</li>
</ul>

<p>The test that kills it is simple. A Postgres node wants to join. Under that
design it cannot even <em>read</em> an incoming request, let alone answer one. The
network would have been a SQLite network wearing a protocol.</p>

<h2 id="splitting-the-two-things-that-were-fused">Splitting the two things that were fused</h2>

<p>The ambition is symmetric heterogeneous peers: a Postgres, DuckDB or Mongo node
joins, answers queries through its own authenticator, and asks questions of its
own. Getting there means separating two concerns the first draft had welded
together:</p>

<ul>
  <li>The <strong>wire language</strong>, which every peer must speak, and which therefore has to
be engine-neutral.</li>
  <li>The <strong>query payload</strong>, which is irreducibly engine-specific and should be left
that way.</li>
</ul>

<p>The wire language is RDF. Every message on the network is an RDF object
serialized as Turtle. A query is <em>also</em> an RDF object — one that carries its
engine-specific text along as a literal:</p>

<div class="language-turtle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[]</span><span class="w"> </span><span class="k">a</span><span class="w"> </span><span class="nn">rsntr:</span><span class="n">Query</span><span class="w"> </span><span class="p">;</span><span class="w">
   </span><span class="nn">rsntr:</span><span class="n">mod</span><span class="w">    </span><span class="s">"sql-sqlite"</span><span class="w"> </span><span class="p">;</span><span class="w">
   </span><span class="nn">rsntr:</span><span class="n">signal</span><span class="w"> </span><span class="s">"SELECT name, seen FROM _peers ORDER BY seen DESC"</span><span class="w"> </span><span class="p">.</span><span class="w">
</span></code></pre></div></div>

<p>Peers speak the payload families they understand, and render what they can. A
node that has no idea what <code class="language-plaintext highlighter-rouge">sql-sqlite</code> means can still parse the envelope, see
that this is a query, and route or refuse it intelligently.</p>

<p>The deliberate non-goal is worth stating: we are not translating every engine
into one universal query language. That path is where data-integration projects
go to die.</p>

<h2 id="this-is-not-a-new-idea">This is not a new idea</h2>

<p>Putting a query inside a statement has precedent we leaned on rather than
invented:</p>

<ul>
  <li><strong>SPIN</strong> represents SPARQL queries as RDF objects — a node typed as a select
query, carrying its query text as a property. Our envelope is that shape with
the engine generalised.</li>
  <li><strong>R2RML</strong>, a W3C recommendation, holds a valid SQL query as a literal in
Turtle.</li>
  <li>Resonator’s own earlier transport already turned every network event into a
one-line Turtle statement and accepted Turtle commands back — no command
vocabulary, only RDF flowing in and out.</li>
</ul>

<h2 id="where-the-rdf-starts">Where the RDF starts</h2>

<p>The handshake has two layers, and only one of them is RDF.</p>

<div class="figure">
  <p><strong>Layer 0 — transport.</strong> iroh does its work over QUIC: it proves both identities
and opens a channel. No RDF is involved here.</p>

  <p><strong>Layer 1 — envelope.</strong> From the first byte of application data onward,
everything is RDF objects, including the hello exchange and the admission
handshake that decides whether a stranger gets to ask anything at all.</p>
</div>

<p>So “everything is RDF” is precise rather than rhetorical: it begins the moment
the cryptography has finished.</p>

<h2 id="where-it-actually-is">Where it actually is</h2>

<p>v3 is a Rust workspace. It builds the core crates, <code class="language-plaintext highlighter-rouge">rsntr</code> (a console tool meant
to be comfortable for shell pipelines and for LLM agents), and a Python package
usable from a notebook. The node is AGPL-3.0-only; the Python package and the
mod PDK are MIT, so writing a client never drags you into the copyleft.</p>

<p>Honest about the moving parts: iroh is the <em>third</em> reconsideration of the base
transport, and it sits behind a transport trait precisely because Bluetooth and
radio should be able to take its place later. The envelope is the part we
expect to outlive the transport under it.</p>

<p>Source and documentation live at
<a href="https://resonator.network">resonator.network</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>What RDF is, and why we use it</title>
      <link>https://resonator.am/2026/08/18/what-rdf-is-and-why-we-use-it.html</link>
      <guid isPermaLink="true">https://resonator.am/2026/08/18/what-rdf-is-and-why-we-use-it.html</guid>
      <pubDate>Tue, 18 Aug 2026 08:00:00 +0200</pubDate>
      <description>RDF has a reputation problem: XML-era baggage, ontology committees, a decade of unfulfilled promises. What we actually use is a very small core, and it earns its place.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Resonator’s wire language is RDF. That choice needs defending, because RDF
arrives carrying two decades of baggage — angle brackets, ontology committees,
and a lot of promises that did not land. So it is worth being precise about
which part of RDF we use, and why that part is the right tool.</p>

<h2 id="a-triple-is-three-columns">A triple is three columns</h2>

<p>RDF’s entire data model is the <strong>statement</strong>: a subject, a predicate, and an
object. That is it. There is no table, no schema to declare first, no
migrations.</p>

<p>Written in Turtle, the serialisation we use, a statement looks like this:</p>

<div class="language-turtle highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">&lt;ed25519:a1b2…&gt;</span><span class="w"> </span><span class="nn">rsntr:</span><span class="n">lastSeen</span><span class="w"> </span><span class="s">"2026-08-18T09:14:00Z"</span><span class="p">^^</span><span class="nn">xsd:</span><span class="n">dateTime</span><span class="w"> </span><span class="p">.</span><span class="w">
</span></code></pre></div></div>

<p>Subject, predicate, object, full stop. A graph is just a pile of these.</p>

<p>Three consequences follow, and all three are load-bearing for a peer-to-peer
network:</p>

<div class="figure">
  <p><strong>Merging is concatenation.</strong> Two graphs combine by putting the statements
together. There is no schema negotiation step, because there is no schema to
negotiate.</p>

  <p><strong>Names are global.</strong> Predicates are URIs, so two parties can mean the same
thing without ever having coordinated. <code class="language-plaintext highlighter-rouge">rsntr:lastSeen</code> means what the document
at that namespace says it means.</p>

  <p><strong>Partial understanding is normal.</strong> A peer that recognises four of the eight
predicates in a message can act on four and carry the rest. Nothing breaks.</p>
</div>

<p>That last one is the property a heterogeneous network lives or dies on. A binary
struct is all-or-nothing: you either have the exact type definition or you have
noise. A pile of statements degrades gracefully.</p>

<h2 id="sparql-and-the-trick-we-lean-on">SPARQL, and the trick we lean on</h2>

<p>SPARQL is the query language for graphs of triples — pattern-matching over
subject/predicate/object, in roughly the shape SQL has over rows.</p>

<p>The part we actually build on is stranger and more useful: <span class="hl">a
SPARQL query can itself be written as RDF</span>. This is an old idea — SPIN
expressed queries as RDF objects, a node typed as a select query carrying its
text as a property.</p>

<p>Which means a message and a query about messages are the <em>same kind of object</em>.
One parser. One dispatch rule: look at the type and act accordingly. That is
what lets <a href="https://resonator.am/2026/08/22/sqlite-databases-talking-to-each-other.html">the envelope</a>
carry a SQL payload for one node and a SPARQL payload for another without the
receiver needing a second protocol.</p>

<h2 id="what-we-deliberately-do-not-use">What we deliberately do not use</h2>

<p>Almost all of it. We are not building an ontology, not doing reasoning, not
running an inference engine, not writing OWL, and not shipping RDF/XML. There is
no committee.</p>

<p>The working set is three things: <strong>triples</strong> as the model, <strong>Turtle</strong> as the
syntax, and <strong>SPARQL</strong> as the query language. Everything else in the RDF world
is available if someone wants it and irrelevant if they do not.</p>

<h2 id="what-it-costs">What it costs</h2>

<p>Being straight about the trade: RDF on the wire is larger than a packed binary
struct carrying the same information. Turtle is text. Predicates are URIs, and
URIs are long.</p>

<p>That cost is real and we pay it deliberately, because the alternative — a
compact format that only nodes sharing our exact type definitions can read — buys
bytes at the price of the one property we actually want, which is that an
unfamiliar peer can still make sense of what it receives.</p>

<p>It does impose limits worth designing around. Gossip messages have a small
default ceiling, on the order of a few kilobytes, so anything large travels as a
reference rather than inline. Compact RDF serialisations exist and are worth
revisiting; for now, legibility beats compression.</p>

<h2 id="the-short-version">The short version</h2>

<p>RDF is a good fit here for an unglamorous reason. A peer-to-peer network is a
place where you cannot assume the other side runs your code, your version, or
your database. The format that suits that is one where a message is a pile of
self-describing statements, and understanding some of them is a valid outcome.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Katir, and knowing what you don&apos;t know</title>
      <link>https://resonator.am/2026/08/14/katir-knowing-what-you-dont-know.html</link>
      <guid isPermaLink="true">https://resonator.am/2026/08/14/katir-knowing-what-you-dont-know.html</guid>
      <pubDate>Fri, 14 Aug 2026 08:00:00 +0200</pubDate>
      <description>Most audio recognition asks whether a sound is one of the classes it was trained on. A room is not made of those classes. Katir is built around admitting that.</description>
      <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Katir is an acoustic monitor that runs entirely on one Android phone. It listens
to a room, decides for itself when something happened, and puts anything it
cannot place into a queue for a human. No audio leaves the device.</p>

<h2 id="the-wrong-question">The wrong question</h2>

<p>A conventional audio classifier asks: <em>is this one of the 527 things I was
trained on?</em> That is a good question if your world is made of those 527 things.</p>

<p>A room is not. A room has a particular fridge, one specific door that sticks, a
tap that drips at a rate you would recognise instantly and no dataset has ever
labelled, and a cough at three in the morning. None of these are classes anyone
shipped. Ask a fixed classifier about them and it will confidently return the
nearest label it owns, which is worse than saying nothing.</p>

<p>Katir never assumes it knows what a sound is. It notices that <span class="hl">something happened</span>, learns which of those somethings keep
recurring, and asks you about the rest.</p>

<h2 id="what-that-looks-like-in-use">What that looks like in use</h2>

<p>You name the fridge once. From then on, the fridge is the fridge.</p>

<p>Sounds that repeat get recognised as the same thing without you labelling every
occurrence. Sounds the system cannot place are not forced into a category to
make the numbers look tidy — they go into a ranked review queue, each item
carrying the reason it ended up there. The queue is the product: it is the
system telling you where its own judgement ran out.</p>

<h2 id="nothing-leaves-the-phone">Nothing leaves the phone</h2>

<p>Capture, detection, recognition and the dashboard all run on the device. There
is no account to sign into and no telemetry. The only thing ever fetched over
the network is the model itself, once.</p>

<p>Speech is discarded before it is written to disk — not deleted afterwards. The
system still records <em>that</em> it heard something and dropped it, so you can audit
the gate working without anything reconstructable being kept.</p>

<p>An ordinary phone is enough. This is not a device that needs a hub, a
subscription, or a server in someone else’s building.</p>

<h2 id="measured">Measured</h2>

<p>Scored on a 600-second household soundscape with exact ground truth, at a
half-second onset tolerance.</p>

<table>
  <thead>
    <tr>
      <th>What</th>
      <th>Score</th>
      <th>Reading</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Detection accuracy (F1)</td>
      <td>0.86</td>
      <td>against 0.52 for the standard few-shot baseline</td>
    </tr>
    <tr>
      <td>Short events (F1)</td>
      <td>0.92</td>
      <td>cough, door lock, footstep, light switch</td>
    </tr>
    <tr>
      <td>Cluster purity</td>
      <td>0.77</td>
      <td>recurring sounds grouped correctly</td>
    </tr>
    <tr>
      <td>Speed, on the phone</td>
      <td>41×</td>
      <td>faster than realtime, full pipeline on-device</td>
    </tr>
  </tbody>
</table>

<p>Katir is Android v0.1.0 and in development. More at
<a href="https://katir.resonator.am">katir.resonator.am</a>.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
