Return Code Protocol
Every FFI function returns a status code following this convention:| Code | Meaning | Action |
|---|---|---|
0 | Success | Operation completed successfully |
-1 | Error | Retrieve error message via tui_get_last_error() |
-2 | Panic | Internal panic caught at FFI boundary |
The TypeScript layer uses
checkResult() to automatically translate error codes into KrakenError exceptions.Handle Protocol
Handles are opaqueu32 identifiers that represent native objects:
- Valid handles: Any non-zero
u32value - Invalid sentinel:
0is permanently reserved as invalid - Lifecycle: Handles are never recycled (sequential allocation)
- Ownership: The Rust native core owns all data; TypeScript holds only handles
String Passing
Strings cross the FFI boundary using explicit length-prefixed byte arrays:Host to Native (Input)
Native to Host (Output)
Two-call pattern for variable-length strings:Error Messages
Error strings use a special borrowed pointer:The error string pointer is valid until the next error occurs. Copy the message immediately.
Safety Guarantees
The FFI boundary enforces these invariants:1. Panic Safety (ADR-T03)
Everyextern "C" entry point is wrapped in catch_unwind:
2. Handle Validation
Every FFI function validates handles before use:-1 with a diagnostic error message.
3. UTF-8 Validation
Incoming strings are validated before processing:-1.
4. Unidirectional Control Flow
The host layer calls into the native core. The native core never calls back into the host layer.tui_next_event).
5. Copy Semantics
No pointers to internal data structures cross the boundary:- Input: Rust copies incoming string bytes
- Output: Rust copies into caller-provided buffers
- No interior pointers: Only handles, codes, and copy-out buffers
Event Delivery Contract
Events use a hybrid buffer-poll model (ADR-T01):- Input ingress:
tui_read_input(timeout_ms)captures terminal input - Buffer population: Events are classified and buffered internally
- Explicit drain: Host calls
tui_next_event(out_ptr)repeatedly until it returns0
This eliminates callback-based FFI complexity and enforces unidirectional control flow.
Memory Management
- Handles: Never manually free handles - use
tui_destroy_node()ortui_destroy_subtree() - Strings: Input strings are copied; output strings use caller-provided buffers
- State: All mutable state lives in the native core
- Cleanup: Call
tui_shutdown()to release terminal and free all resources
Thread Safety
Kraken TUI uses a single-threaded execution model (ADR-T16):- Global state is protected by
OnceLock<RwLock<TuiContext>> - Each FFI call acquires the lock
- Errors set on one thread may not be visible via
tui_get_last_error()on another thread - Thread-local storage used for error snapshots
Related
- Error Handling - KrakenError class and error recovery
- Troubleshooting - Common FFI issues and debugging