← Back to Hub

Spoke ↔ Hub Control Contract

Canonical spec for the two receiver endpoints every Resonance spoke MUST expose so the hub can push updates and validate health. Combined with the existing /api/public/rop/* pull endpoints this gives full push + pull fallback authority: config, tier gates, kill-switches, and ROP optimizations auto-apply without manual per-spoke work.

Required endpoints

Both endpoints live under /api/public/* (auth-bypassed) and are secured with the same per-app HMAC scheme used by ROP. Reject any request that fails verification.

POST /api/public/hub-control/apply

  1. Verify HMAC (see below).
  2. Immediately pull /api/public/hub-control/pull-config from the hub.
  3. Apply the returned feature_flags, tunables, tier_catalog, and suggestions to local runtime state (in-memory + cache).
  4. Return 200 { ok: true, applied_at }.

Payload (JSON): { kind: "push_nudge" | "apply_config", hub_app_id, reason? }.

POST /api/public/hub-control/validate

Health probe. Return 200 { ok: true, version, checks: { db, config_age_s } } within 5 s. Any non-200 is treated as unhealthy on /admin/spoke-health.

HMAC scheme

Headers on every hub → spoke request:

The secret is the value the hub owner shared with you at onboarding (stored as HUB_SIGNING_SECRET in the spoke). It equals hub_apps.signing_key_hash in the hub DB.

Reference implementation

Paste-ready TypeScript reference lives in the repo at docs/spoke-hub-control-contract.md.

Fallback

Every spoke should also poll /api/public/hub-control/pull-config every 60 s in case a push is missed.