Compare commits

...

4 Commits

Author SHA1 Message Date
ruv be4efecbcd cog-ha-matter (ADR-116 P8): app-registry entry stub + release checklist
Two closing P8 deliverables that complete the local-side publishing
scaffolding. The remaining work is all credential-bearing user
action.

1. `cog/app-registry-entry.json` — the exact JSON payload to paste
   into cognitum-one's `app-registry.json`. Schema discovered by
   fetching the live registry (105 cogs, 11 categories) and
   matching the existing `ruview-densepose` entry verbatim. Keys:

     id, name, category, version, size_kb, difficulty, description,
     featured, config[], sha256, binary_size

   cog-ha-matter slots in under `category: "building"` (smart home
   / building automation — the natural HA / Matter category, vs
   `network` which is more about transport bridges).

   7 config[] entries mirror our CLI surface:
     sensing_url, mqtt_host, mqtt_port, privacy_mode,
     mdns_hostname, mdns_ipv4, no_mdns

   Two post-build fields left as `<FILL_IN_...>` markers:
     sha256       (paste from the workflow artifact's .sha256)
     binary_size  (wc -c < the binary)

   Schema validated: all 10 required keys present, parses as JSON.

2. `cog/RELEASE-CHECKLIST.md` — one-page mechanical playbook with
   four explicit "🔑 USER ACTION" gates. Each gate names exactly
   what the user (or org admin) has to do that the pipeline cannot:

     a) provision GCP_CREDENTIALS + HAS_GCP_CREDENTIALS org var
     b) provision COGNITUM_OWNER_SIGNING_KEY GH secret
     c) gcloud auth login (only if uploading locally)
     d) PR app-registry.json into cognitum-one

   Plus pre-release test gate, tag-push command, post-release
   verification curl, and a rollback procedure using GCS object
   versioning (per ADR-100 §"GCS misconfiguration risks").

