// from the shipped image alone

Know what is in the firmware you ship, and whether it changed.

Upload an image and get the component and version inventory, with every byte accounted for. Works from the shipped image alone.

A standard SBOM in CycloneDX 1.6
Every component placed and named
Five answers, each with a next step
A coverage number for the whole image

A hash cannot answer the question

A signature says who published an artifact. A hash says the bytes did not move. Neither says whether the meaning changed, and a rebuilt binary is byte-different every release, so a mismatch is the normal case and carries no information.

In an engagement we compare what the two builds actually do, rather than comparing their bytes. A rebuild of the same code comes back as agreement. A changed check, an added call or a dropped bound comes back as a difference, with the part of the image it sits in named.

The diff view: two builds side by side, the verdict, and the named slice where meaning changed A browser window comparing two builds of one deliverable. A verdict line states whether behavior changed. A table lists each slice with its state: identical, recompiled with no semantic change, or changed, with the changed slice named and the functions inside it counted. 2atomic.com/app/project/supplier-a/diff/v4.0.0...v4.0.1 Projects / supplier-a / v4.0.0 -> v4.0.1 What changed BEHAVIOR CHANGED BY SLICE SLICE BYTES STATE FUNCTIONS DIFFERING usr/sbin/httpd 96,144 CHANGED 4 usr/sbin/dropbear 188,416 RECOMPILED 0 usr/lib/libmbedtls 241,664 IDENTICAL 0 bin/busybox 412,880 IDENTICAL 0 Recompiled is not changed: the bytes differ and the meaning does not. Only httpd is reported as a behavior change, and the four functions inside it are named on the slice's own page.

What an upload gives you, and what an engagement adds

Two different things, and the difference is stated here rather than discovered after you buy. You can start an upload yourself; an engagement is quoted up front, from what the artifact is, what you are afraid of, and when the answer has to exist.

An upload

The member tree, the coverage account, and the SBOM. Self-serve, from the image alone.

Your release history

Point us at the releases you already have and the SBOM names the components it can match against them. Supply nothing and it reports what the image declares about itself. This is a provisioning step on day one, not a license tier.

An engagement

The diff against a baseline you accepted, and per-binary findings. This is work our engineers do for you, not an upload output, and we say so before rather than after.

Every run ends in one of five answers

Three of the five verdicts say we could not decide, so an artifact we failed to read is never reported to you as a match.

VerdictWhat it meansWhat you do
MATCHEvery part we compared behaves the same way, and we read enough of the image for that to be a statement about behaviour rather than about bytes.Accept on this axis.
TAMPERA specific, named change: this slice, this difference.Investigate before it goes anywhere.
BYTE-IDENTICALThe bytes are the blessed ones, but a slice carried too little semantic coverage for any claim beyond bit-exactness. Packed firmware lands here.Treat it as a hash comparison, because that is all it is.
ANALYZER-CHANGEDThe artifact may be unchanged, but our own analyzer moved, so agreement would be a claim about two different readers.Re-baseline. Do not re-bless on this.
UNLIFTABLEA slice could not be fully accounted for, so no honest verdict is possible.Treat it as unverified. Not as clean.
The component SBOM with its byte columns: what each entry is evidenced by, and what is left over A browser window showing a component SBOM for one supplier deliverable. Each row names a component, the evidence count that placed it, the indexed build it matched, and two byte columns: bytes attributed to that component and bytes still unattributed. A totals row closes the account, and a line states the proportion of executable bytes the SBOM covers. 2atomic.com/app/project/supplier-a/firmware/v4.0.1 Projects / supplier-a / v4.0.1 Component SBOM ATTRIBUTED AND UNATTRIBUTED COMPONENT EVIDENCE INDEXED BUILD ATTRIBUTED UNATTRIBUTED busybox 1.31.1 7 openwrt-19.07 412,880 0 dropbear 2019.78 5 openwrt-19.07 188,416 0 mbedtls 2.16.3 4 openwrt-19.07 241,664 1,208 libubox (git) 2 NOT VERSIONED 44,032 512 vendor httpd 0 NO BUILD 0 96,144 Executable bytes accounted for 77.53% The last row is the point of the table: 96,144 executable bytes belong to a binary no indexed build explains, so they are carried as unattributed rather than folded into a component that happens to be nearby.

A bill of materials with a denominator

Which components are in it, at which versions, as CycloneDX 1.6 with a purl on each component whose version the evidence witnessed.

The document carries 2atomic:exec_byte_account_closure printed with the terms it closes over rather than as a bare ratio, so you recompute it. It is exactly 1.0 or the document is not emitted.

