NAOMS Devlog

Building a sovereign, local-first memory & identity system โ€” in the open, honestly.

NAOMS engineering update

Week 10

4,107 commits, over a quarter of them merges โ€” the busiest week to this point. Everything it touched is below, the largest threads first and every area at the end.

๐Ÿ“… May 18 โ€“ May 24 ยท Process
Commits
4,107
Of those, merges
1,073
Busiest day
1,303
Quietest day
100
Commands
44
from 75
Process Dispatch free May 24, 2026ยท23 min readยทmeta

Commits per day โ€” Week 10

Four things changed this week

One per major area, and a week with an unusual shape: 1,303 commits on the Monday, 100 on the Wednesday, 1,028 on the Saturday.

1

Your interface stays responsive while heavy work runs

The background service stopped blocking on its own thread, so a bulk import or a replay no longer holds the screen still while it grinds through the job.

2

Seventy-five flat commands became forty-four noun-verb families

The command line was regrouped so you name the thing and then the action โ€” and every one of the 82 old command names still resolves to something.

3

A founding principle now fails the build instead of sitting in a document

Wholeness โ€” no part of the system reaching outside itself to do its own work โ€” became an automatic check that fails the build rather than a paragraph.

4

Every conversation you have can land in one inbox you control

Threads bridged in from outside messengers and from email write into the same store as native ones, so a single question answers for all of them at once.

A daemon that never blocks itself

An illustration, not a screenshot. Before, a bulk import or a replay occupied the same thread that draws the interface, so nothing repainted while it ran โ€” typing and incoming messages waited with it. After, the work takes just as long, but the app stays usable while it happens. Responsiveness is felt rather than photographed.

The feature: the background service that does the work โ€” importing, replaying, seeding โ€” while you carry on using the interface in front of you.

Before: it blocked on its own thread, so heavy work and the screen shared one queue, and the screen lost. Now: it is bounded by your system's resources rather than by a single lane, and it has a clean restart path for the times it does have to come back. (A daemon that never blocks itself)

A single shared thread is a queue, and everything behind it is waiting. The symptom people report is "the app froze", but nothing was frozen โ€” one long piece of work was holding the only lane, and every quick thing behind it inherited the long thing's duration.

What the service is and why one lane exists

A background service is a program that runs quietly rather than being opened and closed like an app. This one is the local hub every surface talks to: the terminal interface, the browser surface, the command line, and other people's machines reaching in across the network.

At its heart it runs a loop on a single thread, holding a queue of pending work and serving it one item at a time. The premise is cooperative โ€” each task does a small piece of work and then hands the thread back, which is what lets one thread feel like many.

The model is a good one and worth defending. Serving thousands of overlapping operations from a single thread avoids a whole class of bugs that come from two threads touching the same thing at once, and for a program that mostly waits on the network it is close to free.

That model works beautifully until one task declines to hand the thread back. Then nothing else runs. Not the slow thing: everything. Every connected surface, every peer, every pending request waits behind the one call that decided to finish before yielding.

The work this service does makes that risk concrete rather than theoretical. It signs things, calls into compiled native code for the heavy cryptography, and appends to the signed record underneath everything. Each of those is a candidate for running long without a pause.

Why one stalled call is everybody's problem

A freeze here does not degrade one person's experience; it takes the whole system offline at once. The browser surface, the management path, the peer connections and every agent operation in flight all sit behind the same loop, so they stall together and recover together.

In a single-user app a momentary freeze is an annoyance โ€” the spinner hangs, you wait, it comes back. In a local hub serving several surfaces and a fleet of working sessions, the same pause is an outage with a lot of witnesses.

That is why this was the week's most-worked theme despite having nothing to show on a screen. Every visible feature is only as available as the loop that serves it, and an identity system that freezes is not a more careful identity system. It is a down one.

The half of the problem on the native side

Bulk work was the worst offender, and it was fixed by changing its shape. Seeding, importing and replaying used to be carried out in one uninterrupted stretch on the native side. The work is now appended in batches, so the thread comes back between them and the screen keeps drawing.

