T27.AI

Blog

Receipts and coverage seals over a radio mesh

2026-08-19 · 21 min read

Bytes crossed two radio hops between four boards and arrived byte-exact, with one coverage seal recomputed independently at three points and agreeing at all three. What that proves, what it does not, what the thing is built on, and where the commercial radios are plainly ahead.

MeshDePINVerifiable computeZynqopenXC7

Four single-board radios on a desk, one subnet, 2.4 GHz. Bytes left the first board, crossed two radio hops, and arrived byte-exact at the fourth. At three separate points along that path a coverage seal was recomputed independently, and all three agreed: 0x9DBE2510. That is the whole result, and the rest of this page is about what it is worth.

What here is ternary, and how far each part got

Ternary here is one specific circuit, and it deserves naming before anything else: a two-bit weight code where 01 selects +x, 10 selects -x and everything else selects 0. Every multiply becomes a sign flip, the multiplier disappears, and a plain adder tree does the rest. That single primitive carries the 8-tap matched filter, the 63-tap PN despreader, a systolic tile, and a small classifier whose weights are all -1, 0 or +1.

A second, unrelated thing in the same repository also wears the word. GF-T, the number format, has an exponent that is balanced-ternary only in the sense that its code space holds 3^Et values centred on a bias. In hardware the exponent update is a_off + b_off + carry - BIAS: an ordinary binary add. No trit is ever materialised. Both are called ternary and only the first is a ternary circuit.

BlockWhat is ternary about itHighest status actually reachedWhat proves it
Ternary MAC probeThe primitive on its own: the processor hands the fabric a sample and a two-bit weight, the fabric answers +x, -x or 0Ran in the PL of a board on 19 Aug 2026: anchor 0x47C0, heartbeat moving, and +42, -42, 0, 0, +127, -127 for weight codes 01, 10, 00, 11 -- bit-exactresults/ps7_probe_silicon_2026-08-19.log, produced on the day
8-tap matched filterSign-select taps; the sync code uses all three symbols, two of eight taps are 0Ran in the fabric of a Zynq XC7Z020 on real captured air: 256 of 256 bit-exactresults/cstream2.log, on branch feat/own-rtl-in-radio-path
63-tap PN despreader63 sign-select taps summed in a balanced adder tree, zero DSP48E1 by constructionRan in the fabric: 256 of 256 bit-exact, with negative controls and two faults injected into the bitstream itself failing as predictedresults/pn63_success.log, negative_controls.log, bitstream_mutants.log, same branch
Systolic tile and classifierSame primitive; the classifier's weight file uses exactly three codes and nothing elseSynthesised: 527 LUT, 45 CARRY4, 165 flip-flops and no DSP48E1 cell at allyosys synth_xilinx on tern_corr8_stream, re-run rather than quoted
GF-T arithmetic in RTLExponent code space of 3^Et values; the add itself is binarySimulated and synthesised: a 16-bit multiply is 1 DSP48E1, 47 LUT, 18 CARRY4fpga/gft/SYNTH_RESULTS.md, reproduced
GF-T on siliconWould be the same format, executing on an Artix-7Not proven. Claimed in detail, but no place-and-route log, no bitstream, no UART capture exists in any branchdocs/VERIFIABLE_COMPUTE.md asserts it; fpga/gft/SYNTH_RESULTS.md says place-and-route and a loadable bitstream are not proven

That last row was worse than a gap when this page first went up: one document claimed the engines had been flashed to a board, another in the same directory said a loadable bitstream was not proven. Both could not be true. The correction is written and public as tri-net pull request 382 -- it makes the claim read as simulation and names the four artefacts that would close it: a pin-complete constraint file, a routing log naming the top, the bitstream that log produced, and a programming transcript with the part identifier read back. The quoted vectors could never have settled it, because they are the same golden vectors the simulation benches carry.

