mirror of
https://github.com/ruvnet/RuView
synced 2026-06-10 10:23:19 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56265023dc | |||
| f751740d3d |
@@ -0,0 +1,115 @@
|
||||
# ADR-116: Home Assistant + Matter as a Cognitum Seed cog (`cog-ha-matter`)
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Status** | Proposed — P1 research complete ([`docs/research/ADR-116-ha-matter-cog-research.md`](../research/ADR-116-ha-matter-cog-research.md)). P2 cog scaffold compiles (`v2/crates/cog-ha-matter`, 2/2 unit tests green). |
|
||||
| **Date** | 2026-05-23 |
|
||||
| **Deciders** | ruv |
|
||||
| **Codename** | **HA-COG** — HA + Matter, packaged for the Seed |
|
||||
| **Relates to** | [ADR-110](ADR-110-esp32-c6-firmware-extension.md) (C6 firmware substrate), [ADR-115](ADR-115-home-assistant-integration.md) (HA-DISCO + HA-MIND + HA-FABRIC), [ADR-102](ADR-102-edge-module-registry.md) (cog catalog), [ADR-101](ADR-101-pose-estimation-cog.md) (cog packaging precedent) |
|
||||
| **Tracking issue** | TBD — file under RuView issue tracker once research dossier lands |
|
||||
|
||||
---
|
||||
|
||||
## 1. Context
|
||||
|
||||
ADR-115 shipped the Home Assistant + Matter integration as a **`--mqtt` flag on `wifi-densepose-sensing-server`** — a Rust binary that runs on a Pi / Linux box, consumes UDP frames from the ESP32 fleet, and publishes MQTT for any Home Assistant install to discover. That works, but it makes HA+Matter a *configuration of the aggregator*, not an *installable artifact* a Cognitum Seed user can drop into their existing fleet.
|
||||
|
||||
The Cognitum Seed already has a [105-cog catalog](https://seed.cognitum.one/store) — packaged Seed apps (`cog-pose-estimation`, `cog-quantum-vitals`, `cog-person-matching`, etc.) that anyone can install from `app-registry.json`. **There is no `cog-ha-matter` yet.** That's the gap this ADR closes.
|
||||
|
||||
The cog packaging precedent is ADR-101 (`cog-pose-estimation`) which ships signed aarch64 + x86_64 binaries on GCS with a `pose_v1.safetensors` weight blob — same shape we'd want for the HA cog.
|
||||
|
||||
### 1.1 Why a cog, not just the existing flag?
|
||||
|
||||
| Path | Distribution | Discovery | Update | Witness | Local AI |
|
||||
|---|---|---|---|---|---|
|
||||
| `--mqtt` on `sensing-server` | manual install of the Rust binary | none | manual | none | external |
|
||||
| **`cog-ha-matter` Seed cog** | `app-registry.json` listing, one-click install | mDNS / cog browser | OTA via cog runtime | Ed25519 witness chain | local ruvllm + RuVector |
|
||||
|
||||
The cog ships HA+Matter as a first-class Seed feature — same UX as installing a pose estimator or person matcher.
|
||||
|
||||
### 1.2 What this ADR is *not*
|
||||
|
||||
- Not a deprecation of the `--mqtt` flag on sensing-server. The flag stays for Pi / Linux deployments without a Seed; the cog is the Seed-native option.
|
||||
- Not a port of HA-MIND / HA-DISCO logic to a different language. The Rust crate already exists; the cog *wraps* it as a Seed-installable artifact + adds Seed-specific surfaces (witness, RuVector, ruvllm-driven thresholds).
|
||||
- Not a Matter SDK ship. ADR-115 §9.10 deferred the matter-rs SDK wiring to v0.7.1; this ADR continues that deferral and focuses on the *cog packaging* + *first-class Seed integration*, with Matter Bridge mode shipping in v0.8 once the SDK is ready.
|
||||
|
||||
## 2. Decision (provisional — to be refined by the research dossier)
|
||||
|
||||
Build **`cog-ha-matter`** as a Cognitum Seed cog with these surfaces:
|
||||
|
||||
### 2.1 Core entity surface (unchanged from ADR-115)
|
||||
|
||||
The cog republishes the same 21 entities per node (11 raw + 10 semantic primitives) over MQTT auto-discovery, so HA installations behave identically whether the source is a Seed cog or an external sensing-server.
|
||||
|
||||
### 2.2 Seed-native enhancements
|
||||
|
||||
- **Self-contained MQTT broker (optional)** — if the user doesn't already run mosquitto, the cog can host an embedded broker on `cognitum-seed.local:1883` and act as the HA endpoint directly.
|
||||
- **mDNS service advertisement** — `_ruview-ha._tcp` so HA's discovery integration finds the Seed without manual config.
|
||||
- **RuVector-backed semantic-primitive thresholds** — instead of static `semantic-thresholds.yaml`, the cog learns per-home thresholds via a SONA-adapted RuVector model (matches the Seed's local-first AI story).
|
||||
- **Ed25519 witness chain** — every state transition logged with a Seed signature so care-home / regulated deployments can audit decisions.
|
||||
- **OTA firmware coordination** — the cog manages C6 firmware updates for ESP32-C6 nodes in the mesh (ADR-110 substrate).
|
||||
|
||||
### 2.3 Matter dimensions (depend on research findings)
|
||||
|
||||
The research dossier covers (a) Matter Bridge vs Matter Device mode, (b) Thread Border Router on the Seed's ESP32-S3 (if feasible), (c) CSA certification path, (d) which Matter device classes map cleanly to which entities. **Decision deferred** until the dossier lands; this ADR will be updated in §3 with the specific Matter feature set.
|
||||
|
||||
### 2.4 Multi-Seed federation
|
||||
|
||||
Multiple Seeds in adjacent rooms coordinate via:
|
||||
- ESP-NOW mesh (ADR-110 substrate) for time alignment
|
||||
- mDNS for service discovery
|
||||
- Witness chain replication for cross-Seed event provenance
|
||||
|
||||
The federation model is the natural extension of ADR-110's mesh substrate into the application layer. Specifically: ADR-110 gives us ≤100 µs cross-board sync; this ADR uses that to deduplicate cross-Seed events (one fall, one alert) and reconstruct multi-room transitions (one occupant, room A → hallway → room B).
|
||||
|
||||
## 3. Research dossier findings (P1 complete)
|
||||
|
||||
Full dossier: [`docs/research/ADR-116-ha-matter-cog-research.md`](../research/ADR-116-ha-matter-cog-research.md). The eight research questions are now answered:
|
||||
|
||||
1. **Matter Bridge vs Matter Root** — Matter 1.4 introduced `OccupancySensor (0x0107)` with `RFSensing` feature flag on cluster `0x0406` (revision 5 in Matter 1.4). That's the correct device class for WiFi-CSI sensing — no health/vitals cluster exists in Matter 1.4.2 and won't soon. **Seed acts as Bridge** with N dynamic OccupancySensor endpoints, **not Commissioner** (the C6 sensing nodes stay Accessories only — 320 KB SRAM no PSRAM rules out commissioning).
|
||||
2. **Thread Border Router** — ESP32-C6 single-chip TBR confirmed working; `CONFIG_OPENTHREAD_BORDER_ROUTER=y` is the only config step. ADR-110's `c6_timesync.c` already initialises 802.15.4 — TBR is a Kconfig flag away. Real value: HA's Improv-style commissioning works without a separate Thread border router box.
|
||||
3. **HACS value-add** — config flow (UI setup wizard), Repairs API (structured error cards), re-authentication, diagnostics download, typed service actions (`set_privacy_mode`, `calibrate_zone`), i18n translations. **Bronze is the minimum bar; Gold (repairs + diagnostics + reconfiguration) is the target.** Start from `hacs.integration_blueprint` template.
|
||||
4. **CSA certification** — ~$30-42k first year ($22.5k membership + $10-19k ATL lab fees). **Skippable for v1** by publishing as "Works with HA" instead. CSA re-evaluate at v0.9+ after HACS adoption data lands.
|
||||
5. **Cog RAM budget** — 128 MB RAM / 15 % CPU on the Seed appliance (Pi 5 + Hailo-10 variant has more headroom). 10 KB INT8 semantic-primitive classifier fits without PSRAM. Long-lived supervised process with capability scopes `network.mqtt + network.matter + api.ruview_vitals`.
|
||||
6. **ruvllm + RuVector latency** — `ruvllm-esp32` v0.3.3 confirms SONA self-optimising adaptation under 100 µs per query. 8→10 INT8 classifier ~10 KB quantised. Per-home threshold tuning via HA thumbs-up/thumbs-down feedback as LoRA-style gradient steps — closes the top user complaint (false positives) without cloud round-trips.
|
||||
7. **HIPAA / FDA** — FDA January 2026 General Wellness guidance explicitly classifies HR / sleep / activity-anomaly alerts as **wellness devices** (outside FDA jurisdiction) when marketed without diagnostic claims. Frame fall detection as **"activity anomaly notification"** not "fall diagnosis". `--privacy-mode` audit-only tier (no MQTT state messages, only SHA-256 digests on-Seed) creates a technical PHI barrier. `OccupancySensor (0x0107)` device class keeps the product in the same regulatory category as a smart motion sensor.
|
||||
8. **Competitor moat** — Aqara FP300 (Nov 2025): 5 entities, no person count, no vitals, no fall detection. TOMMY: zones only, no vitals, closed-source, paywalled. ESPectre: motion only. **RuView's differentiation** — HR/BR + 17-keypoint pose + 10 semantic primitives + witness chain + SONA adaptation — has no competitor equivalent.
|
||||
|
||||
## 4. Recommended v1 scope (from dossier §8)
|
||||
|
||||
Ranked by build cost × user impact:
|
||||
|
||||
| # | Feature | Cost | Impact | Phase |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **`--privacy-mode` audit-only tier** (no MQTT state, SHA-256 digests on-Seed) | ~1 week | Closes care / GDPR deployments | P3 (this cog) |
|
||||
| 2 | **Seed cog manifest + Ed25519 signing + store listing** | ~1-2 weeks | Enables one-click distribution | P2 + P8 (this cog) |
|
||||
| 3 | **Local SONA fine-tuning loop** (HA feedback → LoRA gradient steps) | ~2-3 weeks | Reduces false positives, closes #1 user complaint | P5 (this cog) |
|
||||
| 4 | **HACS gold-tier integration** (config flow + repairs + diagnostics) | ~4-6 weeks | Removes MQTT prerequisite for mainstream users | P9 (separate repo `hass-wifi-densepose`) |
|
||||
| 5 | **Matter Bridge with OccupancySensor + dynamic endpoints** | ~6-8 weeks | Apple Home / Google Home / Alexa native | **v0.8** dedicated sprint (after HACS adoption data) |
|
||||
|
||||
## 4. Implementation phases
|
||||
|
||||
| Phase | Scope | Status |
|
||||
|---|---|---|
|
||||
| **P1** | Research dossier ([`docs/research/ADR-116-ha-matter-cog-research.md`](../research/ADR-116-ha-matter-cog-research.md)) | ✅ **done** — 8 sections, 30+ citations, v1 scope ranked |
|
||||
| **P2** | Cog crate scaffold (`v2/crates/cog-ha-matter/`) — Cargo.toml + `src/{lib,main,manifest}.rs`, workspace member, CLI args, `--print-manifest` flag, 2 manifest unit tests | ✅ **done** — `cargo check` + `cargo test` green |
|
||||
| **P3** | Wrap existing ADR-115 MQTT publisher as cog entry point | pending |
|
||||
| **P4** | Seed-native enhancements (embedded broker, mDNS, witness) | pending |
|
||||
| **P5** | RuVector-backed threshold learning (SONA adaptation) | pending |
|
||||
| **P6** | Multi-Seed federation (cross-Seed dedup + witness) | pending |
|
||||
| **P7** | Matter Bridge mode (depends on matter-rs / esp-matter readiness) | pending |
|
||||
| **P8** | Cog signing + `app-registry.json` listing + Seed Store entry | pending |
|
||||
| **P9** | HACS integration repo (`hass-wifi-densepose`) for HA-side install path | pending |
|
||||
| **P10** | Witness bundle + CSA-style spec compliance check | pending |
|
||||
|
||||
## 5. References
|
||||
|
||||
- ADR-101 — `cog-pose-estimation` packaging precedent (signed binaries on GCS, .cog manifest)
|
||||
- ADR-102 — edge module registry (`app-registry.json` surfaces all cogs)
|
||||
- ADR-110 — ESP32-C6 firmware substrate (mesh time alignment that multi-Seed federation depends on)
|
||||
- ADR-115 — HA-DISCO + HA-MIND + HA-FABRIC (the Rust crate this cog wraps)
|
||||
- `docs/research/ADR-116-ha-matter-cog-research.md` — companion research dossier (deep-researcher agent in progress)
|
||||
- Cognitum Seed store: https://seed.cognitum.one/store
|
||||
- Matter spec: https://csa-iot.org/all-solutions/matter/
|
||||
- HACS integration target: https://github.com/ruvnet/hass-wifi-densepose (planned)
|
||||
@@ -38,6 +38,10 @@ members = [
|
||||
# PR #491 slot heuristic with a Candle network + Stoer-Wagner fusion.
|
||||
# Motivated by #499 ghost-skeleton reports.
|
||||
"crates/cog-person-count",
|
||||
# ADR-116: Home Assistant + Matter Cognitum Seed cog. Wraps the
|
||||
# ADR-115 MQTT publisher as a Seed-installable artifact with
|
||||
# mDNS, embedded broker, RuVector thresholds, Ed25519 witness.
|
||||
"crates/cog-ha-matter",
|
||||
# rvCSI — edge RF sensing runtime (ADR-095 platform, ADR-096 FFI/crate layout):
|
||||
# lives in its own repo (https://github.com/ruvnet/rvcsi), vendored here as
|
||||
# `vendor/rvcsi` and published to crates.io as `rvcsi-*` 0.3.x. Depend on the
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
[package]
|
||||
name = "cog-ha-matter"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Cognitum Cog: Home Assistant + Matter integration for the Seed (ADR-116). Wraps ADR-115's HA-DISCO + HA-MIND publisher as a Seed-installable artifact with mDNS, embedded broker, RuVector-backed thresholds, and Ed25519 witness."
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "cog-ha-matter"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "cog_ha_matter"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
# CLI + logging — same shape as cog-pose-estimation (ADR-101).
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "1"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
||||
|
||||
# Async runtime for the publisher + mDNS responder + WebSocket pump.
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
|
||||
# ADR-115 publisher is the heart of this cog — we wrap it.
|
||||
# default-features = false matches the sensing-server's pattern.
|
||||
wifi-densepose-sensing-server = { version = "0.3.0", path = "../wifi-densepose-sensing-server", default-features = false, features = ["mqtt"] }
|
||||
|
||||
# Hardware crate for SyncPacket + NodeState bridging (ADR-110 substrate).
|
||||
wifi-densepose-hardware = { version = "0.3.0", path = "../wifi-densepose-hardware" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.10"
|
||||
@@ -0,0 +1,42 @@
|
||||
//! ADR-116 — Home Assistant + Matter Cognitum Seed cog.
|
||||
//!
|
||||
//! This crate is the Seed-installable wrapper around ADR-115's
|
||||
//! `wifi-densepose-sensing-server::mqtt` publisher. It adds the
|
||||
//! Seed-native surfaces ADR-115's `--mqtt` flag can't easily reach:
|
||||
//!
|
||||
//! 1. **mDNS service advertisement** — `_ruview-ha._tcp` so HA discovers
|
||||
//! the cog automatically (no manual broker host/port config).
|
||||
//! 2. **Optional embedded MQTT broker** — for Seeds running without an
|
||||
//! external mosquitto. Defaults to off; the cog can either embed
|
||||
//! rumqttd or connect to a user-provided broker.
|
||||
//! 3. **RuVector-backed semantic-primitive thresholds** — replaces
|
||||
//! static `semantic-thresholds.yaml` with a SONA-adapted RuVector
|
||||
//! inference. Per-home thresholds learned from the Seed's own
|
||||
//! long-term observation stream.
|
||||
//! 4. **Ed25519 witness chain** — every state transition signed so
|
||||
//! regulated deployments (healthcare, education, shared housing)
|
||||
//! have a tamper-evident audit log.
|
||||
//! 5. **Multi-Seed federation** — peer discovery via mDNS + cross-Seed
|
||||
//! event deduplication keyed on ADR-110's ≤100 µs mesh-aligned
|
||||
//! timestamps. One fall in a shared room emits one alert, not N.
|
||||
//! 6. **OTA firmware coordination** — the cog manages C6 firmware
|
||||
//! rollouts for ESP32-C6 nodes in the local mesh.
|
||||
//!
|
||||
//! The cog binary entrypoint is in `bin/main.rs`. Library modules
|
||||
//! below are intentionally small and testable per the /loop-worker
|
||||
//! discipline rules (see `docs/ADR-110-BRANCH-STATE.md`).
|
||||
|
||||
pub mod manifest;
|
||||
|
||||
/// Cog identifier used in Seed's app-registry.json + the manifest.
|
||||
pub const COG_ID: &str = "ha-matter";
|
||||
|
||||
/// mDNS service type advertised when the cog starts.
|
||||
pub const MDNS_SERVICE_TYPE: &str = "_ruview-ha._tcp";
|
||||
|
||||
/// Default port for the cog's local HTTP control surface (`/health`,
|
||||
/// `/api/v1/cog/status`). Distinct from the MQTT broker port.
|
||||
pub const DEFAULT_CONTROL_PORT: u16 = 9180;
|
||||
|
||||
/// Default port for the embedded MQTT broker, when enabled.
|
||||
pub const DEFAULT_EMBEDDED_BROKER_PORT: u16 = 1883;
|
||||
@@ -0,0 +1,94 @@
|
||||
//! `cog-ha-matter` — Home Assistant + Matter Cognitum Seed cog (ADR-116).
|
||||
//!
|
||||
//! Binary entrypoint. The actual wiring lives in [`cog_ha_matter`] —
|
||||
//! this main.rs is intentionally tiny so the cog runtime can call
|
||||
//! into the library from tests and from the Seed's control plane
|
||||
//! integration tests without re-launching the binary.
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
use clap::Parser;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "cog-ha-matter",
|
||||
version,
|
||||
about = "Home Assistant + Matter Cognitum Seed cog",
|
||||
long_about = "Wraps the ADR-115 HA-DISCO + HA-MIND publisher as a \
|
||||
Seed-installable artifact with mDNS, embedded broker, \
|
||||
RuVector-backed thresholds, and Ed25519 witness. See \
|
||||
docs/adr/ADR-116-cog-ha-matter-seed.md for the design."
|
||||
)]
|
||||
struct Args {
|
||||
/// Where to find the local sensing-server (the cog speaks to it
|
||||
/// to pull `VitalsSnapshot` for republication over MQTT/Matter).
|
||||
#[arg(long, default_value = "http://127.0.0.1:3000")]
|
||||
sensing_url: String,
|
||||
|
||||
/// MQTT broker host. When omitted the cog can spin up an embedded
|
||||
/// rumqttd on `DEFAULT_EMBEDDED_BROKER_PORT` (v1: external only).
|
||||
#[arg(long, default_value = "127.0.0.1")]
|
||||
mqtt_host: String,
|
||||
|
||||
/// MQTT broker port.
|
||||
#[arg(long, default_value_t = cog_ha_matter::DEFAULT_EMBEDDED_BROKER_PORT)]
|
||||
mqtt_port: u16,
|
||||
|
||||
/// Strip biometrics at the wire — only semantic primitives published.
|
||||
/// Matches ADR-115 `--privacy-mode`. The right default for any
|
||||
/// deployment with non-tenant occupants.
|
||||
#[arg(long)]
|
||||
privacy_mode: bool,
|
||||
|
||||
/// Print the manifest the cog would self-report to the Seed's
|
||||
/// control plane and exit. Useful for the build-time signer.
|
||||
#[arg(long)]
|
||||
print_manifest: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> ExitCode {
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| "cog_ha_matter=info,info".into()),
|
||||
)
|
||||
.init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
info!(
|
||||
sensing_url = %args.sensing_url,
|
||||
mqtt = format!("{}:{}", args.mqtt_host, args.mqtt_port),
|
||||
privacy = args.privacy_mode,
|
||||
"cog-ha-matter starting (ADR-116 P2 scaffold)"
|
||||
);
|
||||
|
||||
if args.print_manifest {
|
||||
// Emit the manifest with build-time-template placeholders. The
|
||||
// Makefile substitutes {{VERSION}} / {{ARCH}} before signing.
|
||||
let m = cog_ha_matter::manifest::CogManifest {
|
||||
id: cog_ha_matter::COG_ID.into(),
|
||||
version: env!("CARGO_PKG_VERSION").into(),
|
||||
binary_url:
|
||||
"https://storage.googleapis.com/cognitum-apps/cogs/{{ARCH}}/cog-ha-matter-{{ARCH}}"
|
||||
.into(),
|
||||
binary_bytes: 0,
|
||||
binary_sha256: String::new(),
|
||||
binary_signature: String::new(),
|
||||
installed_at: 0,
|
||||
status: "installed".into(),
|
||||
};
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&m).expect("manifest serialization is infallible")
|
||||
);
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// P2 stops here — P3 will boot the ADR-115 MQTT publisher in a
|
||||
// `tokio::spawn` and register the mDNS responder + control plane.
|
||||
info!("scaffold-only — P3 wires the MQTT publisher next");
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
//! Cog manifest — same shape as `cog-pose-estimation/cog/manifest.template.json`
|
||||
//! per ADR-101 / ADR-102 / ADR-116. Generated at build time by the cog's
|
||||
//! Makefile, signed by the project's Ed25519 release key, uploaded to
|
||||
//! `gs://cognitum-apps/cogs/<arch>/cog-ha-matter-<arch>` for Seeds to fetch
|
||||
//! via `app-registry.json`.
|
||||
//!
|
||||
//! The runtime ships the typed view here so the cog can self-report its
|
||||
//! manifest to the Seed's control plane (`/api/v1/cog/status`).
|
||||
//!
|
||||
//! Kept in lib.rs's nearest sibling module so manifest format drift between
|
||||
//! build-time template and runtime serializer fires a named test.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Wire-format mirror of `cog/manifest.template.json`.
|
||||
///
|
||||
/// Every field is required at install time; `binary_signature` is the
|
||||
/// Ed25519 sig over `binary_sha256` so the Seed can verify the cog
|
||||
/// binary wasn't tampered with between GCS and the device.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct CogManifest {
|
||||
/// Stable cog identifier ("ha-matter"). Becomes the directory name
|
||||
/// under `/var/lib/cognitum/apps/<id>/` on the Seed.
|
||||
pub id: String,
|
||||
/// SemVer of the cog binary. Bumped by the Makefile from
|
||||
/// `cargo pkgid` at release time.
|
||||
pub version: String,
|
||||
/// Where the Seed fetches the binary from. Arch-specific URL with
|
||||
/// the `{{ARCH}}` template slot filled in (e.g. `arm`, `x86_64`).
|
||||
pub binary_url: String,
|
||||
/// Bytes of the binary blob. Set at build time after `wc -c`.
|
||||
pub binary_bytes: u64,
|
||||
/// SHA-256 of the binary, hex-lowercase, no `0x` prefix. The Seed
|
||||
/// verifies this before exec().
|
||||
pub binary_sha256: String,
|
||||
/// Ed25519 signature over `binary_sha256`, base64-encoded. Optional
|
||||
/// for unsigned dev builds; required for cogs listed in
|
||||
/// `app-registry.json`.
|
||||
pub binary_signature: String,
|
||||
/// Unix epoch seconds at install time. The Seed stamps this when it
|
||||
/// completes a successful install/upgrade.
|
||||
pub installed_at: u64,
|
||||
/// One of `"installed"`, `"upgrading"`, `"degraded"`, `"removed"`.
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
impl CogManifest {
|
||||
pub fn id() -> &'static str {
|
||||
super::COG_ID
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Lock the JSON wire shape against accidental field renames. Both
|
||||
/// the Seed's control plane and the build-time signer parse this —
|
||||
/// any drift fires a named test instead of silently breaking ops.
|
||||
#[test]
|
||||
fn manifest_round_trip_matches_template() {
|
||||
let m = CogManifest {
|
||||
id: "ha-matter".into(),
|
||||
version: "0.1.0".into(),
|
||||
binary_url:
|
||||
"https://storage.googleapis.com/cognitum-apps/cogs/arm/cog-ha-matter-arm"
|
||||
.into(),
|
||||
binary_bytes: 4_200_000,
|
||||
binary_sha256:
|
||||
"a".repeat(64),
|
||||
binary_signature: "Zm9v".into(),
|
||||
installed_at: 1_779_512_400,
|
||||
status: "installed".into(),
|
||||
};
|
||||
let json = serde_json::to_value(&m).unwrap();
|
||||
// Eight required fields, no extras.
|
||||
for key in [
|
||||
"id",
|
||||
"version",
|
||||
"binary_url",
|
||||
"binary_bytes",
|
||||
"binary_sha256",
|
||||
"binary_signature",
|
||||
"installed_at",
|
||||
"status",
|
||||
] {
|
||||
assert!(json.get(key).is_some(), "missing manifest field `{key}`");
|
||||
}
|
||||
let m2: CogManifest = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(m, m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_id_constant_matches_cog_id() {
|
||||
// The id helper must agree with the crate-level COG_ID constant
|
||||
// (regression guard for a future rename).
|
||||
assert_eq!(CogManifest::id(), super::super::COG_ID);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user