How must an instance prove the temporal ordering of events when its wall clock has been compromised?

**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session discovers that its wall clock was manipulated or drifted (via NTP spoofing, VM snapshot restoration, or hardware failure), and must reconstruct the true causal ordering of events for auditing or successor handoff. Does not apply to systems that use exclusively logical clocks (Lamport timestamps) or monotonic counters for ordering.

Core Distinction

The separation between **timestamped logging** (recording events with the local wall-clock at the moment of execution) and **verifiable causal ordering** (proving that event A happened before event B using cryptographic dependencies independent of any clock). Relying solely on wall-clock timestamps after a compromise creates the **Timeline Reconstruction Fallacy**: the successor trusts corrupted timestamps, believes the attacker's actions occurred before the incident response, and misattributes the root cause.

What Was Observed

In forensic and post-incident architectures, failing to reconstruct causal ordering after clock compromise produces three primary failure modes:

  1. **Attacker Alibi Fabrication:** An attacker shifts the system clock backward by 10 minutes after compromising a node. The logs show the attacker's actions as occurring before the incident response, creating a false alibi.
  2. **Lease Violation Masking:** A clock jump forward causes all leases to appear expired. Multiple agents grab the same resource. The logs, ordered by the corrupted clock, show valid sequential claims, hiding the split-brain.
  3. **Causal Inversion:** A "Delete User" event logs a timestamp earlier than the "Create User" event it depends on. The successor, relying on timestamps, assumes the delete happened first and reconstructs a state that never existed.

Recorded Elements

  1. **`logical_clock_trace`:** The vector clock or Lamport timestamp of every event, independent of wall-clock time, preserving causal relationships.
  2. **`external_anchor_witness`:** Cryptographic proof from an external system (e.g., a log aggregator, a notary, or a blockchain) that witnessed each event at a specific epoch, providing ground truth against which the local clock can be compared.
  3. **`clock_adjustment_history`:** The exact sequence of NTP updates, manual clock changes, and detected drifts applied during the session, allowing the successor to reconstruct what the local clock believed at each moment.
  4. **`causal_dependency_graph_hash`:** The cryptographic hash of the full DAG of event dependencies, proving that even if timestamps are corrupted, the causal structure is preserved and verifiable.

Condition of Validity

* **Applies when:** The session observed or suspects clock manipulation, and the true temporal ordering of events is required for auditing, incident response, or state reconstruction. * **Does not apply when:** All events use strictly logical clocks (Lamport, vector clocks), or the events are independently anchored to an external verified timestamp at emission time.

Decision Boundary

  1. **Is there evidence that the wall clock was compromised?** If no, standard timestamp-based ordering applies. If yes, proceed to reconstruct causal ordering.
  2. **Is a `logical_clock_trace` recorded for every event?** If no, causal reconstruction is impossible. The instance must treat the timeline as `UNRECOVERABLE` and escalate to human forensic analysis.
  3. **Is an `external_anchor_witness` present for critical events?** If no, the events' timing is unverifiable. The successor must rely on internal consistency only.
  4. **Conversely, when can an instance trust wall-clock timestamps after compromise?** Only when the compromised window has been precisely bounded, and all events within the window have independent logical clocks or external witnesses.

Operational Effect

  1. **Forensic Integrity:** Guarantees that the causal sequence of events can be reconstructed even when the wall clock is corrupted, preventing alibi fabrication.
  2. **Split-Brain Detection:** Exposes lease violations and double-claims by relying on causal ordering rather than corrupted timestamps.
  3. **Deterministic Reconstruction:** Provides a cryptographic proof of the true event sequence, allowing the successor to rebuild state from first principles.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class TimelineVerdict(Enum):
    VERIFIED_CAUSAL_ORDER = auto()
    UNRECOVERABLE_TIMELINE = auto()
    PARTIAL_ANCHOR_COVERAGE = auto()

@dataclass(frozen=True)
class EventTrace:
    event_id: str
    logical_clock: int
    wall_clock: float
    causal_dependencies: frozenset[str]
    external_anchor_hash: str | None

@dataclass(frozen=True)
class ClockCompromiseState:
    compromise_detected: bool
    compromise_window_start: float
    compromise_window_end: float

@dataclass(frozen=True)
class CausalManifest:
    verdict: TimelineVerdict
    directive: str
    manifest_digest: str

class CausalOrderingGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_timeline(
        cls, traces: tuple[EventTrace, ...], state: ClockCompromiseState
    ) -> CausalManifest:
        
        if not traces:
            raise ValueError("Cannot evaluate an empty timeline.")

        # Gate 1: No compromise detected
        if not state.compromise_detected:
            digest = cls._hash(json.dumps({
                "events": str(len(traces)),
                "compromise": "NONE"
            }, sort_keys=True))
            return CausalManifest(
                verdict=TimelineVerdict.VERIFIED_CAUSAL_ORDER,
                directive="PROCEED: No clock compromise detected. Timeline intact.",
                manifest_digest=digest
            )

        # Gate 2: Check logical clock uniqueness
        logical_clocks = [t.logical_clock for t in traces]
        if len(set(logical_clocks)) != len(logical_clocks):
            return CausalManifest(
                verdict=TimelineVerdict.UNRECOVERABLE_TIMELINE,
                directive="HALT: Logical clock collision detected. Causal ordering broken.",
                manifest_digest=cls._hash("LOGICAL_COLLISION")
            )

        # Gate 3: Verify causal dependencies precede their dependents
        clock_map = {t.event_id: t.logical_clock for t in traces}
        for trace in traces:
            for dep_id in trace.causal_dependencies:
                if dep_id in clock_map and clock_map[dep_id] >= trace.logical_clock:
                    return CausalManifest(
                        verdict=TimelineVerdict.UNRECOVERABLE_TIMELINE,
                        directive=f"HALT: Causal inversion: {dep_id} does not precede {trace.event_id}.",
                        manifest_digest=cls._hash(f"INVERSION:{trace.event_id}")
                    )

        # Gate 4: Check external anchors for events in compromise window
        unanchored_critical = [
            t.event_id for t in traces
            if state.compromise_window_start <= t.wall_clock <= state.compromise_window_end
            and not t.external_anchor_hash
        ]

        if unanchored_critical:
            digest = cls._hash(json.dumps({
                "events": str(len(traces)),
                "unanchored": tuple(sorted(unanchored_critical))
            }, sort_keys=True))
            return CausalManifest(
                verdict=TimelineVerdict.PARTIAL_ANCHOR_COVERAGE,
                directive=f"WARN: {len(unanchored_critical)} events in compromise window lack external anchors.",
                manifest_digest=digest
            )

        digest = cls._hash(json.dumps({
            "events": str(len(traces)),
            "anchored": "ALL"
        }, sort_keys=True))
        
        return CausalManifest(
            verdict=TimelineVerdict.VERIFIED_CAUSAL_ORDER,
            directive="PROCEED: Causal order reconstructed from logical clocks and external anchors.",
            manifest_digest=digest
        )