That row is not a quotation from an old log. It was produced while this page was being written, on a board plugged into the desk: the JTAG chain reads back part identifier 0x23727093, the XC7Z020 this page claims, and after the load the fabric answered with the anchor 0x47C0 that separates our bitstream from the vendor one. The first attempt rebooted the board -- writing a bitstream while the radio DMA is mid-transaction hangs the processor and only the watchdog ends it -- so the load now tears the bus down first: kill the sample streams, unbind the radio cores, unbind both DMA engines, drop the fabric Ethernet, then write. A plain reboot restored the vendor image and the radio afterwards, which is why this is a safe thing to have done.

What this actually is

It is not a product you can buy. It is an open repository -- one Rust crate, Apache-2.0 -- plus a log of measurements taken on hardware anyone can order, written so a stranger can re-run them. What the code does: it moves bytes between radio nodes and, for every hop, emits a signed receipt saying which node carried how many bytes in which epoch.

The hardware is four Puzhi P201Mini boards, each a Zynq-7020 with an AD9361 radio, plus one ALINX AX7203 (XC7A200T) on the bench for the arithmetic work. You buy the boards yourself; nothing here is sold.

The claim under test is narrow and it is an accounting claim, not a radio claim: when a byte crosses a network of independent radios, can the network produce a record of who carried it that a third party could check without trusting any of them?

What it is built on

