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_10locked 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-networkNEVER 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
CredentialSourceis 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
CredentialSourceadapters — 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