Every binary-SBOM tool drops what it could not identify. The difference is whether it tells you.

What each entry carries

  • How it was identified, as a count. Discriminating canonical function hashes agreeing with a named indexed build. Not a label; a number you can argue with.
  • A version only where the evidence witnesses it. One indexed release makes every match trivially consistent, so no version is emitted. Several pin it.
  • The exact advisory list we matched against, named so your own team can repeat the match and get the same answer.
  • What we could not open, and why. Each one names the part, how large it is and what stopped us, so padding is reported as padding rather than counted as a finding.
  • Agreement too weak to claim stays unattributed, against a published evidence floor, rather than being promoted.

How a run works

Upload in the browser or submit from your build. Same analysis, same bundle, same account of what was not read.

In the browser

Upload the image you ship

Drag in the file your factory flashes or your updater serves. We unpack it, identify the components and versions we can place, read the code we find, and hand back one sealed bundle. Nothing is needed from the vendors whose code is inside it.

From your build

Submit it from CI

The same run, started by 2atomic firmware submit and read back with 2atomic firmware fetch. Add --wait to block until it finishes and --min-coverage to fail a release that explains less of its own bytes than you accept.

The firmware surface, and the state we reach on each part of it

The 23 firmware-bearing components on one motherboard, grouped by how each is reached and coloured by the state the analysis reaches Each square is one of the 23 firmware-bearing components, in 5 rows grouped by the path that reaches it. Code read: 15; Read complete, no program: 4; Closed by silicon: 3; Bounded and identified: 1. The same components are listed as text below the figure. EVERY FIRMWARE-BEARING COMPONENT, LOCATED AND ACCOUNTED FOR Main SPI flash Main system UEFI: Code read Intel ME / CSME: Code read AMD PSP / SEC: Code read Intel ISH: Code read Intel FSP / AMD AGESA: Code read Audio DSP codec: Code read Intel CSME AMT: Code read CPU microcode: Closed by silicon 8 No external store Wi-Fi / Bluetooth: Code read PCIe re-driver: Code read Super I/O: Read complete, no program TPM 2.0: Closed by silicon Microsoft Pluton: Closed by silicon 5 Its own flash chip Embedded controller: Code read Baseboard management: Code read RGB microcontroller: Code read NVMe controller: Bounded and identified 4 Over I2C / SMBus Thunderbolt / USB4: Code read USB Power Delivery: Code read Digital VRM / PWM: Read complete, no program Memory SPD: Read complete, no program 4 SPI, sometimes Option ROMs: Code read Ethernet NIC: Read complete, no program 2 Code read (15) Read complete, no program (4) Closed by silicon (3) Bounded and identified (1)
Analyzed15 of 23

The code was obtained from the artifact and read, so a finding in it is possible.

No code4 of 23

A byte map or a register map, not a program. Read is the complete answer and nothing further exists to find.

Encrypted3 of 23

Sealed behind a hardware boundary. Microcode decrypts inside the CPU, so no analyzer crosses it and we say so.

Bounded1 of 23

Found, bounded, and its instruction set established. What is left is the way in: for some the vendor publishes none, for others a reader for the container they sit in. Each row names which.

Analyzed15
Main system UEFI
Intel ME / CSME
AMD PSP / SEC
Embedded controller
Baseboard management
Intel ISH
Intel FSP / AMD AGESA
Thunderbolt / USB4
USB Power Delivery
Wi-Fi / Bluetooth
Audio DSP codec
RGB microcontroller
Option ROMs
Intel CSME AMT
PCIe re-driver
No code4
Ethernet NIC
Digital VRM / PWM
Memory SPD
Super I/O
Encrypted3
TPM 2.0
CPU microcode
Microsoft Pluton
Bounded1
NVMe controller

How far we take you toward CRA readiness

Per release, from the image alone, with every finding already filed against the Annex I requirement it answers. Signing the declaration stays yours. Getting you to the point where you can sign it is the work we do beside you, release after release.

You receive

A component bill of materials, per release

In CycloneDX 1.6, the format an auditor already accepts, with each component named so your own records can match it. Read from the image you ship rather than from a build file. Where the evidence stops short of a version, the version is left blank: a parts list that guesses is worse than one that says so.

You receive

An account of every executable byte

What we read, and what we could not, as a number rather than a silence. This is the line an auditor asks for and most reports cannot produce, because it requires knowing the denominator.

You receive

Findings filed against Annex I