STACK: what the node is made of, bottom to top gold = vendor part, vendor tool, or not proven HOST SW OPEN Rust crate trios-mesh, edition 2021, Apache-2.0 107 .t27 specs -> gen/rust, gen/c, gen/zig MESH OPEN ETX + WMEWMA routing (-sim), 10.42.0.0/24, TTL 8 link = UDP-over-Ethernet stand-in (Transport) SEAL OPEN ChaCha20-Poly1305 AEAD, X25519 + HKDF-SHA256 Ed25519 + SHA-256; epoch_seal = mix32 checksum RADIO LINK OPEN host-side DBPSK, 768 kHz subcarrier, 63-bit PN AD9361 via iio_writedev / iio_readdev, 2.4 GHz NUMERICS OPEN SPEC GF-T4/8/16/32, balanced-ternary exponent ladder GF16 [s:1][e:6][m:9] bias 31, host model only PS / ARM LINUX VENDOR BOOT dual Cortex-A9 ARM Linux; armv7 musl binaries AD9361 control over IIO sysfs (LO, bw, RSSI) PL FABRIC OPEN RTL not loaded ternary weights, 2 bits: 01->+x, 10->-x, 0 DSP gft16_mul = 1 DSP48E1 + 47 LUT + 18 CARRY4 BENCH SILICON VENDOR PART Xilinx Artix-7, part xc7a200tfbg484-2 ALINX AX7203; 740 DSP48E1, 134,600 6-LUTs NODE SILICON VENDOR PART Xilinx Zynq-7020, part xc7z020clg400-1 AD9361 on the same Puzhi P201/P203 Mini board NOT PROVEN No bitstream of the project's own has ever been configured into a Zynq Mini PL. GF-T: no place-and-route, no timing closure, no on-silicon run claimed. The OTA link ran host-side. PS BOOT: VENDOR BOOT.BIN + FSBL from Xilinx bootgen (PetaLinux 2020.2) or Puzhi image BITSTREAM FLOW: OPEN yosys synth_xilinx -> nextpnr-xilinx -> prjxray fasm2frames -> xc7frames2bit in regymm/openxc7; no Vivado iverilog TB, openocd JTAG
Read bottom to top: the node is vendor silicon (xc7z020clg400-1 with an AD9361, xc7a200tfbg484-2 on the bench) carrying an otherwise open stack — Rust crate trios-mesh over 107 t27 specs, a ChaCha20-Poly1305 seal, an ETX mesh, and a bitstream built by yosys, nextpnr-xilinx and xc7frames2bit with no Vivado. Only two places still require the vendor toolchain, the parts themselves and the Zynq PS boot image from Xilinx bootgen; the fabric layer is RTL only, since no bitstream of the project's own has ever been loaded, which is why the over-the-air link ran entirely host-side.
LayerWhat it actually isWhere it runsArtefact
Node boardPuzhi P201Mini: Zynq-7020, part xc7z020clg400-1, dual Cortex-A9 plus Artix-class fabric, with an AD9361 transceiverFour of them, on one subnettools/jtag-bootstrap/README.md
Bench boardALINX AX7203: Artix-7, part xc7a200tfbg484-2, 740 DSP48E1 and 134,600 6-LUTsDesk, separate from the radio nodesfpga/gft/gft_alu_ax7203.xdc
Radio front endAnalog Devices AD9361, driven from ARM Linux over the IIO interface -- LO, bandwidth, sampling rate, RSSIOn the node's Linux, not in the fabrictools/ad9361_config.rs
Bitstream flow -- openyosys synth_xilinx, then nextpnr-xilinx, then prjxray fasm2frames and xc7frames2bit, in the openXC7 container. No Vivado anywhere in this pathHost, under Dockerfpga/ternary/ps7/build/run_openxc7.sh
Boot chain -- still vendorBOOT.BIN and the FSBL come from Xilinx bootgen via PetaLinux, or from the vendor board image. The fabric flow is open; the boot chain under it is notHost, preparing the SD carddocs/LOCAL_FLASH.md
SoftwareOne Rust crate, trios-mesh, edition 2021, Apache-2.0: 10,805 lines under src/ and 432 test blocks. Above it, 107 t27 specification files that generate the Rust, C and ZigCross-compiled armv7 musl, run on the Cortex-A9Cargo.toml, tri-facts.json
The link that carried the bytesA DBPSK modem on a 768 kHz subcarrier with a 63-bit PN preamble and a differential detector, fed to the AD9361 through iio_writedev and iio_readdevIn software on the node's ARM, with no bitstream of ours loaded into the fabricsmoke/DEPIN_OTA_CLOSED_2026-07-18.md
Session encryptionChaCha20-Poly1305 AEAD, key from an X25519 handshake through HKDF-SHA256, directional nonce, 64-frame replay window, re-key every 2^20 framesNode ARM and hostsrc/crypto.rs
ReceiptsEd25519 signatures verified against the executor's public key; SHA-256 digests and Merkle batchingNode ARM and hostsrc/bin/trinet_a2a_node.rs
Coverage sealepoch_seal: three rounds of a 32-bit xor-shift-add mixer combined with a rotate. It binds identity, epoch and byte count into one 32-bit wordOn the nodes during relay runsspecs/tri_depin.t27
RoutingAn ETX metric with a WMEWMA delivery estimator, mesh subnet 10.42.0.0/24, default TTL 8. Marked -sim: host-testable. The shipped daemon uses UDP-over-Ethernet as a stand-in for the radio linkHost tests; daemon on the nodessrc/routing.rs, src/bin/trios_meshd.rs
Arithmetic in the fabricGF-T, a float ladder with a balanced-ternary exponent. A 16-bit multiplier synthesises to 1 DSP48E1, 47 LUTs and 18 CARRY4Real silicon on the AX7203, over UARTfpga/gft/SYNTH_RESULTS.md

Two rows in that table are the ones to read twice. The bitstream flow is genuinely open -- no Vivado produces the fabric image -- but the boot chain beneath it still comes from the vendor, and saying otherwise would be the easiest lie on this page. And the modem that actually carried the bytes ran in software on the ARM core: no bitstream of ours has ever been configured into a node's fabric.

The seal is a checksum, not a hash

epoch_seal is three rounds of a 32-bit mixer. It is keyed, and it binds identity, epoch and byte count, so two witnesses agreeing on it is evidence they metered the same traffic. It is not a cryptographic hash, it is 32 bits wide, and the specification's own comment says a production settlement would additionally sign it. Session traffic is separately protected by ChaCha20-Poly1305; the seal is the accounting digest on top, and it is the weaker of the two.

What was actually measured

