Skip to content

ADR-0092: RELAY.Art — OOBI ↔ customer-MGMT bridge

  • Status: Accepted (2026-05-24) — RELAY.Art Phase A wave (RELAY-1 already in main + RELAY-2 + RELAY-3 + this PR)
  • Date: 2026-05-24
  • Driver: discuss_module_relay_art_2026_05_10 locked RELAY.Art as the single canonical bridge between the immutable OOBI VXLAN fabric and the customer's existing MGMT VLANs. Every telemetry datagram + control command crossing the boundary goes through RELAY
  • Related: PRs RELAY-1 (already merged, foundation manifest + observability bridge MVP) · #1065 (RELAY-2 ingress) · #1066 (RELAY-3 egress + HA) · this PR (ADR + docs) · ADR-0036 (HyperBridge.Art — sibling DATA-plane bridge) · ADR-0086/0089 (VALIDATOR.Art adapter pattern this wave reuses)

Context

In production, the customer's DUT + switch MGMT interfaces already have IPs / VLANs / gateways / existing tooling. We cannot and must not assume they will join our OOBI fabric. RELAY.Art lives on customer-MGMT VLANs as a guest with operator-assigned IP + bridges all telemetry / control traffic to/from the OOBI fabric where SYSLOG / FLOW / SNMP / CLI / API-INFRA MÓDULOs live.

Hard rule (no exceptions): RELAY.Art toca MGMT do customer apenas. Data-plane jamais. Routers (BGP/OSPF/SDWAN) NEVER peer with NGFW MGMT interfaces; peering is always over routed data-plane interfaces (/30 or /126 transits).

Decision

Ship MÓDULO RELAY.Art as a single Go HTTP/UDP service on OOBI slot .240 primary + .241 HA standby, with 2 axes of bridging:

Axis Protocols Source / Sink
Ingress (customer → OOBI) NetFlow v5/v9 (UDP 2055) · IPFIX (UDP 4739) · sFlow (UDP 6343) · Syslog (UDP/TCP 514) · Syslog-TLS (TCP 6514) · SNMP traps (UDP 162) FLOW.Art / SYSLOG.Art / SNMP.Art
Egress (OOBI → customer) SSH v2 · SNMP polls v2c/v3 · HTTPS REST · NETCONF · RESTCONF CLI.Art / SNMP.Art / API INFRA.Art

Wave layout (Phase A)

Sub-PR Scope Status
RELAY-1 Foundation: ServiceAccount + RBAC + ConfigMap + Deployment .240 + NetworkPolicy strict + pkg/relay-art/ observability bridge MVP (internal/forwarder + internal/audit + internal/server + internal/types) ✅ in main
RELAY-2 internal/ingress/ Collector + 5 protocol enum + Forwarder/Disabled/Fake + Datagram + StripSourceIP + Stats + token-bucket rate limit + FIFO backpressure #1065
RELAY-3 internal/egress/ Orchestrator + 5 protocol enum + Mode read-only/write + UnlockWindow + RateLimited per-target + FileCredentialSource + HA standby Deployment slot .241 with podAntiAffinity #1066
RELAY-4 This ADR + docs/modules/relay-art.{md,pt-BR.md,es.md} expansion + project_relay_art_complete_2026_05_24.md recovery memo

Locked decisions (Phase A)

# Decision Rationale
1 HA semantics = active/standby on K8s Service load-balance + readiness drop Memo §"HA failover semantics" leaned toward active/standby (simpler, customer ACL alignment). K8s native impl: two Deployments behind one Service; readiness probe drops failing pod from endpoints automatically. Floating-IP semantics deferred to Phase B-late Multus whereabouts
2 Read-only Mode default Memo §"DOM mode interactions" — production DOM forces read-only; only explicit UnlockWindow accepts writes. RELAY-3's Mode defaults to read-only regardless of DOM (defense in depth)
3 PII strip enforcement is a Config knob, not always-on Memo §"PII strip enforcement" makes it mandatory ONLY for PURE Discovery Hub use case. RELAY-2 ships it as opt-in (Config.StripSourceIP); Phase B wires the policy that forces it when the dataset destination is the URL extraction pipeline
4 Per-target rate limit via per-target token bucket Memo §"Rate limits hard-enforced" specifies per-target SSH session count + per-target SNMP PPS. RELAY-3's RateLimited wraps any Orchestrator with map[Target.Name]→bucket; independent per-target buckets prevent one chatty target from starving others
5 Credentials are file-based for Phase A FileCredentialSource resolves file:// refs with optional BasePath escape protection. Phase B wires HC Vault + cert-manager adapters using the same CredentialSource interface. Memo's "vault-backed" requirement keeps the interface contract; the impl is the Phase B promotion
6 Real protocol parsing deferred to Phase B RELAY-2 opens the UDP socket + forwards raw bytes verbatim. NetFlow v9 templates, IPFIX information elements, sFlow XDR — all deferred until the operator's bench has real customer devices configured. The wire contract (Datagram {Protocol, Body, SourceIP, ReceivedAt}) is what locks Phase B-real implementations
7 NO data-plane traffic on RELAY ever Memo §"Hard rule (no exceptions)". RELAY's NetworkPolicy explicitly DENY-LISTS data-plane VLANs + the ingress Collector only opens MGMT-protocol UDP ports. Routers (BGP/OSPF/SDWAN) peer over routed data-plane interfaces, never via NGFW MGMT