Each finding carries the requirement it answers and how it was established -- parsed, executed, or inferred. The control catalogue lives in the engine and is checked by its own tests, so the mapping is code rather than a spreadsheet somebody maintains.

You receive

An advisory join you can re-derive

Each component checked against an advisory list you host yourself, with that list named and the number of matches beside it. Your team can run the same check and get the same answer.

The dates

DateWhat applies
11 Sep 2026Article 14 reporting. An actively exploited vulnerability starts a 24-hour early warning and a 72-hour notification to ENISA and your national CSIRT, then a final report 14 days after a corrective or mitigating measure is available (one month after notification, for a severe incident). Binds products already on the market.
11 Dec 2027Annex I essential requirements, conformity assessment, CE marking, technical documentation, Annex II user information. Units placed before this are grandfathered unless substantially modified.

FAQ

Do you need source code?
No. We read the image itself. That covers the cases a source scanner has nothing to work with: a chip vendor's kit, a supplier's sealed delivery, a dependency that arrives already compiled, or your own release image once it has left the build. Where you do have the build, the source path runs beside it for C and C++ and names the line to fix.
What about instructions your analysis has never seen?
A vendor that adds its own instructions to a processor can describe them to us: what each one reads, what it writes, and which register bank it touches. The analysis then lowers them like any other instruction instead of stepping over them. A description missing any of those facts is refused by name, because an instruction whose writes were guessed would quietly delete live values and the result would still look well formed.
Can I point this at something I do not own?
No. Access is gated on a project review: identity verification plus a check that you own the target or are authorized to test it. Analyzing a vendor artifact you received under a contract that permits it is a normal case, and the review is where that gets established.
Do you publish your detection method?
No. We stand behind the result and keep the method under NDA, because publishing it hands an adversary the map to route around it. We publish no precision or recall figure either: it would be a statement about one version of the product against one corpus.

When you would actually use this

Three moments, and they are the three people ask about.

Supplier intake: the first drop becomes the baseline every later one is compared to A browser window showing a supplier's deliverables. The first drop is marked as the baseline. Later drops each carry what changed against it. A panel states what a baseline fixes: the components, the byte account and the hardening posture the next drop is held against. 2atomic.com/app/project/supplier-a Projects / supplier-a Deliverables DROP RECEIVED COMPONENTS STATE v4.0.0 2026-03-04 38 BASELINE v4.0.1 2026-05-19 38 1 CHANGED v4.1.0 2026-08-02 41 3 ADDED WHAT THE BASELINE FIXED Components named and versioned 34 of 38 Executable bytes attributed 91.4% Hardening controls parsed 6 of 6 A baseline is a measurement, not a promise: the four components it could not version are carried as unversioned in it and in every comparison against it.

Supplier intake

Fingerprint the deliverable on arrival, so every later drop is a diff instead of a fresh act of trust.

The release gate: the object you are about to ship, held against the source that was reviewed A browser window showing a release gate. A verdict states whether the object matches the reviewed source. A table lists each check with its state: the digest the reviewed build produced, the digest of the object being shipped, the components that appear in one and not the other, and the findings that were open at review time. 2atomic.com/app/project/gateway-fw/gate Projects / gateway-fw / Release gate v2.3.0 HELD CHECKS Object matches the reviewed build NO MATCH Components in the object, absent from the review 2 Findings open at review time 1 confirmed Hardening controls lost since review 0 The object being shipped does not hash to the build that was reviewed, so nothing else on this page is evidence about the object in the release. The gate holds rather than reporting the reviewed build's result under the shipped build's name.

Release gate

Ask whether the object about to be published still corresponds to the source that was reviewed, while the answer can still change the outcome.

Incident scope: what you shipped against what you accepted A browser window scoping one advisory across an account. Counters give versions shipped that carry the named component, versions accepted from a supplier that carry it, and versions that cannot be answered for. A table lists each version with which side it is on and the evidence that placed the component. 2atomic.com/app/incident/cve-2026-1188 Incidents / CVE-2026-1188 CVE-2026-1188 in mbedtls SCOPING 6 shipped, carries it 11 accepted, carries it 3 cannot answer BY VERSION VERSION SIDE COMPONENT VERSION EVIDENCE ac10u v1.2.4 SHIPPED 2.16.3 5 m30 v16.01.10 SHIPPED 2.16.3 4 supplier-a v4.1.0 ACCEPTED 2.16.3 6 tapo v2.1.9 UNOPENED - 0

Incident scope

When a component is reported compromised, compare what you shipped against what you accepted, rather than reasoning from a manifest.

Where this goes next