From a82b8c5933c59bbf4566fdd30a119b4d71dbe44c Mon Sep 17 00:00:00 2001 From: Undline <103777919+Undline@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:46:13 -0400 Subject: [PATCH] core: add core_genesis SQLite table and repository (stage 2 baseline) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration 007: singleton row for genesis_complete, bootstrap_signing_pubkey_hex, modulr_apex_domain, updated_at — seed for modulr apex trust + wizard completion. CoreGenesisRepository with validation; tests and package export. Made-with: Cursor --- .../migrations/007_core_genesis.sql | 16 +++ src/modulr_core/repositories/__init__.py | 2 + src/modulr_core/repositories/core_genesis.py | 118 ++++++++++++++++++ tests/test_core_genesis_repository.py | 91 ++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 src/modulr_core/persistence/migrations/007_core_genesis.sql create mode 100644 src/modulr_core/repositories/core_genesis.py create mode 100644 tests/test_core_genesis_repository.py diff --git a/src/modulr_core/persistence/migrations/007_core_genesis.sql b/src/modulr_core/persistence/migrations/007_core_genesis.sql new file mode 100644 index 0000000..3bde70f --- /dev/null +++ b/src/modulr_core/persistence/migrations/007_core_genesis.sql @@ -0,0 +1,16 @@ +-- Singleton genesis / bootstrap trust record for this Core instance. +-- Stores whether first-boot wizard finished and the proven Ed25519 signing pubkey +-- used to anchor apex policy (e.g. modulr.* namespace ownership). Enforcement of +-- subdomain rules uses this row in later stages; this migration only persists state. +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS core_genesis ( + singleton INTEGER PRIMARY KEY CHECK (singleton = 1), + genesis_complete INTEGER NOT NULL DEFAULT 0 CHECK (genesis_complete IN (0, 1)), + bootstrap_signing_pubkey_hex TEXT, + modulr_apex_domain TEXT, + updated_at INTEGER NOT NULL +); + +INSERT OR IGNORE INTO core_genesis (singleton, genesis_complete, updated_at) +VALUES (1, 0, 0); diff --git a/src/modulr_core/repositories/__init__.py b/src/modulr_core/repositories/__init__.py index e9e074c..110d7e3 100644 --- a/src/modulr_core/repositories/__init__.py +++ b/src/modulr_core/repositories/__init__.py @@ -1,5 +1,6 @@ """Data access for Modulr.Core MVP tables.""" +from modulr_core.repositories.core_genesis import CoreGenesisRepository from modulr_core.repositories.dial_route_entry import DialRouteEntryRepository from modulr_core.repositories.heartbeat import HeartbeatRepository from modulr_core.repositories.message_dedup import MessageDedupRepository @@ -7,6 +8,7 @@ from modulr_core.repositories.name_bindings import NameBindingsRepository __all__ = [ + "CoreGenesisRepository", "DialRouteEntryRepository", "HeartbeatRepository", "MessageDedupRepository", diff --git a/src/modulr_core/repositories/core_genesis.py b/src/modulr_core/repositories/core_genesis.py new file mode 100644 index 0000000..132edd5 --- /dev/null +++ b/src/modulr_core/repositories/core_genesis.py @@ -0,0 +1,118 @@ +"""Singleton ``core_genesis`` row: wizard completion + bootstrap signing pubkey.""" + +from __future__ import annotations + +import sqlite3 +from dataclasses import dataclass + +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey + +from modulr_core.validation.hex_codec import InvalidHexEncoding, decode_hex_fixed + +_MODULR_APEX_DOMAIN_MAX_LEN = 253 + + +@dataclass(frozen=True) +class CoreGenesisSnapshot: + """Read model for :class:`CoreGenesisRepository`.""" + + genesis_complete: bool + bootstrap_signing_pubkey_hex: str | None + modulr_apex_domain: str | None + updated_at: int + + +def _validate_bootstrap_pubkey_hex(pub_hex: str) -> None: + try: + raw = decode_hex_fixed(pub_hex, byte_length=32) + except InvalidHexEncoding as e: + raise ValueError(str(e)) from e + try: + Ed25519PublicKey.from_public_bytes(raw) + except ValueError as e: + raise ValueError(f"invalid Ed25519 public key: {e}") from e + + +def _validate_apex_domain(domain: str) -> str: + d = domain.strip() + if not d: + raise ValueError("modulr_apex_domain must be non-empty when set") + if len(d) > _MODULR_APEX_DOMAIN_MAX_LEN: + mx = _MODULR_APEX_DOMAIN_MAX_LEN + raise ValueError(f"modulr_apex_domain must be at most {mx} characters") + return d + + +class CoreGenesisRepository: + """Read/update the single ``core_genesis`` row (migration ``007`` seeds it).""" + + def __init__(self, conn: sqlite3.Connection) -> None: + self._conn = conn + + def get(self) -> CoreGenesisSnapshot: + cur = self._conn.execute( + """ + SELECT genesis_complete, bootstrap_signing_pubkey_hex, + modulr_apex_domain, updated_at + FROM core_genesis + WHERE singleton = 1 + """, + ) + row = cur.fetchone() + if row is None: + raise RuntimeError("core_genesis singleton missing; run apply_migrations") + complete = int(row["genesis_complete"]) == 1 + pk = row["bootstrap_signing_pubkey_hex"] + pk_s = str(pk) if pk is not None else None + apex = row["modulr_apex_domain"] + apex_s = str(apex).strip() if apex is not None and str(apex).strip() else None + return CoreGenesisSnapshot( + genesis_complete=complete, + bootstrap_signing_pubkey_hex=pk_s, + modulr_apex_domain=apex_s, + updated_at=int(row["updated_at"]), + ) + + def set_genesis_complete(self, *, complete: bool, updated_at: int) -> None: + self._conn.execute( + """ + UPDATE core_genesis + SET genesis_complete = ?, updated_at = ? + WHERE singleton = 1 + """, + (1 if complete else 0, updated_at), + ) + + def set_bootstrap_signing_pubkey_hex( + self, + *, + pubkey_hex: str | None, + updated_at: int, + ) -> None: + if pubkey_hex is not None: + _validate_bootstrap_pubkey_hex(pubkey_hex) + self._conn.execute( + """ + UPDATE core_genesis + SET bootstrap_signing_pubkey_hex = ?, updated_at = ? + WHERE singleton = 1 + """, + (pubkey_hex, updated_at), + ) + + def set_modulr_apex_domain( + self, + *, + apex_domain: str | None, + updated_at: int, + ) -> None: + if apex_domain is not None: + apex_domain = _validate_apex_domain(apex_domain) + self._conn.execute( + """ + UPDATE core_genesis + SET modulr_apex_domain = ?, updated_at = ? + WHERE singleton = 1 + """, + (apex_domain, updated_at), + ) diff --git a/tests/test_core_genesis_repository.py b/tests/test_core_genesis_repository.py new file mode 100644 index 0000000..bfddd02 --- /dev/null +++ b/tests/test_core_genesis_repository.py @@ -0,0 +1,91 @@ +"""Singleton ``core_genesis`` persistence (migration 007).""" + +from __future__ import annotations + +import sqlite3 + +import pytest +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat + +from modulr_core.persistence import apply_migrations, connect_memory +from modulr_core.repositories.core_genesis import CoreGenesisRepository + + +def _valid_pubkey_hex() -> str: + pk = Ed25519PrivateKey.generate().public_key() + return pk.public_bytes(encoding=Encoding.Raw, format=PublicFormat.Raw).hex() + + +def _conn() -> sqlite3.Connection: + c = connect_memory(check_same_thread=False) + apply_migrations(c) + return c + + +def test_core_genesis_default_after_migration() -> None: + conn = _conn() + repo = CoreGenesisRepository(conn) + s = repo.get() + assert s.genesis_complete is False + assert s.bootstrap_signing_pubkey_hex is None + assert s.modulr_apex_domain is None + assert s.updated_at == 0 + + +def test_core_genesis_set_pubkey_and_complete() -> None: + conn = _conn() + repo = CoreGenesisRepository(conn) + k = _valid_pubkey_hex() + repo.set_bootstrap_signing_pubkey_hex(pubkey_hex=k, updated_at=100) + repo.set_modulr_apex_domain(apex_domain="modulr.network", updated_at=101) + repo.set_genesis_complete(complete=True, updated_at=102) + conn.commit() + + s = repo.get() + assert s.genesis_complete is True + assert s.bootstrap_signing_pubkey_hex == k + assert s.modulr_apex_domain == "modulr.network" + assert s.updated_at == 102 + + +def test_core_genesis_rejects_invalid_pubkey_hex() -> None: + conn = _conn() + repo = CoreGenesisRepository(conn) + with pytest.raises(ValueError, match="expected 64 hex"): + repo.set_bootstrap_signing_pubkey_hex(pubkey_hex="not-hex", updated_at=1) + + +def test_core_genesis_rejects_uppercase_pubkey_hex() -> None: + conn = _conn() + repo = CoreGenesisRepository(conn) + bad = _valid_pubkey_hex().upper() + with pytest.raises(ValueError, match="lowercase"): + repo.set_bootstrap_signing_pubkey_hex(pubkey_hex=bad, updated_at=1) + + +def test_core_genesis_clear_pubkey() -> None: + conn = _conn() + repo = CoreGenesisRepository(conn) + k = _valid_pubkey_hex() + repo.set_bootstrap_signing_pubkey_hex(pubkey_hex=k, updated_at=50) + repo.set_bootstrap_signing_pubkey_hex(pubkey_hex=None, updated_at=51) + conn.commit() + assert repo.get().bootstrap_signing_pubkey_hex is None + + +def test_core_genesis_apex_domain_validation() -> None: + conn = _conn() + repo = CoreGenesisRepository(conn) + with pytest.raises(ValueError, match="non-empty"): + repo.set_modulr_apex_domain(apex_domain=" ", updated_at=1) + with pytest.raises(ValueError, match="at most"): + repo.set_modulr_apex_domain(apex_domain="x" * 254, updated_at=1) + + +def test_schema_migrations_includes_007() -> None: + conn = _conn() + cur = conn.execute( + "SELECT 1 FROM schema_migrations WHERE version = 7", + ) + assert cur.fetchone() is not None