Suppose a network team moves authoritative IPAM data from NetBox to Nautobot.
Deployments still need the same thing they needed before the migration: trusted
prefix and inventory data before infrastructure changes begin. Yet every
operation that says requires_authority: netbox now needs editing.
The migration should change one binding. Instead, a product choice has spread through unrelated consumers.
The original article appeared on HackerNoon. This version includes the public HybridOps resolver, receipt, and regression boundary.
Separate the requirement from the product
A product-coupled contract can look harmless:
steps:
- id: allocate_platform_address
module_ref: platform/onprem/platform-vm
contracts:
addressing_mode: ipam
requires_authority: netbox
The value netbox is carrying several meanings at once: product, instance,
data capability, readiness expectation, and possibly authentication method.
The operation’s actual requirement remains implicit.
A stronger contract gives the authority a logical name and states the capability required from it:
authorities:
primary_ipam:
capability: inventory_ipam
provider: nautobot
config:
endpoint_env: NAUTOBOT_API_URL
token_env: NAUTOBOT_API_TOKEN
api_version: "2.4"
instance_id: shared-nautobot
steps:
- id: allocate_platform_address
module_ref: platform/onprem/platform-vm
contracts:
addressing_mode: ipam
requires_authority:
ref: primary_ipam
capability: inventory_ipam
The operation now asks for inventory_ipam from primary_ipam. The
environment decides which provider supplies it.
If another environment uses NetBox, the operation does not change. Only the binding does:
authorities:
primary_ipam:
capability: inventory_ipam
provider: netbox
This is a narrow substitution boundary. It does not claim that NetBox and Nautobot are interchangeable products. It says that either configured adapter may satisfy one declared capability after proving that its own instance is ready.
Provider-neutral does not mean provider-blind
Moving the product name out of the operation does not remove product-specific validation. It puts that validation behind the correct boundary.
Before admission, the resolver must establish that:
- the logical authority binding exists;
- its capability matches the operation’s requirement;
- the selected provider supports that capability;
- the provider configuration is valid; and
- the provider can prove the authority is ready now.
The HybridOps resolver follows that order. Reduced to its central path, it looks like this:
declaration = declarations.get(requirement.logical_ref)
if declaration is None:
raise ValueError("missing authority binding")
if declaration.capability != requirement.capability:
raise ValueError("authority capability mismatch")
provider = registry.resolve(declaration.provider)
if requirement.capability not in provider.capabilities:
raise ValueError("provider capability mismatch")
provider.validate_configuration(declaration, context)
evaluation = provider.evaluate(declaration, context)
if not evaluation.healthy:
raise ValueError("authority is not ready")
return AuthorityReceipt(
logical_ref=requirement.logical_ref,
capability=requirement.capability,
provider=provider.name,
state=evaluation.state,
result=evaluation.result,
)
The generic path knows about logical references, capabilities, providers, and evaluations. It does not contain a branch for each source-of-truth product.
The full implementation is in the HybridOps authority resolver.
Readiness remains product-specific
NetBox and Nautobot prove readiness through their own adapters.
A NetBox-backed environment may combine retained module state with an authenticated API probe. If the operation requires bounded freshness, an absent or stale timestamp must block admission rather than rely on the memory of an earlier successful run.
A Nautobot adapter can first check service health and then make an authenticated
read against the IPAM API. The second request matters because service health
and authorized access to the required data are different conditions. A 200
response containing an HTML login page must not satisfy an API contract that
expects paginated JSON.
Fail-closed behavior matters at this boundary. Missing bindings, unknown providers, capability mismatches, rejected credentials, stale state, unreachable endpoints, invalid response shapes, and unhealthy instances should all stop the dependent operation before mutation.
Every provider must prove readiness through checks appropriate to that provider.
Record which authority admitted the run
Successful admission should produce a secret-free receipt:
{
"logical_authority": "primary_ipam",
"capability": "inventory_ipam",
"provider": "nautobot",
"verified_state": "ready",
"result": "admitted",
"instance": "shared-nautobot",
"revision": "2.4",
"freshness": {
"source": "live",
"checked_at": "<timestamp>"
}
}
The receipt answers a practical review question: which authority allowed this operation to proceed?
It identifies the logical authority, capability, provider, instance, revision, and verification result without copying a credential into the run record. Provider neutrality should remove consumer coupling, not erase attribution.
Current HybridOps Core can also observe an authority before and after dispatch. That interval record distinguishes a stable authority from one that drifted or became unverifiable while the operation was running. Admission answers whether the operation may start. The completion observation answers whether its authority remained valid through the governed interval.
Compatibility must be explicit
Existing contracts do not disappear when a cleaner model is introduced.
Older HybridOps blueprints can contain a product-named declaration:
policy:
ipam_authority: netbox
contracts:
addressing_mode: ipam
requires_authority: netbox
Breaking every existing blueprint would force an unrelated migration. Quietly assigning new semantics to the old fields would make compatibility impossible to reason about.
The runtime instead translates the older form through an explicit compatibility path. Existing state resolution and live checks remain covered by regression tests, while new blueprints use the logical authority form.
Test the boundary, not the interface name
An adapter interface can still hide if provider == "netbox" in generic
execution. A useful regression suite must prove the architectural boundary.
HybridOps tests that:
- the same logical
inventory_ipamrequirement resolves through NetBox and Nautobot; - a registered test provider works without product code in the executor;
- missing bindings, unknown providers, and capability mismatches fail closed;
- stale or unhealthy authority state blocks execution;
- inline tokens are rejected from declarative configuration;
- API responses must have the expected shape; and
- canonical and compatibility forms remain deliberate.
The implementation, provider adapters, and tests are public in the authority package.
This develops the source-of-truth model described in Using NetBox as the Authority for a Hybrid Platform Foundation. The earlier field note establishes an authority. This contract keeps consumers stable when the product behind that authority changes.
The product serving as source of truth is an implementation choice. The authority an operation needs is part of its contract. Keeping those decisions separate confines a provider migration to the place where the provider is actually selected.