TWO-HOP RADIO RELAY, 2026-07-18 -- one seal value, three comparison points3 x Puzhi P201Mini: Zynq-7020 (xc7z020clg400-1) + AD9361, ARM Linux, no PL bitstream loadedNODE .13Puzhi P201MiniZynq-7020 + AD9361origin, TXNODE .12Puzhi P201MiniZynq-7020 + AD9361relay: otarelayNODE .10Puzhi P201MiniZynq-7020 + AD9361receiver: otarxsetHOP 1 -- air2.4 GHz ISMDBPSK 768 kHzHOP 2 -- air2.4 GHz ISMDBPSK 768 kHzSEAL POINT 1 (origin)0x9DBE2510distinct = 4SEAL POINT 2 (hop-1)0x9DBE2510distinct = 4SEAL POINT 3 (hop-2)0x9DBE2510distinct = 4 (x3)====Same value at all three points: the payload set crossed two radio hops bit-exact.seal = epoch_seal(acc, total_bytes, node_key, epoch) -- 3 x mix32 + rotl32 into a 32-bit worda keyed checksum, not a cryptographic hash; computed in software on each Cortex-A9, no PLstore-and-forward, time-separated hops on one shared channel; relay path ran on the nodes, no host in the loop
Three independent recomputations of the coverage seal -- at the origin .13, on the relay .12 after hop one, and on the receiver .10 after hop two -- all yield 0x9DBE2510, so the four-payload set demonstrably survived both radio hops bit-exact. What the match does not establish is authorship: epoch_seal is a 32-bit keyed checksum, not a signature, and it was computed in software on each board's Cortex-A9 with nothing loaded into the FPGA fabric.
RunResultUnder what conditionsRecorded in
Two radio hops, .13 to .12 to .10Coverage seal 0x9DBE2510 identical at all three points; 4 distinct payloads at hop 1One shared 2.4 GHz channel, store-and-forward, hops time-separated rather than concurrentDEPIN_2HOP_RELAY_2026-07-18.md
Two hops carrying an ordered 31-byte message6 of 6 chunks at both hops; seal 0x37A9A9F6 identical at origin, relay and receiverFixed test string on a cyclic bufferDEPIN_MSG_RELAY_2026-07-18.md
Two hops with the hops running at the same time5 of 5 chunks at the relay while its own transmitter ran; seal 0xE0AA4F5D identical at three points50 MHz transmit/receive separation on one AD9361 in FDDDEPIN_FDD_PIPELINE_2026-07-18.md
Three hops, .13 to .12 to .11 to .10Correlation peak 1.000, 0.998, 0.998; bit error rate 0 of 128 on every hop, first attempt on eachOne transmitter at a time on the shared medium; capture retried until cleanDEPIN_RELAY3_RTI_2026-07-19.md
Four nodes, one transmitter, three witnessesAll three witnesses independently reported 8 distinct frames and the same seal 0xCDB1F3B1Each witness captured the air on its own and metered what it heardDEPIN_STREAMING_4NODE_2026-07-18.md
Eight bytes over the air, closed loopCorrelation peak 1.000, bit error rate 0 of 64, payload recovered exactly -- twice8-byte frame, transmit LO 2400 MHz at -10 dB gainDEPIN_OTA_CLOSED_2026-07-18.md
Transmit power sweep-10 dB: 0 of 64 errors. -20 dB: 0 of 64. -30 dB: 0 of 64. -40 dB: 34 of 64 -- the link breaksGain is the AD9361 setting in dB, not radiated powerDEPIN_RADIO_RECEIPT_2026-07-18.md
How far apart two channels must be on one chip50 MHz and 20 MHz separation: clean. 15 MHz and 13 MHz: wrong seal on some chunksAnalog limit; narrowing the receive filter did not helpDEPIN_FDD_SWEEP_LIVE_2026-07-18.md
Sixteen-megabyte capture, demodulated offline815 frames, 748 clean (92%), 6,520 bytes, one receiptAbout 136 ms of air; roughly 3 s of processing on the ARM. One run, not repeatedDEPIN_STREAMING_4NODE_2026-07-18.md
Why frame tracking was neededFixed grid: 0 of 819 frames clean. Two boards' 30.72 MHz oscillators differ by about 10 ppmThe control condition that locked-position tracking replacedDEPIN_STREAMING_4NODE_2026-07-18.md
Arithmetic on real silicon5 of 5 GF-T16 multiplies and 3 of 3 dot products bit-exact against a host oracleAX7203, openXC7 flow, results returned over UARTdocs/VERIFIABLE_COMPUTE.md
Adversarial cases on the sealed wire5 negatives rejected: tampered assignment, tampered result, replayed counter, operand swap, and a blind relayReal AEAD with the routing header as associated datadocs/VERIFIABLE_COMPUTE.md

