630 lines
18 KiB
Markdown
630 lines
18 KiB
Markdown
|
|
## Shared Memory Formats And Addressing
|
|||
|
|
|
|||
|
|
Date: 2026-07-04
|
|||
|
|
Host: `DOLPHIN`
|
|||
|
|
Scope: all shared-memory formats and SHM-adjacent IPC formats directly inspected from source during UV / BLUE-PRIME / DITAv2 work.
|
|||
|
|
|
|||
|
|
This document is intentionally concrete. It separates:
|
|||
|
|
|
|||
|
|
1. the **transport container** (`Zinc` region, or `iceoryx2` service),
|
|||
|
|
2. the **payload framing inside that container**,
|
|||
|
|
3. the **semantic payload schema** written by a given subsystem,
|
|||
|
|
4. the **addressing rule** by which a writer and a reader find the same object.
|
|||
|
|
|
|||
|
|
It also records which format is actually in use for each known subsystem as inspected on this host.
|
|||
|
|
|
|||
|
|
### Short Answer
|
|||
|
|
|
|||
|
|
- A **Zinc region is addressed by its logical region name**, passed to `SharedRegion.create(name, ...)` / `SharedRegion.open(name)`.
|
|||
|
|
- On Linux, Zinc materializes that as a POSIX SHM object named **`/zinc_<logical_name>`**, which appears in `/dev/shm` as **`zinc_<logical_name>`**.
|
|||
|
|
- Therefore:
|
|||
|
|
- reader/writer open **`uv_shadow_state`**
|
|||
|
|
- the OS object visible under `/dev/shm` is **`zinc_uv_shadow_state`**
|
|||
|
|
- opening `zinc_uv_shadow_state` through the Zinc API is wrong and fails
|
|||
|
|
- Two algo instances avoid collision by using **different logical prefixes** and deriving all region names from that prefix.
|
|||
|
|
|
|||
|
|
For `iceoryx2`, the address is not a Zinc region name. It is the **service name** such as `uv/pulse`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. What Exists
|
|||
|
|
|
|||
|
|
### 1.1 Formats actually encountered
|
|||
|
|
|
|||
|
|
| Layer | Transport | Address form | Payload framing | Current role |
|
|||
|
|
|---|---|---|---|---|
|
|||
|
|
| Zinc region container | Zinc shared memory | logical name `name`; OS object `/dev/shm/zinc_<name>` | Zinc internal region header, then user data area | base transport for Zinc-backed regions |
|
|||
|
|
| DITAv2 plane packet | Zinc region data area | `<prefix>_intent`, `<prefix>_state`, `<prefix>_control`, `<prefix>_venue` | `!QQ` = `(seq, json_size)` + UTF-8 JSON | DITAv2 real Zinc plane |
|
|||
|
|
| DITAv2 control packet | Zinc region data area | `<prefix>_control` | same `!QQ + JSON` envelope | DITAv2 control plane |
|
|||
|
|
| UV / BLUE-PRIME snapshot | Zinc region data area | `uv_shadow_state` | `UVZINC01` + dual-seq seqlock header + UTF-8 JSON | authoritative BLUE-PRIME shadow snapshot, live |
|
|||
|
|
| UV hook frame | Zinc region data area | `uv_shadow_blue_prime_hooks` | same `UVZINC01` + dual-seq + UTF-8 JSON | hook observability frame, live/auxiliary |
|
|||
|
|
| UV test scratch snapshot | Zinc region data area | `uv_t6_<id>_state` | same `UVZINC01` + dual-seq + UTF-8 JSON | test / temporary namespaces |
|
|||
|
|
| UV pulse bridge payload | `iceoryx2` publish-subscribe service | service name `uv/pulse` | fixed 40-byte `PulseFrame` POD | optional derived feed for Rust TUI; not authoritative |
|
|||
|
|
|
|||
|
|
### 1.2 Not shared memory, but easy to confuse with it
|
|||
|
|
|
|||
|
|
The old file transport in `prod/clean_arch/violet/uv/shm.py`:
|
|||
|
|
|
|||
|
|
- explicit mode only
|
|||
|
|
- writes JSON files under `UV_SHM_ROOT` / `/dev/shm/uv`
|
|||
|
|
- **not** the runtime BLUE-PRIME contract
|
|||
|
|
- retained for tests / fallback diagnostics only
|
|||
|
|
|
|||
|
|
That path is not documented further here because the request is specifically about shared memory.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Zinc Region Container Format
|
|||
|
|
|
|||
|
|
Authoritative sources:
|
|||
|
|
|
|||
|
|
- `zinc/core/src/header.rs`
|
|||
|
|
- `zinc/core/src/region.rs`
|
|||
|
|
- `zinc/core/src/platform/unix.rs`
|
|||
|
|
- `zinc/adapters/python/zinc/_ffi.py`
|
|||
|
|
- `zinc/adapters/python/zinc/__init__.py`
|
|||
|
|
|
|||
|
|
### 2.1 Addressing
|
|||
|
|
|
|||
|
|
Zinc validates and opens a **logical name** such as:
|
|||
|
|
|
|||
|
|
- `uv_shadow_state`
|
|||
|
|
- `vst_dita_state`
|
|||
|
|
- `dolphin_violet_control`
|
|||
|
|
|
|||
|
|
The Linux backend then maps that to a POSIX SHM object:
|
|||
|
|
|
|||
|
|
- logical name: `uv_shadow_state`
|
|||
|
|
- OS shm object: `/zinc_uv_shadow_state`
|
|||
|
|
- visible filesystem entry: `/dev/shm/zinc_uv_shadow_state`
|
|||
|
|
|
|||
|
|
This mapping is implemented in `zinc/core/src/platform/unix.rs`.
|
|||
|
|
|
|||
|
|
### 2.2 Name rules
|
|||
|
|
|
|||
|
|
Valid logical characters are:
|
|||
|
|
|
|||
|
|
- ASCII alphanumeric
|
|||
|
|
- `_`
|
|||
|
|
- `-`
|
|||
|
|
|
|||
|
|
Slashes are not allowed in raw Zinc names. Callers that start from path-like prefixes sanitize before opening.
|
|||
|
|
|
|||
|
|
### 2.3 Region layout
|
|||
|
|
|
|||
|
|
Every Zinc region is:
|
|||
|
|
|
|||
|
|
1. one page of Zinc-owned metadata/header
|
|||
|
|
2. followed by the user data area
|
|||
|
|
|
|||
|
|
The Rust core exposes the user data area by returning `page_size()` bytes past the mapping base. The Python adapter likewise exposes only the user data area through `SharedRegion.as_buffer()`.
|
|||
|
|
|
|||
|
|
This point matters:
|
|||
|
|
|
|||
|
|
- the **underlying region really is Zinc**
|
|||
|
|
- but a reader using `as_buffer()` does **not** see the Zinc header at byte 0
|
|||
|
|
- it sees byte 0 of the **user payload**
|
|||
|
|
|
|||
|
|
That is why a live UV reader sees `UVZINC01` at the first visible bytes even though the mapped OS object is a Zinc region.
|
|||
|
|
|
|||
|
|
### 2.4 Zinc internal header
|
|||
|
|
|
|||
|
|
From `zinc/core/src/header.rs`:
|
|||
|
|
|
|||
|
|
- `magic: u64` = `"ZINC_REG"`
|
|||
|
|
- `version: u16` = currently `2`
|
|||
|
|
- `flags: u16`
|
|||
|
|
- `notify_seq: AtomicU32`
|
|||
|
|
- `capacity: u64`
|
|||
|
|
- `ref_count: AtomicU32`
|
|||
|
|
- `owner_pid: AtomicI32`
|
|||
|
|
- `created_at: u64`
|
|||
|
|
- `name_hash: u64`
|
|||
|
|
- `ring_head: AtomicU64`
|
|||
|
|
- `ring_tail: AtomicU64`
|
|||
|
|
|
|||
|
|
This header is aligned to one cache line and occupies the Zinc-managed metadata area, not the caller-visible payload buffer.
|
|||
|
|
|
|||
|
|
### 2.5 Notify/wait semantics
|
|||
|
|
|
|||
|
|
Zinc provides:
|
|||
|
|
|
|||
|
|
- `notify()`
|
|||
|
|
- `wait(timeout_ms)`
|
|||
|
|
|
|||
|
|
These operate on `notify_seq` in the Zinc header. The payload framing on top of Zinc is owned by the higher-level subsystem.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. DITAv2 Real Zinc Plane Format
|
|||
|
|
|
|||
|
|
Authoritative sources:
|
|||
|
|
|
|||
|
|
- `prod/clean_arch/dita_v2/real_zinc_plane.py`
|
|||
|
|
- `prod/clean_arch/dita_v2/real_control_plane.py`
|
|||
|
|
|
|||
|
|
### 3.1 Addressing
|
|||
|
|
|
|||
|
|
`RealZincPlane(prefix=...)` derives names as:
|
|||
|
|
|
|||
|
|
- `base = prefix.strip("/").replace("/", "_")`
|
|||
|
|
- `intent_name = f"{base}_intent"`
|
|||
|
|
- `state_name = f"{base}_state"`
|
|||
|
|
- `control_name = f"{base}_control"`
|
|||
|
|
- `venue_name = f"{base}_venue"`
|
|||
|
|
|
|||
|
|
So if `prefix="vst/dita"`:
|
|||
|
|
|
|||
|
|
- logical names become:
|
|||
|
|
- `vst_dita_intent`
|
|||
|
|
- `vst_dita_state`
|
|||
|
|
- `vst_dita_control`
|
|||
|
|
- `vst_dita_venue`
|
|||
|
|
- OS objects become:
|
|||
|
|
- `/dev/shm/zinc_vst_dita_intent`
|
|||
|
|
- `/dev/shm/zinc_vst_dita_state`
|
|||
|
|
- `/dev/shm/zinc_vst_dita_control`
|
|||
|
|
- `/dev/shm/zinc_vst_dita_venue`
|
|||
|
|
|
|||
|
|
### 3.2 Region capacities
|
|||
|
|
|
|||
|
|
Defaults in `RealZincPlane.__init__`:
|
|||
|
|
|
|||
|
|
- `intent_capacity = 1 << 20` = 1 MiB
|
|||
|
|
- `state_capacity = 1 << 20` = 1 MiB
|
|||
|
|
- `control_capacity = 1 << 20` = 1 MiB
|
|||
|
|
- `venue_region` also uses `control_capacity`
|
|||
|
|
|
|||
|
|
### 3.3 Payload framing inside the Zinc data area
|
|||
|
|
|
|||
|
|
DITAv2 does **not** use the `UVZINC01` header.
|
|||
|
|
|
|||
|
|
It uses:
|
|||
|
|
|
|||
|
|
- `struct.pack("!QQ", seq, len(json_bytes))`
|
|||
|
|
- followed by UTF-8 JSON bytes
|
|||
|
|
|
|||
|
|
That is:
|
|||
|
|
|
|||
|
|
- bytes `0..8`: `seq` as big-endian `u64`
|
|||
|
|
- bytes `8..16`: JSON length as big-endian `u64`
|
|||
|
|
- bytes `16..16+size`: JSON bytes
|
|||
|
|
|
|||
|
|
There is no dual-seq torn-read guard here. Readers trust:
|
|||
|
|
|
|||
|
|
- the header is present
|
|||
|
|
- `size` is sane
|
|||
|
|
- the JSON decodes
|
|||
|
|
|
|||
|
|
### 3.4 Semantic payloads
|
|||
|
|
|
|||
|
|
By region:
|
|||
|
|
|
|||
|
|
- `*_intent`: `{"items": [...]}` where items are serialized `KernelIntent`s
|
|||
|
|
- `*_state`: `{"slots": [...]}` where slots are serialized `TradeSlot`s
|
|||
|
|
- `*_control`: `{"control": {...}}` where value is a `KernelControlSnapshot`
|
|||
|
|
- `*_venue`: `{"venue": {...}}` where value is a `VenueTelemetrySnapshot`
|
|||
|
|
|
|||
|
|
### 3.5 Write semantics
|
|||
|
|
|
|||
|
|
Writers:
|
|||
|
|
|
|||
|
|
- increment a region-local sequence
|
|||
|
|
- build `!QQ + JSON`
|
|||
|
|
- copy packet into the entire visible data buffer
|
|||
|
|
- zero the tail
|
|||
|
|
- call `region.notify()`
|
|||
|
|
|
|||
|
|
This is a simple packet format, not a seqlock format.
|
|||
|
|
|
|||
|
|
### 3.6 Current use
|
|||
|
|
|
|||
|
|
This is the intended real shared-memory format for DITAv2 when `RealZincPlane` / `RealZincControlPlane` are active.
|
|||
|
|
|
|||
|
|
At the time of this writing, I did **not** find live openable regions under the tested names:
|
|||
|
|
|
|||
|
|
- `vst_dita_intent`
|
|||
|
|
- `vst_dita_state`
|
|||
|
|
- `vst_dita_control`
|
|||
|
|
- `vst_dita_venue`
|
|||
|
|
- `dolphin_violet_intent`
|
|||
|
|
- `dolphin_violet_state`
|
|||
|
|
- `dolphin_violet_control`
|
|||
|
|
- `dolphin_violet_venue`
|
|||
|
|
|
|||
|
|
So this format is source-authoritative, but not directly observed live under those tested prefixes at sample time.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. UV / BLUE-PRIME Zinc Snapshot Format
|
|||
|
|
|
|||
|
|
Authoritative source:
|
|||
|
|
|
|||
|
|
- `prod/clean_arch/violet/uv/shm.py`
|
|||
|
|
|
|||
|
|
This is the important one for current UV / BLUE-PRIME observability.
|
|||
|
|
|
|||
|
|
### 4.1 Addressing
|
|||
|
|
|
|||
|
|
Default prefix:
|
|||
|
|
|
|||
|
|
- `UV_ZINC_PREFIX`, default `uv_shadow`
|
|||
|
|
|
|||
|
|
Logical region naming rule:
|
|||
|
|
|
|||
|
|
- `prefix.strip("/").replace("/", "_")`
|
|||
|
|
- plus `_<slot>`
|
|||
|
|
- except slot `"blue_prime"` and slot `"state"` are both normalized to `_<prefix>_state`
|
|||
|
|
|
|||
|
|
Examples:
|
|||
|
|
|
|||
|
|
- `publish("blue_prime", ...)` -> logical region `uv_shadow_state`
|
|||
|
|
- `publish("state", ...)` -> logical region `uv_shadow_state`
|
|||
|
|
- `publish("blue_prime_hooks", ...)` -> logical region `uv_shadow_blue_prime_hooks`
|
|||
|
|
|
|||
|
|
### 4.2 Region capacities
|
|||
|
|
|
|||
|
|
Default:
|
|||
|
|
|
|||
|
|
- `UV_ZINC_STATE_BYTES`, default `64 << 20` = 64 MiB
|
|||
|
|
|
|||
|
|
Observed live regions:
|
|||
|
|
|
|||
|
|
- `/dev/shm/zinc_uv_shadow_state`
|
|||
|
|
- `/dev/shm/zinc_uv_shadow_blue_prime_hooks`
|
|||
|
|
|
|||
|
|
Observed test/scratch leftovers:
|
|||
|
|
|
|||
|
|
- `/dev/shm/zinc_uv_t6_8933e28c84_state`
|
|||
|
|
- `/dev/shm/zinc_uv_t6_a1f7e3254b_state`
|
|||
|
|
- `/dev/shm/zinc_uv_t6_b16924a96a_state`
|
|||
|
|
- `/dev/shm/zinc_uv_t6_eed1be7947_state`
|
|||
|
|
|
|||
|
|
### 4.3 Payload framing inside the Zinc data area
|
|||
|
|
|
|||
|
|
Header:
|
|||
|
|
|
|||
|
|
- magic = `b"UVZINC01"`
|
|||
|
|
- struct = `struct.Struct("!8sQQQ")`
|
|||
|
|
|
|||
|
|
Visible data-area layout:
|
|||
|
|
|
|||
|
|
1. bytes `0..8`: magic `"UVZINC01"`
|
|||
|
|
2. bytes `8..16`: `seq_a` big-endian `u64`
|
|||
|
|
3. bytes `16..24`: `seq_b` big-endian `u64`
|
|||
|
|
4. bytes `24..32`: JSON payload size big-endian `u64`
|
|||
|
|
5. bytes `32..32+size`: UTF-8 JSON payload
|
|||
|
|
|
|||
|
|
### 4.4 Write semantics
|
|||
|
|
|
|||
|
|
Writer uses a simple seqlock pattern:
|
|||
|
|
|
|||
|
|
1. compute next logical sequence `seq`
|
|||
|
|
2. derive:
|
|||
|
|
- `seq_even = seq * 2`
|
|||
|
|
- `seq_odd = seq_even - 1`
|
|||
|
|
3. write header with odd/in-flight sequence and `size = 0`
|
|||
|
|
4. copy JSON body
|
|||
|
|
5. optionally zero one byte after body
|
|||
|
|
6. write header again with even/stable sequence and real `size`
|
|||
|
|
7. call `region.notify()`
|
|||
|
|
|
|||
|
|
### 4.5 Read semantics
|
|||
|
|
|
|||
|
|
Reader accepts payload only if all are true:
|
|||
|
|
|
|||
|
|
- magic == `UVZINC01`
|
|||
|
|
- `seq_a != 0`
|
|||
|
|
- `seq_a` is even
|
|||
|
|
- `seq_a == seq_b`
|
|||
|
|
- `size` is in bounds
|
|||
|
|
- after copying the body, rereading the header yields the exact same
|
|||
|
|
- magic
|
|||
|
|
- `seq_a`
|
|||
|
|
- `seq_b`
|
|||
|
|
- `size`
|
|||
|
|
|
|||
|
|
If any of that fails, the read is treated as torn / not yet initialized.
|
|||
|
|
|
|||
|
|
### 4.6 Semantic payloads
|
|||
|
|
|
|||
|
|
This format carries JSON snapshots rather than a fixed struct. Current known uses:
|
|||
|
|
|
|||
|
|
- `uv_shadow_state`
|
|||
|
|
- authoritative BLUE-PRIME snapshot
|
|||
|
|
- includes domains such as `meta`, `scan`, `live_inputs`, `engine`, `decision`, `efsm`, `dita`, `ram`, `source_trace`
|
|||
|
|
- `uv_shadow_blue_prime_hooks`
|
|||
|
|
- one published hook runner frame per scan
|
|||
|
|
- keys include `scan`, `hooks`, `hook_count`, `ok_count`, `total_us`
|
|||
|
|
|
|||
|
|
### 4.7 Current use
|
|||
|
|
|
|||
|
|
This is the **live authoritative BLUE-PRIME shared-memory format** currently observed on host.
|
|||
|
|
|
|||
|
|
Verification made on host:
|
|||
|
|
|
|||
|
|
- `SharedRegion.open("uv_shadow_state")` succeeds
|
|||
|
|
- `SharedRegion.open("zinc_uv_shadow_state")` fails
|
|||
|
|
- first bytes of visible region buffer are `55565a494e433031...` = `UVZINC01`
|
|||
|
|
|
|||
|
|
That is the definitive proof that:
|
|||
|
|
|
|||
|
|
- the region container is Zinc
|
|||
|
|
- the payload framing currently in use inside the container is `UVZINC01`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. UV Hook Frame Region
|
|||
|
|
|
|||
|
|
Authoritative source:
|
|||
|
|
|
|||
|
|
- `prod/clean_arch/violet/uv/hooks/runner.py`
|
|||
|
|
|
|||
|
|
The hook runner publishes:
|
|||
|
|
|
|||
|
|
- `self.shm.publish("blue_prime_hooks", frame)`
|
|||
|
|
|
|||
|
|
Because `ShmChannel` defaults to Zinc-backed transport, that becomes:
|
|||
|
|
|
|||
|
|
- logical region: `uv_shadow_blue_prime_hooks`
|
|||
|
|
- OS shm object: `/dev/shm/zinc_uv_shadow_blue_prime_hooks`
|
|||
|
|
|
|||
|
|
The payload framing is the same `UVZINC01` seqlock JSON envelope documented above.
|
|||
|
|
|
|||
|
|
The semantic payload differs:
|
|||
|
|
|
|||
|
|
- per-scan hook effects
|
|||
|
|
- timing / ordering / success counts
|
|||
|
|
|
|||
|
|
This is auxiliary observability, not the main state snapshot.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. iceoryx2 Pulse Bridge Format
|
|||
|
|
|
|||
|
|
Authoritative sources:
|
|||
|
|
|
|||
|
|
- `uv_tui/crates/pulse_frame/src/lib.rs`
|
|||
|
|
- `uv_tui/bridge/src/lib.rs`
|
|||
|
|
- `uv_tui/tui/src/lib.rs`
|
|||
|
|
|
|||
|
|
### 6.1 What it is
|
|||
|
|
|
|||
|
|
This is not the authoritative shared-memory snapshot. It is a **derived bridge feed**:
|
|||
|
|
|
|||
|
|
1. bridge opens Zinc region `uv_shadow_state`
|
|||
|
|
2. bridge decodes the `UVZINC01` payload
|
|||
|
|
3. bridge extracts a reduced pulse contract
|
|||
|
|
4. bridge republishes that reduced contract over `iceoryx2` service `uv/pulse`
|
|||
|
|
|
|||
|
|
### 6.2 Addressing
|
|||
|
|
|
|||
|
|
Address is a service name, not a Zinc region name:
|
|||
|
|
|
|||
|
|
- service: `uv/pulse`
|
|||
|
|
|
|||
|
|
Current code hardcodes a singleton default service. Unlike Zinc prefixes, this is **not yet namespaced per parallel algo instance**.
|
|||
|
|
|
|||
|
|
### 6.3 Payload framing
|
|||
|
|
|
|||
|
|
Fixed `PulseFrame`, length 40 bytes:
|
|||
|
|
|
|||
|
|
- `observe_mono_ns: u64`
|
|||
|
|
- `scan_number: u64`
|
|||
|
|
- `region_seq: u64`
|
|||
|
|
- `publish_latency_us: f64`
|
|||
|
|
- `has_entry: u8`
|
|||
|
|
- trailing reserved padding
|
|||
|
|
|
|||
|
|
Serialized little-endian by the shared `pulse_frame` crate.
|
|||
|
|
|
|||
|
|
### 6.4 Current use
|
|||
|
|
|
|||
|
|
- optional
|
|||
|
|
- derived
|
|||
|
|
- non-authoritative
|
|||
|
|
- useful for the Rust TUI, especially when decoupling bridge and display
|
|||
|
|
|
|||
|
|
As of current Rust TUI work, the TUI can also read the Zinc region directly and no longer requires the bridge.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Addressing Rules, Precisely
|
|||
|
|
|
|||
|
|
### 7.1 Zinc regions
|
|||
|
|
|
|||
|
|
There are **three names** to keep distinct:
|
|||
|
|
|
|||
|
|
1. **logical region name**
|
|||
|
|
- what code passes to Zinc
|
|||
|
|
- example: `uv_shadow_state`
|
|||
|
|
2. **POSIX SHM object name**
|
|||
|
|
- what the Linux SHM API sees
|
|||
|
|
- example: `/zinc_uv_shadow_state`
|
|||
|
|
3. **filesystem entry under `/dev/shm`**
|
|||
|
|
- example: `/dev/shm/zinc_uv_shadow_state`
|
|||
|
|
|
|||
|
|
The reader/writer contract uses **(1)**.
|
|||
|
|
|
|||
|
|
The operator inspecting `/dev/shm` sees **(3)**.
|
|||
|
|
|
|||
|
|
Confusing (1) and (3) is the common failure mode.
|
|||
|
|
|
|||
|
|
### 7.2 How a writer and reader agree on the same region
|
|||
|
|
|
|||
|
|
They must share:
|
|||
|
|
|
|||
|
|
- the same transport family
|
|||
|
|
- Zinc or `iceoryx2`
|
|||
|
|
- the same prefix / service namespace
|
|||
|
|
- the same slot derivation rule
|
|||
|
|
- the same payload framing
|
|||
|
|
- the same semantic schema
|
|||
|
|
|
|||
|
|
For Zinc-backed UV:
|
|||
|
|
|
|||
|
|
- prefix source: `UV_ZINC_PREFIX`
|
|||
|
|
- slot naming logic: `_slot_region(prefix, slot)`
|
|||
|
|
- payload format: `UVZINC01`
|
|||
|
|
- state slot: `blue_prime` -> `uv_shadow_state`
|
|||
|
|
|
|||
|
|
For DITAv2:
|
|||
|
|
|
|||
|
|
- prefix passed to `RealZincPlane(prefix=...)`
|
|||
|
|
- suffixes: `_intent`, `_state`, `_control`, `_venue`
|
|||
|
|
- payload format: `!QQ + JSON`
|
|||
|
|
|
|||
|
|
For `iceoryx2`:
|
|||
|
|
|
|||
|
|
- service name must match exactly
|
|||
|
|
- payload struct must match exactly
|
|||
|
|
|
|||
|
|
### 7.3 How two running algo instances know “their” region
|
|||
|
|
|
|||
|
|
They do not discover “theirs” by magic. They must be started with a namespace choice.
|
|||
|
|
|
|||
|
|
Correct pattern:
|
|||
|
|
|
|||
|
|
- instance A:
|
|||
|
|
- `UV_ZINC_PREFIX=uv_shadow_a`
|
|||
|
|
- state region -> `uv_shadow_a_state`
|
|||
|
|
- instance B:
|
|||
|
|
- `UV_ZINC_PREFIX=uv_shadow_b`
|
|||
|
|
- state region -> `uv_shadow_b_state`
|
|||
|
|
|
|||
|
|
Then:
|
|||
|
|
|
|||
|
|
- A writer writes only `uv_shadow_a_*`
|
|||
|
|
- A TUI for A opens only `uv_shadow_a_*`
|
|||
|
|
- B writer/TUI use `uv_shadow_b_*`
|
|||
|
|
|
|||
|
|
For test runs, this pattern is already used:
|
|||
|
|
|
|||
|
|
- `uv_t6_<id>` prefixes produce regions like `uv_t6_8933e28c84_state`
|
|||
|
|
|
|||
|
|
### 7.4 Collision rule
|
|||
|
|
|
|||
|
|
If two independent algo instances reuse the same Zinc logical region name:
|
|||
|
|
|
|||
|
|
- they are not isolated
|
|||
|
|
- last writer wins
|
|||
|
|
- readers observe a mixed stream
|
|||
|
|
- observability becomes invalid
|
|||
|
|
|
|||
|
|
So region naming is not cosmetic. It is the namespace boundary.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. What Is In Use For What, Now
|
|||
|
|
|
|||
|
|
### 8.1 BLUE-PRIME authoritative observability
|
|||
|
|
|
|||
|
|
- transport container: Zinc region
|
|||
|
|
- logical region: `uv_shadow_state`
|
|||
|
|
- payload framing inside region: `UVZINC01`
|
|||
|
|
- semantic schema: BLUE-PRIME snapshot JSON
|
|||
|
|
- status: live and observed
|
|||
|
|
|
|||
|
|
### 8.2 BLUE-PRIME hook observability
|
|||
|
|
|
|||
|
|
- transport container: Zinc region
|
|||
|
|
- logical region: `uv_shadow_blue_prime_hooks`
|
|||
|
|
- payload framing inside region: `UVZINC01`
|
|||
|
|
- semantic schema: hook runner frame JSON
|
|||
|
|
- status: live region observed
|
|||
|
|
|
|||
|
|
### 8.3 UV Rust TUI direct mode
|
|||
|
|
|
|||
|
|
- reads: Zinc region directly
|
|||
|
|
- logical region by default: `uv_shadow_state`
|
|||
|
|
- expects payload framing: `UVZINC01`
|
|||
|
|
- status: implemented and verified
|
|||
|
|
|
|||
|
|
### 8.4 UV Rust TUI bridge mode
|
|||
|
|
|
|||
|
|
- bridge reads Zinc region `uv_shadow_state`
|
|||
|
|
- bridge republishes `PulseFrame` over `iceoryx2` service `uv/pulse`
|
|||
|
|
- TUI subscribes to `uv/pulse`
|
|||
|
|
- status: implemented and verified, but secondary to direct Zinc reads
|
|||
|
|
|
|||
|
|
### 8.5 DITAv2 real shared-memory plane
|
|||
|
|
|
|||
|
|
- transport container: Zinc region
|
|||
|
|
- logical regions: `<prefix>_{intent,state,control,venue}`
|
|||
|
|
- payload framing: `!QQ + JSON`
|
|||
|
|
- status: source-authoritative, but not directly observed live under tested prefixes during this inspection
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Operator Notes
|
|||
|
|
|
|||
|
|
### 9.1 To inspect the live authoritative UV region
|
|||
|
|
|
|||
|
|
Use the logical name:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from zinc import SharedRegion
|
|||
|
|
r = SharedRegion.open("uv_shadow_state")
|
|||
|
|
buf = r.as_buffer()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Do **not** do:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
SharedRegion.open("zinc_uv_shadow_state")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
That is the `/dev/shm` object name, not the logical Zinc name.
|
|||
|
|
|
|||
|
|
### 9.2 To tell whether a region uses DITAv2 or UV framing
|
|||
|
|
|
|||
|
|
Look at the first visible bytes of `as_buffer()`:
|
|||
|
|
|
|||
|
|
- `UVZINC01` -> UV / BLUE-PRIME seqlock payload
|
|||
|
|
- otherwise, if the first 16 bytes parse as `!QQ` and the JSON decodes, likely DITAv2 packet framing
|
|||
|
|
|
|||
|
|
You will **not** see `ZINC_REG` there through normal adapter reads, because that Zinc header lives before the exposed data area.
|
|||
|
|
|
|||
|
|
### 9.3 To run two independent observability stacks
|
|||
|
|
|
|||
|
|
Assign different prefixes up front. Example:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
export UV_ZINC_PREFIX=uv_shadow_main
|
|||
|
|
export UV_ZINC_PREFIX=uv_shadow_soak
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Then point each consumer at its matching logical region names.
|
|||
|
|
|
|||
|
|
If `iceoryx2` bridge mode is used in parallel too, it needs the same kind of namespacing. Current bridge service default is singleton `uv/pulse`; that should be parameterized if simultaneous parallel bridges are required.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Bottom Line
|
|||
|
|
|
|||
|
|
The current live BLUE-PRIME observability stack is:
|
|||
|
|
|
|||
|
|
- **container:** Zinc shared memory
|
|||
|
|
- **live authoritative region:** `uv_shadow_state`
|
|||
|
|
- **live payload framing:** `UVZINC01` seqlock JSON
|
|||
|
|
- **live hook side region:** `uv_shadow_blue_prime_hooks`
|
|||
|
|
|
|||
|
|
DITAv2’s real Zinc plane is a different format:
|
|||
|
|
|
|||
|
|
- same Zinc container idea
|
|||
|
|
- different region family
|
|||
|
|
- different payload envelope: `!QQ + JSON`
|
|||
|
|
|
|||
|
|
The Rust `PulseFrame` is yet another layer:
|
|||
|
|
|
|||
|
|
- `iceoryx2` service payload
|
|||
|
|
- derived from the authoritative Zinc region
|
|||
|
|
- not itself the source of truth
|
|||
|
|
|
|||
|
|
The rule for addressing is simple and must be followed strictly:
|
|||
|
|
|
|||
|
|
- **open logical Zinc names through Zinc**
|
|||
|
|
- **inspect `/dev/shm/zinc_*` only as an operator artifact**
|
|||
|
|
- **namespace parallel instances by prefix**
|
|||
|
|
- **match the payload framing to the subsystem**
|