Skip to content

feat(orb): add a typed config-push write path to the relay pipeline (migration + endpoint) #7522

Description

@JSONbored

Context

The self-host relay pipeline (orb_relay_pending table, added in migrations/0079_orb_relay_pull.sql)
is currently GitHub-webhook-only: every row has a free-text event_name and a raw_body that the
pull-drain loop (src/selfhost/monitored-work.ts's drainOrbRelayWithMonitor) always hands to
enqueueWebhookByEnv (src/github/webhook.ts), which unconditionally does
JSON.parse(rawBody) as GitHubWebhookPayload.

Per #4902's own design comment (2026-07-18), this pipeline is the correct place to add an
Orb-operational config-push channel — it already has real per-installation auth (a hashed
enrollment secret bound to an installation_id, orb_enrollments.secret_hash), and self-host
containers in pull mode are already polling it. This issue adds the write side: a new discriminator
column and an internal, operator-gated endpoint that enqueues a typed payload. It does not
change dispatch behavior for any existing row — see the companion issue for the read/dispatch side.

Scope boundary, stated explicitly per this repo's own "gate only enforces what's written" rule:
this issue is restricted to Orb's own operational state (enrollment lifecycle notices, capability
announcements, deprecation notices) — it must NOT auto-apply anything that overrides an operator's
own .loopover.yml/DB settings. It targets an explicit list of installation_ids only — no
percentage/canary rollout selector (there is no existing rollout-percentage primitive in this
codebase to build one on top of; inventing one here is out of scope).

Requirements

  1. Add migration migrations/0168_orb_relay_pending_kind.sql:
    ALTER TABLE orb_relay_pending ADD COLUMN kind TEXT NOT NULL DEFAULT 'github_webhook'; — the
    default preserves identical behavior for every existing and future GitHub-webhook row.
  2. Define a typed config_push payload shape: { pushId: string, message: string, capability?: string, deprecatesAt?: string }.
  3. Add a new internal endpoint (e.g. POST /v1/internal/fleet/config-push) gated via
    requireAppRole(c, ["operator"]), mirroring the existing kill-switch endpoint's auth shape
    (src/api/routes.ts, POST /v1/app/kill-switch, ~L1874-1897) and its zod-validated-body pattern
    (killSwitchUpdateSchema).
  4. The endpoint accepts an explicit array of target installation_ids and inserts one
    orb_relay_pending row per target with kind = 'config_push' and the typed payload as
    raw_body (JSON-stringified).
  5. Do not modify enqueueWebhookByEnv, drainOrbRelayWithMonitor, or any other read/dispatch code
    in this issue — this is the write side only.

Deliverables

  • Migration adding the kind column (default 'github_webhook')
  • Typed config_push payload schema (zod), validated the same way killSwitchUpdateSchema is
  • New operator-gated endpoint inserting one orb_relay_pending row per targeted installation_id
  • Unit/integration tests: successful multi-target enqueue; non-operator caller rejected (403);
    malformed payload rejected (400); a pre-existing row (no kind set explicitly) still defaults
    to 'github_webhook' and is unaffected

Test Coverage Requirements

This touches src/api/routes.ts, src/db/* (or wherever the migration runner lives), and a new
schema module — all under src/**, so the repo's 99%+ Codecov patch gate (branch-counted) applies
to every changed line/branch. Add tests under test/integration/api.test.ts (endpoint
auth/validation/insert paths) and a dedicated unit test file for the payload schema.

Expected Outcome

An operator can push a typed, addressed config/capability-notice message to an explicit list of
installations through a new internal endpoint. The message lands in the existing
orb_relay_pending queue, indistinguishable in storage from a GitHub-webhook row except for its new
kind column — no existing webhook delivery behavior changes.

Links & Resources

  • Parent: Add a config-push/rollout-control write path to fleet analytics #4902 (design comment, 2026-07-18) — this issue implements piece 1 of that comment's
    3-piece breakdown
  • migrations/0079_orb_relay_pull.sql — the table this extends
  • src/api/routes.ts (~L1863-1897) — the kill-switch endpoint's auth/validation pattern to mirror
  • src/orb/relay.ts, src/orb/broker-client.ts's drainOrbRelay — existing relay read-side, for
    the response shape (deliveryId/eventName/rawBody) this write side must stay compatible with
  • Companion issue (dispatch side) — the read side this write path feeds

Metadata

Metadata

Assignees

Labels

gittensor:featureGittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.help wantedExtra attention is neededmaintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions