Embedded C — zeq-observer.c
The C form factor of the Observer Agent is a single, portable C99 source file. It targets the runtimes that cannot host Python or a JavaScript engine: medical implants, RTOS-bound microcontrollers, aerospace flight software, microwave firmware, industrial PLCs, smart meters.
It speaks the same JSON wire contract as the Web JS, Python, and Network Tap form factors, so a row from a pacemaker and a row from a browser tab end up in the same entangled state with the same shape.
Build line
cc -O2 -pthread zeq-observer.c -lcurl -lcrypto -o zeq-observer
Three external dependencies, all standard:
- libcurl for HTTPS POST
- OpenSSL libcrypto for SHA-256 + AES-256-GCM
- POSIX pthreads for the heartbeat ticker
For environments without libcurl, the ZEQ_HTTP_BACKEND macro lets you
substitute a custom transport by implementing two functions
(zeq_http_post, zeq_http_init). The reference build assumes libcurl
because it is on every Linux distro that hosts a serious C runtime.
Public API
#include "zeq-observer.h"
// Initialise once at process start
int zeq_observer_init(const zeq_observer_config_t *cfg);
// Enqueue an observation
int zeq_observer_observe(const char *type,
const char *state_hash,
const char *meta_json /* may be NULL */);
// Force outbox drain (blocking, with timeout)
int zeq_observer_flush(int timeout_zeqonds);
// Drain + stop heartbeat thread + free resources
void zeq_observer_shutdown(void);
zeq_observer_config_t carries the machine, key, endpoint, encrypt flag,
heartbeat cadence, outbox cap, and an optional file path for the
on-disk outbox (default /var/lib/zeq/observer-outbox.bin). All
fields except slug and key are optional.
Five-line firmware drop-in
zeq_observer_config_t cfg = {
.slug = "zeq07792026349",
.key = "zsm_live_2c5e12b5b3f1ad99…",
.encrypt = 1,
};
zeq_observer_init(&cfg);
zeq_observer_observe("vitals.tick", state_hash_hex,
"{\"hr\":72,\"spo2\":97}");
That's the full surface area for a continuous-monitor implant. The
heartbeat thread emits type=heartbeat rows every 8 zeqonds; the
outbox queues to flash if the radio link drops; HF6/HF8/HF12/HF14/HF18
are computed inline against a 256-emission ring buffer.
Memory model — predictable, no surprises
The agent allocates at most 64 KiB on the heap (configurable via
ZEQ_OBSERVER_HEAP_BUDGET). Internally:
| Region | Size | Purpose |
|---|---|---|
| Outbox ring | 32 KiB | 200 events × ~160 B each |
| HF6 phase ring | 256 B | last 256 phase buckets |
| TLS context | 16 KiB | libcurl/OpenSSL |
| Heartbeat thread stack | 16 KiB | one thread, daemonised |
No realloc-during-emission. No printf-style format ambiguity. The
outbox is a fixed-capacity ring buffer; oldest-first eviction is
constant-time.
For RTOS targets where dynamic allocation is forbidden, set
ZEQ_OBSERVER_STATIC_HEAP=1 at compile time and provide a 64 KiB
static buffer via zeq_observer_set_heap(buf, 65536) before
zeq_observer_init. The agent will use that buffer exclusively.
Phase-locked encryption
When cfg.encrypt = 1, every emission becomes:
unsigned char nonce[12];
char nonce_input[256];
snprintf(nonce_input, sizeof nonce_input, "phase-lock|%llu|%s",
current_zeqond, cfg.slug);
SHA256((unsigned char *)nonce_input, strlen(nonce_input), full_hash);
memcpy(nonce, full_hash, 12);
EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key_bytes, nonce);
EVP_EncryptUpdate(ctx, ct, &len, plaintext, plen);
EVP_EncryptFinal_ex(ctx, ct + len, &final_len);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, ct + plen);
Same 12-byte nonce derivation as every other form factor, same domain
separation via "ZeqField/PhaseLockedNonce/v1" HMAC label baked into
the key derivation. See R3 — Cipher + HZC
for the six properties and the GCM auth-tag tamper guarantees.
Outbox — flash-friendly, atomic
The on-disk outbox is a single binary file written via the
write-temp + rename pattern. On flash, the file is opened with
O_DSYNC so a power loss between write() and rename() cannot
leave a half-written event.
For embedded targets where a filesystem isn't available, set
cfg.outbox_path = NULL. The agent runs purely in-RAM with no
persistence — events lost during a power blip are lost. The
heartbeat row that arrives after recovery carries meta.gap_zeqonds
explicitly so the entangled state row notes the outage.
Use cases — three with regulatory weight
Pacemakers / implants (IEC 62304)
A continuous-monitor implant emits one row per heartbeat. The entangled state becomes the medical-grade audit trail. Every row carries:
- A SHA-256 of the recorded vitals (no PHI on the wire)
- The HulyaPulse phase at the moment of emission
- The agent's
_zeq.sessionso a single implant lifetime is one contiguous session ID
The ZeqCompliance v1 envelope returned by the server-side compute call carries the HIPAA 164.312 + IEC 62304 booleans the device's regulatory file already requires.
Aerospace flight software (DO-178C)
The cadence-drift signal HF8 doubles as the tool-qualification
trace required by DO-178C: every row proves the agent's local clock
remained within ≤0.1% of the canonical 0.777 s tick during the flight.
Drift past the bound flags the row server-side; passes appear in the
envelope's do_178c.tool_qualification_data: true boolean.
Industrial / microwave / smart-meter firmware
Single-file, no package manager, no Python interpreter, runs in 64 KiB. The entangled state row makes the device's state stream tamper-evident under ISO 27001 A.12.4.x audit-log requirements with no extra infrastructure on the device side.
File map
apps/zeq-dev/public/zeq-observer.c— the agent (single file)shared/api-core/src/routes/chain.ts— receivershared/api-core/src/lib/zeqField.ts— phase-locked ciphershared/api-core/src/lib/compliance.ts— DO-178C / IEC 62304 booleans