Skip to content

The documentation

Start by what you want to do

you want to read
know what works, on which board guide/what-works.md, what one appliance has and the other lacks in families/parity.md, and each board's own limits in families/board-limits.md
install it, or update it guide/installing.md - its note on DNS first
see every page before installing guide/the-pages.md - each one photographed on an appliance, in English and in Arabic
find a photograph images/README.md - every image by kind, with what it shows and where it is used
understand how it works guide/how-it-works.md, then a driver's pages: npuep/ for the ARMADA, octep/ for the OCTEON TX
know what it cannot do guide/limitations.md
see the numbers, and how each was taken measurements/ - the newest is the XGS 136 forwarding by its coprocessor
find out why something is the way it is findings/, dated, in the order below
know which board belongs to which family guide/families-and-hardware.md, families/
take it to another operating system npuep/porting-notes.md, provenance.md
change something ../CONTRIBUTING.md and ../DESIGN.md
find any page at all INDEX.md: every page, one line each, with when it was last read

The documents, by kind

Everything under docs/ is in one of eight folders, and the folder says what kind of page it is. A page that describes a thing stays where it is and is corrected in place; a page that records a finding carries its date and is never rewritten to say something it did not say then - a claim withdrawn stays on the page, struck through, with what replaced it after.

folder what is in it
guide/ What the README used to carry, at full length since 2026-10-18: what works, how it works, what it cannot do, the families and the hardware, installing, the background, and every badge
hardware/ The appliances as objects: one page per board, what is on it, photographed and read from software. xgs136.md, xgs3300.md
npuep/ The ARMADA host driver's protocols, what contrib/npuep speaks: the facility handshake, the management link, the datapath, the control channel, per-port control
octep/ The OCTEON TX driver's design, what contrib/octep is built against: where a flow comes from, how it binds to a pf state, the LIF table, the reload barrier, the kernel's IPsec surface, and offloading as a setting
findings/ What was found, dated: one page per fault or discovery, in the order below
families/ The six coprocessor families and the long reference pages for OCTEON TX
measurements/ The numbers, one page per appliance, with how each was taken and what it does not mean - and two for the three-appliance bench of 2026-10-17: clear text and IPsec, the MTU from 576 to 9000, IPv6 and the transmit queues of the next night, and the XGS 136 forwarding by its coprocessor the day after
images/ The photographs and diagrams, one folder per appliance, and readme/ for the README's banner and status board (tools/readme-art.py draws them)

Five pages stay at the top: the-road.md, how it was found, drawn; provenance.md, what was read for the ARMADA work and from where; INDEX.md, every page and when it is read again; REVIEW.md, how that schedule works; and this one. octeontx/ holds the OCTEON TX provenance, kept apart because it is a different upstream, and the plan for capturing the XGS 3300 under its own firmware.

The appliances

hardware measurements photographs driver
XGS 136, ARMADA CN913x hardware/xgs136.md none yet the bench, the MTU, in its coprocessor images/hardware/xgs136/ contrib/npuep, npuep/
XGS 3300, OCTEON TX CN83xx hardware/xgs3300.md, families/octeon-tx-peripherals.md measurements/xgs3300.md images/hardware/xgs3300/ contrib/octep, octep/, families/octeon-tx.md

The ARMADA driver, npuep/

page
npu-bring-up.md Getting the coprocessor out of reset, over a USB-to-SPI bridge
facility-protocol.md The five facilities, the barmap, the handshake
mvmgmt.md mvmgmt0, the management interface
mvmgmt-in-opnsense.md mvmgmt0's place in OPNsense, as built: assignment, the plugin's rules, up at boot, a trusted key (issue 374)
offload-plan.md Offload on the ARMADA family: what is known, the order of work
rpc-payloads.md Step 1 of that plan: every control-channel payload read from this family's own module, beside the OCTEON TX's - the same bytes
giu.md The datapath that carries all fourteen ports, and the 66-byte header
rpc.md The control channel and the forwarding tables
netagent.md Per-port state, link, media and address
porting-notes.md What a port to another OS would hit

The OCTEON TX driver, octep/

page
pf-state-to-flow.md Where a flow has to come from: read out of the kernel sources
flow-lifecycle.md Binding an offloaded flow to a pf state
lif-provisioning.md The LIF table: what has to be created, in what order, and why one entry is not enough
ruleset-reload.md Where OPNsense reloads pf, and where a revision bump has to attach
ipsec-offload-surface.md if_ipsec_accel_methods: the six calls, their contracts, and the order
offload-is-a-setting.md Offloading, as a setting the owner decides
appliance-defaults.md OPNsense's Miscellaneous page against these appliances: amdtemp set once, PowerD measured and left off, swap, logs in memory, the beep
coprocessor-section.md The plugin's one section of the menu, and every setting it has on one page, written where each takes effect
interface-assignment.md The plan, to implement Built (2026-10-16): the front ports assign themselves by their panel labels at the first boot, with a countdown any key stops
expansion-modules.md An expansion module's ports, read from the coprocessor at every boot and named as the metal names them - any module, measured with a 4x10G one

The long references for the family itself are under families/: octeon-tx.md, octeon-tx-reference.md, octeon-tx-rpc.md, octeon-tx-crypto-path.md, octeon-tx-peripherals.md.

The findings, in order

Each page is dated by the first measurement on it. All of these are on the XGS 3300 until 2026-10-14; from that day the XGS 136 has its own.

