Coverage for node / src / stigmem_node / federation / dnssec / epoch.py: 100%
23 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-18 05:34 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-18 05:34 +0000
1"""Per-host monotonic epoch pin + sticky-signedness (Rev 6 I4 / build-phase 3b.5).
3State for the ``dnssec_epoch_pins`` table (migration 054), keyed by **host** (the
4canonical host derived from the signed wire ``entity_uri`` per I3) — *not* by
5identity. The epoch floor and the signed-delegation fact are properties of the
6zone, so a host that serves more than one ``node_id`` shares one floor: a second
7node arriving under the same host at a lower epoch is a rollback, never a fresh
8first-contact (Rev 6 plan TB-5).
10Two pieces of state live here:
12 * **Monotonic epoch** — ``accept_epoch(conn, host, epoch)`` enforces that a
13 host's ``max_epoch_seen`` never decreases. First contact (no row) takes the
14 handed epoch and pins it (honestly unauthenticated as to recency, §15.3);
15 ``epoch < max_epoch_seen`` is rejected (``dnssec_epoch_rollback``);
16 ``epoch >= max_epoch_seen`` is accepted and advances the floor.
17 * **Sticky-signedness** — ``mark_signed_delegation`` records that a signed
18 delegation has been observed for a host; once set it never clears, so a
19 later authenticated "absent" can be treated as an attack by the caller (I2).
21These are off-path DB primitives: nothing here is wired into the resolver yet
22(that is a later 3b task). All helpers take an open ``sqlite3``-style connection
23and participate in its transaction; the caller owns commit/rollback. No DNSSEC /
24``dnspython`` import is reachable from this module (I11).
25"""
27from __future__ import annotations
29from datetime import UTC, datetime
30from typing import Any
32# An epoch-only upsert needs a placeholder validation timestamp for the
33# NOT NULL last_validated_at column on a fresh row. The real first-trust ladder
34# stamps the genuine validation time; for these primitives "now" suffices and is
35# advanced on every accepted epoch.
38def _now_iso() -> str:
39 return datetime.now(UTC).isoformat()
42def accept_epoch(conn: Any, host: str, epoch: int) -> bool:
43 """Apply the monotonic-epoch rule for ``host`` (Rev 6 I4).
45 * No prior row (first contact) -> accept, pin ``max_epoch_seen=epoch``, True.
46 * ``epoch < max_epoch_seen`` -> reject (``dnssec_epoch_rollback``), the
47 stored floor is left unchanged, return False.
48 * ``epoch >= max_epoch_seen`` -> accept, advance the floor to ``epoch``,
49 refresh ``last_validated_at``, return True.
51 Keyed by ``host`` only (TB-5): a different ``node_id`` under the same host is
52 governed by the same floor and cannot reset it by being "new".
54 The caller owns the transaction; this never commits.
55 """
56 row = conn.execute(
57 "SELECT max_epoch_seen FROM dnssec_epoch_pins WHERE host=?", (host,)
58 ).fetchone()
60 if row is None:
61 # First contact: take the handed epoch and pin it. signed_delegation_seen
62 # defaults to 0 (migration 054); the caller marks it separately.
63 conn.execute(
64 "INSERT INTO dnssec_epoch_pins "
65 "(host, max_epoch_seen, signed_delegation_seen, last_validated_at) "
66 "VALUES (?, ?, 0, ?)",
67 (host, epoch, _now_iso()),
68 )
69 return True
71 current = row[0]
72 if epoch < current:
73 # Rollback: leave the floor untouched and reject.
74 return False
76 # Equal or higher: advance (or hold) the floor; refresh the validation time.
77 # signed_delegation_seen is deliberately NOT touched here so the sticky flag
78 # survives ordinary epoch advances.
79 conn.execute(
80 "UPDATE dnssec_epoch_pins SET max_epoch_seen=?, last_validated_at=? WHERE host=?",
81 (epoch, _now_iso(), host),
82 )
83 return True
86def mark_signed_delegation(conn: Any, host: str) -> None:
87 """Record (stickily) that a signed delegation has been observed for ``host``.
89 Idempotent: once ``signed_delegation_seen`` is 1 it stays 1. Creates the row
90 if the host has not been seen yet (with ``max_epoch_seen=0`` as a neutral
91 floor that any real first-contact epoch will meet-or-exceed). The caller owns
92 the transaction.
93 """
94 row = conn.execute(
95 "SELECT host FROM dnssec_epoch_pins WHERE host=?", (host,)
96 ).fetchone()
97 if row is None:
98 conn.execute(
99 "INSERT INTO dnssec_epoch_pins "
100 "(host, max_epoch_seen, signed_delegation_seen, last_validated_at) "
101 "VALUES (?, 0, 1, ?)",
102 (host, _now_iso()),
103 )
104 else:
105 conn.execute(
106 "UPDATE dnssec_epoch_pins SET signed_delegation_seen=1 WHERE host=?",
107 (host,),
108 )
111def signed_delegation_seen(conn: Any, host: str) -> bool:
112 """Return whether a signed delegation has ever been observed for ``host``.
114 False when the host has no row yet (never observed signed)."""
115 row = conn.execute(
116 "SELECT signed_delegation_seen FROM dnssec_epoch_pins WHERE host=?", (host,)
117 ).fetchone()
118 return bool(row[0]) if row is not None else False