Bridge interface contracts (locked)

// RELAY-2 ingress side
type Collector interface { Run(ctx context.Context) error }       // pkg/relay-art/internal/ingress
type Forwarder interface { Forward(ctx, Datagram) error }
type Datagram struct { Protocol; Body []byte; SourceIP net.IP; ReceivedAt time.Time }

// RELAY-3 egress side
type Orchestrator    interface { Dispatch(ctx, Command) (Response, error) }
type CredentialSource interface { Resolve(ref string) ([]byte, error) }
type Command struct { Target; Protocol; Verb string; Body []byte; IsWrite bool; OperatorID string }

// Phase A impls: Disabled / Fake / file-based real
// Phase B-real swaps in real protocol clients + Vault-backed credentials

Consequences

Positive

  • Closes the entire RELAY.Art Phase A scope from the strategic memo (foundation + ingress + egress + HA + governance)
  • Adapter pattern (Disabled/Fake/real) reuses the same wins from Phase B foundation wave — /forward-to-operator-network NEVER fails because a bridge is unavailable; downstream MÓDULOs degrade gracefully
  • HA standby cuts MTTR for a node failure from ~minutes (Pod restart elsewhere) to seconds (Service drops the failing endpoint)
  • Strong separation of vault (memo §"Vault separation from GATEWAY") — RELAY-3 CredentialSource is per-RELAY-pod; a breach of one pod doesn't expose GATEWAY operator creds

Negative / deferred

  • Real protocol parsing (NetFlow v9 templates, IPFIX IEs, sFlow XDR, SNMP trap decoder) — Phase B
  • HC Vault / cert-manager CredentialSource adapters — Phase B (file-based works today)
  • Multus whereabouts floating-IP for true VRRP-style HA — Phase B-late
  • K-anonymity threshold (≥10 hits) for PII strip — Phase B (depends on URL extraction pipeline)
  • Per-vendor session multiplexer (FMC / Panorama long-lived sessions) — Phase B
  • Credential rotation auto-mediated by vendor APIs — Phase B-late

Alternatives considered

  • Single Orchestrator covering both ingress + egress — rejected; ingress is per-datagram async fire-and-forget, egress is per-Command sync request/response. Two interfaces is the right granularity
  • Active/active HA with anycast — rejected per memo Q1 lean; customer ACL alignment + audit clarity favour active/standby
  • gRPC streaming for ingress — rejected; UDP is what the customer devices speak; gRPC layer would just be a wrapper without semantic value
  • Embed RELAY in GATEWAY.Art — rejected per memo §"Why separate from GATEWAY.Art": different vault scope, different operational profile, different blast radius

Patent posture

Memo §"Patent claim potential" reserved claim #12 in the DOM/OOBI/GATEWAY family for: "Bidirectional MGMT-plane bridge between immutable overlay fabric and operator's existing prod-MGMT network with hard separation of data-plane vs MGMT-plane traffic, vault-isolated per-target credential scoping, and ML-cortex-integrated auto-discovery."

This wave delivers the implementation; the provisional draft lands in a follow-up PR when the patent push catches up.

Compliance

Per CLAUDE.md: operator-facing config lives in CPOS (Dashboard-editable); operator never types kubectl against relay-art pods.

Per ZTP-prem posture: both relay-art + relay-art-standby pods run readOnlyRootFilesystem: true, runAsNonRoot: true, runAsUser: 2096, drop: [ALL] capabilities. NetworkPolicy default-deny on egress.

Per ADR-0044 (OOBI Intrusion Detection): RELAY's customer-MGMT-facing macvlan IPs are operator-declared via CPOS; the OOBI overlay side stays on .240/.241 as canonical.

Per ADR-0086 (VALIDATOR.Art) adapter pattern: RELAY.Art uses the same Disabled/Fake/real triple — operator opts in to real bridges via Config mode toggles; silent fallback on bridge unavailable.

References

  • discuss_module_relay_art_2026_05_10 — strategic memo (locked HA pair / 8 ingress protocols / 5 egress protocols / vault separation / DOM mode interactions / patent claim #12 reservation)
  • discuss_oobi_immutable_gateway_art_2026_05_10 — OOBI fabric + GATEWAY.Art sibling
  • ADR-0036 (HyperBridge.Art) — DATA-plane sibling bridge (this is the MGMT-plane peer)
  • ADR-0086 (VALIDATOR.Art Phase A) — adapter pattern this wave reuses
  • ADR-0089 (Phase B foundation bridges) — same pattern for CA / k3s / WG / GitOps
  • PRs: RELAY-1 (foundation, already in main) · #1065 (RELAY-2) · #1066 (RELAY-3) · this PR (RELAY-4)
  • Patent reservation: claim #12 in DOM/OOBI/GATEWAY/RELAY family