Skip to content

HF functionality extraction and Seader hardening - #36

Merged
bettse merged 13 commits into
bettse:mainfrom
cindersocket:feat-snmp
Mar 25, 2026
Merged

bettse merged 13 commits into
bettse:mainfrom
cindersocket:feat-snmp

Conversation

@cindersocket

Copy link
Copy Markdown
Collaborator

Summary

This PR hardens Seader’s HF/UHF runtime lifecycle by making ownership explicit and enforcing it in
code. It removes teardown-time use of shared Flipper NFC objects, makes HF and wiegand in-repo plugins, and adds an ownership contract so future changes have a single source of truth.

What changed

  • made hf a plugin under hf_interface_fal/
  • removed the plugin submodule/gitlink and updated build wiring to use tracked repo sources
  • added docs/OWNERSHIP_MODEL.md as the lifecycle and ownership contract
  • made NfcDevice loading callback host-owned and app-lifetime, instead of session-time mutable
    state
  • made HF teardown the sole owner of HF runtime stop/free/unload
  • moved worker poller-session reset out of scene code and into worker-owned startup/teardown paths
  • tightened HF session state transitions and teardown ordering
  • added real runtime tracking for the UHF SNMP probe flow
  • cleared cached SAM/UHF metadata when a new SAM is accepted so hot-swap does not show stale info
  • fixed unsafe UHF status-label formatting for short output buffers
  • ASN.1 card-detect ownership fixes and Picopass detect stop/join correctness hardening

Why

There were several potential ways for this app to fail:

  • scene code could mutate worker runtime state
  • HF teardown was flaky
  • HF lifecycle was split across host, scene, and worker paths
  • stale SAM/UHF metadata could bleed across card swaps

This PR makes invalid states much harder to represent by construction.

Contract / invariants

After this change:

  • app-lifetime host owns Nfc, NfcDevice, UART/SAM transport, and UI infrastructure
  • worker owns all live HF runtime state and is the only owner of HF teardown
  • scenes own presentation only and do not reset worker queue/stage/poller state
  • HF .fal is session-scoped
  • UHF and HF runtime are explicitly tracked and mutually exclusive
  • docs/OWNERSHIP_MODEL.md is the required contract for future lifecycle changes

Validation

  • ufbt builds cleanly
  • ownership contract updated to match implemented startup/teardown behavior
  • short-buffer UHF label formatting is now bounded
  • new-SAM acceptance clears stale firmware/UHF metadata before async refresh repopulates it
  • I am unable to crash seader by any combination of more, back, reloading, switching SAMs, etc.

@netlify

netlify Bot commented Mar 24, 2026

Copy link
Copy Markdown

👷 Deploy request for seader pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit b7b6855

@bettse bettse left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wont' be able to look till this evening, so I wanted to drop a few notes

Comment thread apdu_runner.c
Comment thread apdu_runner.c Outdated

bool seader_apdu_runner_send_next_line(Seader* seader) {
SeaderWorker* seader_worker = seader->worker;
if(!seader_worker || !seader_worker->uart) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

furi_check ?

Comment thread docs/OWNERSHIP_MODEL.md Outdated
@@ -0,0 +1,172 @@
# Ownership Model

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if this was it's own PR? Given it is english, feels like an easy win

Comment thread hf_interface_fal/.clang-format Outdated
@@ -0,0 +1,191 @@
---

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should just be handled ufbt format? or is this the same?

Comment thread hf_interface_fal/hf.c Outdated
static bool plugin_hf_start_read_for_type(void* plugin_ctx, SeaderCredentialType type) {
PluginHfContext* ctx = plugin_ctx;
NfcPoller* poller_detect = NULL;
if(!ctx || !ctx->nfc || !ctx->api) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

furi_check ?

Comment thread apdu_runner.c
#define SEADER_APDU_MAX_LEN 732

void seader_apdu_runner_cleanup(Seader* seader, SeaderWorkerEvent event) {
furi_check(seader);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should not be recoverable.

@cindersocket
cindersocket force-pushed the feat-snmp branch 2 times, most recently from d828af3 to c6301b5 Compare March 25, 2026 02:06
@bettse
bettse merged commit aa86e95 into bettse:main Mar 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants