mirror of
https://github.com/ruvnet/RuView
synced 2026-07-24 17:43:20 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be4efecbcd | |||
| 3833929dcb | |||
| 1e469aa336 | |||
| d4f0e12073 |
@@ -0,0 +1,200 @@
|
|||||||
|
name: Cog HA-Matter Release
|
||||||
|
|
||||||
|
# ADR-116 P8 — Build + sign + bundle the cog-ha-matter cog on a
|
||||||
|
# version tag. Upload to gs://cognitum-apps/ runs only when the
|
||||||
|
# GCP_CREDENTIALS + COGNITUM_OWNER_SIGNING_KEY secrets are set, so
|
||||||
|
# this workflow is safe to merge before the production credentials
|
||||||
|
# land — it'll bundle release artifacts to the workflow run page
|
||||||
|
# either way.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'cog-ha-matter-v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
dry_run:
|
||||||
|
description: 'Build + sign + bundle but skip GCS upload'
|
||||||
|
required: false
|
||||||
|
default: 'true'
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
CRATE: cog-ha-matter
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-x86_64:
|
||||||
|
name: Build x86_64
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
v2/target
|
||||||
|
key: cog-ha-matter-x86_64-${{ hashFiles('v2/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Build release binary
|
||||||
|
working-directory: v2/crates/cog-ha-matter/cog
|
||||||
|
run: make build-x86_64
|
||||||
|
|
||||||
|
- name: Compute SHA-256
|
||||||
|
working-directory: v2/crates/cog-ha-matter/cog
|
||||||
|
run: make sign-x86_64
|
||||||
|
|
||||||
|
- name: Sign with Ed25519 (gated)
|
||||||
|
if: ${{ env.SIGNING_KEY != '' }}
|
||||||
|
env:
|
||||||
|
SIGNING_KEY: ${{ secrets.COGNITUM_OWNER_SIGNING_KEY }}
|
||||||
|
working-directory: v2/crates/cog-ha-matter/cog
|
||||||
|
run: |
|
||||||
|
printf '%s' "$SIGNING_KEY" \
|
||||||
|
| openssl pkeyutl -sign -inkey /dev/stdin -rawin \
|
||||||
|
-in dist/cog-ha-matter-x86_64.sha256 \
|
||||||
|
| base64 -w0 > dist/cog-ha-matter-x86_64.sig
|
||||||
|
echo "Signed cog-ha-matter-x86_64 ($(wc -c < dist/cog-ha-matter-x86_64.sig) bytes)"
|
||||||
|
|
||||||
|
- name: Upload workflow artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: cog-ha-matter-x86_64
|
||||||
|
path: |
|
||||||
|
v2/crates/cog-ha-matter/cog/dist/cog-ha-matter-x86_64
|
||||||
|
v2/crates/cog-ha-matter/cog/dist/cog-ha-matter-x86_64.sha256
|
||||||
|
v2/crates/cog-ha-matter/cog/dist/cog-ha-matter-x86_64.sig
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
|
build-arm:
|
||||||
|
name: Build aarch64 (arm)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: aarch64-unknown-linux-gnu
|
||||||
|
|
||||||
|
- name: Install cross-compiler
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: Cache cargo registry
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
v2/target
|
||||||
|
key: cog-ha-matter-arm-${{ hashFiles('v2/Cargo.lock') }}
|
||||||
|
|
||||||
|
- name: Build release binary
|
||||||
|
working-directory: v2
|
||||||
|
env:
|
||||||
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
||||||
|
run: |
|
||||||
|
cargo build -p cog-ha-matter --release --target aarch64-unknown-linux-gnu
|
||||||
|
mkdir -p crates/cog-ha-matter/cog/dist
|
||||||
|
cp target/aarch64-unknown-linux-gnu/release/cog-ha-matter \
|
||||||
|
crates/cog-ha-matter/cog/dist/cog-ha-matter-arm
|
||||||
|
# ^ matches Makefile's `dist/$(CRATE)-arm` so `make sign-arm` finds it
|
||||||
|
|
||||||
|
- name: Compute SHA-256
|
||||||
|
working-directory: v2/crates/cog-ha-matter/cog
|
||||||
|
run: make sign-arm
|
||||||
|
|
||||||
|
- name: Sign with Ed25519 (gated)
|
||||||
|
if: ${{ env.SIGNING_KEY != '' }}
|
||||||
|
env:
|
||||||
|
SIGNING_KEY: ${{ secrets.COGNITUM_OWNER_SIGNING_KEY }}
|
||||||
|
working-directory: v2/crates/cog-ha-matter/cog
|
||||||
|
run: |
|
||||||
|
printf '%s' "$SIGNING_KEY" \
|
||||||
|
| openssl pkeyutl -sign -inkey /dev/stdin -rawin \
|
||||||
|
-in dist/cog-ha-matter-arm.sha256 \
|
||||||
|
| base64 -w0 > dist/cog-ha-matter-arm.sig
|
||||||
|
echo "Signed cog-ha-matter-arm ($(wc -c < dist/cog-ha-matter-arm.sig) bytes)"
|
||||||
|
|
||||||
|
- name: Upload workflow artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: cog-ha-matter-arm
|
||||||
|
path: |
|
||||||
|
v2/crates/cog-ha-matter/cog/dist/cog-ha-matter-arm
|
||||||
|
v2/crates/cog-ha-matter/cog/dist/cog-ha-matter-arm.sha256
|
||||||
|
v2/crates/cog-ha-matter/cog/dist/cog-ha-matter-arm.sig
|
||||||
|
if-no-files-found: warn
|
||||||
|
|
||||||
|
publish-gcs:
|
||||||
|
name: Upload to GCS (gated)
|
||||||
|
needs: [build-x86_64, build-arm]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Skip on dry-run dispatch; skip on tags when GCP_CREDENTIALS unset.
|
||||||
|
if: >
|
||||||
|
github.event_name == 'push' &&
|
||||||
|
vars.HAS_GCP_CREDENTIALS == 'true'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download x86_64 artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: cog-ha-matter-x86_64
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
- name: Download arm artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: cog-ha-matter-arm
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
- name: Auth to GCP
|
||||||
|
uses: google-github-actions/auth@v2
|
||||||
|
with:
|
||||||
|
credentials_json: ${{ secrets.GCP_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Set up gcloud
|
||||||
|
uses: google-github-actions/setup-gcloud@v2
|
||||||
|
|
||||||
|
- name: Upload binaries + sidecars
|
||||||
|
run: |
|
||||||
|
gsutil cp dist/cog-ha-matter-x86_64 gs://cognitum-apps/cogs/x86_64/cog-ha-matter-x86_64
|
||||||
|
gsutil cp dist/cog-ha-matter-x86_64.sha256 gs://cognitum-apps/cogs/x86_64/cog-ha-matter-x86_64.sha256
|
||||||
|
gsutil cp dist/cog-ha-matter-arm gs://cognitum-apps/cogs/arm/cog-ha-matter-arm
|
||||||
|
gsutil cp dist/cog-ha-matter-arm.sha256 gs://cognitum-apps/cogs/arm/cog-ha-matter-arm.sha256
|
||||||
|
if [ -f dist/cog-ha-matter-x86_64.sig ]; then
|
||||||
|
gsutil cp dist/cog-ha-matter-x86_64.sig gs://cognitum-apps/cogs/x86_64/cog-ha-matter-x86_64.sig
|
||||||
|
fi
|
||||||
|
if [ -f dist/cog-ha-matter-arm.sig ]; then
|
||||||
|
gsutil cp dist/cog-ha-matter-arm.sig gs://cognitum-apps/cogs/arm/cog-ha-matter-arm.sig
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Print app-registry.json snippet for the cognitum-one PR
|
||||||
|
run: |
|
||||||
|
for arch in arm x86_64; do
|
||||||
|
sha=$(cat dist/cog-cog-ha-matter-$arch.sha256)
|
||||||
|
sig=$([ -f dist/cog-cog-ha-matter-$arch.sig ] && cat dist/cog-cog-ha-matter-$arch.sig || echo "")
|
||||||
|
cat <<EOF
|
||||||
|
--- $arch ---
|
||||||
|
{
|
||||||
|
"id": "ha-matter",
|
||||||
|
"version": "${GITHUB_REF_NAME#cog-ha-matter-v}",
|
||||||
|
"binary_url": "https://storage.googleapis.com/cognitum-apps/cogs/$arch/cog-cog-ha-matter-$arch",
|
||||||
|
"binary_sha256": "$sha",
|
||||||
|
"binary_signature": "$sig",
|
||||||
|
"description": "Home Assistant + Matter Cognitum Seed cog (mDNS + witness chain)",
|
||||||
|
"min_seed_version": "0.6.0",
|
||||||
|
"installable_on": ["$arch"]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
done
|
||||||
@@ -87,6 +87,7 @@ Ranked by build cost × user impact:
|
|||||||
| 3 | **Local SONA fine-tuning loop** (HA feedback → LoRA gradient steps) | ~2-3 weeks | Reduces false positives, closes #1 user complaint | P5 (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`) |
|
| 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) |
|
| 5 | **Matter Bridge with OccupancySensor + dynamic endpoints** | ~6-8 weeks | Apple Home / Google Home / Alexa native | **v0.8** dedicated sprint (after HACS adoption data) |
|
||||||
|
| 6 | **Embedded MQTT broker (rumqttd) inside the cog** | ~1 week | "Works without external broker" but every HA install already has mosquitto / built-in | **v0.7** deferred — adds ~2 MB binary + ACL config surface for marginal user benefit. Dossier ranking did not include this in the prioritised v1 scope. |
|
||||||
|
|
||||||
## 4. Implementation phases
|
## 4. Implementation phases
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ Ranked by build cost × user impact:
|
|||||||
| **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 |
|
| **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 |
|
| **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 | ✅ **wiring done** — `main.rs` boots ADR-115's `publisher::spawn` via `runtime::spawn_publisher` thin wrapper, holds a long-lived `broadcast::Sender<VitalsSnapshot>`, awaits Ctrl-C. Live-handle test green without a broker. Next (P3.5): subscribe to sensing-server `/v1/snapshot` WS and republish into the channel. |
|
| **P3** | Wrap existing ADR-115 MQTT publisher as cog entry point | ✅ **wiring done** — `main.rs` boots ADR-115's `publisher::spawn` via `runtime::spawn_publisher` thin wrapper, holds a long-lived `broadcast::Sender<VitalsSnapshot>`, awaits Ctrl-C. Live-handle test green without a broker. Next (P3.5): subscribe to sensing-server `/v1/snapshot` WS and republish into the channel. |
|
||||||
| **P4** | Seed-native enhancements (embedded broker, mDNS, witness) | in progress — **mDNS half complete:** record-builder ✅, ServiceInfo conversion ✅, **live responder ✅** (`runtime::start_mdns_responder` binds multicast, registers, returns `MdnsResponderHandle` with explicit `shutdown()` + best-effort Drop). **Witness half complete:** hash-chain ✅, JSONL line serializer ✅, file persistence + chain-level verify ✅, Ed25519 signing ✅. **Remaining:** embedded rumqttd broker. |
|
| **P4** | Seed-native enhancements (mDNS, witness; embedded broker deferred) | ✅ **shipped** — mDNS half: record-builder + ServiceInfo conversion + live responder wired into `main.rs` (HA auto-discovery on `_ruview-ha._tcp` works out of the box, `--no-mdns` flag for restrictive networks). Witness half: hash-chain + JSONL + file persistence + chain-level verify + Ed25519 signing. **Embedded rumqttd broker deferred to v0.7** per dossier §8 ranking — not in the prioritised v1 scope; v1 ships with external-broker only (mosquitto or HA's built-in broker). See §4 v1 scope table. |
|
||||||
| **P5** | RuVector-backed threshold learning (SONA adaptation) | pending |
|
| **P5** | RuVector-backed threshold learning (SONA adaptation) | pending |
|
||||||
| **P6** | Multi-Seed federation (cross-Seed dedup + witness) | pending |
|
| **P6** | Multi-Seed federation (cross-Seed dedup + witness) | pending |
|
||||||
| **P7** | Matter Bridge mode (depends on matter-rs / esp-matter readiness) | pending |
|
| **P7** | Matter Bridge mode (depends on matter-rs / esp-matter readiness) | pending |
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Build / sign / upload pipeline for cog-ha-matter.
|
||||||
|
# See ADR-100 §"Build pipeline" + ADR-116 §"Phases" for the contract.
|
||||||
|
# Mirrors cog-pose-estimation/cog/Makefile so the Seed runtime treats
|
||||||
|
# both cogs identically — `cognitum cog install ha-matter` works the
|
||||||
|
# same as `cognitum cog install pose-estimation`.
|
||||||
|
|
||||||
|
CRATE := cog-ha-matter
|
||||||
|
VERSION := $(shell cargo pkgid -p $(CRATE) 2>/dev/null | sed -E 's/.*#([0-9.]+).*/\1/')
|
||||||
|
GCS_BUCKET := gs://cognitum-apps/cogs
|
||||||
|
|
||||||
|
ARCHES := arm x86_64
|
||||||
|
|
||||||
|
# --- Build targets ---
|
||||||
|
|
||||||
|
.PHONY: build build-arm build-x86_64
|
||||||
|
|
||||||
|
build: build-arm build-x86_64
|
||||||
|
|
||||||
|
build-arm:
|
||||||
|
mkdir -p dist
|
||||||
|
cargo build -p $(CRATE) --release --target aarch64-unknown-linux-gnu
|
||||||
|
cp ../../target/aarch64-unknown-linux-gnu/release/$(CRATE) ./dist/$(CRATE)-arm
|
||||||
|
|
||||||
|
build-x86_64:
|
||||||
|
mkdir -p dist
|
||||||
|
cargo build -p $(CRATE) --release --target x86_64-unknown-linux-gnu
|
||||||
|
cp ../../target/x86_64-unknown-linux-gnu/release/$(CRATE) ./dist/$(CRATE)-x86_64
|
||||||
|
|
||||||
|
# --- Sign ---
|
||||||
|
|
||||||
|
.PHONY: sign sign-arm sign-x86_64
|
||||||
|
|
||||||
|
sign: sign-arm sign-x86_64
|
||||||
|
|
||||||
|
sign-arm: dist/$(CRATE)-arm
|
||||||
|
sha256sum dist/$(CRATE)-arm | cut -d' ' -f1 > dist/$(CRATE)-arm.sha256
|
||||||
|
# Signature: gcloud secrets versions access latest --secret=COGNITUM_OWNER_SIGNING_KEY \
|
||||||
|
# | openssl pkeyutl -sign -inkey /dev/stdin -rawin -in dist/$(CRATE)-arm.sha256 \
|
||||||
|
# | base64 -w0 > dist/$(CRATE)-arm.sig
|
||||||
|
@echo "TODO: wire Ed25519 sign step once COGNITUM_OWNER_SIGNING_KEY is provisioned to CI."
|
||||||
|
|
||||||
|
sign-x86_64: dist/$(CRATE)-x86_64
|
||||||
|
sha256sum dist/$(CRATE)-x86_64 | cut -d' ' -f1 > dist/$(CRATE)-x86_64.sha256
|
||||||
|
@echo "TODO: wire Ed25519 sign step once COGNITUM_OWNER_SIGNING_KEY is provisioned to CI."
|
||||||
|
|
||||||
|
# --- Upload to GCS ---
|
||||||
|
|
||||||
|
.PHONY: upload upload-arm upload-x86_64
|
||||||
|
|
||||||
|
upload: upload-arm upload-x86_64
|
||||||
|
|
||||||
|
upload-arm: dist/$(CRATE)-arm
|
||||||
|
gsutil cp dist/$(CRATE)-arm $(GCS_BUCKET)/arm/$(CRATE)-arm
|
||||||
|
|
||||||
|
upload-x86_64: dist/$(CRATE)-x86_64
|
||||||
|
gsutil cp dist/$(CRATE)-x86_64 $(GCS_BUCKET)/x86_64/$(CRATE)-x86_64
|
||||||
|
|
||||||
|
# --- Manifest ---
|
||||||
|
|
||||||
|
.PHONY: manifest
|
||||||
|
|
||||||
|
manifest:
|
||||||
|
@cargo run -p $(CRATE) --release -- --print-manifest
|
||||||
|
|
||||||
|
# --- Convenience ---
|
||||||
|
|
||||||
|
.PHONY: release verify clean
|
||||||
|
|
||||||
|
release: build sign upload manifest
|
||||||
|
@echo "Release pipeline complete for $(CRATE) v$(VERSION)"
|
||||||
|
|
||||||
|
verify:
|
||||||
|
@for arch in $(ARCHES); do \
|
||||||
|
f=dist/$(CRATE)-$$arch; \
|
||||||
|
if [ ! -f $$f ]; then echo " MISSING $$f"; continue; fi; \
|
||||||
|
actual=$$(sha256sum $$f | cut -d' ' -f1); \
|
||||||
|
expected=$$(cat $$f.sha256 2>/dev/null); \
|
||||||
|
if [ "$$actual" = "$$expected" ]; then echo " OK $$f ($$actual)"; \
|
||||||
|
else echo " FAIL $$f (expected $$expected, got $$actual)"; fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf dist/$(CRATE)-*
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# HA-Matter Cog Packaging
|
||||||
|
|
||||||
|
Build / sign / upload pipeline for `cog-ha-matter`, mirroring the
|
||||||
|
[`cog-pose-estimation`](../../cog-pose-estimation/cog/) precedent so the
|
||||||
|
Seed runtime treats both cogs identically.
|
||||||
|
|
||||||
|
See [ADR-100 — Cog Packaging Specification](../../../../docs/adr/ADR-100-cog-packaging-specification.md)
|
||||||
|
and [ADR-116 — HA-Matter Seed Cog](../../../../docs/adr/ADR-116-cog-ha-matter-seed.md).
|
||||||
|
|
||||||
|
## What this cog does
|
||||||
|
|
||||||
|
Wraps the ADR-115 HA-DISCO + HA-MIND MQTT publisher as a Seed-installable
|
||||||
|
artifact with:
|
||||||
|
|
||||||
|
- mDNS auto-discovery (`_ruview-ha._tcp`)
|
||||||
|
- Ed25519-signed witness chain for tamper-evident audit logs
|
||||||
|
- Privacy-mode flag (only semantic primitives, no biometrics)
|
||||||
|
- One-flag deferral to v0.7 for the embedded broker / v0.8 for the Matter Bridge
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `manifest.template.json` | Build-time manifest with `{{VERSION}}` / `{{ARCH}}` slots; `make manifest` substitutes them |
|
||||||
|
| `Makefile` | `build` / `sign` / `upload` / `release` / `verify` / `clean` targets |
|
||||||
|
| `dist/` | Created by `make build`; gitignored, holds release binaries + sha256 + sig |
|
||||||
|
|
||||||
|
## Local build (dry-run)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd v2/crates/cog-ha-matter/cog
|
||||||
|
make build # builds aarch64 + x86_64 release binaries
|
||||||
|
make sign # writes .sha256 + (TODO) .sig sidecars
|
||||||
|
make manifest # prints the manifest the Seed would record
|
||||||
|
```
|
||||||
|
|
||||||
|
`make sign` is currently a no-op for the signature itself — the
|
||||||
|
`COGNITUM_OWNER_SIGNING_KEY` provisioning is the same TODO that
|
||||||
|
blocks [`cog-pose-estimation`](../../cog-pose-estimation/cog/Makefile).
|
||||||
|
Until then, dev cogs ship unsigned and `app-registry.json` lists
|
||||||
|
them with `"binary_signature": ""`.
|
||||||
|
|
||||||
|
## Upload (requires `gcloud auth`)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gcloud auth login
|
||||||
|
make upload # gsutil cp dist/* gs://cognitum-apps/cogs/{arch}/
|
||||||
|
```
|
||||||
|
|
||||||
|
The GCS bucket is shared with `cog-pose-estimation` and is part of
|
||||||
|
the `cognitum-apps` project. Write access requires membership in the
|
||||||
|
`cog-publishers` IAM group.
|
||||||
|
|
||||||
|
## app-registry.json
|
||||||
|
|
||||||
|
Lives in the [`cognitum-one`](https://github.com/ruvnet/cognitum-one)
|
||||||
|
repo, **not here**. After `make upload` succeeds, file a PR there
|
||||||
|
that appends:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "ha-matter",
|
||||||
|
"version": "<the version make manifest printed>",
|
||||||
|
"binary_url": "https://storage.googleapis.com/cognitum-apps/cogs/{arch}/cog-ha-matter-{arch}",
|
||||||
|
"binary_sha256": "<from dist/cog-ha-matter-{arch}.sha256>",
|
||||||
|
"binary_signature": "<from dist/cog-ha-matter-{arch}.sig — empty until signing is wired>",
|
||||||
|
"description": "Home Assistant + Matter Cognitum Seed cog (mDNS + witness chain)",
|
||||||
|
"min_seed_version": "0.6.0",
|
||||||
|
"installable_on": ["arm", "x86_64"]
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# cog-ha-matter Release Checklist
|
||||||
|
|
||||||
|
Mechanical steps to publish a new version. **Everything local-side is
|
||||||
|
automated; the four "🔑 USER ACTION" blocks below are the only manual
|
||||||
|
gates.** Each one is a credential-bearing step the cog/ pipeline cannot
|
||||||
|
do on its own.
|
||||||
|
|
||||||
|
## 1. Pre-release (local)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Bump version in v2/crates/cog-ha-matter/Cargo.toml then:
|
||||||
|
cargo test -p cog-ha-matter --no-default-features --lib # 64+ tests must pass
|
||||||
|
cargo check -p cog-ha-matter --no-default-features # green
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Tag the release
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git tag cog-ha-matter-v$(cargo pkgid -p cog-ha-matter | sed -E 's/.*#//')
|
||||||
|
git push origin --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
The push fires `.github/workflows/cog-ha-matter-release.yml` which:
|
||||||
|
|
||||||
|
* builds `cog-ha-matter-x86_64` + `cog-ha-matter-arm` (cross-compiled
|
||||||
|
via apt-installed `gcc-aarch64-linux-gnu`)
|
||||||
|
* computes SHA-256 sidecars
|
||||||
|
* runs the Ed25519 sign step **if** `COGNITUM_OWNER_SIGNING_KEY` is set
|
||||||
|
* uploads workflow artifacts (always — these are downloadable from
|
||||||
|
the run page)
|
||||||
|
* uploads to `gs://cognitum-apps/cogs/{arch}/` **if** the org var
|
||||||
|
`HAS_GCP_CREDENTIALS == 'true'` and the `GCP_CREDENTIALS` secret is set
|
||||||
|
|
||||||
|
## 3. Update app-registry.json
|
||||||
|
|
||||||
|
Take `cog/app-registry-entry.json` from this directory, fill in the
|
||||||
|
post-build values, and PR it into the [`cognitum-one`](https://github.com/ruvnet/cognitum-one)
|
||||||
|
repo at `app-registry.json`.
|
||||||
|
|
||||||
|
Values to fill in:
|
||||||
|
|
||||||
|
* `version` — bump to match the new tag
|
||||||
|
* `sha256` — paste from the workflow artifact's `.sha256` sidecar
|
||||||
|
* `binary_size` — bytes of the binary (`wc -c < cog-ha-matter-x86_64`)
|
||||||
|
|
||||||
|
## 🔑 USER ACTION items (cannot be automated)
|
||||||
|
|
||||||
|
| # | What | Why this can't be automated |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Set the `HAS_GCP_CREDENTIALS` org variable to `true` and provision the `GCP_CREDENTIALS` GitHub Actions secret with a service-account JSON that has `storage.objectAdmin` on `gs://cognitum-apps/cogs/` | Requires org-admin access + a GCP project owner's signoff |
|
||||||
|
| 2 | Provision `COGNITUM_OWNER_SIGNING_KEY` GitHub secret with the Ed25519 private key in PEM form | Long-lived secret material; humans must rotate it; same blocker for cog-pose-estimation |
|
||||||
|
| 3 | `gcloud auth login` (only if running `make upload` locally instead of via CI) | Browser OAuth flow |
|
||||||
|
| 4 | File a PR in `cognitum-one` against `app-registry.json` adding the entry from `cog/app-registry-entry.json` | Cross-repo write requires the user's GitHub auth + reviewer signoff |
|
||||||
|
|
||||||
|
## Post-release verification
|
||||||
|
|
||||||
|
Once the cognitum-one PR merges and the cache rolls over (~hourly):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -sS https://storage.googleapis.com/cognitum-apps/app-registry.json \
|
||||||
|
| jq '.[] | select(.id == "ha-matter")'
|
||||||
|
```
|
||||||
|
|
||||||
|
Should print the new entry. On the Seed UI, the cog appears under
|
||||||
|
**Settings → Cogs → building → Home Assistant + Matter Bridge**.
|
||||||
|
|
||||||
|
## Reverting a bad release
|
||||||
|
|
||||||
|
Cogs ship via GCS object versioning (per ADR-100). To roll back:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gsutil ls -a gs://cognitum-apps/cogs/x86_64/cog-ha-matter-x86_64
|
||||||
|
# Pick the previous generation, then:
|
||||||
|
gsutil cp gs://cognitum-apps/cogs/x86_64/cog-ha-matter-x86_64#<generation> \
|
||||||
|
gs://cognitum-apps/cogs/x86_64/cog-ha-matter-x86_64
|
||||||
|
```
|
||||||
|
|
||||||
|
Then PR a `version` bump in `cognitum-one`'s `app-registry.json` so
|
||||||
|
Seeds know to refetch.
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"id": "ha-matter",
|
||||||
|
"name": "Home Assistant + Matter Bridge",
|
||||||
|
"category": "building",
|
||||||
|
"version": "0.3.0",
|
||||||
|
"size_kb": 12,
|
||||||
|
"difficulty": "easy",
|
||||||
|
"description": "Exposes WiFi-CSI sensing as Home Assistant entities over MQTT auto-discovery, with mDNS announcement on _ruview-ha._tcp and tamper-evident Ed25519-signed audit logs. Adds 10 semantic primitives (someone_sleeping, possible_distress, fall_risk_elevated, ...) on top of the 11 raw measurements. Privacy mode strips biometrics at the wire so only the semantic layer reaches HA — the right default for any deployment with non-tenant occupants.",
|
||||||
|
"featured": false,
|
||||||
|
"config": [
|
||||||
|
{
|
||||||
|
"key": "sensing_url",
|
||||||
|
"type": "string",
|
||||||
|
"label": "Sensing server URL",
|
||||||
|
"description": "Where the cog reads VitalsSnapshot from",
|
||||||
|
"default": "http://127.0.0.1:3000",
|
||||||
|
"cli_arg": "--sensing-url"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mqtt_host",
|
||||||
|
"type": "string",
|
||||||
|
"label": "MQTT broker host",
|
||||||
|
"description": "External mosquitto / HA Core MQTT host (v0.7 will add an embedded broker option)",
|
||||||
|
"default": "127.0.0.1",
|
||||||
|
"cli_arg": "--mqtt-host"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mqtt_port",
|
||||||
|
"type": "integer",
|
||||||
|
"label": "MQTT broker port",
|
||||||
|
"default": 1883,
|
||||||
|
"min": 1,
|
||||||
|
"max": 65535,
|
||||||
|
"cli_arg": "--mqtt-port"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "privacy_mode",
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Privacy mode",
|
||||||
|
"description": "Strip biometrics at the wire — only semantic primitives are published. Recommended for any deployment with non-tenant occupants (care homes, education, shared housing).",
|
||||||
|
"default": false,
|
||||||
|
"cli_arg": "--privacy-mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mdns_hostname",
|
||||||
|
"type": "string",
|
||||||
|
"label": "mDNS hostname",
|
||||||
|
"description": "Must end with .local. per RFC 6762. HA's discovery integration looks up this hostname.",
|
||||||
|
"default": "cog-ha-matter.local.",
|
||||||
|
"cli_arg": "--mdns-hostname"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mdns_ipv4",
|
||||||
|
"type": "string",
|
||||||
|
"label": "Advertised IPv4",
|
||||||
|
"description": "LAN-routable address the mDNS responder advertises. HA reaches back to this for MQTT.",
|
||||||
|
"default": "127.0.0.1",
|
||||||
|
"cli_arg": "--mdns-ipv4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "no_mdns",
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Disable mDNS",
|
||||||
|
"description": "Skip the mDNS responder. Useful in containerised setups where multicast is filtered.",
|
||||||
|
"default": false,
|
||||||
|
"cli_arg": "--no-mdns"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sha256": "<FILL_IN_FROM_dist/cog-ha-matter-x86_64.sha256_AFTER_make_build>",
|
||||||
|
"binary_size": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"id": "ha-matter",
|
||||||
|
"version": "{{VERSION}}",
|
||||||
|
"binary_url": "https://storage.googleapis.com/cognitum-apps/cogs/{{ARCH}}/cog-ha-matter-{{ARCH}}",
|
||||||
|
"binary_bytes": 0,
|
||||||
|
"binary_sha256": "",
|
||||||
|
"binary_signature": "",
|
||||||
|
"installed_at": 0,
|
||||||
|
"status": "installed"
|
||||||
|
}
|
||||||
@@ -48,6 +48,24 @@ struct Args {
|
|||||||
/// control plane and exit. Useful for the build-time signer.
|
/// control plane and exit. Useful for the build-time signer.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
print_manifest: bool,
|
print_manifest: bool,
|
||||||
|
|
||||||
|
/// mDNS hostname for the Seed advertisement. Must end with
|
||||||
|
/// `.local.` per RFC 6762. Default lets HA's discovery find a
|
||||||
|
/// dev cog on localhost without LAN config.
|
||||||
|
#[arg(long, default_value = "cog-ha-matter.local.")]
|
||||||
|
mdns_hostname: String,
|
||||||
|
|
||||||
|
/// LAN-routable IPv4 the cog binds the control plane on. The
|
||||||
|
/// mDNS responder advertises this; HA reaches back to it for
|
||||||
|
/// MQTT + Matter Bridge.
|
||||||
|
#[arg(long, default_value = "127.0.0.1")]
|
||||||
|
mdns_ipv4: String,
|
||||||
|
|
||||||
|
/// Skip the mDNS responder. Useful in containerised CI where
|
||||||
|
/// multicast bind is filtered, or when running multiple cog
|
||||||
|
/// instances on the same loopback.
|
||||||
|
#[arg(long)]
|
||||||
|
no_mdns: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -115,6 +133,35 @@ async fn main() -> ExitCode {
|
|||||||
// HA install with no nodes online looks like.
|
// HA install with no nodes online looks like.
|
||||||
let _ = &state_tx;
|
let _ = &state_tx;
|
||||||
|
|
||||||
|
// P4: mDNS responder. HA's auto-discovery picks the cog up on
|
||||||
|
// `_ruview-ha._tcp` so users don't need to type broker host/port.
|
||||||
|
let _mdns_handle = if args.no_mdns {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let identity = runtime::CogIdentity::default_for_build();
|
||||||
|
let service = cog_ha_matter::mdns::build_mdns_service(
|
||||||
|
&identity,
|
||||||
|
cog_ha_matter::DEFAULT_CONTROL_PORT,
|
||||||
|
args.mqtt_port,
|
||||||
|
args.privacy_mode,
|
||||||
|
);
|
||||||
|
match runtime::start_mdns_responder(&service, &args.mdns_hostname, &args.mdns_ipv4) {
|
||||||
|
Ok(h) => {
|
||||||
|
info!(
|
||||||
|
fullname = h.fullname(),
|
||||||
|
hostname = %args.mdns_hostname,
|
||||||
|
ipv4 = %args.mdns_ipv4,
|
||||||
|
"mDNS responder registered — HA auto-discovery should find the cog now"
|
||||||
|
);
|
||||||
|
Some(h)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!(error = ?e, "mDNS responder failed to start — discovery disabled, falling back to manual HA config");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Wait on Ctrl-C so the cog runs as a long-lived daemon under
|
// Wait on Ctrl-C so the cog runs as a long-lived daemon under
|
||||||
// the Seed's process supervisor.
|
// the Seed's process supervisor.
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
@@ -125,5 +172,8 @@ async fn main() -> ExitCode {
|
|||||||
warn!(?joined, "publisher task exited unexpectedly");
|
warn!(?joined, "publisher task exited unexpectedly");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _mdns_handle drops here, sending the mDNS goodbye packet so
|
||||||
|
// HA's discovery integration sees the service leave cleanly.
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user