TEST RESULTS
Stack test results
Live verification of relay-edge + relay-pubsub + Relay (+ Zynera) on a co-deployed lab stack.
← Docs hub · Integration guide · Event matrix · Browser docs
Last re-run: 2026-08-29 · Outcome: all gates PASS on labs 212 (:8443) and 175 (:18080)
| Path | Script | Result |
|---|---|---|
| Unit tests | go test ./... |
PASS |
| Smoke | smoke.sh · smoke-firewater.sh · smoke-remote-edge.sh · smoke-fleet.sh |
PASS (also in GitHub Actions CI) |
| Gateway stack (212) | e2e-stack.sh + config/lab-stack.env |
PASS — farm 10/10 Accept + 5/5 Act · FW 5 · remote-edge 6 · fleet 6 |
| Gateway stack (175) | e2e-stack.sh + config/lab-stack-175.env |
PASS — same matrix; Relay on :18080 |
| Direct Relay | e2e-direct-stack.sh |
PASS — farm 10 · firewater 13 · remote-edge 6 · fleet 6 |
| CI / release | Actions CI + tag Release |
PASS — v0.1.1 binaries + ghcr.io/zyvorai/relay-edge |
Act wiring (required for Farm Act): Relay must call a reachable pubsub /v1/actions (remote host URL if pubsub is not on the Relay box) with outbound RELAY_TLS_INSECURE when using self-signed TLS (relay#8bef494). Helper: scripts/lab-wire-relay-act.sh (defaults to co-located 127.0.0.1 — edit targets for remote pubsub).
CI note: GitHub Actions runs unit tests + the four smoke scripts against a mock Relay. Full gateway/direct matrices above remain lab-only (need live Relay + pubsub).
What we tested¶
End-to-end proof that all four event families from relay-edge reach Relay — via relay-pubsub (gateway) or direct POST /v1/events — plus Act via the pubsub Action Gateway (rpg_*) and optional Zynera Decision Records.
| Layer | Verified behaviour |
|---|---|
| relay-edge | Farm seasons/sites + firewater plant + remote-edge NOC + fleet IoT simulators; stamp + publish |
| relay-pubsub | 40-topic catalog; Pub/Sub REST → Relay; inbound /v1/actions for all controller targets |
| Relay | Accept, notify, ack, act, verify for every family; Zynera awaiting_decision only when policy + Zynera configured |
| Zynera | Decision Record create (Relay), human freeze Approved → act; Rejected → failed (optional) |
Lab topology¶
Any service can be remote. From your workstation, always use host IPs in BASE / GATEWAY / EDGE — never 127.0.0.1 (that is the machine running the script).
| Service | URL pattern | Repo |
|---|---|---|
| relay-edge | https://<edge-host>:18086 |
relay-edge |
| relay-pubsub | https://<pubsub-host>:8081 |
relay-pubsub |
| relay-pubsub console | https://<pubsub-host>:8082/ |
relay-pubsub |
| Relay | https://<relay-host>:8443 or :18080 |
relay |
| Zynera gateway | http://<zynera-host>:30631 |
forge |
| Zynera UI (Zeus) | http://<zynera-host>:30862 |
forge |
| Example lab | Relay port | Env template |
|---|---|---|
<ephemeral-ip> (all-on-one) |
8443 |
config/lab-stack.env |
<ephemeral-ip> (all-on-one) |
18080 |
config/lab-stack-175.env |
Relay process env — Act targets must reach pubsub’s host (127.0.0.1 only when Relay and pubsub share a machine):
RELAY_TENANT=fasal-edge
RELAY_ACTION_TARGETS=farm-controller=https://<pubsub-host>:8081/v1/actions,\
firewater-controller=https://<pubsub-host>:8081/v1/actions,\
remote-edge-controller=https://<pubsub-host>:8081/v1/actions,\
fleet-controller=https://<pubsub-host>:8081/v1/actions
RELAY_TLS_INSECURE=1 # outbound action HTTPS to self-signed pubsub
RELAY_FORGE_BASE_URL=http://<zynera-host>:30631 # optional
RELAY_FORGE_API_KEY=<k8s zynera-api-gateway-secret>
pubsub and relay-edge share the same RELAY_AUTH_TOKEN (JWT from demo/demo login).
Set external URLs in config/lab-stack.env or config/lab-stack-175.env (from the matching .example).
How we tested¶
1. Prepare environment¶
git clone https://github.com/zyvorai/relay-edge.git && cd relay-edge
cp config/lab-stack.env.example config/lab-stack.env
# Edit BASE, GATEWAY, EDGE, ZYNERA_BASE for your lab host
Fill in config/lab-stack.env:
| Variable | Source |
|---|---|
RELAY_AUTH_TOKEN |
curl -fsSk -X POST $BASE/v1/auth/login -d '{"username":"demo","password":"demo"}' → .token |
ZYNERA_API_KEY |
kubectl -n zynera get secret zynera-api-gateway-secret -o jsonpath='{.data.api-key}' \| base64 -d |
Ensure Relay has RELAY_FORGE_* and RELAY_TLS_INSECURE=1 (see above). After any Relay restart, re-sync pubsub:
# on lab host
sudo sed -i "s|^RELAY_AUTH_TOKEN=.*|RELAY_AUTH_TOKEN=$TOKEN|" /etc/relay-pubsub/relay-pubsub.env
sudo systemctl restart relay-pubsub
Redeploy relay-edge if needed:
RELAY_AUTH_TOKEN=$TOKEN ./scripts/deploy-remote.sh <HOST> [USER]
2. Health probe¶
set -a && source config/lab-stack.env && set +a
./scripts/stack-probe.sh
Checks: relay-edge /healthz, pubsub /healthz, Relay /healthz, Zynera POST /api/zeus/decisions probe.
3. Event matrix (integration gate)¶
./scripts/e2e-events-matrix.sh
| Phase | Cases | Pass criteria |
|---|---|---|
| A. Farm | 10 catalog types via gateway REST | 5 critical → pol_critical_farm + ack + Act executed with rpg_* provider; 5 advisory → pol_advisory, notify-only |
| B. Firewater | 5 scenarios | New Relay event per type (firewater.*, edge.*) |
| C. Remote edge | 6 scenarios (incl. drone_patrol) |
New Relay event per type (remote-edge.*) |
| D. Fleet | 6 scenarios | New Relay event per type (fleet.*) |
4. Full stack + Zynera path¶
./scripts/e2e-zynera-stack.sh
Runs the event matrix (Phase A), then:
| Phase | Action |
|---|---|
| B | Patch pol_critical_farm → decision_backend: forge (restored on exit) |
| C | relay-edge farm publish irrigation.required |
| D | Operator ack approve in Relay → awaiting_decision |
| E–F | Zynera freeze Approved → Relay resumes → act/verify |
| G | Second event → Zynera freeze Rejected → Relay failed |
Skip flags:
./scripts/stack-probe.sh --zynera-optional— when Zynera is not deployed./scripts/e2e-zynera-stack.sh --skip-matrix— Zynera path only
Without Zynera (tested 2026-08-28)¶
Most edge sites run relay-edge + relay-pubsub + Relay only — all four event families (farm, firewater/edge IoT, remote edge, fleet/multi-IoT), not farm alone. Zynera is optional for human Decision Records.
Plan¶
| Step | What | Families | Requires Zynera? |
|---|---|---|---|
| 1 | Health: edge, pubsub, Relay | all | No |
| 2 | Farm catalog via gateway REST | farm (10 types, 5 Act) | No |
| 3 | Firewater / edge IoT scenarios | industrial plant + edge AI/comms | No |
| 4 | Remote edge scenarios | Starlink, Galleon, UAV, vision, IoT | No |
| 5 | Fleet scenarios | AMR, OT, energy, BMS, security, … | No |
| 6 | Native ack → act (all stamped targets) | farm · firewater · remote-edge · fleet | No |
| 7 | Zynera freeze approve/reject | farm (policy patch in e2e) | Yes |
Most edge sites run relay-edge + relay-pubsub + Relay only. Zynera is optional.
→ Stack without Zynera — architecture, sequence, state diagrams · Test results
Leave ZYNERA_BASE and ZYNERA_API_KEY empty in config/lab-stack.env. Do not set RELAY_FORGE_* on Relay unless you need step 5.
How to run¶
cp config/lab-stack.env.example config/lab-stack.env
# BASE, GATEWAY, EDGE, RELAY_AUTH_TOKEN only — leave ZYNERA_* blank
set -a && source config/lab-stack.env && set +a
./scripts/e2e-stack.sh
Alternative (same coverage):
./scripts/stack-probe.sh --zynera-optional
./scripts/e2e-events-matrix.sh
# or: ./scripts/e2e-zynera-stack.sh # skips Zynera phases B–G automatically
Results (2026-08-28 morning — Accept + Act OK)¶
| Script | Result |
|---|---|
stack-probe.sh --zynera-optional |
PASS — Zynera skipped |
e2e-events-matrix.sh |
PASS — A farm 10/10 Accept + 5/5 Act · B firewater 5 · C remote-edge 5 · D fleet 6 |
e2e-stack.sh |
PASS — probe + matrix |
e2e-zynera-stack.sh |
PASS — matrix only; Zynera path skipped |
Re-run (2026-08-28 → 2026-08-29 — Act wiring fix + lab 175)¶
After Farm Act failed with tls: certificate signed by unknown authority, we redeployed Relay (RELAY_TLS_INSECURE=1 honored on the action HTTP client) and set action targets. Final verification 2026-08-29:
| Lab | Script | Result |
|---|---|---|
212 (:8443) |
e2e-stack.sh |
PASS — farm 10/10 Accept + 5/5 Act · FW 5 · remote-edge 6 · fleet 6 |
175 (:18080) |
e2e-stack.sh |
PASS — same matrix after Act TLS patch + JWT sync |
| both | stack-probe.sh --zynera-optional |
PASS |
| 212 | e2e-direct-stack.sh |
PASS |
Wire Act on a fresh lab:
# From a checkout of zyvorai/relay (commit 8bef494+), or copy action TLS patch from a working host:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o /tmp/relay-linux ./cmd/relay
RELAY_BIN=/tmp/relay-linux ./scripts/lab-wire-relay-act.sh <HOST>
# then sync pubsub JWT + gateway deploy + e2e-stack.sh
If ZYNERA_BASE is set but Zynera is down, e2e-zynera-stack.sh still PASS after the event matrix (Zynera phases skipped with warning).
Direct Relay (tested 2026-08-28)¶
relay-edge → Relay without relay-pubsub (GATEWAY_BASE_URL empty on edge). Accept-only gate — Act requires pubsub Action Gateway.
How to run¶
cp config/lab-direct.env.example config/lab-direct.env
# BASE, EDGE, RELAY_AUTH_TOKEN
RELAY_EDGE_DIRECT=1 RELAY_AUTH_TOKEN=<jwt> ./scripts/deploy-remote.sh <HOST> [USER]
set -a && source config/lab-direct.env && set +a
./scripts/stack-probe.sh --direct
./scripts/e2e-direct-relay.sh
# or: ./scripts/e2e-direct-stack.sh
Restore gateway mode after test:
RELAY_AUTH_TOKEN=<jwt> ./scripts/deploy-remote.sh <HOST> [USER]
Scenario coverage¶
| Section | Cases | Pass criteria |
|---|---|---|
| 0. Guard | open season | publish.path == "relay" |
| A. Farm | 10 types via season API | Accept in Relay; path relay |
| B. Firewater | 13 scenarios | New Relay event per type |
| C. Remote edge | 6 scenarios (+ 2 readings-only skip) | New Relay event per type |
| D. Fleet | 6 scenarios (+ 2 readings-only skip) | New Relay event per type |
Results (2026-08-29, lab)¶
| Script | Result |
|---|---|
stack-probe.sh --direct |
PASS |
e2e-direct-relay.sh |
PASS — farm 10 · firewater 13 · remote-edge 6 · fleet 6 |
e2e-direct-stack.sh |
PASS when Relay stays up for the full run |
Transient: Relay briefly unreachable mid-matrix once (curl: Couldn't connect on :8443); re-run after recovery was full PASS. Always restore gateway mode after direct tests (deploy-remote.sh without RELAY_EDGE_DIRECT).
With Zynera (tested 2026-08-28)¶
stack-probe.sh¶
ok relay-edge $EDGE/healthz
ok relay-pubsub $GATEWAY/healthz
ok Relay $BASE/healthz
ok Zynera $ZYNERA_BASE/api/zeus/decisions
PASS: stack probe
e2e-events-matrix.sh¶
| Section | Result |
|---|---|
| A. Farm catalog | 10/10 Accept; 5/5 Act via rpg_* |
| B. Firewater / edge | 5/5 events in Relay |
| C. Remote edge | 6/6 events in Relay (incl. drone_patrol) |
| D. Fleet | 6/6 events in Relay |
e2e-zynera-stack.sh¶
| Phase | Result |
|---|---|
| A. Event matrix | PASS |
| B. Policy → forge backend | PASS (restored on exit) |
| C. relay-edge publish | PASS |
| D. ack → awaiting_decision | PASS |
| E–F. Zynera Approved → act | PASS (verifying / action_pending) |
| G. Zynera Rejected → failed | PASS |
Exit code: 0
Issues hit during verification¶
| Symptom | Fix |
|---|---|
Gateway publish 500 / 401 |
Re-sync RELAY_AUTH_TOKEN on pubsub after Relay restart; restart relay-pubsub |
Farm Act failed — TLS unknown authority |
Relay action client must skip verify when RELAY_TLS_INSECURE=1 (relay#8bef494); older trees may lack the patch — sync from a working lab or rebuild. Helper: lab-wire-relay-act.sh |
Farm Act failed — mock / wrong host |
Point all controllers at the reachable pubsub /v1/actions (not mock://; not laptop 127.0.0.1 if pubsub is remote) |
| e2e hits wrong machine | BASE/GATEWAY/EDGE must be remote host URLs when testing from a laptop — 127.0.0.1 is your laptop |
| Action circuit breaker open | Reset by fixing TLS + restarting Relay; publish fresh catalog events |
publish.path=gateway when expecting direct |
Set GATEWAY_BASE_URL= explicitly (unset ≠ direct); use RELAY_EDGE_DIRECT=1 deploy |
| Edge left in direct mode after direct tests | Redeploy without RELAY_EDGE_DIRECT before gateway e2e |
Relay blip mid long matrix (:8443 / :18080 down) |
Wait for healthz; re-run matrix |
| Wrong Relay port on 175 | Use :18080 (config/lab-stack-175.env), not :8443 |
relay-edge unreachable on :18086 |
./scripts/deploy-remote.sh <HOST> |
| Zynera ack curl SSL error | Scripts use RELAY_TLS_INSECURE=1 / curl -k for Relay HTTPS |
| Policy patch JSON error | Fetch policy via GET /v1/policies list (no get-by-id route) |
Reproduce on any lab¶
# 0) If Farm Act fails with TLS unknown authority / mock targets:
# RELAY_BIN=/path/to/linux-amd64-relay ./scripts/lab-wire-relay-act.sh <HOST>
# then re-sync pubsub RELAY_AUTH_TOKEN and restart relay-pubsub
export BASE=https://<relay-host>:8443 # or :18080 on lab 175
export GATEWAY=https://<gateway-host>:8081
export EDGE=https://<edge-host>:18086
export ZYNERA_BASE=http://<zynera-host>:30631 # optional
export ZYNERA_API_KEY=<secret> # optional
export RELAY_AUTH_TOKEN=<jwt>
export RELAY_TLS_INSECURE=1
./scripts/stack-probe.sh --zynera-optional
./scripts/e2e-stack.sh
# Lab 175 shortcut:
# set -a && source config/lab-stack-175.env && set +a && ./scripts/e2e-stack.sh
# Direct (no pubsub) — then restore gateway deploy without RELAY_EDGE_DIRECT
RELAY_EDGE_DIRECT=1 RELAY_AUTH_TOKEN=<jwt> ./scripts/deploy-remote.sh <HOST>
./scripts/e2e-direct-stack.sh
RELAY_AUTH_TOKEN=<jwt> ./scripts/deploy-remote.sh <HOST>
Unit / smoke tests (no Relay required for smoke; unit always). Also run in GitHub Actions CI:
go test ./...
# or: make vet test
EDGE=http://127.0.0.1:18086 make smoke-all
# equivalent:
EDGE=http://127.0.0.1:18086 ./scripts/smoke.sh
EDGE=http://127.0.0.1:18086 ./scripts/smoke-firewater.sh
EDGE=http://127.0.0.1:18086 ./scripts/smoke-remote-edge.sh
EDGE=http://127.0.0.1:18086 ./scripts/smoke-fleet.sh
Related commits¶
| Repo | Commit | Change |
|---|---|---|
| relay-edge | 0dfc219 | CI gaps closed: fleet smoke, tag-gated release, GHCR, /readyz//version, systemd |
| relay-edge | 7588d7f | Initial GitHub Actions CI + first release v0.1.0 |
| relay-edge | b206e8f | lab-wire-relay-act.sh; document full PASS after Act TLS fix |
| relay-edge | 1247041 | e2e-direct-stack.sh, drone_patrol in gateway matrix, gateway env tests |
| relay-edge | 70e4742 | Direct Relay e2e script, expanded scenarios, GATEWAY_BASE_URL= fix |
| relay | 8bef494 | RELAY_TLS_INSECURE for outbound action gateway |
Related documentation¶
| Topic | Document |
|---|---|
| Simulate all one-liner | INTEGRATION.md — Simulate all |
| Direct Relay path | RELAY.md — Try direct mode |
| Event type tables | EVENT_MATRIX.md |
| Configuration / empty gateway | CONFIGURATION.md |
| Lab ports & deploy | DEPLOYMENT.md |
| CI / releases / GHCR | DEPLOYMENT.md — CI and releases |
| Browser summary | /ui/docs.html |