From d98d6626bd367e41dbcec747f763ca3f62b19aa0 Mon Sep 17 00:00:00 2001 From: aidankhogg Date: Tue, 30 Jun 2026 16:55:18 +0100 Subject: [PATCH] Keep authority posture optional in specs --- netengine/spec/__init__.py | 3 +++ netengine/spec/authority.py | 30 +++++++++++++++++++++--------- netengine/spec/models.py | 22 ++++++++++++++++++++++ tests/test_authority_spec.py | 29 +++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/netengine/spec/__init__.py b/netengine/spec/__init__.py index 62a0a65..8c80a9f 100644 --- a/netengine/spec/__init__.py +++ b/netengine/spec/__init__.py @@ -1,5 +1,7 @@ """Spec parsing and models for NetEngine declarative specifications.""" +from netengine.spec.models import AuthorityPosture + from netengine.spec.authority import ( Authority, AuthorityKind, @@ -15,6 +17,7 @@ ) __all__ = [ + "AuthorityPosture", "Authority", "AuthorityKind", "AuthorityScope", diff --git a/netengine/spec/authority.py b/netengine/spec/authority.py index 5e06824..e96d4be 100644 --- a/netengine/spec/authority.py +++ b/netengine/spec/authority.py @@ -375,20 +375,32 @@ def world_manifest_from_spec(spec: "NetEngineSpec") -> WorldManifest: authorities = default_authorities_for_spec(spec) + posture = spec.authority + return WorldManifest( world_id=spec.metadata.name, world_name=spec.metadata.name, lifecycle=spec.metadata.lifecycle, - authority_model="default", + authority_model=posture.authority_model if posture else "default", authorities=authorities, - dns_root_authority="root-naming", - ca_trust_authority="trust-root", - platform_identity_issuer="platform-identity", - inworld_identity_issuer="inworld-identity", - world_registry_authority="world-root", - domain_registry_authority="domain-registry", - numbering_authority="numbering", - transit_boundary_authority="transit-boundary", + dns_root_authority=(posture.dns_root_authority if posture else None) or "root-naming", + ca_trust_authority=(posture.ca_trust_authority if posture else None) or "trust-root", + platform_identity_issuer=( + (posture.platform_identity_issuer if posture else None) or "platform-identity" + ), + inworld_identity_issuer=( + (posture.inworld_identity_issuer if posture else None) or "inworld-identity" + ), + world_registry_authority=( + (posture.world_registry_authority if posture else None) or "world-root" + ), + domain_registry_authority=( + (posture.domain_registry_authority if posture else None) or "domain-registry" + ), + numbering_authority=(posture.numbering_authority if posture else None) or "numbering", + transit_boundary_authority=( + (posture.transit_boundary_authority if posture else None) or "transit-boundary" + ), real_internet_posture=spec.gateway_portal.real_internet.mode, cross_world_posture=spec.gateway_portal.cross_world.mode, trust_bundles=_trust_bundles_from_spec(spec), diff --git a/netengine/spec/models.py b/netengine/spec/models.py index 66dfc40..7b2db00 100644 --- a/netengine/spec/models.py +++ b/netengine/spec/models.py @@ -663,6 +663,24 @@ class OperatorConfig(SpecModel): auth: OperatorAuthConfig = Field(default_factory=OperatorAuthConfig) +class AuthorityPosture(SpecModel): + """Optional top-level authority posture overrides. + + This is intentionally not required by MVP specs. Existing examples derive + their authority manifest from established top-level sections via + ``default_authorities_for_spec`` and ``world_manifest_from_spec``. + """ + + authority_model: str = Field(default="default", description="Authority posture profile") + dns_root_authority: str | None = Field(default=None) + ca_trust_authority: str | None = Field(default=None) + platform_identity_issuer: str | None = Field(default=None) + inworld_identity_issuer: str | None = Field(default=None) + world_registry_authority: str | None = Field(default=None) + domain_registry_authority: str | None = Field(default=None) + numbering_authority: str | None = Field(default=None) + transit_boundary_authority: str | None = Field(default=None) + # ───────────────────────────────────────────── # ROOT: NETENGINESPEC # ───────────────────────────────────────────── @@ -688,3 +706,7 @@ class NetEngineSpec(SpecModel): org_apps: OrgAppsPhase = Field(..., description="Phase 9 — org apps") gateway_portal: GatewayPortal = Field(..., description="Gateway boundary") operator: OperatorConfig = Field(..., description="Operator API") + authority: AuthorityPosture | None = Field( + default=None, + description="Optional authority posture overrides; omitted specs derive defaults", + ) diff --git a/tests/test_authority_spec.py b/tests/test_authority_spec.py index 3d7326e..7edea5e 100644 --- a/tests/test_authority_spec.py +++ b/tests/test_authority_spec.py @@ -372,3 +372,32 @@ def test_world_manifest_from_spec_derives_cross_world_trust_bundles(minimal_spec assert manifest.trust_bundles[0].mode == GatewayCrossWorldMode.FEDERATED assert manifest.trust_bundles[0].dns_suffixes == ["world-b.internal"] assert manifest.trust_bundles[0].peer_root_ca == "-----BEGIN CERTIFICATE-----..." + + +def test_authority_posture_is_optional_for_existing_examples(minimal_spec) -> None: + from netengine.spec import AuthorityPosture + + assert minimal_spec.authority is None + posture = AuthorityPosture(authority_model="custom") + assert posture.authority_model == "custom" + + +def test_world_manifest_from_spec_applies_optional_authority_posture(minimal_spec) -> None: + from netengine.spec import AuthorityPosture, world_manifest_from_spec + + spec = minimal_spec.model_copy( + update={ + "authority": AuthorityPosture( + authority_model="custom", + dns_root_authority="custom-root", + ca_trust_authority="custom-trust", + ) + } + ) + + manifest = world_manifest_from_spec(spec) + + assert manifest.authority_model == "custom" + assert manifest.dns_root_authority == "custom-root" + assert manifest.ca_trust_authority == "custom-trust" + assert manifest.platform_identity_issuer == "platform-identity"