What changed, and why it matters
This commit is a normal feature update for the Krux Bitcoin signing device. It adds the ability to detect QR codes that have inverted colors (black background with white QR code) and renames some internal camera-mode functions. There is no indication of a security vulnerability or malicious change in the code.
No security action required. Treat as a routine feature improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds find_inverted=first_frame to img.find_qrcodes() so the camera tries to decode inverted-color QR codes on the first frame of each scan. It also renames has_antiglare() to has_mode_control() and anti_glare_control() to mode_control() to reflect that the PAGE button now toggles between normal scan mode and anti-glare mode on supported cameras. Version strings and changelog are updated accordingly. No security-relevant code paths are altered.
Changed components
src/krux/pages/qr_capture.pysrc/krux/camera.pytests/test_camera.pyInspect captured patch +23 / −17
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9295d3..2c8b41b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ Exported Uniform Resource (UR) QR codes, a widely adopted standard for exchangin
- Bugfix: Screensaver not activating in menu pages without statusbar
- Embit: Improved BIP39 mnemonic validation
- Bug Fix: Corrected handling of certain binary-encoded QR codes
+- Improved QR code decoding performance and added inverted color QR code detection
# Changelog 25.10.1 - October 2025
diff --git a/mkdocs.yml b/mkdocs.yml
index 1d4067a..4d0d69c 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -52,7 +52,7 @@ edit_uri: edit/main/docs
docs_dir: docs
site_dir: public
extra:
- latest_krux: krux-v25.10.1
+ latest_krux: krux-v25.11.beta2
latest_installer: v0.0.21
latest_installer_rpm: krux_installer-0.0.21-1.x86_64.rpm
latest_installer_deb: krux_installer_0.0.21_amd64.deb
diff --git a/pyproject.toml b/pyproject.toml
index ec335a7..5281f55 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -22,7 +22,7 @@
[tool.poetry]
name = "krux"
-version = "25.11.beta1"
+version = "25.11.beta2"
description = "Open-source signing device firmware for Bitcoin"
authors = ["Jeff S <jeffreesun@protonmail.com>"]
readme = "README.md"
diff --git a/src/krux/camera.py b/src/krux/camera.py
index 2780ef1..9508807 100644
--- a/src/krux/camera.py
+++ b/src/krux/camera.py
@@ -176,7 +176,7 @@ class Camera:
# Center weight mode = 7, default=0x01 (center mode = 0)
sensor.__write_reg(0x0C, 0x71)
- def has_antiglare(self):
+ def has_mode_control(self):
"""Returns whether the camera has anti-glare functionality"""
return self.cam_id in (OV7740_ID, OV2640_ID, GC2145_ID, GC0328_ID)
diff --git a/src/krux/metadata.py b/src/krux/metadata.py
index afe21d5..d2b27c9 100644
--- a/src/krux/metadata.py
+++ b/src/krux/metadata.py
@@ -19,5 +19,5 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-VERSION = "25.11.beta1"
+VERSION = "25.11.beta2"
SIGNER_PUBKEY = "03339e883157e45891e61ca9df4cd3bb895ef32d475b8e793559ea10a36766689b"
diff --git a/src/krux/pages/qr_capture.py b/src/krux/pages/qr_capture.py
index 1fedf0c..647cdf8 100644
--- a/src/krux/pages/qr_capture.py
+++ b/src/krux/pages/qr_capture.py
@@ -53,8 +53,8 @@ class QRCodeCapture(Page):
else:
self.ctx.light.turn_off()
- def anti_glare_control(self):
- """Controls the anti-glare based on the user input"""
+ def mode_control(self):
+ """Controls camera mode based on the user input"""
self.ctx.display.to_portrait()
mode = self.ctx.camera.toggle_camera_mode()
if mode == QR_SCAN_MODE:
@@ -137,7 +137,7 @@ class QRCodeCapture(Page):
previous_part = None
ur_highlighted = False
- # Flush events ocurred while loading camera
+ # Flush events occurred while loading camera
self.ctx.input.reset_ios_state()
start_time = time.ticks_ms()
self.ctx.display.clear()
@@ -148,6 +148,11 @@ class QRCodeCapture(Page):
t("Press PAGE to toggle mode"), offset_y
)
self.ctx.display.to_landscape()
+
+ # Cache end time for message display to avoid repeated addition
+ message_end_time = start_time + MESSAGE_DISPLAY_PERIOD
+ first_frame = True
+
while True:
wdt.feed()
@@ -159,8 +164,8 @@ class QRCodeCapture(Page):
# Anti-glare / zoom / normal mode
page_prev_event = self.ctx.input.page_prev_event()
if self.ctx.input.page_event() or (kboard.is_yahboom and page_prev_event):
- if self.ctx.camera.has_antiglare():
- self.anti_glare_control()
+ if self.ctx.camera.has_mode_control():
+ self.mode_control()
else:
break
@@ -184,15 +189,15 @@ class QRCodeCapture(Page):
ur_highlighted = False
img = self.ctx.camera.snapshot()
- if time.ticks_ms() < start_time + MESSAGE_DISPLAY_PERIOD:
+ if time.ticks_ms() < message_end_time:
self.ctx.display.render_image(img, title_lines=title_lines)
else:
self.ctx.display.render_image(img)
- res = img.find_qrcodes()
+ res = img.find_qrcodes(find_inverted=first_frame)
if res:
- data = res[0].payload()
- new_part = parser.parse(data)
+ first_frame = False
+ new_part = parser.parse(res[0].payload())
if (
parser.format == FORMAT_UR
diff --git a/tests/test_camera.py b/tests/test_camera.py
index a35e171..78854ee 100644
--- a/tests/test_camera.py
+++ b/tests/test_camera.py
@@ -134,7 +134,7 @@ def test_initialize_run_with_anti_glair_enabled(mocker, m5stickv):
assert c.cam_id is not None
-def test_toggle_antiglare(mocker, m5stickv):
+def test_toggle_mode(mocker, m5stickv):
import krux
from krux.camera import (
Camera,
@@ -153,9 +153,9 @@ def test_toggle_antiglare(mocker, m5stickv):
for sensor_id in SENSORS_LIST:
mocker.patch("krux.camera.sensor.get_id", lambda: sensor_id)
c = Camera()
- mocker.spy(c, "has_antiglare")
+ mocker.spy(c, "has_mode_control")
c.initialize_sensor()
- if c.has_antiglare():
+ if c.has_mode_control():
assert c.mode == QR_SCAN_MODE
c.toggle_camera_mode()
assert c.mode == ANTI_GLARE_MODE
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.