Batching is the unglamorous fix and it is the right one. Making the long operation faster buys a smaller freeze rather than no freeze, and the freeze returns the first time someone imports twice as much. Handing the work over in pieces changes the shape of the problem instead of its size.

The side door that stays open

A second channel runs beside the main one, addressed by a file path on your own machine rather than by a network port. Status and restart travel over it, so they do not have to wait behind whatever the loop is currently chewing on.

That separation is the deliberate part. A service you can only ask about through the same congested front door as everything else is a service you cannot manage at precisely the moment you most need to. The management channel is the one that has to stay reachable.

Beside it sits a queryable record of whether the service is alive, so liveness is a fact you can ask for rather than something you infer from a request that has not come back. Guessing from silence is how a busy service and a dead one end up looking identical.

The clean restart path is the other half, and it is easy to undervalue. A service that never blocks can still need to come back, and a restart that loses its place is its own kind of freeze โ€” the interface returns, and the work you were waiting on begins again from nothing.

This one is finished, not in flight

Three pieces landed together: the queryable liveness record, the separate management channel, and the clean restart path. They are reported here as complete rather than as progress, which matters because a partly-held invariant is not an invariant at all.

An invariant only earns the name when nothing is allowed to violate it. Held most of the time, it is merely a tendency โ€” and a tendency is exactly what the next heavy operation quietly breaks without anyone noticing until a screen stops moving.

Honest limits and dishonest ones

The point of the invariant is not that slowness becomes impossible; it is that slowness becomes honest. An honest limit comes from the world: the processor is saturated, the disk is full, there is more work than hardware. A dishonest limit comes from the structure โ€” the system was slow because it fought itself.

Committing to only ever hitting the honest kind means that when this thing is slow, the slowness points at something true. You respond by adding hardware or reducing load, rather than by hunting for a flaw the system was never going to admit to.

The same distinction showed up on the infrastructure side the same day. A private network link between machines stopped working and was restored four minutes later โ€” a genuinely honest limit, fixed by repairing the network rather than by working around it in software.

Knowing which kind you are looking at is the whole diagnostic value. A real limit tells you to change the world; a self-inflicted one tells you to change the code, and treating the second as the first buys hardware that fixes nothing.

What is still missing. No latency figure is quoted here. What changed is structural โ€” one lane became many โ€” and this report does not claim a measured improvement it did not measure.

One noun, one verb: 75 commands became 44 families

The feature: the command line โ€” the way you ask NAOMS to do something without a screen in front of you.

Before: roughly 75 flat top-level commands, each named on its own terms, so finding the right one meant already knowing it existed. Now: 44 noun-verb families, where you name the thing and then what you want done to it. (One noun, one verb: cleaning up the NAOMS CLI)

A flat command list is a vocabulary test. Every flat name is a word you either know or do not, and there is no way to reason from one to the next. A noun-verb surface lets you guess: if you know the noun, you can try the verb you would expect, and be right more often than not.

What the regrouping actually did

Device management is the clearest case. Revoking a device, adding one and listing them had been three unrelated top-level names sitting next to each other alphabetically. They are now three verbs under one noun, where somebody looking for any of them would think to look.

Keys went the same way: generating, showing, exporting and rotating all became verbs on a single noun, so the old awkward compound name for rotation disappeared. Package operations, the largest family of all, stopped spreading across the top level and nested under one word.

The count matters less than the shape. Going from 75 to 44 is a regrouping rather than a cut: commands that were siblings by accident became siblings by subject. The ones with no subject at all had to acquire one before they could be placed anywhere.

Nothing was allowed to become unreachable

The scariest thing about reorganising a command line is breaking the habits and the scripts people already have. All 82 of the old command names still resolve โ€” each through its new home under a noun, or through an alias kept deliberately rather than by accident.

That discipline is the transferable half of this work. A rename that maps every old path forward costs a table of aliases; a rename that does not costs everybody who had learned the old surface, and they find out one broken script at a time.

One command was deliberately left in two places. The project's own health check lives under its noun and also stays at the top level, because automation invokes it by the short name. A clean taxonomy serves people, and it should not be allowed to break a path something depends on.

A command line is a teaching surface