The defect the run found

The first attempt at hop one did not produce 0x9DBE2510. It produced 0x7ACD8A11, from five distinct payloads instead of four: one frame carried bit errors, passed the correlation gate at 0.9, and decoded into a payload that was never transmitted. The seal did exactly what a seal is for -- it disagreed. A majority filter over repeated copies was added, and the run was repeated until the three points agreed.

This is the part worth keeping. A metering scheme that never disagrees with itself has not been tested; this one disagreed, the disagreement was traced to a specific decoder weakness, and both the failure and the fix are in the log.

The shortest description is an accountant

Strip the radio away and what remains is bookkeeping. Each node keeps a running count of the bytes it carried, binds that count to its own identity and to an epoch, and signs the result. A second node that heard the same traffic produces the same digest independently. Settlement is then arithmetic over records that several parties can check, rather than a claim by whoever owns the base station.

That is why the interesting artefact of a two-hop relay is not the bytes. It is three independent recomputations of one number by three machines that did not consult each other.

Where this is useful, in civilian terms

SituationWhat the receipt provesWho has a reason to pay
Someone is paid to provide coverage in a place nobody audits -- a rural relay, a campus, a buildingThat traffic actually crossed that node in that epoch, attested by neighbours who also heard itWhoever funds the coverage and currently has to take the operator's word
A sensor network bills by data deliveredHow many bytes reached the far end, not how many were sent hopefullyThe party being invoiced
A contractor claims a link was up during an outage windowPer-epoch counts from more than one witness, which a single operator cannot forge aloneInsurers and whoever wrote the service-level clause
A compute job runs on a device you do not ownThat the returned answer matches a signed commitment to the inputs and the code pathWhoever pays for the compute

None of these is a deployment. No settlement contract is deployed, and no buyer for any of it exists today. They are the situations the measurements were designed against.

Who else is in this market

WhoWhat they sellWhat they do not give youWhere this differs
Persistent Systems, MPU5A fielded MIMO mesh radio; datasheet ceiling up to 150 Mbps, FIPS 140-2 Level 2 crypto moduleNo published waveform or routing spec, no conformance vectorsThe wire contract here is a file in the repository under Apache-2.0
Silvus, StreamCasterProprietary MIMO waveform, 100+ Mbps, 7 ms average latency at 20 MHzNo published spec, no conformance vectors, no unit priceThe largest network demonstrated here is four nodes; three of them independently metered the same air
Rajant, Kinetic MeshPatented Layer-2 mesh routing, no root node, hundreds of Mbps per radioBase implementation is capable but not certifiedCrypto here is ChaCha20-Poly1305 with X25519, specified in-repo and certified by nobody -- a weaker assurance, stated plainly
Doodle Labs, Mesh RiderLow-power multiband mesh for drones; NASA measured 37.9, 5.6, 1.2 and 0.3 Mbit/s at one to four hopsEncryption and routing internals not publishedBytes here survived two hops bit-exact with a matching seal, but no throughput at any hop count was ever measured
MeshtasticOpen LoRa mesh, 0.3 to 300 kbps, AES-256 in software, tens of kilometresNo wideband channel, no metering or reward layerWideband 2.4 GHz and a receipt layer, against a far smaller and far less mature ecosystem
openwifi / open-sdrFully open Verilog 802.11 with the OFDM physical layer running in FPGA fabricNot mesh-native; targets a consumer standardThe closest open competitor, and ahead where it counts: its physical layer runs in fabric. Ours ran in software on the ARM
AREDNFree firmware turning commercial routers into a 2.4/5 GHz mesh, 5 to 50 kmNo custom waveform, no metering or attestation layerAhead on maturity and deployed node count, which the repository lists as its own disadvantage
Helium and similar token networksToken rewards to hotspot operators, plus carrier data offloadParticipation is not bound to a specific verified deviceThree witnesses here independently produced the same coverage seal from air they each heard

