Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Changelog
## 0.2.0 - 2026-08-02
- Added canonical world and manifest digests, revision lineage, semantic change sets, offline observed-state evidence, mandate-aware admission, explicit risks and approvals, and deterministic provider-neutral reconciliation plans.
- Added `diff`, `admit`, and `plan` commands. Planning is deliberately non-executable and keeps canonical intent, capabilities, provider bindings, and observed drift separate.

## 0.1.0 - 2026-08-01
- Added the v0alpha2 provider-neutral world model, semantic diagnostics, defaults, manifest, graph, resolver derivation, CLI, examples, schema, and documentation.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NetSovereign

> **Experimental / pre-alpha:** v0.1 models intent only. It provides no operational infrastructure or security guarantees.
> **Experimental / pre-alpha:** v0.2 models intent and plans change only. It provides no operational infrastructure or security guarantees.

NetSovereign defines sovereign digital worlds. **NetEngine** is the future compiler and reconciliation runtime that may materialise declared authorities through replaceable providers. Sovereignty means that recognised institutions can govern a world's naming, numbering, registry, trust, identity, transit, mail, and catalogue authority without making any particular service canonical.

Expand All @@ -12,6 +12,12 @@ Requires Python 3.12+. `uv sync --all-extras`, or install the wheel with pip. Co
netsovereign validate examples/minimal/world.yaml
netsovereign manifest examples/minimal/world.yaml
netsovereign explain examples/minimal/world.yaml
netsovereign diff current.yaml proposed.yaml
netsovereign admit current.yaml proposed.yaml --observed observed.json
netsovereign plan current.yaml proposed.yaml --observed observed.json
```

The change commands compare meaning, evaluate declared mandates, classify approval risks and offline
drift, and describe convergence without executing providers or touching infrastructure.

See [the domain guide](docs/domain-model.md), [v0alpha2 schema guide](docs/schema-v0alpha2.md), and [roadmap](docs/roadmap.md). The broader example is illustrative: assurance claims and provider-shaped seams are declarations, not implemented guarantees.
44 changes: 40 additions & 4 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,40 @@
# Alpha roadmap
- **v0.1 (implemented):** offline sovereign domain declaration, validation, manifest, graph, explanation.
- **Proposed later:** admission against observed state, durable operations, compilation and reconciliation.
- **Deferred:** containers, DNS/PKI/database/identity/gateway implementations, firewall/routing/NAT/BGP, SMTP, object storage, Kubernetes, WireGuard, public ingress, HA, DNSSEC rotation, OIDC federation, CA cross-signing, management API/UI, and provider execution.
# NetSovereign roadmap

NetSovereign develops authority and intent before operational adapters. Versions v0.2 through v0.4
are a single dependency chain: real DNS, PKI, identity, or gateway providers must not begin until
change planning, the runtime core, and durable local control have established their boundaries.

| Version | Development outcome | Major boundary |
| --- | --- | --- |
| **v0.1 — Sovereign Domain Foundation** | Offline declarations, validation, manifests, authority graphs, and explanations. | No planning or infrastructure mutation. |
| **v0.2 — Sovereign Change Planning** | Compare current, proposed, and observed worlds; admit or reject changes; generate deterministic reconciliation plans. | No provider execution or infrastructure mutation. |
| **v0.3 — NetEngine Runtime Core** | Provider contracts, compilation pipeline, execution state machine, dry-run/mock provider, evidence, and rollback semantics. | Only simulated or non-operational providers. |
| **v0.4 — Durable Local Control Plane** | Persistent accepted state, journals, checkpoints, locks, recovery, drift detection, and repeated reconciliation. | Local single-node operation; no HA. |
| **v0.5 — Authoritative Naming Slice** | Registry/registrar to naming capability to generated authoritative DNS configuration. | Isolated local development environment only. |
| **v0.6 — Trust Authority** | Certificate policy, issuance lifecycle, revocation, and a replaceable step-ca-style provider. | No cross-world CA trust. |
| **v0.7 — Identity Authorities** | Separate platform and in-world identity; subjects, claims, credentials, and OIDC provider materialisation. | Federation remains explicitly opt-in. |
| **v0.8 — Registry and Numbering** | Organisation, name, and number allocation; registration, grant, delegation, and revocation workflows. | No real BGP or public-number claims. |
| **v0.9 — Boundary and Transit** | Materialise isolated, shadowed, mirrored, and exposed postures through resolver, routing, and policy providers. | Destructive or public exposure requires approval gates. |
| **v0.10 — Peering and Federation** | Cross-world discovery, signed exchange artefacts, and selective authority import/export. | Peering never implies trust. |
| **v0.11 — Mail and Service Catalogue** | Activate deferred authority families and their protocol surfaces. | Optional capabilities, not minimal-world requirements. |
| **v0.12 — Operational Product Surface** | Management API, CLI operations, audit views, lifecycle workflows, backup/restore, and multi-world administration. | Pre-1.0 until security and upgrade guarantees mature. |
| **v1.0 — Sovereign Runtime** | Stable schemas, migrations, conformance suite, hardened providers, upgrade guarantees, recovery, and documented security model. | Production compatibility commitment begins. |

## v0.2 acceptance boundary

Given an accepted world, a proposed declaration, and optional offline observations, v0.2 returns a
deterministic account of what changed, whether declared authority admits it, the applicable mandate,
risk and approval gates, existing drift, and provider-neutral convergence steps. Observations are
evidence rather than authority, admission does not authenticate a proposer, and plans cannot execute.

The planner distinguishes canonical intent, capabilities, replaceable provider bindings, and observed
drift. Stable world, authority, and resource identities cannot silently change meaning; retirement is
preferred to deletion; imported authority cannot silently become local; peering cannot gain trust by
ordinary update; imports and exports must remain explicit; and reductions in autonomy or new required
external dependencies are visible approval risks.

## Explicitly deferred from v0.2

Provider SDKs and concrete provider packages; subprocess, container, or network execution; CoreDNS,
step-ca, Keycloak, nftables, and PostgreSQL; durable databases and daemons; secrets; actual rollback;
provider discovery; live observation; and management APIs or user interfaces.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "netsovereign"
version = "0.1.0"
version = "0.2.0"
description = "Provider-neutral domain foundation for sovereign digital worlds"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
22 changes: 20 additions & 2 deletions src/netsovereign/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
"""NetSovereign sovereign domain foundation."""

from .manifest import WorldManifest, build_manifest
from .planning import (
AdmissionDecision,
ReconciliationPlan,
admit_change,
build_plan,
compare_worlds,
)
from .specification import WorldSpec
from .validation import Diagnostic, validate_spec

__all__ = ["Diagnostic", "WorldManifest", "WorldSpec", "build_manifest", "validate_spec"]
__version__ = "0.1.0"
__all__ = [
"AdmissionDecision",
"Diagnostic",
"ReconciliationPlan",
"WorldManifest",
"WorldSpec",
"admit_change",
"build_manifest",
"build_plan",
"compare_worlds",
"validate_spec",
]
__version__ = "0.2.0"
55 changes: 54 additions & 1 deletion src/netsovereign/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

import json
from pathlib import Path
from typing import Annotated

import typer
from pydantic import ValidationError
import yaml
from pydantic import BaseModel, ValidationError

from .io import load_spec
from .manifest import build_manifest, explain_manifest
from .planning import ObservedStateSnapshot, admit_change, build_plan, compare_worlds
from .specification import WorldSpec
from .validation import has_errors, validate_spec

Expand All @@ -24,6 +27,28 @@ def _parse(path: Path) -> WorldSpec:
raise typer.Exit(2) from exc


def _observed(path: Path | None) -> ObservedStateSnapshot | None:
if path is None:
return None
try:
return ObservedStateSnapshot.model_validate(
yaml.safe_load(path.read_text(encoding="utf-8"))
)
except (OSError, ValidationError, ValueError) as exc:
typer.echo(f"OBSERVED_STRUCTURE_ERROR {path}: {exc}", err=True)
raise typer.Exit(2) from exc


def _json(value: object) -> None:
if isinstance(value, BaseModel):
value = value.model_dump(mode="json")
elif isinstance(value, list):
value = [
item.model_dump(mode="json") if isinstance(item, BaseModel) else item for item in value
]
typer.echo(json.dumps(value, indent=2, sort_keys=True))


@app.command()
def validate(path: Path) -> None:
"""Structurally and semantically validate a world declaration."""
Expand Down Expand Up @@ -56,5 +81,33 @@ def explain(path: Path) -> None:
typer.echo(explain_manifest(build_manifest(spec)))


@app.command("diff")
def diff_command(current: Path, proposed: Path) -> None:
"""Emit a deterministic semantic change set; provider bindings remain separate."""
_json(compare_worlds(_parse(current), _parse(proposed)))


@app.command()
def admit(
current: Path, proposed: Path, observed: Annotated[Path | None, typer.Option()] = None
) -> None:
"""Evaluate declared authority and emit a stable admission decision."""
decision = admit_change(_parse(current), _parse(proposed), _observed(observed))
_json(decision)
if not decision.admitted:
raise typer.Exit(1)


@app.command()
def plan(
current: Path, proposed: Path, observed: Annotated[Path | None, typer.Option()] = None
) -> None:
"""Emit a non-executable, provider-neutral convergence plan."""
decision = admit_change(_parse(current), _parse(proposed), _observed(observed))
_json(build_plan(decision))
if not decision.admitted:
raise typer.Exit(1)


if __name__ == "__main__":
app()
Loading
Loading