Every name in a tool either helps somebody predict the next name or forces them to look it up. A flat list gives no help at all: knowing one name tells you nothing about the others, so the only route to competence is memorising the whole set.

Grouping by noun changes what a newcomer has to hold. Learn that there is a device noun, and listing, pairing and revoking are guesses you will usually get right โ€” which means the tool can be explored rather than studied.

The exploration works because the help text follows the same shape. Ask the tool what it can do and you get the nouns; ask a noun what it can do and you get its verbs. Walking that tree is a different experience from scrolling seventy-five unrelated names.

How they knew nothing fell off

Every entry in the command index โ€” 508 of them โ€” is exercised by an automated sweep rather than eyeballed. Nothing was quietly excused from the check, which is what makes the count worth quoting at all.

That is the difference between reorganising a menu and proving nothing fell off it. A careful person checking 508 entries by hand would miss some, and the misses would be invisible until somebody hit one.

One caveat belongs with any description of a command surface: they move. The shape of this idea โ€” nouns you learn, verbs you guess, old names forwarded โ€” is the durable part, and the exact verbs are always worth confirming against the tool's own help before copying them anywhere.

What is still moving. The regrouping of the top-level surface landed inside this window. The wider work โ€” a command surface other parts of the system can extend, and parity between what the command line offers and what the other surfaces do โ€” was still being aligned rather than shipped.

What this means, in plain terms

Five general lessons this week's work paid for, each learned by getting something wrong somewhere first. None of them is about NAOMS specifically โ€” each should survive being lifted out of this codebase and applied to whatever you are building instead.

A single lane is a queue, whatever you call it

The daemon blocked on its own thread, so a long import and a screen redraw competed for one lane and the redraw waited. Users describe that as a freeze; the code describes it as correct sequencing. Any resource with one lane converts every long operation into a delay for everything unrelated behind it.

A principle is either enforced or drifting

Wholeness sat in a design document for six weeks and was widely agreed with. This week it became a check that fails the build, because a written rule drifts the moment everybody gets busy and nobody is reading documents at three in the morning. Agreement is not a state a rule can be in; it is a state people are in, and they leave it.

Demonstrate a shared secret instead of transmitting one

Adding a second device could have worked by copying the master key across. It instead has both devices derive the same value independently and prove the match to each other, so the valuable thing never travels. A secret in transit is a secret at risk, and proof of possession is almost always available instead.

Map every old path forward before you rename anything

The command line moved 75 flat names into 44 families, and all 82 old names still resolve through a new home or a kept alias. The alias table is small; the alternative is every person and script that learned the old surface finding out by breaking. A rename with no forwarding is a deletion you have not announced.

A claim with no witness is a liability with confidence attached

A handoff note this week carried statements nobody could source, written as fluently as the true ones beside them. The rule that came out of it is that a claim is either cited to something on disk, labelled unverified, or cut. Fluent output costs the same whether or not it is grounded, so the check has to be structural.

How much healthier is it than a week ago?

CommitsEvery commit landing on the main line of the shared codebase inside the window
4,107
Of those, non-mergeCommits carrying an actual change rather than joining two lines of history
3,034
Of those, mergesCommits that joined two lines of history
1,073
Busiest dayMonday 2026-05-18, the week's peak
1,303
Quietest dayWednesday 2026-05-20, the week's trough
100
Top-level commandsCommand families in the redesigned surface, against the flat commands they replace
75โ†’44
โ–ผ โˆ’31

All counts were re-derived on 2026-06-12 from the window's commit history. No net line-count figure is quoted: at this volume the diff is dominated by fixtures and generated catalogues, and any net figure would describe those rather than the work. The honest size signal is commit shape and what landed.

Three honest notes

  1. The week's shape is the finding. 1,303 commits on Monday 18 May, 100 on Wednesday 20 May, then 1,028 again on Saturday 23 May. A single weekly total hides a swing of that size entirely.

  2. The command redesign landed; the surface work around it did not. 44 noun-verb families replace about 75 flat commands, with all 82 old names still resolving. The wider effort โ€” an extensible surface, and parity with the other ways of driving the system โ€” was still being aligned.

  3. Responsiveness is claimed structurally, not measured. The daemon no longer shares one lane between heavy work and the screen. No latency figure is offered, because none was taken.

