A site of its own
The project has a domain of its own, npuforge.org, and this is its site: the documentation, the measurements and the story in one place, searchable, in light and dark, with a home page in English and in Arabic.
Every release, and every measurement as it is published - written from the work itself, so nothing here is announced before it is done. Follow it with any feed reader at news/feed.xml, or on GitHub with Watch - Custom - Releases on os-xgs-npu.
The notices by e-mail are not open yet. Until they are, the feed above and GitHub's Watch - Releases carry every release.
The project has a domain of its own, npuforge.org, and this is its site: the documentation, the measurements and the story in one place, searchable, in light and dark, with a home page in English and in Arabic.
XGS: Settings - Watch for unusual traffic. Off until it is turned on, and it never blocks: it
says, in the XGS log, what no normal day looks like. Every minute scripts/xgs/anomaly.py (cron)
reads pf's state table every ten seconds through a window of fifty, keeps every connection any reading
saw, and for each host that opened connections works out what it did in the window - bytes sent and
received, connections opened, new addresses reached, and the most ports it tried on any one address.
Then:
A port in a cage reported a medium that nobody had read (#533):
No - not on the XGS 3300's firmware (v22). The question came from the vendor's own design: its
command list has three commands between the QoS ones and the first IPsec one - DOS_SET_POLICY,
DOS_ADD_BLACKLIST_ENTRY and DOS_CLEAR_BLACKLIST, 27, 28 and 29 - with their structures in
dos_table.h: a policy (a packets-per-second ceiling, a type, whether a source over the ceiling is
blacklisted, and for how long) and an entry (a family, a type, an address, a duration). The XGS 136's
helper module was built with two more facts about them - an even type is a source and the next odd
one its destination; an entry lasts 30 seconds by default and 3,600 at most - and with an error code
for a full DoS table. If the coprocessor kept such a table, a block list could be enforced there, at
the port, before a frame reached the host.
Once a connection is in hardware the host stops seeing it: its frames go from port to port inside
the coprocessor, and pf's state, the interface counters and every graph built on them stop growing -
which is why every throughput on these pages is read at the far end, not on the appliance. The
coprocessor itself counts every connection it forwards, and the XGS 3300's sends those counts to the
host unasked, about once a second, as control messages on tag 254 - which the driver had dropped
unread since the receive path was written. Since this change it reads them
(octep_dp_cmsg_rx in contrib/octep/octep_dp.c): each connection in the table keeps the packets and
bytes the coprocessor counted for each direction, dev.octep.0.dp.flow_table prints them, and the
Offloaded Flows page shows them as Counted in hardware.
XGS: Settings - Cut connections by TLS fingerprint. JA4 is a fingerprint of a TLS ClientHello -
the version, ciphers, extensions and protocols a client offers - so it names the software that opened
a connection rather than the address it went to. OPNsense's Intrusion Detection (Suricata 8) writes
one for every TLS connection it sees once its TLS log carries the JA4 field. With fingerprints listed
here, scripts/xgs/ja4_cut.py reads that log every two seconds (started each minute by cron) and
when the settings are saved, and kills by id every pf state of a connection it shows with a listed
fingerprint; the driver then takes the connection out of hardware, as it does for a block list
(lists-cut-2026-10-19.md).
XGS: Settings - Cut open connections when listed. A block list in OPNsense is an alias - a pf
table filled from a URL, a feed or by hand - and a rule that blocks it blocks new connections only. A
connection opened before its address was listed keeps its pf state, and with offloading on its flow in
the coprocessor, for as long as it carries traffic. With a list chosen here,
scripts/xgs/lists_cut.py compares it every minute (cron) and when the settings are saved with what
it held before, and kills by id every pf state with either end in what was added; each driver then
takes the connection out of hardware. Choosing a list cuts what it already names.
Both drivers post some commands to the coprocessor without waiting for the answer: on the high RPC ring, each into a slot of its own. A review found that neither driver kept such a command inside its slot (#524):
Four times on 2026-10-19 a stream from the bench PC through the XGS 136 to the second XGS 3300 ran at a quarter to a third of its speed for a short while (#531, open). This page is what was done to find the cause, and what was found on the way. The cause has not been found: in 28 minutes of watching and one boot watched, it did not happen again.
The XGS 3300's coprocessor sends the host the counts of every connection it forwards, and of every port, as control messages on tag 254 - which the driver now reads and gives to pf and the interfaces (hw-counters-2026-10-19.md). The XGS 136 is as blind to its own hardware traffic as the XGS 3300 was: once a connection is in hardware, pf's state and the interface counters stop growing. This page is the first step of the same work on the XGS 136: does its coprocessor send such messages at all, and in what form.
When the second XGS 3300 sent to the bench PC through the XGS 136, a stream in hardware ran slower than the same stream through the host, and retransmitted far more. The fast path kept giving the connection back, and the driver kept making it again at once - and making it again was what gave it back. The driver now waits, with the connection left with the host, before it makes one the fast path gave back soon after it was made. In that direction the hardware path now runs level with the host's, and a minute of it retransmits a quarter of what it did (#529).
The XGS 3300's integrity soak (two hours of TLS), made on the XGS 136 for the first time:
The same run as on the XGS 136 (a thousand connections at once):
A small update on the same day: the Updates page, as the three appliances were updated to 0.1.0-alpha.2 from it. An appliance installed from a test branch is offered the release, a reboot is asked for only when the driver changed, and the changes are listed with their links. Every step of an update is photographed in the guide, docs/guide/updating.md.
A small update: from this release an appliance sends its problem reports to the project's own
domain, reports.npuforge.org. The earlier address keeps answering, for the appliances that have not
updated yet.