<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cleiton Augusto Correa Bezerra</title>
    <description>The latest articles on DEV Community by Cleiton Augusto Correa Bezerra (@cleiton_augusto_).</description>
    <link>https://dev.to/cleiton_augusto_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3547488%2Ffea3af52-a724-429f-af20-3a2121bd4a27.jpg</url>
      <title>DEV Community: Cleiton Augusto Correa Bezerra</title>
      <link>https://dev.to/cleiton_augusto_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cleiton_augusto_"/>
    <language>en</language>
    <item>
      <title>The test asserted exactly what could not fail</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Tue, 18 Aug 2026 00:05:54 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/the-test-asserted-exactly-what-could-not-fail-77</link>
      <guid>https://dev.to/cleiton_augusto_/the-test-asserted-exactly-what-could-not-fail-77</guid>
      <description>&lt;p&gt;Over the last month I found correctness bugs in three different quantum&lt;br&gt;
software projects. All three had tests covering the broken code. All three&lt;br&gt;
tests passed.&lt;/p&gt;

&lt;p&gt;That is not a story about quantum computing. It is a story about a way of&lt;br&gt;
writing assertions that feels like testing and is not, and I keep running into&lt;br&gt;
it, so I want to write it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of it
&lt;/h2&gt;

&lt;p&gt;Here is the one that made it click. It is from a compiler framework that exports&lt;br&gt;
quantum circuits to OpenQASM:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert!(qasm.contains("OPENQASM 3.0;"));
assert!(qasm.contains("qubit[2] q;"));
assert!(qasm.contains("h q["));
assert!(qasm.contains("cx q["));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Look at the third line. It checks that an h gate was emitted. It stops the&lt;br&gt;
string one character before the qubit index.&lt;/p&gt;

&lt;p&gt;The exporter was putting every gate on the wrong qubit. It derived the index&lt;br&gt;
from a counter that went up once per gate instead of reading the operands, so&lt;br&gt;
the Nth gate landed on qubit N. Three gates on one qubit came out on three&lt;br&gt;
different qubits.&lt;/p&gt;

&lt;p&gt;That test passes on the broken output. It would pass on almost any output. It&lt;br&gt;
is not testing the export, it is testing that the function returned a string&lt;br&gt;
with some letters in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same shape, twice more
&lt;/h2&gt;

&lt;p&gt;In another project, a Rust simulator, the Rz gate had a sign error: Rz(theta)&lt;br&gt;
was implemented with the sign of theta flipped, so Rz(pi/2) gave you S dagger&lt;br&gt;
where it should give you S.&lt;/p&gt;

&lt;p&gt;The test:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert_eq!(magnitudes(result), expected_magnitudes);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Magnitudes. The bug was in the phase. A sign flip does not move a magnitude, so&lt;br&gt;
the test could not see it, and it had been passing since the code was written.&lt;/p&gt;

&lt;p&gt;Back in the first project, a different test, this time for a pass that rewrites&lt;br&gt;
gates into a hardware native set:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert!(names.contains(&amp;amp;"quantum.rz".to_string()));
assert!(names.contains(&amp;amp;"quantum.sx".to_string()));
assert!(names.contains(&amp;amp;"quantum.cx".to_string()));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The pass is supposed to replace a gate with an equivalent sequence. This checks&lt;br&gt;
that the new sequence showed up. It never checks that the old gate went away.&lt;/p&gt;

&lt;p&gt;It did not go away. The pass inserted the replacement and left the original in&lt;br&gt;
place, so the circuit ended up doing both. A T gate became an S gate. That one&lt;br&gt;
had been shipping in a release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to write a test that cannot fail
&lt;/h2&gt;

&lt;p&gt;Once you have seen them together the pattern is easy to name.&lt;/p&gt;

&lt;p&gt;Asserting a magnitude when the defect is a sign. The assertion projects away the&lt;br&gt;
exact dimension the bug lives in.&lt;/p&gt;

&lt;p&gt;Cutting the string before the field that breaks. contains("h q[") is one&lt;br&gt;
character short of useful, and the missing character is the whole subject of the&lt;br&gt;
function under test.&lt;/p&gt;

&lt;p&gt;Asserting presence when the defect is absence. A correct rewrite is two claims:&lt;br&gt;
the new thing appears, and the old thing is gone. Checking only the first half&lt;br&gt;
lets every "forgot to delete" bug through.&lt;/p&gt;

&lt;p&gt;There is a fourth one I found in the same file, and it is the pure form:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let _ = (result, rz_before);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That is the last line of a test called test_rz_stays_untouched. It computes the&lt;br&gt;
before state, runs the pass, and then discards both values. It asserts nothing.&lt;br&gt;
It passes unconditionally. Line coverage counts it as covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do about it now
&lt;/h2&gt;

&lt;p&gt;I have one question I ask, and it is boring, and it works:&lt;/p&gt;

&lt;p&gt;What change to the code under test would make this assertion fail?&lt;/p&gt;

&lt;p&gt;If I cannot answer quickly and concretely, the assertion is decoration. For the&lt;br&gt;
export test the honest answer was "almost nothing", since any output with an h&lt;br&gt;
somewhere passes. For the magnitudes test the answer was "any change that&lt;br&gt;
affects magnitude", which excludes the entire class of bug that was actually&lt;br&gt;
there.&lt;/p&gt;

&lt;p&gt;The other thing I do now is run the test against the broken code on purpose.&lt;br&gt;
When I sent a patch for the sign error I described above, I flipped the sign&lt;br&gt;
back before opening the pull request, watched my new test fail, and then flipped&lt;br&gt;
it again. Two minutes. It is the only way to know the test has any power, and&lt;br&gt;
you can only really do it while the bug is still in front of you.&lt;/p&gt;

&lt;p&gt;That is also the honest reason these tests exist in the first place. Nobody sets&lt;br&gt;
out to write a test that cannot fail. You write it after the code already works,&lt;br&gt;
so it passes on the first run, and a test that passes on the first run never&lt;br&gt;
shows you what it would have caught.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this bites harder in numerical code
&lt;/h2&gt;

&lt;p&gt;In ordinary application code a wrong result usually turns into something visibly&lt;br&gt;
wrong: a crash, an exception, a null, a screen full of garbage.&lt;/p&gt;

&lt;p&gt;In numerical and quantum code, a wrong result is a perfectly well formed number.&lt;br&gt;
An amplitude with a flipped sign is still a valid amplitude. A circuit on the&lt;br&gt;
wrong qubits is still a valid circuit, and the file it exports still parses.&lt;br&gt;
There is no shape you can check for that says "this is broken", which is exactly&lt;br&gt;
why weak assertions survive here: nothing downstream complains about them either.&lt;/p&gt;

