SGKEMBEDDED PERFORMANCEEpicTunerSDK · PrereleaseContact ↗
EpicTuner SDK/ Runtime contract: ABI 1.1 / wire 1 / development manifest 1

Download the complete Markdown manual ↓
One file with guides, API headers and examples for developers and coding agents.

Runtime contract: ABI 1.1 / wire 1 / development manifest 1

SDK 1.0 retains the frozen M0 foundation. See common types and lifetime rules, the package contract, and assigned decisions. New services need their own negotiated tables and tests. The broader product contract is PLAN-plugin-sdk.md in the private repository; this document stands alone in the exported SDK.

ABI and ownership

Only fixed-width integers, pointers local to the worker, explicit byte spans and function pointers cross the C ABI. Native 64-bit pointers, maximum packing of 8, Windows __cdecl and the platform C calling convention on Linux are required. No Qt/STL objects, C++ exceptions, RTTI, compiler enums, bools or heap ownership cross the boundary. Headers assert layouts in both C and C++:

Structure Size Selected offsets
et_string 16 data 0, size 8, reserved 12
et_query 16 size 0, ABI 4, capabilities 8
et_host_api 40 context 16, log 24, optional get_service 32; ABI 1.0 prefix is 32 bytes
et_event 40 operation 8, generation 16, text 24
et_plugin_api 56 ID 16, start 32, stop 40, event 48

Strings are valid UTF-8, without embedded NUL, with explicit byte lengths. Empty strings may use a null pointer. All reserved fields are zero. Inputs are borrowed only during a call; the service copies before returning. Plugin ID is immutable plugin-owned storage valid until worker exit. Host table is worker-owned and valid through stop; copy Context to retain a reference to it, not its temporary wrapper. No host call is legal after stop returns. Instance is plugin-owned; its stop handler destroys it using that module's allocator. A failed start returns a null instance and releases its own resources. The C++ adapter enforces this even on exceptions.

et_plugin_query receives the host's maximum ABI minor and available capabilities. The caller supplies output capacity. A plugin rejects short mandatory prefixes or different major versions, writes only its negotiated prefix, and reports that size. Future fields are append-only within a major. ABI 1.0 and 1.1 negotiate to the lower supported minor; 1.1 adds optional service discovery. Incompatible required features fail before loading the library. Runtime identity must equal the manifest identity and required capabilities must be a subset of grants. Context metadata is exposed by a separately versioned table; reserved future services return UNSUPPORTED.

Lifecycle/event/completion callbacks and service calls run on the worker dispatcher. Only background job work and its cancellation probe run on other threads. The log proxy returns ET_ERROR_STATE if called from another thread. Background task queues are implemented in M1; see JOBS.md. Callbacks must return promptly; start and normal callbacks have a 3-second supervision deadline and stop has 1 second. Nothing executes or waits on the GUI/ECU thread. The broker destructor only kills and reaps its own child at application teardown (bounded to 1 second).

Capabilities, features and errors

Capability Bit Feature Implemented behavior
log 1 et.log.v1 bounded UTF-8 info/warning/error diagnostics
events 2 et.events.v1 ordered developer message callback with operation ID
none 0 et.context.v1 service/feature metadata and frozen limits

Events are a foundation for later UI/project events; no project/tune access exists yet. Unknown required features and capabilities are rejected. Unknown optional features are ignored. Recognized features require their capability in the manifest.

Code Meaning
0 OK (log means queued, not a durable disk write)
1 invalid argument or mandatory structure prefix
2 incompatible ABI/table/identity
3 missing capability
4 message, string, log or queue limit
5 invalid lifecycle/thread state
6 caught C++ callback exception
7 unsupported operation
8 stale handle/generation
9 cancelled on worker loss; not replayed
10 deadline exceeded
11 protocol violation
12 revision/expected-value conflict
13 not found
14 I/O failure
15 busy or already committed

Async event replies carry the operation ID and code. Broker failures retain a human-readable reason and cancel outstanding events. Immediate C calls return a code; optional service discovery also fills caller-owned et_error. The common et_completion/et_async_options contracts are frozen for later asynchronous services.

Transport and wire encoding