Stop-condition check (cron's predicate: "ALL local-side publishing
scaffolding is complete and the only remaining work requires user
action"):

   cog/manifest.template.json
   cog/Makefile (build / sign / upload / verify / clean)
   cog/README.md
   cog/app-registry-entry.json (this commit)
   cog/RELEASE-CHECKLIST.md (this commit)
   .github/workflows/cog-ha-matter-release.yml (3 jobs, gated)
   dist/ handling (gitignored, created by make)

  🔑 4 user-action gates explicitly enumerated in the checklist

The cron should STOP after this iter — the local-side scaffolding
is complete and the remaining work is the four named credential
gates that the pipeline cannot self-serve.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 23:12:14 -04:00
ruv 3833929dcb cog-ha-matter (ADR-116 P8): CI release workflow + fix inherited filename bug
New `.github/workflows/cog-ha-matter-release.yml`:

  * Triggers on `cog-ha-matter-v*` tag-push + manual dispatch
  * Three jobs: build-x86_64, build-arm, publish-gcs
  * x86_64: native ubuntu-latest cargo build
  * arm: aarch64-unknown-linux-gnu via apt-installed gcc-aarch64-linux-gnu
    linker (no `cross` dep needed — keeps workflow self-contained)
  * Each build job runs make build-{arch} + make sign-{arch} +
    gated Ed25519 sign step (skipped when COGNITUM_OWNER_SIGNING_KEY
    secret is unset — workflow still produces unsigned artifacts so
    we get build coverage now and signing later without re-merging)
  * publish-gcs job gated on `vars.HAS_GCP_CREDENTIALS == 'true'`
    so the workflow is safe to merge before credentials land —
    no-op until the org admin sets the variable
  * Uploads binary + sha256 + (optional) sig to
    `gs://cognitum-apps/cogs/{arch}/cog-ha-matter-{arch}`
  * Prints the app-registry.json snippet for the cognitum-one PR
    (so the publish step's output is the exact JSON the user pastes)

Fixed a bug inherited from cog-pose-estimation's Makefile: the
precedent produces `dist/cog-cog-pose-estimation-arm` (double
`cog-` prefix because CRATE name already starts with `cog-`) but
the manifest URL has single prefix `cog-pose-estimation-arm`. The
upload path doesn't match the binary_url — a latent bug in the
pose cog's pipeline.

My copy now produces `dist/cog-ha-matter-arm` matching the
manifest URL `cog-ha-matter-{{ARCH}}`. Changed: Makefile (build /
sign / upload / verify / clean targets), workflow (artifact names
+ gsutil paths), README (local dry-run instructions). The
cog-pose-estimation precedent is unchanged — separate fix if/when
the user wants to align it.

What this iter does NOT do (P8 remaining):
  * provision GCP_CREDENTIALS / COGNITUM_OWNER_SIGNING_KEY secrets
    (user action — needs org admin access)
  * actually run the workflow (needs a `cog-ha-matter-v0.1.0` tag
    push, or workflow_dispatch from the Actions tab)
  * append to app-registry.json in cognitum-one (separate repo PR)

Next iter: tag a v0.0.1-dev (so the workflow runs once + we see
any build-time errors on real CI runners) OR scaffold the
app-registry.json patch payload as a check-in doc.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 23:05:54 -04:00
ruv 1e469aa336 cog-ha-matter (ADR-116 P8): scaffold cog/ publishing layout
Mirrors v2/crates/cog-pose-estimation/cog/ so the Seed runtime
treats cog-ha-matter identically — `cognitum cog install ha-matter`
behaves like `cognitum cog install pose-estimation`.

Files:

  * cog/manifest.template.json — 9-field manifest with {{VERSION}}
    + {{ARCH}} slots, hand-edited by the Makefile signer
  * cog/Makefile — same target set as cog-pose-estimation:
      build / build-arm / build-x86_64
      sign  / sign-arm  / sign-x86_64   (Ed25519 step is TODO,
        blocked on COGNITUM_OWNER_SIGNING_KEY provisioning —
        same blocker as cog-pose-estimation)
      upload / upload-arm / upload-x86_64
      manifest (delegates to `cargo run -- --print-manifest`)
      release (= build + sign + upload + manifest)
      verify (sha256sum vs sidecar)
      clean
    Adds `mkdir -p dist` to build steps so the gitignored dist/
    folder is created on first build.
  * cog/README.md — what this cog does, layout map, local dry-run
    instructions, gcloud auth requirements, the JSON snippet to
    paste into app-registry.json (in the separate cognitum-one
    repo, not this one)

Local dist/ is intentionally not committed: top-level .gitignore
matches `dist/` globally, the Makefile creates it on demand.

What this commit does NOT do (P8 remaining):
  * cross-compile build (needs `rustup target add
    aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu` + linker)
  * sign the binaries (COGNITUM_OWNER_SIGNING_KEY not provisioned)
  * gsutil cp to gs://cognitum-apps/ (needs user's gcloud auth)
  * append to app-registry.json (lives in cognitum-one repo —
    separate PR there)

Next iter: a CI workflow that runs `make build sign verify` on
tag-push, so the local-side pipeline is fully exercised even
without the production credentials.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 22:55:44 -04:00
ruv d4f0e12073 cog-ha-matter (ADR-116): P4 — mDNS wired into main, broker deferred
Two landings that flip P4 to shipped:

1. main.rs now actually registers the mDNS responder. New CLI:
     --mdns-hostname (default: cog-ha-matter.local.)
     --mdns-ipv4     (default: 127.0.0.1)
     --no-mdns       (skip for restrictive CI / multi-instance)

   Responder boots after the publisher; failure logs WARN + falls
   back to manual HA config instead of killing the cog. The
   handle's Drop sends the mDNS goodbye packet on shutdown so HA's
   discovery sees a clean service-leave (no stale device card).

2. Embedded rumqttd broker DEFERRED to v0.7 per dossier §8 ranking.

   The dossier's prioritised v1 scope is:
     1. --privacy-mode audit-only
     2. cog manifest + Ed25519 signing + store listing
     3. local SONA fine-tuning loop
     4. HACS gold-tier integration
     5. Matter Bridge (v0.8)

   Embedded broker is not in that list. Every HA install already
   has mosquitto or HA Core's built-in broker — adding ~2 MB of
   binary + ACL config surface for marginal benefit didn't earn a
   v1 slot. Documented as row 6 of §4 v1 scope table with explicit
   v0.7 target.

P4 row updated to : mDNS half complete (record-builder +
ServiceInfo + live responder + main.rs wiring), witness half
complete (chain + JSONL + file + Ed25519), embedded broker
explicitly deferred with rationale citation to dossier §8.

Stop-condition check:
  * dossier has "Recommended scope" section  (§8, folded into
    ADR §4)
  * P2 (cog scaffold) 
  * P3 (MQTT publisher wrap) 
  * P4 (Seed-native enhancements) 

Cron's stop predicate evaluates: P2-P4 shipped AND dossier has
the recommended-scope section → STOP. The loop should TaskStop
itself after this iter unless the user wants P5 (RuVector
thresholds), P8 (cog signing), or P9 (HACS repo) to keep going.

64/64 tests green.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 18:36:14 -04:00
8 changed files with 566 additions and 1 deletions
+200
View File
@@ -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
+2 -1
View File
@@ -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) |
| 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) |
| 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
@@ -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 |
| **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. |
| **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 |
| **P6** | Multi-Seed federation (cross-Seed dedup + witness) | pending |
| **P7** | Matter Bridge mode (depends on matter-rs / esp-matter readiness) | pending |
+83
View File
@@ -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)-*
+71
View File
@@ -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"
}
+50
View File
@@ -48,6 +48,24 @@ struct Args {
/// control plane and exit. Useful for the build-time signer.
#[arg(long)]
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]
@@ -115,6 +133,35 @@ async fn main() -> ExitCode {
// HA install with no nodes online looks like.
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
// the Seed's process supervisor.
tokio::select! {
@@ -125,5 +172,8 @@ async fn main() -> ExitCode {
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
}