<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>tie – tie</title><link>http://tiedb.com/</link><description>Recent content on tie</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 12 Nov 2026 00:00:00 +0000</lastBuildDate><atom:link href="http://tiedb.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Tie it all together</title><link>http://tiedb.com/blog/tie-everything/</link><pubDate>Thu, 12 Nov 2026 00:00:00 +0000</pubDate><guid>http://tiedb.com/blog/tie-everything/</guid><description>
&lt;p&gt;tiedb.com used to be a single page about a key-value-value store and a recipe
example. The project is now three things that belong together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tie&lt;/strong&gt;, a content-addressed library where tags are a filesystem;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;the tie apps&lt;/strong&gt;, five native tools that use it;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;anneal&lt;/strong&gt;, a coding agent that stores its session memory in it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This site is the front door. It is built with Hugo and Hextra, it has no
analytics, no CDNs and no web fonts, and its screenshots are generated from the
real applications by scripts in the repository. If a page and the app disagree,
the app is right and the page is a bug.&lt;/p&gt;
&lt;p&gt;The plan is simple: a landing page that explains the idea, a page per app that
shows the actual UI, documentation that is useful, and stories that say what was
measured and what was reverted. There is a brand page with the logo family, a
glossary, and a 404 page that is a tag query.&lt;/p&gt;
&lt;p&gt;The old page is not lost. The pasta-and-basil example still explains a triple
better than any diagram, so it lives on in
&lt;a href="http://tiedb.com/docs/tie/concepts/#triples-101"&gt;Triples 101&lt;/a&gt;. The name moved on; the idea
didn&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>The logo that was a bug</title><link>http://tiedb.com/blog/the-logo-that-was-a-bug/</link><pubDate>Tue, 10 Nov 2026 00:00:00 +0000</pubDate><guid>http://tiedb.com/blog/the-logo-that-was-a-bug/</guid><description>
&lt;p&gt;anneal&amp;rsquo;s logo was drawn as a thin amber outline of an &lt;strong&gt;A&lt;/strong&gt; on a dark rounded
tile: two legs, a crossbar and a small white dot on the apex. In the app it looks
like a fat, solid amber shape with a pinhole. That is not what the SVG says.&lt;/p&gt;
&lt;p&gt;The cause is a rendering bug. Fyne rasterises SVG with oksvg. In
&lt;code&gt;oksvg/svg_path.go&lt;/code&gt;, &lt;code&gt;DrawTransformed&lt;/code&gt; applies the transform matrix to the path
points, but calls &lt;code&gt;SetStroke&lt;/code&gt; with the raw &lt;code&gt;LineWidth&lt;/code&gt;. The stroke is therefore
always &lt;strong&gt;10 device pixels&lt;/strong&gt;, regardless of the scale.&lt;/p&gt;
&lt;p&gt;In anneal&amp;rsquo;s 26 px activity rail, a 10 px stroke is 38% of the icon. On the
128-unit viewBox that is a stroke width of 49.2, not 10. The thin outline fills
in: the crossbar disappears, the upper counter closes, a small notch remains
between the legs, and the apex dot becomes a white pinhole. The amber nearly
reaches the tile edges.&lt;/p&gt;
&lt;p&gt;It looks good. It is also fragile: at a different display scale the stroke would
be proportionally thinner, and if the bug is ever fixed the logo turns back into
an outline. So the site does not depend on the bug. The marks are drawn with the
heavy geometry directly, as strokes with &lt;code&gt;stroke-width=&amp;quot;49.2&amp;quot;&lt;/code&gt; (rendered
correctly by every browser), and the generator includes a fidelity check that
compares the result with the app&amp;rsquo;s actual 26 px render.&lt;/p&gt;
&lt;p&gt;That gave us a family rule:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a 128-unit tile, &lt;code&gt;#181819&lt;/code&gt;, radius 28;&lt;/li&gt;
&lt;li&gt;one heavy stroke, round caps and joins;&lt;/li&gt;
&lt;li&gt;one white pinhole on the shape;&lt;/li&gt;
&lt;li&gt;a silhouette that survives being filled in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The marks are the &lt;strong&gt;A&lt;/strong&gt; for anneal, a bow tie for tie, a folder for tie-fm,
headphones for tie-audio, a star for tie-view, two peaks for imgview and a map
pin for tie-browser. The palette is anneal&amp;rsquo;s: teal, amber, and a small set of
saturated hues for the apps.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://tiedb.com/brand/"&gt;brand page&lt;/a&gt; has the downloads and the full family. The generator
is in &lt;code&gt;tools/&lt;/code&gt;; the contact sheet is one &lt;code&gt;make logos&lt;/code&gt; away.&lt;/p&gt;</description></item><item><title>Halving the heap</title><link>http://tiedb.com/blog/halving-the-heap/</link><pubDate>Thu, 05 Nov 2026 00:00:00 +0000</pubDate><guid>http://tiedb.com/blog/halving-the-heap/</guid><description>
&lt;p&gt;The real benchmark library is 7.4 GiB on disk with 60.7 million entries and 98.2
million associations. Loading it used to take 19.4 GiB of heap. The optimization
note in the source repo is a record of what actually moved the number.&lt;/p&gt;
&lt;p&gt;At 1M triples, the heap went &lt;strong&gt;862 → 786 → 633 → 473 MiB&lt;/strong&gt; — a 45% reduction —
and objects per triple went from &lt;strong&gt;21.5 to 10.0&lt;/strong&gt;. Three changes did most of the
work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;A &lt;code&gt;TYPE_HASH&lt;/code&gt; record.&lt;/strong&gt; Hashes were being stored as strings in the trie.
A raw 32-byte record cut the heap by &lt;strong&gt;28%&lt;/strong&gt; at 100k triples.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interning the trie chunks.&lt;/strong&gt; Identical chunks in different levels are
referenced in memory but written to disk, which is the same trade the
original design made for strings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sharding the outer tree.&lt;/strong&gt; Load time went from &lt;strong&gt;4m32s to 2m46s&lt;/strong&gt; (−39%)
and the peak heap came down with it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Two other experiments were reverted. They looked better in a microbenchmark and
were worse on the real database. The note says why, and that is the point:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Measure both axes on the real DB. Guessing that a design &amp;ldquo;should&amp;rdquo; be better is
not sufficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The opt-in reverse index saves about 29% of heap. That is a real trade: reverse
queries become fast, but the index is opt-in per relation and the server&amp;rsquo;s
default subset is deliberately small (&lt;code&gt;tag,path,parent,tie-type,version-of,member-of&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The full note, including the two reverted experiments, is
&lt;a href="https://github.com/uidbz/tie/blob/master/docs/memory-optimization.md" rel="noopener"&gt;&lt;code&gt;docs/memory-optimization.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Why triples, and why not just SQL</title><link>http://tiedb.com/blog/why-triples/</link><pubDate>Sun, 01 Nov 2026 00:00:00 +0000</pubDate><guid>http://tiedb.com/blog/why-triples/</guid><description>
&lt;p&gt;There is a section in tie&amp;rsquo;s internals note called &amp;ldquo;Why triples, and why not just
SQL&amp;rdquo;. It contains this sentence:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We rebuilt a database… SQLite provides all of that for free.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is the right place to start, because the interesting part of tie was never
the triple store. It is the combination of a &lt;strong&gt;content-addressed blob store&lt;/strong&gt; and
a &lt;strong&gt;FUSE tag tree&lt;/strong&gt;, joined by the content hash. The triple store exists because
the schema is open and the core query is a reverse tag lookup, but a project that
only wanted a database should use SQLite.&lt;/p&gt;
&lt;p&gt;What triples buy in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No migrations.&lt;/strong&gt; A new relation is just a triple. The apps add &lt;code&gt;rating&lt;/code&gt;,
&lt;code&gt;member-of&lt;/code&gt;, &lt;code&gt;play-count&lt;/code&gt;, &lt;code&gt;thumbnail&lt;/code&gt;, &lt;code&gt;dimensions&lt;/code&gt; without a schema change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-valued by default.&lt;/strong&gt; A file has many tags and many paths. In SQL this
is a join table; here it is the native shape.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reverse queries are the point.&lt;/strong&gt; &amp;ldquo;Which files have this tag?&amp;rdquo; and &amp;ldquo;which
scenes use this photo?&amp;rdquo; are the same operation, and the opt-in reverse index
makes it cheap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A path is a query.&lt;/strong&gt; The mount turns the index into a directory tree. That is
not a SQL feature; it is the API the apps and the file manager share.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The cost is also real: a custom storage engine, a custom query path, a custom
fsck. The note documents the trade, including RDF/SPARQL and Datomic as prior
art.&lt;/p&gt;
&lt;p&gt;If you are choosing a database for a new project, read the note before choosing
this one. If you want your photos, music and files in one place with tags that
outlive the paths, the combination is worth it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/uidbz/tie/blob/master/docs/internals.md" rel="noopener"&gt;&lt;code&gt;docs/internals.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>81 findings, 81 regression tests</title><link>http://tiedb.com/blog/81-findings/</link><pubDate>Tue, 20 Oct 2026 00:00:00 +0000</pubDate><guid>http://tiedb.com/blog/81-findings/</guid><description>
&lt;p&gt;In October 2026, tie got a full code review: 81 findings across the storage
engine, the HTTP layer, the mounts, the importers and the tests. The rule for the
fixes was simple and slow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;one commit per issue;&lt;/li&gt;
&lt;li&gt;a regression test that fails before the fix;&lt;/li&gt;
&lt;li&gt;the fix, and the test passes;&lt;/li&gt;
&lt;li&gt;no drive-by cleanups in the same commit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The categories were the usual ones for a young storage project: error paths that
lost the error, lock ordering, partial writes on a full disk, path traversal in
archive extraction, a cache that could return a stale size, and a handful of
places where a context was not threaded through a retry.&lt;/p&gt;
&lt;p&gt;None of it is glamorous. All of it is why the library now has a &lt;code&gt;verify&lt;/code&gt; command
you can trust, a filehost that treats its input as untrusted, and a test suite
that includes the bug that was fixed.&lt;/p&gt;
&lt;p&gt;The full review is in the source repo
(&lt;a href="https://github.com/uidbz/tie/blob/master/docs/code-review.md" rel="noopener"&gt;&lt;code&gt;docs/code-review.md&lt;/code&gt;&lt;/a&gt;).
It is worth reading as a picture of what &amp;ldquo;done&amp;rdquo; meant at that point, not as a
list of current bugs.&lt;/p&gt;</description></item></channel></rss>