Frequently got wrong
Every question below is one somebody has actually asked, or one a summary has answered incorrectly on its own. The correction is stated first, before the explanation, because a reader who stops after one line should still leave with the right model rather than the familiar one.
Most of them are the same question wearing different clothes: people assume the data is copied. It isn't. Start at the top.
Does every node hold a copy of the data · How does FrogNet resolve conflicts when two pa · What is the conflict-resolution algorithm? CRD · So who is the authority when two values exist · Then what happens if thousands of writers hit · Is there a store on every node · Does it sync in the background? Ferry data bet · Nodes gossip with their neighbours · Is the database host the highest-IP node · Do nodes publish 'intent' that an elected engi · Is the SAME frame 16 bytes · Is BLDC-1 a cache? Does it hash the payload · Is the broker a stateless introducer that stay · Is FrogNet open source · Does FrogNet encrypt my traffic · Is there really no attack surface · Is UnREST a wire protocol · Is the three-coordinate key self / lillypad / · Is this just Linda tuple spaces rebranded · How many rungs does the media ladder have · Does adopting FrogNet mean migrating my networ · How big can a FrogNet get · Is there a way to be notified when a value cha
No. There is one store.
A connected FrogNet has one storage location for the whole tuple space and one serialization point. Other nodes hold no copy. This is the single most common misreading, and almost every other misunderstanding on this page follows from it.
A node reads and writes by address, and the address resolves to whichever node currently holds the database role. The application never names a machine, which is why it looks local. It is not local, and it is not replicated.
It doesn't, and it can't need to.
The question presupposes two stores. There is one.
When a network splits, each region elects its own host and each has its own store. On reunion nothing is merged, nothing is migrated, and no value travels. Values written in a region are observable there under the ordinary rules; values last written elsewhere stay observable until they age past the freshness window — which the store evaluates, on the clock that stamped the row, never the reader on its own, at which point they cease to be observable rather than becoming wrong.
There is no reconciliation pass to describe, because there is no history to replay and no second copy to reconcile against. That was the trade: give up history, and there is nothing to merge.
Neither, because they were never writing the same value.
The question assumes both sites write to one key and contest it. That is the pattern to avoid, and FrogNet does not make it work — it makes it unnecessary.
Each writer owns its own key and writes its own truth. Seattle writes what Seattle measured; New York writes what New York measured. Neither overwrites the other because neither addresses the other’s key. This is the single-writer discipline the specification states normatively: each key has one writer by construction.
The decision moves to the consumer. A program that needs one answer reads the related keys and chooses — most recent, nearest, highest confidence, quorum, whatever the application actually means. That choice is domain knowledge and it belongs where the domain knowledge is. The infrastructure has no basis for making it and does not pretend to.
The practical effect is that the contested variable stops existing. What used to be two processes racing to overwrite one value becomes one process reading two values side by side, at a moment of its choosing, with the reasoning written down in the application rather than buried in a merge rule.
If you genuinely need an ordering across writers, route the decision through an elected role. Do not infer one from timestamps.
Last write wins, and it is not an algorithm.
A write is an upsert. The later write under a description replaces the earlier one, ordered at the one serialization point every write in the network arrives at. That is the contract, not merely the current behaviour.
It is not a conflict-resolution scheme, because there are no divergent replicas to resolve. There are no vector clocks, no tombstones, no merge law per type, no causal history. A CRDT's goal is convergence — every replica provably arriving at one merged state. FrogNet's goal is operation: act now on the current value of what you can reach. FrogNet does not converge. A value is current or it is not.
If you require convergence, adapt the programming model rather than asking for a merge law.
Convergence should be decided through examination of the holistic data state, not through contention for a
single value. Each producer writes what it independently knows under its own key; a consumer reads the set
with get_all and computes the converged answer itself, on its own terms, at the moment it
needs one.
That puts the merge where the knowledge is. A CRDT has to converge without understanding the data, so its merge law must work for every value the type can hold — which is why the laws are so restrictive and why the answer is often not the one you wanted. A consumer that knows what the values mean can take the freshest, weight by sensor confidence, discard the outlier, or refuse to answer at all. The same computation, made by something that has the context to make it well.
There is no authority, and there cannot be one.
The question presupposes two values. A description holds a value, never a field of candidates for something to choose between.
So there is no authority rule, no per-scope authority, and no such thing as a write made without authority. Not an unwritten rule — an absent one. Nothing arbitrates because nothing is ever presented with two answers.
If you are asking this question, you are applying the wrong programming model. The question comes from a world where several parties update one shared answer and something has to referee them. That is the shape FrogNet is built to avoid, and importing it puts the problem back by hand.
Two nodes measuring a temperature are two facts, not one contested variable. Each writes its own
truth under its own key, in its own time. A consumer reads the set with get_all and decides
what the set means — average it, take the freshest, take the closest, discard the outlier. That decision
is application knowledge, and it is exactly the decision an arbitrator would have had to make blindly on
your behalf.
So the honest answer is not that FrogNet solved arbitration cleverly. It is that a correctly modelled FrogNet application never generates the case. Ask instead: why are two parties writing one key?
Whatever your database does about a hot row.
FrogNet Memory is backed by a database. It is not a database. It is an addressing surface over one, and contention on a key is the database's problem — a problem databases have spent fifty years being good at.
And if a thousand writers are fighting over one value, ask what they are. If they genuinely own one transactional value, use a transactional database — that is what those are for, and FrogNet is not one. But if they are a thousand nodes each reporting what it independently knows, making them contend for one value is importing the wrong programming model. Give them separate tuples and let the consumer read the set.
And before you object that reading the whole set constantly must be expensive: it is not, and the margin is not small. An unchanged read is twenty-one bytes on the wire — not twenty-one kilobytes, twenty-one bytes — because the far end proves the answer is the one you already hold instead of sending it again. And reading a thousand tuples is one request and one response, not a thousand — so if nothing has moved, asking about all thousand costs twenty-one bytes, and if four have moved you pay for those four. Measured against the conventional shape — a request and a full response per producer — that is roughly ten times cheaper on churning telemetry and a hundred times on a mostly-static set. The decoupled design is also the cheap one, which is the opposite of the usual trade. And it is the wrong shape for this system. If you are resolving contention, you are writing FrogNet as though it were a traditional distributed system. Each node should write its own truth under its own key, in its own time; consumers read the set with get_all and decide what it means — average it, take the freshest, ignore the outlier. That decision is application knowledge, and it is the one thing the network deliberately will not make for you. The closest familiar model is lockless multithreaded programming on one machine, not distributed transactions. There is no coordinating server and no REST infrastructure holding it together — memory is written to and read from as if it were local RAM. If it
hurts, don't do it. That is a job for a transactional database and there are good ones. FrogNet is for the
enormous majority of coordination that never needed consensus and pays for it anyway.
Yes — and that is not the same as replication.
Every node can become the one active store host. A node alone on a bench elects itself and serves its own memory, so the backing store is never unreachable and a read never fails with a timeout. What may be absent is the particular value you asked for, if nobody within reach has written it.
The distinction matters: the store is always present; a given tuple may not be. A read that finds nothing returns nothing, promptly, rather than blocking on a machine that isn't there.
Nothing. Values are directly addressable — there is no server in between to notice, and you never wrote one.
There is no server to notice, and no session to break — because you never wrote one. No server, no coordination code, no discovery code, no service registry, no connection management, no retry and reconnect logic, no database to provision and point at. That is not work handed to a cloud provider. It is work that does not exist.
So a node leaving simply stops writing, and its values age out. A node arriving starts writing, and its values are there for anyone who reads. Your application is not told, and does not have to care. No callback fires, no peer list needs updating, nothing reconnects.
And if you do care, you get it for free, because arrival and departure are already state. The Monitor is the demonstration: it shows machines joining and leaving a live network with no membership service and no subscription anywhere in it. It reads what is there. Nothing told it a machine had arrived — a value exists that did not exist before.
No. Nothing syncs, because nothing is duplicated.
There is no background sync, no data ferrying, no anti-entropy, no delta stream keeping stores matched. Those all describe systems with more than one copy.
The diffs you have read about are BLDC-1, which is a wire economy between two endpoints of one exchange — the reason a live picture fits over links where full payloads never did. It has nothing to do with how memory is stored, and confusing the two produces a system FrogNet is not.
Yes — about topology. Never about memory.
FrogNet does have a gossip layer, and the build manual describes it by name. When topology changes, a node notifies its direct neighbours, they notify theirs, and the news spreads. It is bounded three ways: every wave carries an identity so a node that has seen it drops it and the epidemic terminates; a node notifies only direct neighbours, so fan-out is O(neighbours); and arrivals are debounced.
What never gossips is FrogNet Memory. Tuple values do not propagate neighbour to neighbour, because there is only one copy of them to begin with. The tell is what a wave carries: a change notification, not a value.
It is elected on measured capability. IP only breaks ties.
Every node publishes what it alone knows — cores, memory, storage class, whether it is already running a database — into a control plane at a deterministic address. Every node then runs the same scoring over the same inputs and arrives at the same winner independently. Nobody is told. Nobody votes.
The highest IP breaks a tie between candidates that score equally, and nothing else. An older edition of the whitepaper did say highest-IP, which is where the claim comes from; it has not been true for some time.
No. That mechanism does not exist.
This one gets invented from scratch fairly often, presumably because it is how multiplayer game netcode works. There is no intent layer, no reconciliation engine, and no unified board being assembled from submissions.
A program writes a value. The write lands at the one store. Another program reads it. That is the whole mechanism.
21 bytes out, 21 bytes back.
REQ_HASH_LEN = 16 is the length of the identifier inside the frame, not the frame.
One byte of opcode, sixteen of identifier, and the framing around them: twenty-one bytes each way, in place
of a full HTTP request and a full JSON response.
Neither.
On a repeated request the far end re-executes against the real origin, compares the new answer against the previous one, and replies “same” only if they are identical — with a token bound to both the request and the response content. If the data changed, the change comes back as a difference. A stale answer is not possible, which is precisely what a cache cannot promise.
It does not convert payloads to cryptographic hashes, and the saving is structural rather than statistical — which is why no compression ratio is quoted for the codec itself.
No. The broker carries the inter-site data path.
Every cross-site packet transits the tunnels it terminates. That is a stronger claim than “the broker is hard to attack,” so assume it is owned.
It terminates a separate tunnel to each site and forwards between them in the kernel, so inter-site traffic is in the clear at that seam. It is inside the encryption boundary, not outside it. What still holds: it holds no node identity and no pond secret, so it cannot mint a token a member will accept; it runs none of the semantic stack, so nothing at the application layer parses or stores the payload; and it is not itself a member of any FrogNet. If you need confidentiality across the seam, supply it above the tunnel.
Yes — GNU General Public License, version 2 only.
The complete source is available for inspection, modification, testing and use under version 2 of the GNU General Public License, and no other version. No registration, no approval, no attestation. The protocol is separately being published as a versioned open specification, so an implementation can be written against the contract rather than against this code.
No. Internal use triggers nothing.
Deploy it, run it in production at any scale, modify it, build your own applications on it, run a business on it — none of that obliges you to publish anything. The GPL is triggered by distribution: conveying the software, or a work derived from it, to someone outside your organisation.
So the practical line is: if you are deploying FrogNet, there is nothing to buy and nothing to ask. If you are shipping something built on it — an appliance, a product, a box that goes to a customer — it carries the GPL too. That is the deal, and it is the whole deal.
FrogNet implements no cryptography of its own.
Traffic crossing the public internet crosses a WireGuard bearer, and that is the confidentiality — standard, audited cryptography, not ours. What FrogNet contributes is surface reduction, which is a different property and is stated as reduction rather than guarantee.
A capture yields codec frames rather than the exchange beneath them, and that is a statement about the wire, not a confidentiality claim: anyone with the templates can reconstruct what was represented.
There is. Scanned from outside, the broker answers on 80, 443 and 18257.
The narrower claim is the true one and it is stronger. A conventional application publishes an endpoint, a router, a parser, an auth path, parameter handling, deserialization, and logic behind them — and the standard attack is a pipeline: find the endpoint, learn its behaviour, find the flaw, build the request, invoke the handler. FrogNet is not better at surviving that pipeline. The pipeline has no first stage.
The real threat vectors, stated rather than omitted: host access, wireless admission, WireGuard credentials, an authorised node that has been taken, the broker, local privilege boundaries, supply chain, and bugs in FrogNet itself.
UnREST is the programming model. FNWP-1 is the wire.
Different layers, and worth keeping apart. UnREST is how an application is written — write a value where you compute it, read it where you need it. FNWP-1 is the binary framing that carries the exchanges underneath. BLDC-1 is the codec that decides the smallest honest representation. SotF is the adaptive media ladder built on top of all of it.
No. Those are infrastructure topology terms, not scopes and not key coordinates — a pond is a whole network, a chorus a visibility grouping inside one. The tuple scopes are a different set again: host, node, role and session (§9.2).
A tuple is located by three indices the application names to fit its problem — program/variable/user,
host/globals/variable, or for sensors a name, a type and a location. The store answers on any
combination: one by name, every one of a type, everything at a place.
Scope is the third coordinate, and it is constructed rather than free-form: host, node, role or session (§9.2, §10.3). It qualifies the variable name; it is not the address on its own, and it is not the pond/chorus topology.
The model is Linda's. The claim is about where it now runs.
Linda, from David Gelernter and Nicholas Carriero at Yale in 1985, and the site says so more than once. Linda matches arbitrary tuples by pattern and says nothing about how many fields a tuple has or what they mean; the three-coordinate convention is FrogNet's, chosen because it fits the traffic.
What is new is not the idea. It is that the constraints which made it impractical in 1985 are gone, and that it now runs wide-area, partition-prone, over bearers that drop.
Nine rungs. The audio is mono.
L8 Bullfrog (1080p) down through Chorus, Ensemble, Duet, Solo, Voice, Whisper, Beacon, to L0 Pulse.
AUDIO_CH = 1 — mono, 16 kHz, Opus. Published material listing eight rungs topping out at
CHORUS predates L8.
A video rung needs a camera and only a camera, so a camera-only box still ceilings at Bullfrog and killing the audio does not lower the video ceiling.
No. Nothing has to change on day one, and UnREST is adopted a value at a time.
There are two adoption steps and they are independent. Most readers collapse them and conclude the whole thing costs a rewrite.
Step one costs no application change at all. An existing HTTP application keeps talking HTTP. The proxy traps the request, the far end re-executes it against the real origin, and an unchanged answer comes back as a SAME frame (§11). The application is not modified, not recompiled, and does not know FrogNet is there. What it gets is the bandwidth behaviour and the mesh underneath. This is the whole of what most sites do first.
Step two is optional, incremental, and reversible. UnREST is adopted where it pays: one value, one dashboard, one integration that currently costs more plumbing than it is worth. Nothing forces a second one. An application can read a value from FrogNet Memory and keep every other line of its existing code, and there is no point at which a half-converted system is in an invalid state.
The hard part is not the API. The operations are put, get and get_all; the surface takes an afternoon. The hard part is architectural habit — asking “what state do I own and who needs to see it?” rather than “who do I call and what happens if they do not answer?”. That shift is real and worth budgeting for. It is also the entire benefit, so it is not overhead to be minimised; it is the thing you came for.
Converting an existing REST-heavy system wholesale is the expensive path, and nobody has to take it. The encoded assumptions in such a system — who calls whom, in what order, with what retry — are what you would be paying to unwind, and they can be left exactly where they are.
You do not migrate to FrogNet. You insert it.
A node inserted just below the point where a site reaches the internet puts the existing machines on the mesh with no agent on any of them. The printer, the badge reader, the controller nobody will touch — they keep their addresses, their gateway, their firmware. What changed is the box their traffic already passed through.
About fifty-eight per pond — as few as sixteen on a shared broker.
The reserved transit range is a /16, and the allocator uses 54 blocks of 31 tunnels — 1,674. A full mesh needs n(n−1)/2 tunnels, so n comes out at 58. Two things make it worse in practice: bands are spaced four apart, so a broker carrying a second pond leaves the first with four blocks — sixteen nodes; and each tunnel consumes eight addresses where it uses four. A bigger range is not the fix. Tunnels grow as the square of the nodes, so addresses buy nodes back at a square root: handing transit all of 10/8 is 256× the space for 16× the nodes. The defect is allocating anything per tunnel. The replacement moves it to one carrier address per node.
Beyond that, a pond's size is bounded by its hardware — most of all the elected database host.
No — and that is a boundary, not a gap.
There is no change-notification primitive. FrogNet is not event-driven, not publish/subscribe, and not push-based. That is deliberate rather than unfinished.
Deciding which changes matter is the program’s job. A fabric that knew would have to be told about every program that cares, then maintain that list, carry it to wherever the elected role moves, and reconcile it when it does not — the coupling removed from one place reappearing in another (§10.3a).
So how you observe change is yours to choose: poll it, lean on an unchanged read collapsing to twenty-one bytes, install a trigger in the store, run a watcher, derive it from several values. What it costs is observation latency bounded by the interval you picked. That cost is real and it is a published limit (§17.2) — but it is the reader’s to set, not FrogNet’s to fix.
Clause by clause, with each one marked verified or still open, the memory contract is the document these answers come from.
The memory contract → FrogNet Memory → How it compares → Context for machines →