&lt;p&gt;So the burden falls entirely on the assertion. If the assertion looks away, the&lt;br&gt;
bug ships, and it ships quietly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reports
&lt;/h2&gt;

&lt;p&gt;If you want the details, they are public:&lt;/p&gt;

&lt;p&gt;Qiskit, sign of the dividend in a Rust transpiler pass, fixed in 2.5.1:&lt;br&gt;
&lt;a href="https://github.com/Qiskit/qiskit/issues/16594" rel="noopener noreferrer"&gt;https://github.com/Qiskit/qiskit/issues/16594&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lift, the export and decomposition bugs above:&lt;br&gt;
&lt;a href="https://github.com/rustnew/Lift/issues/2" rel="noopener noreferrer"&gt;https://github.com/rustnew/Lift/issues/2&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/rustnew/Lift/issues/3" rel="noopener noreferrer"&gt;https://github.com/rustnew/Lift/issues/3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool I use to find them is a differential fuzzer, which is a longer post:&lt;br&gt;
&lt;a href="https://github.com/cleitonaugusto/CleitonForge" rel="noopener noreferrer"&gt;https://github.com/cleitonaugusto/CleitonForge&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>rust</category>
      <category>quantum</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Three gates that should have left one behind</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Tue, 18 Aug 2026 00:04:04 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/three-gates-that-should-have-left-one-behind-409h</link>
      <guid>https://dev.to/cleiton_augusto_/three-gates-that-should-have-left-one-behind-409h</guid>
      <description>&lt;p&gt;I have been building a linter for quantum chemistry setups, nqf-lint. Some of it&lt;br&gt;
is easy. Some of it fights back, mostly the borrow checker. I spend my nights in&lt;br&gt;
there, and around it I keep studying the algorithms I like, VQE and QAOA.&lt;/p&gt;

&lt;p&gt;At some point I stopped trusting the simulators underneath all of it. They&lt;br&gt;
looked too tidy. Every run handed me a clean number and never complained about&lt;br&gt;
anything. Nothing that never complains is that correct.&lt;/p&gt;

&lt;p&gt;So I wrote a small fuzzer that throws random circuits at them and checks if they&lt;br&gt;
disagree with each other. The first time it stopped and told me two backends did&lt;br&gt;
not match, I was not excited. I assumed the bug was mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug
&lt;/h2&gt;

&lt;p&gt;It was small enough to hold in your head. Three gates, one qubit.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from qiskit import QuantumCircuit
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import CommutativeCancellation

qc = QuantumCircuit(1)
qc.sxdg(0)
qc.sxdg(0)
qc.sx(0)

out = PassManager([CommutativeCancellation()]).run(qc)
print(out.count_ops())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;On paper those three add up to a quarter turn in the negative direction. One&lt;br&gt;
gate should come out the other side. What comes out is an empty circuit.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderedDict()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;No error. No warning. Nothing in the logs. The rotation is just gone, and if&lt;br&gt;
you measure that qubit you get a different answer than the one your circuit&lt;br&gt;
says you should get. Nothing tells you why, because as far as the compiler is&lt;br&gt;
concerned nothing went wrong.&lt;/p&gt;

&lt;p&gt;I ran it again. I was sure I had typed the command wrong.&lt;/p&gt;

&lt;p&gt;If you want to see it yourself, you need pip install qiskit==2.5.0. It is fixed&lt;br&gt;
in 2.5.1, which is the whole point of the rest of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I went wrong
&lt;/h2&gt;

&lt;p&gt;So I opened an issue. And I did not stop at "here is a circuit that comes out&lt;br&gt;
wrong". I had read the code, or I thought I had, so I told them why.&lt;/p&gt;

&lt;p&gt;What I wrote was this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Looks like the cause is in the pass itself: _x_rotations includes sx and
sxdg (line 69 of commutative_cancellation.py) and two sxdg get treated as
an inverse pair.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I put "looks like" in front of it. That does not count for much. When you name a&lt;br&gt;
file and a line number, you are not hedging anymore, you are making a claim.&lt;/p&gt;

&lt;p&gt;I had come at the problem from several directions by then. There was one&lt;br&gt;
explanation left that I had not tested yet, and that was enough for me. That is&lt;br&gt;
where the certainty came from.&lt;/p&gt;

&lt;p&gt;I was wrong. And I was wrong in a worse way than being off by a line.&lt;/p&gt;

&lt;p&gt;The answer came the same day, from Jake Lishman, one of the core maintainers:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thanks for the report, we can get this fixed. The actual root cause is
quite different (all this code is in Rust), and commutative cancellation
is calculating the necessary rotation angle correctly, it just fails to
correctly synthesise that into gates when the combined X rotation is a
negative odd multiple of pi/2 and sx appears to be a supported gate.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Read that first parenthesis again. All this code is in Rust.&lt;/p&gt;

&lt;p&gt;I had pointed at a Python file. The logic I was describing does not live in&lt;br&gt;
Python at all, and the part I said was broken was working correctly. The pass&lt;br&gt;
computes the angle right. It fails when it turns that angle back into gates.&lt;/p&gt;

&lt;p&gt;So I was not slightly off. I was reading a different file than the one that&lt;br&gt;
runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring instead of arguing
&lt;/h2&gt;

&lt;p&gt;Here is the part I want to be honest about, because it is the only part I would&lt;br&gt;
do the same way again.&lt;/p&gt;

&lt;p&gt;I did not argue. I wrote this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thanks for the quick triage, and for correcting me on the cause. I was
reading the Python and guessed wrong.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then I went to measure, because that was the only thing I had that was&lt;br&gt;
worth anything. If I could not explain the bug, I could at least map it.&lt;/p&gt;

&lt;p&gt;I swept the combined rotation across every multiple of pi/2 from -8 to 8,&lt;br&gt;
forcing exact angles with pairs of rx gates, and checked each result against&lt;br&gt;
the exact operator. It came back with something neither of us had said: it is&lt;br&gt;
not only the odd multiples. There were even multiples failing too.&lt;/p&gt;

