Network shared memory, and what it deletes
A network that can maintain itself can have memory. Programs on a FrogNet do not talk to each other. They share what they know. This is the shared memory. The threads are on different continents. Nothing else about it differs. One instance of the programming surface →




Intro to Tuples — addressing memory by three keys. Linda: Gelernter and Carriero, Yale, 1985; the three-coordinate key is FrogNet’s convention, not Linda’s.
Setting databasehost.frognet — the election, correctly, and the mechanism three other videos depend on.
Memory Backing Store — MySQL open, where the bytes actually live.
John W. Fawcett, CEO · Daniel Tone, VP of Engineering · Fawcett Innovations LLC
Dan was at his bench in New York. He set a sensor value. A physical actuator on that same bench threw, and a lamp came on.
Nothing about that is remarkable, until you follow where the value went. The write crossed 2,400 miles to the elected database in Seattle, and the consequence came back to a lamp three feet from his hand. No cloud service was in the path. No broker. Nothing had been configured to connect the switch to the lamp, because nothing connected them.
The part worth stopping on is not the lamp. Dan did not know he was setting a tuple. He was not thinking about a distributed system, an endpoint, a subscription, or a delivery guarantee. As far as he was concerned he was setting a sensor, and something that reads sensors noticed.
A surface has moved when competent people stop noticing the thing underneath it.
That is the whole claim of this page, demonstrated before it is stated. What follows is what was actually underneath, and what it costs you to use.
FrogNet gives programs shared memory across a network. Address a value, write it, read it. For current operational state, last write wins — because that is what a write is. FrogNet does not pretend this gives you consensus, cross-value atomicity, or a universal ordering model. When the problem actually requires an order, FrogNet elects for order rather than letting the shared picture pretend to provide one.
That paragraph is the entire model, and the rest of this page is consequences. Four claims, each mechanical, each checkable in the source.
There is one database per network, so there is one serialization point, so there is one order. Nobody writes a coherence protocol for a single-socket machine's DRAM either — the memory controller is the coherence. Across an internet, a deterministic election keeps there being exactly one. The election is not an implementation detail bolted to the side; it is the thing that preserves the memory model when the network spans more than one place.
Three coordinates, and you name them to fit your problem. There is no schema to declare and no registry to register with.
| Your problem | Your address |
|---|---|
| A program's state | program / variable / user |
| A machine's globals | host / globals / variable |
| A sensor fleet | sensor name / type / location |
Those are the same store. Nothing had to be told that a new shape of address was about to exist. And because names sort, you get a hierarchy nobody designed — the FrogNet monitor reads NY1.SemanticProxy.Engine and NY1.System.Perf.load out of the same space, and asking a node for everything it knows is a prefix query on a name.
A new reader of a value requires no change in the writer, because the writer expressed state rather than delivery. No endpoint. No schema bump. No notify list. No conversation with the team that owns the other service. The producer never learns the consumer exists, and never has to.
This is the claim that repays the most thought, because it is the one that changes what a system costs to extend. Every reader in a message-passing architecture is a relationship somebody maintains. Here there is no relationship to maintain, because there was never a conversation to have.
A write is an upsert against a unique tuple address — INSERT ... ON DUPLICATE KEY UPDATE, atomic resolve-or-create. One statement. A write does not require a read-modify-write cycle merely to resolve or create its tuple, which is the step that would otherwise race: two writers arrive at one serialization point, the database orders them, and the second one wins. Last-write-wins stops being a concession you make and becomes the arithmetic consequence of an atomic write at a single controller.
Be precise about the scope of that. It is a guarantee about the write, not about your application. Nothing stops you reading a value and deriving the next one from it, and if you do, you have built a read-modify-write cycle and you own its races — the same as you would in RAM, where the answer is a lock or an atomic. What FrogNet removes is the race that used to be unavoidable in the plumbing. What it does not remove is the one you choose to introduce. The reason this rarely bites in practice is the single-writer discipline below.
The FrogNet monitor is a mesh-wide dashboard. Select a node and you get its sensors; select a sensor and you get the JSON bag for that tuple. Two steps from a dashboard to the raw contents of network shared memory. If you have ever run top or read /proc, you already know what you are looking at.
Here is what it does not have. No agent on the monitored machines. No collector. No SNMP. No time-series backend. No per-node polling protocol. No registry of which nodes exist.
Selecting a node is a prefix query on a name. Learning about a peer does not involve the peer: the monitor builds the name <domain>.SemanticCache.Peer.<ip> and reads it. Every node writes what it knows about itself, and the monitor reads names.
| Lines | What it is | |
|---|---|---|
| Whole tool | 1,945 | curses UI, drill-down, probes, formatting, colour |
| Data layer | 318 | every read the entire tool performs |
Price a monitoring stack the way you normally would — agents to deploy, a collector to run, a schema to version, a vendor to talk to — and then set 318 lines against it. That is not a smaller implementation of the same thing. The thing stopped needing an implementation.
The difficult part did not get a better implementation. It stopped needing one.
REST is to FrogNet what assembly is to C: still there, still useful, and no longer the surface most application programmers should have to work at.
That is not a figure of speech. FrogNet's own memory calls are implemented as HTTP requests — a write is a POST to api.php, a read is a GET. The higher surface is built out of the lower one, exactly the way a compiler emits the instructions you stopped writing by hand. The lower surface has not been eliminated and is not going away, and when you want it you can have it: existing REST keeps working, and the compression and transport apply to it whether or not you ever write a line of UnREST.
The argument is not that REST is bad. Assembly was not bad. It is that recurring machinery eventually becomes understood well enough to move below the programming surface, and when it does, the application code that used to express it simply stops existing. Distributed programming has been waiting a long time for its turn.
If your code depends on knowing where the data came from, you cannot properly mock or simulate it.
That is the thirty-second test, and it is worth running against something you maintain. FrogNet's own discovery, routing and election code runs against a modelled topology on a laptop — the real code, not a reimplementation — because none of it asks where anything came from.
An elegant model that collapses the moment the network gets ugly is not worth adopting. So: the FrogNet Communicator carries a live transcontinental call over a 900 MHz radio link, and the recorded run reads like this.
| Measured | Rung | What was happening |
|---|---|---|
| 987 kbps | — | top of the run |
| 850 kbps | L7 | 720p held, 17.5 fps |
| 637 kbps | L6 | the picture steps down |
| 450 kbps | L5 | picture thinned, audio intact, call still up |
720p at 850 kbps is the headline, and it is a measurement rather than a bench estimate. But the number that should convince you is the other one. At 450 kbps the call is still up, the picture has thinned, and the audio has not — and when the ceiling comes back off, it climbs on its own. No reconnect. No redial.
That is the same shared memory underneath. The ladder is not a media feature bolted on; each participant publishes what it alone can know — what it is putting out, what is arriving, whether it is keeping up — and every participant derives the same rate from those values independently. Nothing is asked of anybody and nothing waits on a reply, which is why the call adapts without negotiating anything.
This is not distributed shared memory in the 1990s sense, and the difference is not a better algorithm. Those systems replicated memory across machines with no serialization point, so they had to manufacture an order out of message traffic, and that is where the latency came from. FrogNet has a controller. Different problem, not a better solution to the same one.
It is not a CRDT either. CRDTs add merge semantics that RAM never had, and charge for it in representation size and in programmers reasoning about a lattice instead of a variable. FrogNet does not converge; a value is current or it is not.
What you get is bounded-stale current state: a read returns some real moment, with a timestamp so you can judge how old it is. What you do not get is consensus, a total order, or two values moving as one. When the problem needs an order, elect for order. The shared picture is never allowed to pretend it is the order.
The Communicator is not a two-party tool. Each participant uploads one stream to the media host, the host mixes, each participant downloads one mixed stream — so adding participants does not change the bandwidth at any edge. That is what a mixer buys you, and mixers are not new: this is an MCU, and saying so costs nothing. What is new is where the mixer lives. mediahost.frognet is an elected role on hardware you own, found through shared memory — no account, no bridge service, no third party in the media path, and if the box holding the role goes away, the role floats.
The cost, plainly: the edges are constant, the middle is not. The media host carries N in, N out, and the mix. How many participants a call supports is a property of that box — cores, encoders, hardware acceleration — and it moves when the role moves. Publishing a single number for it would be the same error as publishing a single kbps floor.
And the limitation the ladder section above implies away. A client's send level is set by a tuple, and transmitter and receiver must sit at the same rung. Two-party, that is invisible. Multi-party, it is not: one participant on a starved link sets the level of the mixed stream everyone receives. Three on fibre and one on a bad radio, and all four get the radio's ladder. Per-leg independence is where this is going, not where it is — the fix is host-side transcoding, and it is the largest open piece in the media stack.
One further consequence worth stating plainly rather than leaving to be discovered: a write is one statement against one address, so composition is what you give up. In RAM you would take a lock across two locations. Here, each address has one writer by construction — a node writes its own state, and nobody increments anybody else's counter.
There is a complete technical book — the mechanism, the protocol, the elections, the compression, the media ladder, and a part on what is not built. It is finished, and it is free to read.
There is a simulator that runs the real discovery, routing and election code against modelled topologies, which is the part of this claim that is easiest to check and hardest to fake.
FrogNet is free for developers and for individual and non-commercial use. Commercial use requires a paid licence from Fawcett Innovations LLC. It is not open source and I am not going to call it that.
If you get to the point of asking what the consistency semantics are, what happens with two writers, or whether this could replace a piece of your own service choreography — that is the conversation I want to have.
Hand-tune your inner loops. Know your register pressure. Count cycles. Every one of those was correct, and every one was quietly conditional on something a compiler later removed. The people who worked out which wisdom had expired wrote the next thirty years of software.
A great deal of what this profession knows about distributed systems has the same shape. Real-time media rides UDP — because a late frame is worse than a lost one, which is true when you have no better way to shed load. Exactly-once delivery is impossible — for events, which compound when duplicated. Cache invalidation is hard — when a cache is a copy held at a distance from the authority. APIs must be versioned — when the producer owes a shape contract to named consumers. You need a service registry — when services have locations.
None of those is wrong. Each is downstream of a premise, and the premise is message passing.
And a memory model owes you a contract. What a get() must observe, what absence means, which write wins, what happens on reunion — stated as properties of the memory, with what is verified, implied, and still undetermined marked honestly. The contract, first draft →
Name a law in your own field. Recover the assumption that made it true. Check whether this substrate still supplies it.
That audit is open across every field and protocol, and almost none of it has been done. It needs no licence, no install, and nothing from us. And publishing "this one still holds, and here is why" is worth as much as finding one that has expired — arguably more, because it tells everyone else where not to dig.
Not every law expires. Reads scale by replication, and one elected host is a serialization point, which is a ceiling — the coherence win and the scaling cost are the same fact. Anyone doing this honestly will find laws that are still laws. That is what makes it research rather than advocacy.