Observer Agents — The Universal Sensor
The Observer Agent is Zeq's universal sensor. It is not a tracker. A tracker measures the user; an observer measures the state of the system that the user is part of, and posts that state to a state machine which folds it into the entangled state.
One wire contract. Four form factors. Identical JSON envelope on
the network in every case, so a pacemaker, a Python AI loop, a browser
session, and a Linux network daemon all speak the same protocol to the
same /api/chain/<slug>/event endpoint.
That is the load-bearing claim of the framework: language-agnostic because everything moves as JSON; substrate-agnostic because the ZeqAuth-V3 equation-as-credential does not care what computed the observation; tamper-evident because every observation eventually folds into a hash-linked row that R1 (atomic writer), R2 (awareness), and R3 (phase-locked cipher + HZC archiver) defend.
The four form factors
| Form factor | File | Drops into | Typical use |
|---|---|---|---|
| Web JS | zeq-observer-client.js | Any HTML page | Web apps, dashboards, IoT consoles |
| Python | zeq-observer-client.py | Any Python process | AI/ML training loops, robotics, scientific compute |
| Embedded C | zeq-observer.c | Any C-linkable runtime | Pacemakers, aerospace, microwaves, medical devices |
| Network Tap | zeq-observer-tap.py | Any Linux/macOS host | SOC 2 / NIST AU-12 audit gold — interface state, listening ports, established connections |
All four serve from https://zeqapi.com/zeq-observer-client.js (etc.) so
you can drop them into a project with one <script> tag, one pip download, one curl -O, or one git pull.
The wire contract
Every observation, regardless of form factor, flattens to the same body:
POST /api/chain/<slug>/event
Authorization: Bearer zsm_<state-machine-key>
Content-Type: application/json
{
"type": "observation",
"payload": {
"observation": "<short label>",
"state_hash": "<sha256 of the observed state>",
"zeqond": "2287568432",
"phase": 0.523,
"meta": { /* free-form, agent-specific */ },
"agent_signals": {
"HF6": 0.142,
"HF8": 0.0,
"HF12": 0.881,
"HF14": 1.0,
"HF18": 0.029
},
"_zeq": {
"session": "<random per-process id>",
"agent": "zeq-observer-py/1.287.5"
}
}
}
The endpoint authenticates with the state-machine key (zsm_*) on
the Authorization: Bearer header. The body is plaintext JSON by
default. When the agent has the machine as originId, it can flip into
phase-locked mode and send payload_ct instead — a single hex
ciphertext built with R3's zeqEncryptPhaseLocked(plaintext, zeqond, originId). The server recomputes the same nonce from (zeqond, slug)
and the row commits.
That single shape is why a C agent on a pacemaker, a Python loop on a training server, a browser tab, and a network tap can all post into the same entangled state without the writer ever needing to know who they are.
Local HF computation
Each observation carries a small bundle of HF forensic signals that the agent computes locally before the row leaves the device. The five that ship in every form factor:
| Signal | Meaning |
|---|---|
| HF6 | Phase-bucket entropy — Shannon entropy of how the agent's emissions distribute across the 8-bucket phase wheel |
| HF8 | Cadence drift — |observed_dt − τ_z| / τ_z — how far the agent's own ticker has drifted from 0.777 s |
| HF12 | Run-length anomaly — longest streak of identical state hashes in the local outbox |
| HF14 | Network-loss flag — 0/1 set when the agent had to retry past one outbox flush |
| HF18 | Local-clock skew — agent's now() / τ_z vs the last successful server-acknowledged zeqond |
These five mean the entangled state row carries forensic posture as observed at
the source — independent of anything the server adds. R2's awareness
operators (ZEQ-PROTECT-001, XI1, MK1) compute a server-side view
on top, and the two views combined are what compliance auditors get.
The full 20-operator HF ladder (HF1–HF20 + the S_forensic composite)
runs server-side in api-core/lib/awareness.ts;
the agent ships the five most useful of them so a row never lacks
forensic context, even if the entangled state row arrives late.
The fallback ticker
An observer is always ticking, even with no events. Each form factor carries a heartbeat that emits a typed row every 8 zeqonds ≈ 6.2 seconds of wall time. Reasons:
- Liveness proof — an entangled state that sees a heartbeat row every 8z is provably online. The State Observer page renders the cadence as "● awareness firing" in the footer.
- Cold-row anchor — the heartbeat row carries the agent's current
_zeq.session, last user-event zeqond, and outbox depth. Replaying the entangled state forward is sufficient to reconstruct the agent's liveness window without any side state. - Network-failure recovery — if the agent goes offline, the
outbox queues observations locally. When the next heartbeat
succeeds, the queued events drain in order. The row that finally
succeeds carries the gap explicitly:
meta.gap_zeqonds: <count>.
The heartbeat is fire-and-forget; failure to land does not block the agent's next observation.
Encryption (R3 phase-locked)
Every form factor supports the optional R3 phase-locked encryption
layer. With data-encrypt="true" (Web JS) or encrypt=True (Python /
C / Tap), the agent encrypts the JSON payload with AES-256-GCM using a
deterministic 12-byte nonce derived as:
nonce = sha256("phase-lock|" + zeqond + "|" + slug)[0:12]
The ciphertext is sent as payload_ct (hex, no IV component) and the
server recomputes the same nonce from (zeqond, originId) to decrypt.
Tamper either of those two fields and the GCM auth tag fails — the row
is rejected.
This means even if the network transport is hostile (a public WiFi, a shared corporate proxy, a hostile cellular provider), the device's state stream is sealed end-to-end. See R3 — Cipher + HZC for the full nonce derivation, the six verified properties, and the HZC cold-row archiver that uses the same primitive at rest.
Substrate composition
| Layer | What it provides | How the agent participates |
|---|---|---|
| R1 — Atomic writer | pg_advisory_xact_lock per origin + UNIQUE (origin_id, zeqond_number) | Agent posts to /event; R1 serialises against any other writer hitting the same entangled state |
| R2 — Awareness | 1024-row trailing window, ZEQ-PROTECT-001, XI1, MK1, forward-relink self-heal | Agent sends HF6/HF8/HF12/HF14/HF18 in the row; server fills in the rest |
| R3 — Cipher + HZC | Phase-locked AES-256-GCM, deterministic nonce, HZC cold archives | Agent uses zeqEncryptPhaseLocked when encrypt=true; archived rows decrypt with the same recipe |
| ZeqCompliance v1 | 13-standard regulatory envelope returned per-call | Agent observations feed actor.user_id, action.input_digest, temporal.zeqond_tick |
Choosing a form factor
- Web JS — when the device is a browser tab or any V8/JSCore runtime. One script tag.
- Python — when the agent runs inside a long-lived process: training loops, robotic controllers, scientific pipelines.
- Embedded C — when the device cannot host Python or JS: medical implants, RTOS-bound microcontrollers, aerospace flight software. Single-file,
cc -O2 -pthread. - Network Tap — when the goal is observing the host itself: interface state, listening ports, established connections. Privacy-first (SHA-256 hashed IPs).
You can run multiple form factors against the same state machine.
A web tab and a python loop and a network tap all posting to
zeq07792026349 produce a single chain that interleaves all three
agents' emissions. The R1 atomic lock guarantees no two rows collide
even at full concurrency.
File map
apps/zeq-dev/public/zeq-observer-client.js— Web JS form factor (25 KB)apps/zeq-dev/public/zeq-observer-client.py— Python form factor (17 KB)apps/zeq-dev/public/zeq-observer.c— Embedded C form factor (18 KB)apps/zeq-dev/public/zeq-observer-tap.py— Network tap daemon (12 KB)shared/api-core/src/routes/chain.ts—/api/chain/:slug/eventwritershared/api-core/src/lib/awareness.ts— server-side HF ladder