&lt;p&gt;Then I found something worse, and it was in my own report. I had written that&lt;br&gt;
the bug needed a gate on another qubit sitting between the two sxdg. That was&lt;br&gt;
wrong. It happens on a single qubit, alone, with nothing else in the circuit:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qc = QuantumCircuit(1)
qc.sxdg(0); qc.sxdg(0); qc.sx(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I posted that correction against myself before anyone asked for it.&lt;/p&gt;

&lt;p&gt;Somewhere in there I stopped trying to be right and started trying to be&lt;br&gt;
useful, and the second one turned out to be much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The line
&lt;/h2&gt;

&lt;p&gt;With the map in hand, the line was not hard to find:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let num_sx = (total_angle / FRAC_PI_2).round();
for _ in 0..(num_sx as i64) % 4 {
    dag.insert_1q_on_incoming_qubit((StandardGate::SX, &amp;amp;[]), cancel_set[0]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In Rust, % keeps the sign of the dividend. So -1 % 4 is -1, not 3. That makes&lt;br&gt;
the loop 0..-1, which is an empty range, so no sx is emitted and the rotation&lt;br&gt;
is dropped on the floor. rem_euclid(4) gives 0..3 for either sign.&lt;/p&gt;

&lt;p&gt;I posted it. Jake replied:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Yes, there's a PR (two, actually) linked above your comment that changes
that line.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;He had already opened it. My comment was not the fix, and I want to be clear&lt;br&gt;
about that because the timeline is public and anyone can check it. We landed on&lt;br&gt;
the same one line change from different directions, and he got there first.&lt;/p&gt;

&lt;p&gt;It was still the best day I have had in a long time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I take from it
&lt;/h2&gt;

&lt;p&gt;The habit I actually changed is smaller than a lesson. When I want to write "the&lt;br&gt;
cause is X", I now go and make X happen on purpose first. If I cannot make it&lt;br&gt;
happen, I do not know it yet, and I write what I measured instead.&lt;/p&gt;

&lt;p&gt;It sounds obvious written down. It was not obvious at the time, because being&lt;br&gt;
almost sure feels exactly like being sure.&lt;/p&gt;

&lt;p&gt;I used it again this week, on someone else's compiler. I thought I had found a&lt;br&gt;
sign error in a gate decomposition, and instead of writing that down I put the&lt;br&gt;
old sign back, ran the test I had just written, and watched it fail. Then I put&lt;br&gt;
the new sign in and watched it pass. Two minutes. That is the whole habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;Issue: &lt;a href="https://github.com/Qiskit/qiskit/issues/16594" rel="noopener noreferrer"&gt;https://github.com/Qiskit/qiskit/issues/16594&lt;/a&gt;&lt;br&gt;
Fixed in Qiskit 2.5.1, PR #16599.&lt;br&gt;
The fuzzer: &lt;a href="https://github.com/cleitonaugusto/CleitonForge" rel="noopener noreferrer"&gt;https://github.com/cleitonaugusto/CleitonForge&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>quantum</category>
      <category>testing</category>
      <category>debugging</category>
    </item>
    <item>
      <title>A conformant DICOM de-identifier silently strips your image's signature</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Wed, 12 Aug 2026 14:24:06 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/a-conformant-dicom-de-identifier-silently-strips-your-images-signature-7h2</link>
      <guid>https://dev.to/cleiton_augusto_/a-conformant-dicom-de-identifier-silently-strips-your-images-signature-7h2</guid>
      <description>&lt;p&gt;If you sign DICOM objects and then de-identify them, the signature is gone on the other side. Nobody re-signs it, nobody logs it, and the receiver has no way to tell the image was ever signed. I measured this, and it's quieter than I expected.&lt;/p&gt;

&lt;p&gt;Here's the setup. DICOM has a digital signature mechanism in PS3.15: a Digital Signatures Sequence that attests where an object came from and that it hasn't been altered. De-identification is the step you run before images leave your four walls, for research, for AI training, for a data-sharing deal. It rewrites the object to strip patient identifiers.&lt;/p&gt;

&lt;p&gt;I built a small Secondary Capture image, signed content the de-identifier doesn't touch (the pixel data, the dimensions, the modality), and ran it through a real de-identifier. Then I checked whether the signature survived.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tool&lt;/th&gt;
&lt;th&gt;Digital Signatures Sequence after de-id&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dcm4che &lt;code&gt;deidentify&lt;/code&gt; (PS3.15 Basic Profile)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;removed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;dicognito&lt;/td&gt;
&lt;td&gt;kept&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;dcm4che's &lt;code&gt;deidentify&lt;/code&gt;, which implements the PS3.15 Basic Confidentiality Profile, removed the Digital Signatures Sequence outright. It blanked the patient name and ID, as it should. But it left the signed image content untouched, which means the signature it deleted would still have verified. It didn't re-sign. It even left an orphaned MAC Parameters Sequence pointing at a signature that no longer exists.&lt;/p&gt;

&lt;p&gt;dicognito, a lighter anonymizer that only targets specific identifiers, left the signature in place.&lt;/p&gt;

&lt;p&gt;So two conformant tools disagree on whether your image keeps its provenance, and you find out which one you have by running it, not by reading the spec.&lt;/p&gt;

&lt;h2&gt;
  
  
  This isn't a bug
&lt;/h2&gt;

&lt;p&gt;The part that caught me is that dcm4che is right. PS3.15 says to remove the Digital Signatures Sequence during de-identification, because the Certificate of Signer can itself carry identifying information. And it says, in as many words, that re-signing by the de-identifier is not required. So the standard's own recipe deletes the origin proof and leaves restoring it optional. The tool is doing exactly what it's told.&lt;/p&gt;

&lt;p&gt;There's no attacker anywhere in this. A legitimate sender signs, a legitimate pipeline de-identifies, and the proof is gone. That's why it's easy to miss. Nothing in the flow looks wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it bites, and where it doesn't
&lt;/h2&gt;

&lt;p&gt;If you don't sign your DICOM objects, none of this touches you today, and most shops don't sign. I'll say that plainly, because the opposite would be scaremongering. But the moment you do sign, for provenance, for integrity, for a data-authenticity claim in a regulatory file, a downstream de-identification step quietly undoes it. And de-identification is exactly where your AI training and validation data comes from. An image that entered your training set can't be traced back to the device that produced it.&lt;/p&gt;

&lt;p&gt;Under FDA Section 524B this reads as a data-integrity control gap for your Security Risk Management, not a product vulnerability. It's the kind of thing a threat model should name, then either accept with eyes open or fix.&lt;/p&gt;

&lt;p&gt;The fix is boring. Re-sign after de-identification. The standard lets you, it just doesn't make you. Where you can't, carry provenance in a record the de-identifier preserves, and have downstream consumers treat a missing signature as unverified rather than as absent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it yourself
&lt;/h2&gt;

&lt;p&gt;Sign a DICOM object, then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# de-identify with a PS3.15-conformant tool&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:/w &lt;span class="nt"&gt;-w&lt;/span&gt; /w dcm4che/dcm4che-tools &lt;span class="se"&gt;\&lt;/span&gt;
    deidentify /w/signed.dcm /w/out.dcm

&lt;span class="c"&gt;# did the signature survive?&lt;/span&gt;
python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import pydicom; d=pydicom.dcmread('out.dcm'); &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
    print('signature present:', 0xFFFAFFFA in [e.tag for e in d])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the Digital Signatures Sequence is gone and nothing re-signed the object, this applies to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger pattern
&lt;/h2&gt;

&lt;p&gt;This is one instance of something I've been measuring across protocols: authentication or integrity material that a conformant intermediary drops when it parses a message and rebuilds it, with no attacker at the moment of removal. MAVLink relays, SOME/IP gateways, gRPC-JSON transcoders, and now DICOM de-identification. Same shape, different boundary.&lt;/p&gt;

&lt;p&gt;The full threat-catalogue entry, written to paste into a 524B risk file, with the exact test: &lt;a href="https://github.com/cleitonaugusto/CleitonQ/blob/main/docs/dicom-provenance-threat-entry.md" rel="noopener noreferrer"&gt;https://github.com/cleitonaugusto/CleitonQ/blob/main/docs/dicom-provenance-threat-entry.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The class write-up across the other protocols, including the results that argue against the thesis: &lt;a href="https://doi.org/10.5281/zenodo.21840073" rel="noopener noreferrer"&gt;https://doi.org/10.5281/zenodo.21840073&lt;/a&gt;&lt;/p&gt;

</description>
      <category>healthcare</category>
      <category>security</category>
      <category>dicom</category>
      <category>ai</category>
    </item>
    <item>
      <title>We Benchmarked 4 Rust Quantum Simulators. Three Agreed. One Didn't.</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Sun, 05 Jul 2026 11:42:18 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/we-benchmarked-4-rust-quantum-simulators-three-agreed-one-didnt-4dd2</link>
      <guid>https://dev.to/cleiton_augusto_/we-benchmarked-4-rust-quantum-simulators-three-agreed-one-didnt-4dd2</guid>
      <description>&lt;p&gt;We built &lt;strong&gt;CleitonForge&lt;/strong&gt; — a neutral benchmarking layer for quantum simulation written in Rust. The idea is simple: take the same quantum circuit, run it through multiple simulators using an identical canonical IR, and compare. No favorites. No built-in assumptions about which framework is correct.&lt;/p&gt;

&lt;p&gt;We plugged in four backends: our own native statevector, &lt;strong&gt;quantrs2&lt;/strong&gt;, &lt;strong&gt;roqoqo&lt;/strong&gt;, and &lt;strong&gt;q1tsim&lt;/strong&gt;. On Bell states, Grover search, QFT, and Bernstein-Vazirani — all four agreed to machine precision. Then we ran QAOA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native vs quantrs2 fidelity: 0.00000000.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a rounding error. Not a bug in our code. A fundamental sign disagreement in how one framework defines the Rz gate — invisible in standard benchmarks, catastrophic in parameterized quantum algorithms.&lt;/p&gt;




&lt;h2&gt;
  
  
  The finding: two definitions of the same gate
&lt;/h2&gt;

&lt;p&gt;The Rz(λ) gate is one of the most common in quantum computing. IBM, Qiskit, OpenQASM, and virtually every textbook define it as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IBM / Qiskit convention — used by: native · roqoqo · q1tsim&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rz(λ) = ⎡ e^{−iλ/2}    0        ⎤
         ⎣    0       e^{+iλ/2}  ⎦
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Opposite sign convention — used by: quantrs2-core&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rz(λ) = ⎡ e^{+iλ/2}    0        ⎤
         ⎣    0       e^{−iλ/2}  ⎦
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is a global sign flip in the exponent. For λ = 0, both give the identity — you can't tell them apart. For λ = π/4 (as in QAOA's cost layer), the two matrices produce orthogonal quantum states. Fidelity: exactly zero.&lt;/p&gt;




&lt;h2&gt;
  
  
  The evidence
&lt;/h2&gt;

&lt;p&gt;We ran every backend through the same QAOA MaxCut circuit (2 qubits, γ = −3π/4, β = −π/8) and measured cross-backend fidelity:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pair&lt;/th&gt;
&lt;th&gt;Fidelity&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;native ↔ roqoqo&lt;/td&gt;
&lt;td&gt;1.00000000&lt;/td&gt;
&lt;td&gt;✅ agree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;native ↔ q1tsim&lt;/td&gt;
&lt;td&gt;1.00000000&lt;/td&gt;
&lt;td&gt;✅ agree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;roqoqo ↔ q1tsim&lt;/td&gt;
&lt;td&gt;1.00000000&lt;/td&gt;
&lt;td&gt;✅ agree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;native ↔ quantrs2&lt;/td&gt;
&lt;td&gt;0.00000000&lt;/td&gt;
&lt;td&gt;❌ differ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;roqoqo ↔ quantrs2&lt;/td&gt;
&lt;td&gt;0.00000000&lt;/td&gt;
&lt;td&gt;❌ differ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;q1tsim ↔ quantrs2&lt;/td&gt;
&lt;td&gt;0.00000000&lt;/td&gt;
&lt;td&gt;❌ differ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why Quantum Volume doesn't catch it
&lt;/h3&gt;

&lt;p&gt;We also ran the IBM Quantum Volume benchmark — Haar-random SU(4) circuits, 100 trials per width. All four backends returned &lt;strong&gt;identical HOG fractions to 4 decimal places&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Width n&lt;/th&gt;
&lt;th&gt;QV&lt;/th&gt;
&lt;th&gt;native&lt;/th&gt;
&lt;th&gt;quantrs2&lt;/th&gt;
&lt;th&gt;roqoqo&lt;/th&gt;
&lt;th&gt;q1tsim&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;n = 2&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0.5355&lt;/td&gt;
&lt;td&gt;0.5355&lt;/td&gt;
&lt;td&gt;0.5355&lt;/td&gt;
&lt;td&gt;0.5355&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.7340 ✅&lt;/td&gt;
&lt;td&gt;0.7340 ✅&lt;/td&gt;
&lt;td&gt;0.7340 ✅&lt;/td&gt;
&lt;td&gt;0.7340 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 4&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;0.7991 ✅&lt;/td&gt;
&lt;td&gt;0.7991 ✅&lt;/td&gt;
&lt;td&gt;0.7991 ✅&lt;/td&gt;
&lt;td&gt;0.7991 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n = 5&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;0.8459 ✅&lt;/td&gt;
&lt;td&gt;0.8459 ✅&lt;/td&gt;
&lt;td&gt;0.8459 ✅&lt;/td&gt;
&lt;td&gt;0.8459 ✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is not a contradiction — it's the key insight. Quantum Volume uses &lt;em&gt;random&lt;/em&gt; angles. When λ is drawn from a uniform distribution over [0, 2π), the sign flip averages out across the ensemble. The divergence only surfaces with &lt;strong&gt;specific, purposeful angles&lt;/strong&gt; — exactly the kind used in QAOA, VQE, and other variational algorithms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The implication:&lt;/strong&gt; Standard Clifford benchmarks and random circuit tests are blind to this class of convention disagreement. You need a benchmark that exercises parameterized gates at specific angles — which is exactly what QAOA does, and exactly what CleitonForge's benchmark suite runs automatically across all backends.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The architecture that found it
&lt;/h2&gt;

&lt;p&gt;CleitonForge is not a simulator. It's a &lt;strong&gt;neutral benchmarking layer&lt;/strong&gt; that sits between circuits and simulators. Every backend receives the same canonical intermediate representation — a flat list of typed operations with no framework-specific encoding — and returns a statevector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Same circuit, four backends, zero code duplication&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;circuit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_qasm2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qaoa_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;SimulationBackend&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"native"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;NativeStateVectorBackend&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"quantrs2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;QuantRS2Backend&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"roqoqo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;RoqoqoBackend&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"q1tsim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Q1tSimBackend&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;backends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;circuit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/*shots=*/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;measure_fidelity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;reference_sv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="py"&gt;.statevector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The canonical IR means backends never see each other's types. roqoqo's gate structs, q1tsim's matrix API, and quantrs2's internal representation are all adapted at the backend boundary. This is also why the Rz divergence is detectable: the input angle λ is the same floating-point value for all backends. The difference is entirely in how each framework's gate definition applies it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond the finding: what we built along the way
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Exact noisy simulation
&lt;/h3&gt;

&lt;p&gt;We implemented a &lt;strong&gt;density matrix backend&lt;/strong&gt; — exact noisy simulation via ρ ∈ ℂ^(4ⁿ), no Monte Carlo variance. Four Kraus noise channels (depolarizing, amplitude damping, bit-flip, phase-flip) applied after each gate. At 12 qubits you get the exact probability distribution from a single simulation run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cforge&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cforge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Circuit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# IBM Nairobi calibration: avg SX err 0.03%, avg CX err 0.64%
&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cforge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;density-matrix&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;depolarizing_1q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.000315&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;depolarizing_2q&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.00638&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;top_states&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# [('00', 0.487), ('11', 0.487), ('01', 0.013), ('10', 0.013)]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real hardware calibration
&lt;/h3&gt;

&lt;p&gt;We ship a parser for IBM's public calibration JSON format — T1/T2 times, per-qubit SX error rates, per-pair CX error rates — and convert it directly to a &lt;code&gt;NoisyConfig&lt;/code&gt;. IBM Nairobi numbers are included as a reference snapshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python bindings
&lt;/h3&gt;

&lt;p&gt;Everything above is available from Python via &lt;code&gt;pip install cleitonforge&lt;/code&gt;. The bindings expose all backends, noise channels, and the QASM parser. Built with PyO3 and maturin; Linux/macOS/Windows wheels on PyPI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance (release build)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;Qubits&lt;/th&gt;
&lt;th&gt;Gates&lt;/th&gt;
&lt;th&gt;Time (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bell state&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GHZ state&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QFT&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bernstein-Vazirani&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grover search&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;0.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What we are — and what we're not
&lt;/h2&gt;

&lt;p&gt;The temptation when you find a convention divergence is to build your own simulator and do it "right." We're not doing that.&lt;/p&gt;

&lt;p&gt;Our moat is &lt;strong&gt;neutrality&lt;/strong&gt;. The moment CleitonForge ships its own simulator, it becomes a competitor to the projects it benchmarks — and loses the only thing that makes its findings credible: no skin in the game. roqoqo cannot tell you "our Rz convention matches IBM and quantrs2's doesn't," because they're a party to the comparison. We can.&lt;/p&gt;

&lt;p&gt;What &lt;em&gt;does&lt;/em&gt; make sense: a &lt;strong&gt;convention normalization transpiler&lt;/strong&gt; — a layer that detects which Rz convention a circuit assumes and corrects it for the target backend. That's an extension of the benchmarking mission, not a departure from it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Open source:&lt;/strong&gt; Convention normalization transpiler — detect and correct Rz sign on any circuit. Planned as part of &lt;code&gt;cforge-parser&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;arXiv:&lt;/strong&gt; Preprint: &lt;em&gt;"Neutral cross-framework benchmarking reveals Rz sign convention divergence in quantum simulation backends."&lt;/em&gt; Formally citable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise SaaS:&lt;/strong&gt; &lt;code&gt;cforge-enterprise&lt;/code&gt;: per-device calibration tables, Zero-Noise Extrapolation, convention-aware circuit routing. API for teams running multi-backend quantum workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tooling:&lt;/strong&gt; Jupyter notebook companion, QASM linter that flags convention-sensitive gates, dashboard for multi-hardware fidelity tracking.&lt;/p&gt;

&lt;p&gt;The SaaS angle is straightforward: quantum teams running the same algorithm on different hardware providers need consistency guarantees that no single provider can offer. CleitonForge is the neutral layer that can.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;cleitonforge

&lt;span class="c"&gt;# or from source — reproduce the exact findings:&lt;/span&gt;
cargo run &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--example&lt;/span&gt; benchmark_suite &lt;span class="nt"&gt;-p&lt;/span&gt; cforge-cli
cargo run &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="nt"&gt;--example&lt;/span&gt; quantum_volume &lt;span class="nt"&gt;-p&lt;/span&gt; cforge-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/cleitonaugusto/cleitonforge" rel="noopener noreferrer"&gt;github.com/cleitonaugusto/cleitonforge&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>quantumcomputing</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>I built a neutral benchmarking layer for quantum simulators in Rust — and it revealed a silent disagreement between two backends</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Sat, 04 Jul 2026 18:26:18 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/i-built-a-neutral-benchmarking-layer-for-quantum-simulators-in-rust-and-it-revealed-a-silent-2i59</link>
      <guid>https://dev.to/cleiton_augusto_/i-built-a-neutral-benchmarking-layer-for-quantum-simulators-in-rust-and-it-revealed-a-silent-2i59</guid>
      <description>&lt;p&gt;placeholder&lt;/p&gt;

</description>
      <category>rust</category>
      <category>quantumcomputing</category>
      <category>opensource</category>
      <category>benchmark</category>
    </item>
    <item>
      <title>Nonce Design for Safety-Critical Systems: Lessons from a Post-Quantum MAVLink Protocol</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Tue, 23 Jun 2026 02:39:57 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/nonce-design-for-safety-critical-systems-lessons-from-a-post-quantum-mavlink-protocol-2kmc</link>
      <guid>https://dev.to/cleiton_augusto_/nonce-design-for-safety-critical-systems-lessons-from-a-post-quantum-mavlink-protocol-2kmc</guid>
      <description>&lt;p&gt;Replay attacks on drone command links are not theoretical. A ground station sends &lt;code&gt;ARM&lt;/code&gt; at timestamp T. An adversary records the packet. Thirty seconds later they retransmit it verbatim. If the drone accepts it, you have a serious problem — and in a jammed or contested environment, the attacker can do this silently.&lt;/p&gt;

&lt;p&gt;The standard defense is a monotonically increasing nonce: every packet carries a counter, and the receiver only accepts packets with counters strictly greater than the last accepted value. Simple in concept. The implementation details are where things get interesting.&lt;/p&gt;

&lt;p&gt;This post walks through the nonce design in &lt;a href="https://github.com/cleitonaugusto/CleitonQ" rel="noopener noreferrer"&gt;CleitonQ&lt;/a&gt;, a post-quantum authentication layer for MAVLink v2, and the three decisions that are non-obvious but matter for security.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Concurrent Control Loops
&lt;/h2&gt;

&lt;p&gt;A drone's onboard software runs several concurrent threads: a 100 Hz telemetry loop, a command processor, and potentially a mesh relay. All of them sign outbound packets. All of them need nonces.&lt;/p&gt;

&lt;p&gt;The naive implementation is a shared &lt;code&gt;u64&lt;/code&gt; behind a &lt;code&gt;Mutex&lt;/code&gt;. It works. It's also a footgun: if two threads call &lt;code&gt;next_nonce()&lt;/code&gt; simultaneously without proper synchronization, they can read the same value, both increment to the same next value, and emit duplicate nonces. The receiver sees the duplicate and treats it as a replay — silently dropping a legitimate command.&lt;/p&gt;

&lt;p&gt;In a flight-critical system, a dropped command is not an acceptable error mode.&lt;/p&gt;

&lt;p&gt;The second naive implementation is &lt;code&gt;fetch_add&lt;/code&gt; on an &lt;code&gt;AtomicU64&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tempting, but has a subtle problem&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.fetch_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fixes the race. But it wraps silently at &lt;code&gt;u64::MAX&lt;/code&gt;. After 18.4 quintillion packets — unlikely in practice, but not impossible over the lifetime of a long-running system — nonce 0 becomes valid again. An adversary who stored a packet from the beginning of time can now replay it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Decision 1: Saturate, Don't Wrap
&lt;/h2&gt;

&lt;p&gt;CleitonQ's &lt;code&gt;AtomicNonce::next()&lt;/code&gt; uses a compare-and-exchange loop that saturates at &lt;code&gt;u64::MAX&lt;/code&gt; instead of wrapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// channel is exhausted, not rolled over&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.compare_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Relaxed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;observed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the counter saturates, the receiver rejects &lt;code&gt;u64::MAX&lt;/code&gt; as a replay (it was already accepted). The channel stops working. That is the correct behavior: a locked channel surfaces as an observable failure — an operator sees it, investigates, and re-establishes the session. A silently rolled-over channel surfaces as an intermittent security hole that nobody notices until it's too late.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fail loudly rather than fail silently.&lt;/strong&gt; In safety-critical systems, this principle is not optional.&lt;/p&gt;

&lt;p&gt;The CAS loop also handles the race correctly: if two threads read the same &lt;code&gt;current&lt;/code&gt;, one wins the exchange and the other retries with the updated value. No duplicates, no locks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Decision 2: Memory Ordering Is Not Symmetric
&lt;/h2&gt;

&lt;p&gt;The sender (&lt;code&gt;AtomicNonce&lt;/code&gt;) and the receiver (&lt;code&gt;NonceTracker&lt;/code&gt;) have different memory ordering requirements, and they are not interchangeable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AtomicNonce::next()&lt;/code&gt; uses &lt;code&gt;Relaxed&lt;/code&gt; for both the load and the CAS. This is intentional. The only property needed is that each call returns a unique, strictly increasing value. There is no requirement that the nonce emission &lt;em&gt;happens-before&lt;/em&gt; anything else in the caller's memory. The packet containing the nonce will be serialized and sent over the network — the network ordering establishes the happens-before relationship with the receiver. Using &lt;code&gt;SeqCst&lt;/code&gt; here would be correct but unnecessary, adding synchronization overhead on every outbound packet in a 100 Hz loop.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NonceTracker::accept()&lt;/code&gt; is different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Acquire&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.compare_exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AcqRel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Ordering&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Acquire&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;observed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The receiver uses &lt;code&gt;Acquire&lt;/code&gt; on the load and &lt;code&gt;AcqRel&lt;/code&gt; on the successful exchange. This establishes a happens-before edge: any thread that subsequently reads the tracker's value with &lt;code&gt;Acquire&lt;/code&gt; sees all writes that preceded the accepted nonce. In practice this means: the authentication check that accepted a packet happens-before any processing of that packet's payload. If two threads race to accept the same nonce, exactly one wins the CAS — the other sees &lt;code&gt;nonce &amp;lt;= current&lt;/code&gt; on retry and returns &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;Relaxed&lt;/code&gt; on the receiver would be wrong. It would allow a theoretical reordering where a thread begins processing a payload before the nonce check completes — which, in a language with a memory model that permits this, is a real vulnerability class.&lt;/p&gt;




&lt;h2&gt;
  
  
  Decision 3: Process Restarts Without Persistent State
&lt;/h2&gt;

&lt;p&gt;What happens when the companion computer reboots mid-flight? The &lt;code&gt;AtomicU64&lt;/code&gt; in RAM is gone. If the new process starts from 0, every nonce it emits is below the receiver's &lt;code&gt;last_accepted&lt;/code&gt; — the channel is dead until a new session is established.&lt;/p&gt;

&lt;p&gt;One answer is NVRAM persistence: write the nonce to flash periodically, read it on boot. This works but adds I/O latency on the critical path and creates a new failure mode: flash write corruption during power loss.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AtomicNonce::from_time()&lt;/code&gt; takes a different approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;from_time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;nanos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;SystemTime&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.duration_since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UNIX_EPOCH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap_or_default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.as_nanos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;try_from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nanos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeding from nanoseconds since the Unix epoch means a restarted process almost certainly starts with nonces higher than anything emitted before the restart. A 10-second reboot adds 10 billion nonces of headroom. A 1-millisecond glitch adds 1 million. The wall clock is the implicit persistent store.&lt;/p&gt;

&lt;p&gt;This relies on the system clock being monotonic across reboots — which is true on any platform with a battery-backed RTC and NTP. On systems without one (some deeply embedded targets), &lt;code&gt;from_time()&lt;/code&gt; is unavailable and the application must manage initial nonce values explicitly.&lt;/p&gt;

&lt;p&gt;The deeper architectural answer is that CleitonQ's session boundary makes this largely moot: a reboot forces a new ML-KEM session, which establishes a new session key. Since nonces are checked within a session (HMAC tags include the session key), cross-session replay is impossible regardless of nonce values.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Embedded Target
&lt;/h2&gt;

&lt;p&gt;Not all targets have 64-bit atomics. Cortex-M4 (the processor in most Pixhawk flight controllers) does not. For these, CleitonQ provides &lt;code&gt;SimpleNonce&lt;/code&gt; and &lt;code&gt;SimpleNonceTracker&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;SimpleNonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;SimpleNonce&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;next_nonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.wrapping_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not thread-safe — the &lt;code&gt;&amp;amp;mut self&lt;/code&gt; receiver makes that explicit at the type level. On a single-threaded embedded executor, this is correct and zero-overhead. On a multi-threaded target with 64-bit atomics, the compiler will refuse to compile the single-threaded variant in a shared context.&lt;/p&gt;

&lt;p&gt;The platform split is expressed via &lt;code&gt;#[cfg(target_has_atomic = "64")]&lt;/code&gt;, not runtime checks — it's a compile-time guarantee, not a runtime assertion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing the Properties
&lt;/h2&gt;

&lt;p&gt;Three properties need tests, not documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uniqueness under concurrency&lt;/strong&gt; — 8 threads each calling &lt;code&gt;next()&lt;/code&gt; 1000 times should produce 8000 distinct nonces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;AtomicNonce&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="py"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HashSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;nonce&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"duplicate nonce — race in AtomicNonce::next"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replay rejection&lt;/strong&gt; — the tracker must reject exact replays and regressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;NonceTracker&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// exact replay&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// regression&lt;/span&gt;
&lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;    &lt;span class="c1"&gt;// forward progress&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No double-accept under concurrency&lt;/strong&gt; — 4 threads racing to accept nonces 1..=500 should produce exactly 500 total acceptances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;NonceTracker&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// 4 threads, each tries to accept all 500 nonces&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;total_accepted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="nf"&gt;.into_iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.sum&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_accepted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="nf"&gt;.last_accepted&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests run on every CI push, including on a Neoverse-N2 ARM64 runner that mirrors the hardware profile of production companion computers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Broader Point
&lt;/h2&gt;

&lt;p&gt;Nonce design looks simple until you consider the combination of concurrent writers, concurrent readers, process restarts, and an adversary who stores packets indefinitely. Each of those constraints pushes the design in a different direction. Getting all four right simultaneously requires explicit reasoning about each decision — not just picking the first implementation that passes the unit tests.&lt;/p&gt;

&lt;p&gt;The three decisions above — saturating arithmetic, asymmetric memory ordering, and clock-seeded initialization — are each defensible in isolation. Together they form a design that fails loudly, maintains happens-before guarantees where they matter, and survives the most common production failure mode (process restart) without persistent state.&lt;/p&gt;

&lt;p&gt;CleitonQ is open source under MIT OR Apache-2.0. The full nonce implementation, with all tests, is in &lt;a href="https://github.com/cleitonaugusto/CleitonQ/blob/main/src/nonce.rs" rel="noopener noreferrer"&gt;&lt;code&gt;src/nonce.rs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;CleitonQ is a post-quantum authentication layer for MAVLink v2, combining ML-KEM-1024 (FIPS 203) for session establishment and ML-DSA-87 (FIPS 204) for command signing. A formal security model in ProVerif 2.05 verifies session key secrecy (Q1) and command authenticity (Q2) against a Dolev-Yao attacker. &lt;a href="https://doi.org/10.5281/zenodo.20776349" rel="noopener noreferrer"&gt;Paper on Zenodo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>security</category>
      <category>embedded</category>
      <category>drone</category>
    </item>
    <item>
      <title>Implementing Adaptive Backpressure in Rust with FlowGuard</title>
      <dc:creator>Cleiton Augusto Correa Bezerra</dc:creator>
      <pubDate>Sat, 27 Dec 2025 15:43:34 +0000</pubDate>
      <link>https://dev.to/cleiton_augusto_/implementing-adaptive-backpressure-in-rust-with-flowguard-1iof</link>
      <guid>https://dev.to/cleiton_augusto_/implementing-adaptive-backpressure-in-rust-with-flowguard-1iof</guid>
      <description>&lt;p&gt;Implementing Adaptive Backpressure in Rust with FlowGuard&lt;br&gt;
Hey fellow Rustaceans! 👋&lt;/p&gt;

&lt;p&gt;I recently open-sourced FlowGuard, a library for adaptive concurrency control and backpressure in Rust services. In this post, I'll share why static rate limiting fails and how FlowGuard solves it with TCP Vegas congestion control.&lt;/p&gt;

&lt;p&gt;🤔 The Problem with Static Limits&lt;br&gt;
We've all done this:&lt;/p&gt;

&lt;p&gt;rust&lt;br&gt;
// "Maximum 100 concurrent connections"&lt;br&gt;
let max_connections = 100;&lt;br&gt;
But static limits are a trap:&lt;/p&gt;

&lt;p&gt;Set too high? Your system crashes before reaching the limit&lt;/p&gt;

&lt;p&gt;Set too low? You waste resources and refuse legitimate traffic&lt;/p&gt;

&lt;p&gt;Guessing game? You're always tuning based on hunches&lt;/p&gt;

&lt;p&gt;🚀 The Solution: Dynamic Backpressure&lt;br&gt;
Instead of guessing, what if your system could self-adjust based on real-time performance? That's where FlowGuard comes in.&lt;/p&gt;

&lt;p&gt;Introducing FlowGuard&lt;br&gt;
FlowGuard implements the TCP Vegas congestion control algorithm to dynamically adjust concurrency limits based on actual system latency.&lt;/p&gt;

&lt;p&gt;🎯 How It Works&lt;br&gt;
rust&lt;br&gt;
use flow_guard::{FlowGuard, VegasStrategy};&lt;br&gt;
use std::sync::Arc;&lt;/p&gt;

&lt;h1&gt;
  
  
  [tokio::main]
&lt;/h1&gt;

&lt;p&gt;async fn main() {&lt;br&gt;
    // Start with 10 concurrent operations&lt;br&gt;
    let strategy = Arc::new(VegasStrategy::new(10));&lt;br&gt;
    let guard = FlowGuard::new(Arc::clone(&amp;amp;strategy));&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;println!("Initial limit: {}", guard.current_limit());

// Execute tasks with adaptive backpressure
let result = guard.run(async {
    // Your database query, API call, etc.
    tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    Ok::&amp;lt;_, &amp;amp;str&amp;gt;("Success!")
}).await;

println!("Final limit: {}", guard.current_limit()); // Adjusted!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
✨ Key Features&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Real-time Adjustment
rust
// Watch limits adjust dynamically
println!("Current limit: {}", guard.current_limit());
println!("Available permits: {}", guard.available_permits());&lt;/li&gt;
&lt;li&gt;Vegas Algorithm
Based on the difference between expected and actual throughput:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;✅ Increases limit when system has spare capacity&lt;/p&gt;

&lt;p&gt;✅ Decreases limit when latency indicates congestion&lt;/p&gt;

&lt;p&gt;✅ Self-tuning - no manual configuration needed&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Web Framework Integration
rust
// Axum 0.8 middleware
let strategy = VegasStrategy::new(50);
let flow_layer = FlowGuardLayer::new(strategy);&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;let app = Router::new()&lt;br&gt;
    .route("/api/data", get(handler))&lt;br&gt;
    .layer(flow_layer);&lt;br&gt;
📦 Getting Started&lt;br&gt;
Add to your Cargo.toml:&lt;/p&gt;

&lt;p&gt;toml&lt;br&gt;
[dependencies]&lt;br&gt;
flow-guard = "0.2.1"&lt;/p&gt;

&lt;h1&gt;
  
  
  With Axum/Tower support
&lt;/h1&gt;

&lt;p&gt;flow-guard = { version = "0.2.1", features = ["axum", "tower"] }&lt;br&gt;
🔧 Under the Hood&lt;br&gt;
FlowGuard replaces tokio::sync::Semaphore with a custom DynamicSemaphore that can adjust its limit up and down in real-time:&lt;/p&gt;

&lt;p&gt;rust&lt;br&gt;
pub struct DynamicSemaphore {&lt;br&gt;
    max_permits: AtomicUsize,&lt;br&gt;
    available_permits: AtomicUsize,&lt;br&gt;
    notify: Notify,&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;impl DynamicSemaphore {&lt;br&gt;
    pub fn set_limit(&amp;amp;self, new_limit: usize) {&lt;br&gt;
        // Adjusts permits dynamically based on Vegas calculations&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
🎯 Use Cases&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Database Protection
rust
// Prevent database overload
let db_guard = FlowGuard::new(VegasStrategy::new(20));&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;async fn query_database() -&amp;gt; Result {&lt;br&gt;
    db_guard.run(|| async {&lt;br&gt;
        // Your database query here&lt;br&gt;
        database.query("SELECT * FROM users").await&lt;br&gt;
    }).await``&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;API Rate Limiting
rust
// Adaptive rate limiting for external APIs
let api_guard = FlowGuard::new(VegasStrategy::new(5));&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;async fn call_external_api() -&amp;gt; Result {&lt;br&gt;
    api_guard.run(|| async {&lt;br&gt;
        client.get("&lt;a href="https://api.example.com/data%22).await" rel="noopener noreferrer"&gt;https://api.example.com/data").await&lt;/a&gt;&lt;br&gt;
    }).await&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Microservices
rust
// Protect services from cascading failures
let service_guard = FlowGuard::new(VegasStrategy::new(100));
📊 Benchmarks
In testing, FlowGuard showed:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;5 → 12 limit adjustment under optimal conditions&lt;/p&gt;

&lt;p&gt;Sub-millisecond overhead per request&lt;/p&gt;

&lt;p&gt;Zero allocation in hot path&lt;/p&gt;

&lt;p&gt;Thread-safe with atomic operations&lt;/p&gt;

&lt;p&gt;🚀 Try It Yourself&lt;br&gt;
bash&lt;/p&gt;

&lt;h1&gt;
  
  
  Clone and run examples
&lt;/h1&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/cleitonaugusto/flow-guard" rel="noopener noreferrer"&gt;https://github.com/cleitonaugusto/flow-guard&lt;/a&gt;&lt;br&gt;
cd flow-guard&lt;br&gt;
cargo run --example basic_usage&lt;br&gt;
🔗 Resources&lt;br&gt;
GitHub: &lt;a href="https://github.com/cleitonaugusto/flow-guard" rel="noopener noreferrer"&gt;https://github.com/cleitonaugusto/flow-guard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Crates.io: &lt;a href="https://crates.io/crates/flow-guard" rel="noopener noreferrer"&gt;https://crates.io/crates/flow-guard&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Documentation: &lt;a href="https://docs.rs/flow-guard/0.2.1/" rel="noopener noreferrer"&gt;https://docs.rs/flow-guard/0.2.1/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Examples: basic_usage.rs, server_demo.rs&lt;/p&gt;

&lt;p&gt;💭 Why I Built This&lt;br&gt;
After seeing too many services crash from static limits or waste resources with conservative settings, I wanted a solution that adapts to actual system performance. The TCP Vegas algorithm has been battle-tested for decades in networking - why not apply it to service concurrency?&lt;/p&gt;

&lt;p&gt;🤝 Contributing &amp;amp; Feedback&lt;br&gt;
FlowGuard is open source under MIT license. I'd love your:&lt;/p&gt;

&lt;p&gt;Feedback on the API design&lt;/p&gt;

&lt;p&gt;Use cases from your projects&lt;/p&gt;

&lt;p&gt;Contributions to the codebase&lt;/p&gt;

&lt;p&gt;Ideas for improvements&lt;/p&gt;

&lt;p&gt;What adaptive concurrency patterns have you used in your Rust projects? Share in the comments!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>rust</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
