Saltar al contenido principal

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:

RegionSizePurpose
Outbox ring32 KiB200 events × ~160 B each
HF6 phase ring256 Blast 256 phase buckets
TLS context16 KiBlibcurl/OpenSSL
Heartbeat thread stack16 KiBone 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.session so 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 — receiver
  • shared/api-core/src/lib/zeqField.ts — phase-locked cipher
  • shared/api-core/src/lib/compliance.ts — DO-178C / IEC 62304 booleans