Qt local sockets provide Windows named pipes and Linux Unix-domain sockets. No TCP listener is used. See Qt local server permissions. Each launch uses a fresh random endpoint/session, a 256-bit system-random token, same-user endpoint permissions, and native peer PID validation in both directions (Windows pipe PID APIs; Linux SO_PEERCRED including UID). The token and endpoint arrive over the child's inherited stdin pipe, never command-line arguments or an environment variable. No plugin library is loaded before the welcome handshake. This prevents accidental session crossing, not hostile code under the same account.

Frame: four-byte unsigned little-endian payload length, followed by a UTF-8 JSON object. C structs are never copied over the wire. A frame's payload is 1..65536 bytes. Numeric envelope fields are exact uint32 integers. The session is a UUID string. Sequences start at 1 independently in each direction and must increase by exactly one. Request IDs are globally increasing for host requests; replies echo them. Generation must be zero in this worker-global preview. Nonzero/stale generations, session mismatches, unknown envelope/body fields and messages in the wrong state fail the connection. Unknown optional manifest features are the only ignored fields. JSON objects use Qt's parser (duplicate member names resolve to the last value); signed package manifests instead forbid duplicates and use the frozen JCS subset defined in PACKAGE.md. Production verification is M5.

Envelope keys: wire, session, seq, request, generation, type, body. request=0 means unsolicited/lifecycle; events and ping use nonzero request IDs.

Message Direction Exact body
hello worker -> broker token:string, id:string
welcome broker -> worker capabilities:uint32
ready worker -> broker empty
log worker -> broker level:1..3, text:string
ping / pong broker -> worker / reply empty
event broker -> worker type:1 (message), text:string
result worker -> broker code:0..15
stop / stopped broker -> worker / reply empty
error worker -> broker code:1..15, detail:string (max 512 characters)

Limits and lifecycle

Resource Preview limit
Manifest / frame payload 64 KiB
ABI text / log / event 4096 UTF-8 bytes
Outbound socket queue 256 KiB per direction
Decoded message rate 200 per fixed 1-second window, per direction
Logs 100 per fixed 1-second window (caller sees LIMIT)
Pending event requests 32 per worker
Startup / event / heartbeat response 3000 ms
Heartbeat interval 500 ms
Shutdown 1000 ms
Feature lists 32 entries each
Future service quotas Nonzero frozen ceilings in FOUNDATION.md; services are unavailable until negotiated

The decoder buffers at most two bounded frames while parsing; socket reads yield between bounded batches. Pending events, outbound buffers and diagnostic messages are bounded; arbitrary native stdout/stderr goes to the OS null device. No growing native-output capture is retained. Public SDK thread queues are not implemented. Supervision runs every 100 ms, so deadlines allow that scheduling granularity. The five-worker 100 Hz/30-second transport benchmark records p50/p95/p99/max, queue occupancy and explicit fault gaps. Budgets: p95 <50 ms, p99 <100 ms, max <3000 ms. Actual telemetry/ECU-poll baseline verification remains M3.

Development lifecycle: manifest validated/compatible -> Starting -> Ready -> Stopping -> Stopped. Any failure transitions to Failed, cancels pending operations, and terminates only that session's worker. Manual restart waits until it has exited, creates a new session and never replays events. Safe mode refuses all starts. Signed verification, enabled/discovered management, a GUI recovery state, and application startup integration are not part of this checker yet.

Platform and dependency rules

Initial API targets: windows-x64, linux-x64 and linux-arm64. macOS is deferred. Native modules must match the running worker's OS and architecture. There is no Windows-to-Linux binary portability promise. Use CMake MODULE output (.dll on Windows, .so on Linux) beside the development manifest, no path traversal or symlink outside its directory. The worker uses restricted LoadLibraryEx search on Windows and absolute-path RTLD_NOW/RTLD_LOCAL on Linux. Linux packaged dependencies should use an $ORIGIN RPATH; native OS loaders still resolve system dependencies. LD_PRELOAD/LD_LIBRARY_PATH/DYLD injection variables are removed from worker launch.

The standalone runtime needs Qt Core/Network/Test 6.4+ to build its own test suite; the main EpicTuner build continues to use its existing Qt 6.11 kit. SDK author code never links Qt. Compiler runtime dependencies remain the plugin author's packaging responsibility. Production packaging/licensing, task dispatch, UI rendering and real data/tune services retain their assigned later gates in DECISIONS.md; their common contracts and identifiers are defined now.