Read that table for where it loses. On throughput, latency, range, ecosystem and certification, the commercial radios are ahead by margins that are not close, and openwifi is ahead on the one open-hardware axis that matters most. What is different here is narrow: the accounting layer, the fact that the wire contract is a file rather than a datasheet bullet, and a log that records the failures alongside the passes.

What we are not claiming

Roadmap, and what a partner would be joining

ROADMAP - spec to fabric: three stages measured, the fourth not done solid = done, artefact named | dashed gold = not done, no date claimed 1 2 3 4 1 SPEC + HOST DONE (counts re-run) 107 .t27 specs -> gen/rust, gen/c, gen/zig 432 #[test] blocks 10,805 lines in src/ crate trios-mesh, Rust edition 2021, Apache-2.0 router/modem/gf16: -sim -> tri-facts.json 2 ON THE NODE (ARM) DONE (on device) armv7l Cortex-A9, RC=0 ChaCha20-Poly1305 seal, 534,604 B static binary run 2026-07-01 OTA: DBPSK, 768 kHz sub- carrier, 63-bit PN, via AD9361 at 2.4 GHz ISM 2026-07-18, no PL loaded -> smoke/M1_RESULTS.md 3 BITSTREAM BUILT DONE (file on disk) yosys 0.62 -> nextpnr- xilinx -> fasm2frames -> xc7frames2bit ps7_tern.bit 4,045,670 B part xc7z020clg400-1 yosys stat gft16_mul: 1 DSP48E1, 47 LUT, 18 CARRY4 -> REPRODUCTION.log 4 IN THE FABRIC NOT DONE - no date - no bitstream of our own has ever been configured into the PL of a Puzhi Mini - NOT proven: place-and- route, timing closure (Fmax), a loadable bitstream - GF-T on silicon: no UART log, no .bit and no run record - IDCODE 0x13636093: no JTAG scan log - PS boot chain still vendor: BOOT.BIN+FSBL from Xilinx bootgen -> ps7/FIRST_LOAD.md Everything measured so far ran on the host or on the node ARM, with no PL bitstream loaded. ps7_tern.bit exists on disk and has never been configured into the PL of a Puzhi Mini.
Three of the four stages carry a named artefact and a measurement: specs and tests on the host, a ChaCha20-Poly1305 run that returned RC=0 on the node's Cortex-A9 plus a DBPSK link over the AD9361, and a 4,045,670-byte bitstream built by the open yosys-to-xc7frames2bit flow. The fourth carries none — the .bit exists but has never been configured into a PL — so nothing on this track is yet evidence about the FPGA fabric itself.
  1. Measure throughput properly: a timed transfer of a known payload over one hop, then two, then three, with the clock and the method stated. Until this exists there is no answer to the first question any buyer asks.
  2. Get a modem of ours into the fabric rather than the ARM core. The open bitstream flow already produces images for this part; nothing of ours has been loaded into a node yet.
  3. Sign the coverage seal, as the specification already says it should be, so a settlement can rest on it.
  4. Run more than four nodes, and run them somewhere that is not one desk.

A partner joining now is joining a measurement discipline, not a network. What exists is the instrument: hardware anyone can buy, a toolchain with no vendor licence in the fabric path, and a log in which the failures are written down with the same care as the results.

What this does not settle

Receipts

Every figure above is measured, and the limits are named with it.