Each note follows Observation → Hypothesis → Experiment → Result → Discussion → Takeaway. Where a note calls for measured results the repository does not publish, the Result section states Additional validation required and the Experiment section describes the validation approach without inventing outcomes.
Note 1 — One core, many clients
Observation. The architecture doc states a hard rule: if a capability must exist in more than one interface, it belongs in the core first, and the desktop app, CLI, and SDK consume the same implementation. The repository layout reflects this: core/ holds loader, memory, disasm, cfg, ir, ssa, analysis, type, decompiler, debugger, database, and sdk, while apps/desktop_qt and apps/cli are thin clients.
Hypothesis. Enforcing a single core prevents behavioral drift between interfaces, so a binary analyzed in the CLI and the same binary opened in the desktop app should produce identical recovered structure.
Experiment. Analyze a fixed set of binaries through the CLI, persist to SQLite, then open the same project databases in the desktop app and compare function lists, CFG edges, and xref counts for divergence. Repeat through the SDK’s analyze_binary path to confirm the same records.
Result. Additional validation required — the repository does not publish cross-surface equivalence measurements. The design intent is documented; the empirical equivalence is not.
Discussion. The value of the shared-core rule is that it makes equivalence a property of the architecture rather than of test discipline. The cost is that every feature must land in the core, which slows GUI-only additions. The rule is only as strong as its enforcement in review, and a single-contributor project has no external gate on that.
Takeaway. Shared-core is the right invariant for a multi-surface analysis tool, but it should be backed by an automated cross-surface equivalence test so the invariant is verified, not assumed.
Note 2 — Conservative AI response parsing with heuristic fallback
Observation. The AI integration doc states the response parser is deliberately conservative: if a provider response is malformed or unusable, Rothalyx falls back to the heuristic path instead of blocking analysis. Provider responses are normalized into a single insight shape (suggested name, short summary, analyst hints, pattern detections, vulnerability hints).
Hypothesis. Treating the model as an unreliable component and normalizing its output into a fixed internal shape isolates analysis correctness from provider behavior, so a broken or hostile model response degrades to heuristics rather than corrupting the run.
Experiment. Feed the parser a battery of malformed provider responses (truncated JSON, wrong schema, oversized fields, injected control content) and confirm each triggers the heuristic fallback with the analysis run still completing and persisting.
Result. Additional validation required — the fallback behavior is documented as a design property; a published test matrix of malformed-response handling is not present.
Discussion. This mirrors the loader’s posture toward hostile binaries: assume the input is adversarial and fail into a safe default. Normalizing to one insight shape also decouples the rest of the system from provider-specific response formats (Responses API, Messages API, generateContent, chat-completions).
Takeaway. Treating model output as untrusted input, with a fixed normalized shape and a heuristic fallback, is the correct pattern for optional AI in a security tool.
Note 3 — Secrets in the OS keyring, never plaintext
Observation. The AI doc specifies that non-secret config lives in normal desktop settings, but secrets are stored through the host OS: Windows Credential Manager, macOS Keychain, Linux Secret Service via secret-tool. If secure storage is unavailable, Rothalyx does not silently save keys in plaintext app settings.
Hypothesis. Delegating secret storage to the OS keyring, and refusing to fall back to plaintext, reduces the risk of credential leakage from config files or exported projects.
Experiment. On each platform, configure a provider key, then inspect on-disk settings and any exported project database to confirm no key material is present, and confirm that on a system with no keyring the app declines to persist the key rather than writing it in the clear.
Result. Additional validation required — the policy is documented; a published verification across the three platforms is not present.
Discussion. Credential leakage is explicitly in the security policy’s scope, so this behavior is a direct mitigation. The tradeoff is setup friction: on a minimal Linux environment without Secret Service, the user must install keyring tooling or use environment variables.
Takeaway. “Fail closed” on secret storage (no silent plaintext fallback) is the right default for a tool that handles provider API keys, even at the cost of setup friction.
Note 4 — A C ABI as the public boundary
Observation. The SDK is a C ABI exported from core/sdk/include/rothalyx/sdk/api.h, with version constants (ROTHALYX_SDK_VERSION_*, ROTHALYX_SDK_ABI_VERSION, ROTHALYX_SDK_PLUGIN_API_VERSION) and functions to analyze a binary, open a project, read the latest run, and enumerate functions and AI insights. String lifetimes are owned by the project handle until the next refreshing call or close_project.
Hypothesis. A C-shaped boundary keeps internal C++ types out of the public surface, enabling stable FFI consumption from Python, Rust, and Go without ABI churn.
Experiment. Bind the SDK from at least two FFI targets (for example Python ctypes and Rust) against successive builds and confirm the ABI version discipline catches incompatible changes and that the documented string-lifetime rule holds under repeated calls.
Result. Additional validation required — the header surface and lifetime rules are published; cross-language binding conformance tests are not.
Discussion. Explicit ABI and plugin-API version constants signal that compatibility is meant to be managed rather than incidental. The manual string-lifetime contract is a classic C-ABI tradeoff: it avoids allocation ownership ambiguity but pushes correctness onto the caller.
Takeaway. A versioned C ABI with explicit lifetime rules is the pragmatic boundary for a C++ engine that wants polyglot tooling; the lifetime contract needs conformance tests to stay safe.
Note 5 — Sanitizer-backed corpus fuzzing as a release gate
Observation. The fuzzing doc defines two runners, rothalyx_loader_corpus_runner and rothalyx_trace_corpus_runner, built under an asan-fuzz preset, with corpora of valid, malformed, truncated, oversized, and mutated inputs. Sanitizer findings are release blockers until triaged, and adversarial corpora are kept separate from benchmarks.
Hypothesis. Gating releases on clean sanitizer runs over adversarial corpora catches hostile-input bugs in the loader and trace parsers before they ship.
Experiment. Run the sustained campaign (run_sustained_campaign.sh) with elevated repeat counts (ROTHALYX_FUZZ_REPEAT_LOADER, ROTHALYX_FUZZ_REPEAT_TRACE) after parser changes and track findings over time.
Result. Additional validation required — the harness and policy exist; published campaign results, crash counts, or coverage figures are not present.
Discussion. The doc’s note that LeakSanitizer may refuse to start under a tracer or restricted sandbox (mitigated with ASAN_OPTIONS=detect_leaks=0 while keeping ASan and UBSan on) shows the harness is meant to run in real, constrained environments. Keeping adversarial corpora separate from benchmarks avoids conflating robustness with performance.
Takeaway. Wiring sanitizer corpus runners into the release gate converts parser robustness from an aspiration into a pass/fail criterion; publishing campaign metrics would make the guarantee auditable.
Note 6 — Bounding AI requests instead of pricing them
Observation. The AI doc states Rothalyx controls request size, not provider pricing. The desktop flow limits max functions per run, request timeout, and an optional daily remote-request cap, and leaves billing with the user’s own key.
Hypothesis. Bounding request volume (function count, timeout, daily cap) keeps AI-assisted runs predictable and prevents runaway cost or latency without hard-coding provider pricing that changes over time.
Experiment. Run AI-assisted analysis across binaries of varying size with different max_model_functions and daily-cap settings and confirm requests stay within the configured bounds and that exceeding the cap degrades gracefully.
Result. Additional validation required — the bounding parameters are documented; measured request-volume behavior is not published.
Discussion. Choosing to bound size rather than model price is a durable decision: pricing is external and volatile, request size is internal and controllable. It also caps the data-egress surface, since fewer functions leave the machine per run.
Takeaway. For optional hosted AI, control the inputs you own (request size, timeout, cap) and leave pricing to the provider; this is both a cost and a privacy control.
Note 7 — SQLite as the project substrate
Observation. core/database persists projects, annotations, AI output, and analysis artifacts to SQLite, and the desktop app can open a .sqlite project directly. The SDK opens a project, reads the latest run, and enumerates persisted functions and insights by run id.
Hypothesis. A single-file SQLite project database gives portable, inspectable, run-versioned state that the desktop app, CLI, and SDK can all read without a running service.
Experiment. Analyze a binary via the CLI, then read the resulting database through the SDK (get_latest_run, get_function_at, get_ai_insight_at) and open it in the desktop app, confirming identical run contents across surfaces.
Result. Additional validation required — the persistence model and SDK read path are documented; cross-surface read equivalence is not published.
Discussion. A run-id-scoped schema implies multiple analysis runs per project, which supports comparison over time. The single-file format is portable but the concurrency story (multiple surfaces writing simultaneously) is Additional validation required.
Takeaway. SQLite is a strong fit for portable, serviceless, run-versioned RE project state; concurrent-write semantics should be documented before heavy multi-surface use.
Note 8 — IR then SSA before decompilation
Observation. The pipeline lifts disassembly into IR, transforms into SSA, then runs analysis, type recovery, and simplification before the decompiler emits structured C-like output (core/ir, core/ssa, core/analysis, core/type, core/decompiler).
Hypothesis. Routing through SSA before decompilation makes data-flow and type-recovery passes tractable, improving the structure and readability of emitted pseudocode versus decompiling directly from disassembly.
Experiment. Compare decompiler output on a labeled set of binaries with type recovery enabled and disabled, scoring structural fidelity and variable/type accuracy against source truth.
Result. Additional validation required — the stage ordering is documented; decompiler quality measurements are not published.
Discussion. SSA before decompilation is the conventional and defensible choice, since most modern data-flow and type-recovery passes assume SSA form. The internal pass details and structuring algorithm are Additional validation required.
Takeaway. The IR→SSA→analysis→decompiler ordering is sound in principle; its payoff should be quantified with a labeled decompilation benchmark.
Note 9 — Normalizing four provider APIs into one insight shape
Observation. Provider mapping is explicit: OpenAI via Responses API, Anthropic via Messages API, Gemini via generateContent, OpenAI-compatible and local LLM via chat-completions style endpoints. All are normalized into one internal insight shape, and backends include heuristic, openai, anthropic, gemini, openai_compatible, local_llm, and auto.
Hypothesis. Normalizing heterogeneous provider APIs into one insight record keeps the rest of the pipeline provider-agnostic, so adding or swapping a provider does not ripple into analysis or persistence.
Experiment. Run the same binary through each backend and confirm the persisted insight records share one schema regardless of provider, and that auto selection behaves deterministically given configuration.
Result. Additional validation required — the mapping and normalized shape are documented; cross-provider output-shape equivalence is not published.
Discussion. A single insight shape is what makes the local-LLM mode a drop-in for hosted providers: same bounded selection, same structured parsing, different endpoint. It also localizes provider-specific fragility to the mapping layer.
Takeaway. Normalize external model outputs at the boundary; a single internal insight schema is what makes providers interchangeable, including local endpoints.
Note 10 — Renaming a project without breaking its release surface
Observation. v1.0.3 completed a rename from Zara to Rothalyx across the source tree, desktop resources (PNG, SVG, ICO, ICNS logo assets), CMake packaging metadata, SDK version strings, and Linux/macOS/Windows/Arch packaging scripts, standardizing on the rothalyx-re-framework slug.
Hypothesis. A rename that touches identity strings, asset names, package metadata, and release automation is safe only if it is done as one coordinated pass rather than incrementally, or downstream packaging and SDK version strings will drift.
Experiment. Build all five package targets and the SDK from the post-rename tree and confirm no residual zara identifiers remain in artifacts, metadata, or version strings.
Result. Additional validation required — the release notes describe the coordinated rename; a published verification that no legacy identifiers remain is not present.
Discussion. Doing the rename as a dedicated release (rather than folding it into a feature release) isolates risk: if packaging breaks, the cause is unambiguous. The historical zara repository alias still resolves, which preserves inbound references.
Takeaway. Treat a product rename as its own release with a single coordinated pass across code, assets, metadata, and CI; isolating it makes packaging regressions easy to attribute.
REGAAN R