Files
harmony/ROADMAP/11-named-config-instances.md
Jean-Gabriel Gill-Couture cb2a650d8b feat(opnsense): add FirewallPairTopology for HA firewall pair management
Introduces a higher-order topology that wraps two OPNSenseFirewall
instances (primary + backup) and orchestrates score application across
both. CARP VIPs get differentiated advskew values (primary=0,
backup=configurable) while all other scores apply identically to both
firewalls.

Includes CarpVipScore, DhcpServer delegation, pair Score impls for all
existing OPNsense scores, and opnsense_from_config() factory method.

Also adds ROADMAP entries for generic firewall trait (10), delegation
macro, integration tests, and named config instances (11).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 05:12:03 -04:00

3.7 KiB

Phase 11: Named Config Instances & Cross-Namespace Access

Goal

Allow multiple instances of the same config type within a single namespace, identified by name. Also allow explicit namespace specification when retrieving config items, enabling cross-deployment orchestration.

Context

The current harmony_config system identifies config items by type only (T::KEY from #[derive(Config)]). This works for singletons but breaks when you need multiple instances of the same type:

  • Firewall pair: primary and backup need separate OPNSenseApiCredentials (different API keys for different devices)
  • Worker nodes: each BMC has its own IpmiCredentials with different username/password
  • Firewall administrators: multiple OPNSenseApiCredentials with different permission levels
  • Multi-tenant: customer firewalls vs. NationTech infrastructure firewalls need separate credential sets

Using separate namespaces per device is not the answer — a firewall pair belongs to a single deployment, and forcing namespace switches for each device in a pair adds unnecessary friction.

Cross-namespace access is a separate but related need: the NT firewall pair and C1 customer firewall pair live in separate namespaces (the customer manages their own firewall), but NationTech needs read access to the C1 namespace for BINAT coordination.

Tasks

11.1 Named config instances within a namespace

Priority: HIGH Status: Not started

Extend the Config trait and ConfigManager to support an optional instance name:

// Current (singleton): gets "OPNSenseApiCredentials" from the active namespace
let creds = ConfigManager::get::<OPNSenseApiCredentials>().await?;

// New (named): gets "OPNSenseApiCredentials/fw-primary" from the active namespace
let primary_creds = ConfigManager::get_named::<OPNSenseApiCredentials>("fw-primary").await?;
let backup_creds = ConfigManager::get_named::<OPNSenseApiCredentials>("fw-backup").await?;

Storage key becomes {T::KEY}/{instance_name} (or similar). The unnamed get() remains unchanged for backward compatibility.

This needs to work across all config sources:

  • EnvSource: HARMONY_CONFIG_{KEY}_{NAME} (e.g., HARMONY_CONFIG_OPNSENSE_API_CREDENTIALS_FW_PRIMARY)
  • SqliteSource: composite key {key}/{name}
  • StoreSource (OpenBao): path {namespace}/{key}/{name}
  • PromptSource: prompt includes the instance name for clarity

11.2 Cross-namespace config access

Priority: MEDIUM Status: Not started

Allow specifying an explicit namespace when retrieving a config item:

// Get from the active namespace (current behavior)
let nt_creds = ConfigManager::get::<OPNSenseApiCredentials>().await?;

// Get from a specific namespace
let c1_creds = ConfigManager::get_from_namespace::<OPNSenseApiCredentials>("c1").await?;

This enables orchestration across deployments: the NT deployment can read C1's firewall credentials for BINAT coordination without switching the global namespace.

For the StoreSource (OpenBao), this maps to reading from a different KV path prefix. For SqliteSource, it maps to a different database file or a namespace column. For EnvSource, it could use a different prefix (HARMONY_CONFIG_C1_{KEY}).

11.3 Update FirewallPairTopology to use named configs

Priority: MEDIUM Status: Blocked by 11.1

Once named config instances are available, update FirewallPairTopology::opnsense_from_config() to use them:

let primary_creds = ConfigManager::get_named::<OPNSenseApiCredentials>("fw-primary").await?;
let backup_creds = ConfigManager::get_named::<OPNSenseApiCredentials>("fw-backup").await?;

This removes the current limitation of shared credentials between primary and backup.