In one line

heavy work stopped holding the only lane, bulk imports and replays stopped freezing the screen, a founding principle became a check that fails the build, cross-device pairing gained tests that can see it, and the command line went from 75 flat commands to 44 noun-verb families.

What changed, area by area

Every area that moved this week. Per-area file counts are not available for this window, so the areas are listed by what landed rather than ranked by traffic.

The four threads, in detail

The daemon.

It no longer blocks on its own thread, so the interface stays responsive while heavy work runs.

Its ceiling is your system's resources, it answers status and restart over a separate local channel, its liveness is a fact you can query rather than infer, and it has a clean restart path.

The promise it holds is narrow and worth stating exactly: when this thing is slow, the machine is busy โ€” it is never the process fighting itself.

Bulk import and replay.

Seeding, importing and replaying no longer freeze the screen.

The native side appends the work in batches instead of stalling the main thread until the entire job has finished, so control returns between batches rather than only at the end.

Making the same work faster would have bought a shorter freeze that came back the moment somebody imported twice as much; changing its shape survives the data growing.

The checker gate.

Wholeness stopped being prose and became a check that fails the build when one part of the system reaches outside itself for its own core work.

Three things landed together: a real coupling removed by giving a package its own literals, one genuine false positive excluded with a written justification, and a reconciliation of the exception list that ended at 2,420 valid entries after adding 2,404, dropping 66 stale ones and 356 references to files that no longer exist.

( When the axioms got teeth )

One inbox for every conversation.

Threads from outside messengers and from email can land beside native ones, because every bridge writes into the same conversation store and the inbox is a single question asked across it.

Bridges come in two shapes โ€” one that reads history in, one that also carries your replies back out โ€” and each runs walled off from the rest of the system, able to reach only its own platform. A reply routes back out through the bridge it arrived on.

The assistant may never join one of these conversations silently: permission is granted per conversation, and it identifies itself on every entry.