day page
2026-10-04 LO_MFLOW_READ: a claim withdrawn, and the instrument decoded
2026-10-05 A flow table, a sweep, and the panic that came with them
A microflow is inert until its state says MF_ACTIVE
Asking pf, and two ways of reaching it that do not work
A frame was accelerated
Address translation lives in the connection, not the microflow
The receive ring was the ceiling
The frames arrive, and TCP will not have them
Reading the frame settled it, and the fault was in the request all along
2026-10-06 The bridge cannot be asked, so the flow answers
The handlers were readable all along
The engine accepts the frame, and no completion path is armed
Both ends exist, and the counter means something smaller than it was
The crypto path completes, and the counter that said otherwise was not it
The encrypted frames are not lost, and the trigger cannot keep up
Accelerating a flow breaks it, and it always has
The outbound half was programmed without its translation
2026-10-07 The association belongs to an interface, and that was the whole refusal
The handle is the index plus one
One connection, two microflows: the table the coprocessor expects
Half the connections kept one strict state
The kernel drives the coprocessor
One encryptor per association
The second acknowledge: measured, and made unnecessary
The NetAgent transaction is not serialised, and npuep already fixed it
2026-10-08 The flow carries the association
A state that sees nothing runs out
A give-back is answered at once
Installed first, taken second
A connection made between polls
A connection that collides with itself
A visit is not a pass
A ring that fills
2026-10-09 A packet is not a buffer
2026-10-11 A slot holds both directions
The way back keeps up
2026-10-12 The UDP header is the host's to write
Under ESN, what arrives stays with the kernel
A window wider than 256
AES-CBC with an HMAC runs at two thirds of GCM's rate
GCM-12 is the kernel's no, transport mode the vendor's
The outer header can be IPv6
IPv6 inside the tunnel, delivered
The MTU is the LIF's, and a frame is as many buffers as it needs
2026-10-13 The sensors answer on the host's own bus, and the bus had been there all along
2026-10-14 The entry first, then the gate, then the flow table: the ARMADA's chain, measured - the first finding on the XGS 136
A flow the host named, dropped by the ARMADA's fast path - the first action on this family
The driver's flow table on the ARMADA begins: the candidates, and the vnet that killed the sweep
The driver accelerates a flow on the ARMADA: FLOW_CREATE_FP, and the microflow goes active
The sweep accelerates flows on its own on the ARMADA
A sleeping thread held the rpc lock
The revision barrier on the ARMADA
One next hop for many flows on the ARMADA
The kernel offers an association on the ARMADA
The engine takes an association on the ARMADA
The engine decrypts inbound ESP on the ARMADA
The decrypted frame is dropped on the ARMADA - the open-entry regime; its family-wide conclusion struck the same day by the next page
The engine hands up the decrypted frame on the ARMADA: the OCTEON TX's receive model after all
The host answers through the tunnel on the ARMADA: the receive termination
AMD's platform crypto engine breaks IPsec AES-GCM on the XGS 136 host - not an offload finding, and it hid one
The engine encrypts outbound ESP on the ARMADA: the transmit side, and the two doors to it
Fragmentation before the envelope on the ARMADA, and a neighbour not yet resolved
The resolver asked lo0 for a neighbour: looutput: af=35 unexpected, once a second - the host driver's, not the coprocessor's; #402
ESP behind UDP on the ARMADA: NAT-T, both ways in hardware
AES-CBC with an HMAC on the ARMADA: the four hashes taken, both ways in hardware
A replay window on the ARMADA: the engine's check, measured with a replayed frame
2026-10-17 A hundred polls a second: a TCP flow into the XGS 136 was capped at 18 Mbit/s by its receive poll - 773 Mbit/s in, 934 out, after
2026-10-18 The switch dropped IPv6's neighbour solicitations on the ARMADA - a routed port could not be found over IPv6 until it spoke first
Every command cost ten milliseconds on the ARMADA - 138 microseconds after, and the flow table sized by the board
Every next hop named an empty cage on the ARMADA, and the entries the flow maker needed - a connection made in hardware went out by PortF2; delivered both ways, after, and the maker opens the entries itself

Keeping the pages current

Every page opens with the day it was last read against the tree, and is read again on a schedule set by its kind - sixty days for the guide, ninety for a driver's pages, a year for a dated finding. INDEX.md lists them all with their dates; REVIEW.md says how the schedule works and why each kind gets the time it gets. Once a month a workflow keeps one issue open with the pages that are due.

Where the pages were

Until 2026-10-11 every page above sat directly under docs/. A link from an issue or a pull request of before that day to docs/<name>.md now reads docs/findings/<name>.md, docs/npuep/<name>.md or docs/octep/<name>.md by the tables above; the page that was hardware.md at the top is hardware/xgs136.md, and sfos-capture-plan.md is octeontx/sfos-capture-plan.md. Nothing was removed or rewritten in the move; tools/move-doc-pages.py did it and tools/check-doc-links.py says every link in the tree still lands.

On 2026-10-18 the README's long sections moved here, word for word, into guide/: what works, how it works, what it cannot do, the families and the hardware, installing, the background and the full wall of badges. A link to one of the README's old sections - #-what-works, #-families - finds it in that folder; the README itself keeps the notice the note on what is still being completed, the two roads onto an appliance, the licence and a short version of the rest.

Discussion

The discussion of this page is held in the project's GitHub Discussions. Showing it here loads giscus from giscus.app and GitHub, and writing in it needs a GitHub account - what that means.