Named gaps: some platforms still have two routes in rather than one, reactions and edits carry less faithfully than plain text, and two people who talked elsewhere and later both arrive here have no settled rule for how their shared history reconciles. ( One inbox for every conversation you've ever had )

The command line.

About 75 flat top-level commands became 44 noun-verb families, with all 82 old names still resolving through a new home or a deliberate alias, and the health check kept at the top level because automation calls it there.

A sweep across all 508 entries in the command index confirms every one still resolves, with none excused by hand. The three moves transfer to any tool: group by noun, forward every old path, and test every leaf rather than trusting a careful read.

What the system is made of

Honest refusal on the wire.

A peer speaking an outdated version of the protocol is now recorded rather than quietly tolerated.

The receiving side emits a signed audit entry at the handshake and at the cross-machine connection points, and increments a counter on the refusal path, so an old dialect leaves a trace instead of being absorbed in silence.

Silently coping with a peer that has fallen behind is the kind of kindness that hides a real fact from the only people who could act on it.

Data that anyone can check.

A slice of your knowledge packaged to keep, back up or hand to someone now leaves the device in a published, externally verifiable format rather than one only NAOMS could fully read.

Native records stay in their own form on the device; the portable format is what crosses the wire, and the comparison that chose it explicitly rejected a path that would have swapped the internal hash function and flattened group signatures into a single one.

Five verifiable pack shapes ship as real verifiers in the project's own signed tree, a small general fix for reading nested fields was offered back upstream, and the new path stays switched off by default until parity work finishes.

The same exchange produced a standing rule adopted the same day: ship the minimum that delivers the contract, and delete the scaffolding, the just-in-case files and the comments that only restate the name above them. ( From memory packs to Aqua )

Chains.

Your personal record, your vault, a session, a group, a friendship, a shared repository and a channel are one append-only signed primitive rather than seven engines that resemble each other.

Four settings carry the whole difference: who may append, who is involved, which kinds of event the chain may speak, and how it is laid out internally. Syncing and governance become properties of the substrate, so a feature built for one chain is built for all of them.

The direction of travel is deliberately fewer kinds rather than more: things that once looked like they deserved their own substrate turned out to be better expressed as strands on a chain that already existed. ( Many kinds of chain, one engine )

Group governance.

A group is treated as a being with its own identity, memory and authority, using the same cryptographic surface a person does โ€” no admin panel and no privileged account outside the structure.

The default is consent rather than majority: a proposal passes when nobody raises a principled objection inside the window, so any single member can block, while fundamental changes need an affirmative threshold instead. Membership is a revocable link between two sovereign records, so leaving takes nothing of you with it.

The unresolved edge is named rather than papered over: the same veto that protects a sincere objector also arms someone determined to block everything, and exit only disciplines that when there is somewhere to go. ( The sovereignty of the we )

Device pairing.

A second device joins by proving it shares your root rather than by receiving a copy of it.

Both sides derive the same value independently, prove the match by challenge and response, and a short code read off one screen and confirmed on the other seals it against anyone trying to insert themselves.

Each device then holds its own distinct key, and the material that wraps your data stays on the machine that made it. Cross-device pairing is now also covered by automatic tests that stand up two peers on a shared helper, so a regression there is caught rather than reported by you.

( Adding a second device without copying the key )

Contacts.

A contact is a relationship both people signed rather than a string one person typed.

It is keyed on an identity that proves itself instead of a phone number that can be reassigned, comes into being only once both parties have signed over the presentations they chose to exchange, and stays directional โ€” the edge from you to me is a separate fact from the edge from me to you.

Standing is computed from those signed edges when someone asks, never filed as a score, and revoking is itself a signed event the other side can see.

A timing problem where the exchanged details and the signing steps could race โ€” quietly dropping a late-arriving claim โ€” was corrected, which is what decides whether a mutual relationship is whole or merely looks whole. ( An address book where every entry signs back )

Scheduling.

Two people agree a meeting time by passing signed proposals straight between their devices, with no scheduler in the middle.

Candidate slots carry a weight rather than a flat free-or-busy, either side may accept, decline or counter, and a hard cap on rounds stops a negotiation running forever. How much of your week the other side sees scales with the trust on that relationship and is capped at the more cautious side.

Every proposal names a deadline and what happens when it arrives โ€” expire, let the organiser pick from what is on the table, or roll forward a bounded number of times โ€” with a background timer firing the chosen rule.

Agreement opens a meeting record with a conferencing link and a short briefing assembled from context already held. Someone who does not use NAOMS simply receives an ordinary calendar invitation by email and replies to it from whatever they already use. Meetings between two people are the well-worn path; larger groups still have rough corners.

( Finding a time without a shared calendar server )

How the work itself changed

Agent runs.

A run can be targeted by capability tier or by naming the exact model.

The coarse form is for getting work done and describes the class of machine you want; the exact form is for comparing one model against another and names precisely what should run. Neither replaces the other: a control offering only precision is unusable day to day, and one offering only convenience cannot be tested.

Rules and procedures.

The rules that run NAOMS became a queryable engine, moved out of a wall of prose into a consolidated policy-and-procedure layer.

They are asked rather than read, which is the same move the checker gate makes one layer down. A rule the machine can be queried about is a rule that behaves the same at three in the morning as it does in a design review.

Handoff discipline.

A note passed between working sessions must now record only verified state, cited to code and history on disk.

Claims that could not be sourced were either labelled unverified or deleted, which made the note shorter and true rather than long and confident.

The failure it closes is specific: an unsupported sentence reads exactly like a supported one, because fluency costs the same either way, and the next person inherits the confidence without the evidence. ( Write the citation before the claim )

Networking study.

A close read of a mesh that runs over radio at as little as five bits per second, with no central addressing authority and no way to form an unencrypted link at all, produced three patterns worth borrowing: store-and-forward relays that hold a message for someone offline, a handshake where the destination proves itself while the caller stays anonymous until accepted, and discovery by small signed announcements that expire rather than by a global directory.

Dropping sender identity everywhere was rejected โ€” accountability inside a group is part of the point. The study also confirmed a direction already taken rather than changing one, which is a cheap and useful outcome for a week to buy. ( Fellow travelers: Reticulum )

โ† more in Process   home โœฆ   all โ†’