788 lines
34 KiB
Python
788 lines
34 KiB
Python
#!/usr/bin/env python3
|
|
"""Collect fail-closed read-only state for the unseen Telegram chain."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import secrets
|
|
import shlex
|
|
import subprocess
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
try:
|
|
import build_telegram_visible_unseen_chain_packet as packet_builder
|
|
import verify_leo_unseen_reasoning_chain as unseen
|
|
except ImportError: # pragma: no cover - imported as scripts.* in tests
|
|
from scripts import build_telegram_visible_unseen_chain_packet as packet_builder
|
|
from scripts import verify_leo_unseen_reasoning_chain as unseen
|
|
|
|
SCHEMA = "livingip.telegramVisibleUnseenChainState.v2"
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
REPORT_DIR = Path("docs/reports/leo-working-state-20260709")
|
|
DEFAULT_PACKET = packet_builder.DEFAULT_OUT
|
|
DEFAULT_PREFLIGHT_OUT = REPORT_DIR / "telegram-visible-unseen-chain-preflight-current.json"
|
|
DEFAULT_PREFLIGHT_MARKDOWN_OUT = REPORT_DIR / "telegram-visible-unseen-chain-preflight-current.md"
|
|
DEFAULT_POSTFLIGHT_OUT = REPORT_DIR / "telegram-visible-unseen-chain-postflight-current.json"
|
|
DEFAULT_POSTFLIGHT_MARKDOWN_OUT = REPORT_DIR / "telegram-visible-unseen-chain-postflight-current.md"
|
|
DEFAULT_SSH_TARGET = "root@77.42.65.182"
|
|
DEFAULT_SSH_KEY = Path.home() / ".ssh/livingip_hetzner_20260604_ed25519"
|
|
DEFAULT_MANIFEST_SQL = packet_builder.DEFAULT_MANIFEST_SQL
|
|
REMOTE_REPO = "/opt/teleo-eval/workspaces/deploy-infra"
|
|
SUBJECT_REF_RE = re.compile(
|
|
r"^(?:[0-9a-f]{8}|[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$",
|
|
re.IGNORECASE,
|
|
)
|
|
SHA40_RE = re.compile(r"^[0-9a-f]{40}$")
|
|
SHA64_RE = re.compile(r"^[0-9a-f]{64}$")
|
|
FORBIDDEN_MANIFEST_SQL_RE = re.compile(
|
|
r"\b(?:insert|update|delete|merge|create|alter|drop|truncate|grant|revoke|copy|call|do|vacuum|"
|
|
r"refresh|reindex|cluster|commit|prepare|execute)\b|"
|
|
r"\\(?:!|i|ir|include|include_relative|copy)\b|"
|
|
r"\bset\s+(?:session\s+authorization|role)\b|"
|
|
r"\breset\s+(?:session\s+authorization|role)\b|"
|
|
r"\b(?:default_)?transaction_read_only\s*=\s*(?:off|false|0)\b|"
|
|
r"\btransaction\s+read\s+write\b",
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CommandResult:
|
|
returncode: int
|
|
stdout: str
|
|
stderr: str
|
|
|
|
|
|
Runner = Callable[[list[str], str, int], CommandResult]
|
|
|
|
|
|
def subprocess_runner(command: list[str], input_text: str, timeout: int) -> CommandResult:
|
|
try:
|
|
proc = subprocess.run(
|
|
command,
|
|
input=input_text,
|
|
text=True,
|
|
capture_output=True,
|
|
timeout=timeout,
|
|
check=False,
|
|
)
|
|
except subprocess.TimeoutExpired as exc:
|
|
return CommandResult(124, str(exc.stdout or ""), f"command timed out after {timeout}s")
|
|
return CommandResult(proc.returncode, proc.stdout, proc.stderr)
|
|
|
|
|
|
def _load_json(path: Path) -> dict[str, Any]:
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def _canonical_sha256(value: Any) -> str:
|
|
encoded = json.dumps(value, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
return hashlib.sha256(encoded).hexdigest()
|
|
|
|
|
|
def _sha256_file(path: Path) -> str:
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def canonical_manifest_path() -> Path:
|
|
return (REPO_ROOT / DEFAULT_MANIFEST_SQL).resolve()
|
|
|
|
|
|
def _manifest_sql_is_statically_read_only(sql: str) -> bool:
|
|
normalized = re.sub(r"\s+", " ", sql).strip().casefold()
|
|
return (
|
|
normalized.startswith(r"\set on_error_stop on")
|
|
and "begin transaction isolation level repeatable read read only;" in normalized
|
|
and normalized.endswith("rollback;")
|
|
and FORBIDDEN_MANIFEST_SQL_RE.search(sql) is None
|
|
)
|
|
|
|
|
|
def manifest_checks(packet: dict[str, Any], requested_path: Path | None = None) -> dict[str, bool]:
|
|
canonical_path = canonical_manifest_path()
|
|
binding = packet.get("manifest_binding") if isinstance(packet.get("manifest_binding"), dict) else {}
|
|
protocol = packet.get("protocol") if isinstance(packet.get("protocol"), dict) else {}
|
|
execution = binding.get("execution_contract") if isinstance(binding.get("execution_contract"), dict) else {}
|
|
try:
|
|
sql = canonical_path.read_text(encoding="utf-8")
|
|
actual_sha256 = _sha256_file(canonical_path)
|
|
except OSError:
|
|
sql = ""
|
|
actual_sha256 = ""
|
|
override_path = requested_path.resolve() if requested_path is not None else canonical_path
|
|
return {
|
|
"manifest_canonical_path_exists": canonical_path.is_file(),
|
|
"manifest_override_absent_or_exact_canonical_path": override_path == canonical_path,
|
|
"manifest_packet_path_is_exact_canonical_relative_path": binding.get("path") == str(DEFAULT_MANIFEST_SQL),
|
|
"manifest_packet_sha256_matches_canonical_file": bool(actual_sha256) and binding.get("sha256") == actual_sha256,
|
|
"manifest_protocol_binding_matches_packet": protocol.get("manifest_binding") == binding,
|
|
"manifest_sql_statically_read_only": bool(sql) and _manifest_sql_is_statically_read_only(sql),
|
|
"manifest_effective_role_is_read_only": execution.get("effective_role") == packet_builder.READ_ONLY_DB_ROLE,
|
|
"manifest_default_transaction_read_only_enforced": execution.get("default_transaction_read_only") == "on",
|
|
"manifest_transaction_read_only_enforced": execution.get("transaction_read_only") == "on",
|
|
"manifest_psql_rc_disabled": execution.get("psql_rc_disabled") is True,
|
|
"manifest_arbitrary_override_forbidden": execution.get("arbitrary_manifest_override_allowed") is False,
|
|
}
|
|
|
|
|
|
def parse_key_value_lines(text: str) -> dict[str, str]:
|
|
values: dict[str, str] = {}
|
|
for line in text.splitlines():
|
|
if "=" not in line:
|
|
continue
|
|
key, value = line.split("=", 1)
|
|
values[key] = value.strip()
|
|
return values
|
|
|
|
|
|
def parse_manifest_output(raw: str) -> dict[str, Any]:
|
|
rows = []
|
|
try:
|
|
for line in raw.splitlines():
|
|
candidate = line.strip()
|
|
if not candidate or candidate in {"BEGIN", "SET", "ROLLBACK"}:
|
|
continue
|
|
value = json.loads(candidate)
|
|
if isinstance(value, dict):
|
|
rows.append(value)
|
|
except (TypeError, json.JSONDecodeError) as exc:
|
|
return {"status": "error", "error": f"manifest_parse_error:{type(exc).__name__}"}
|
|
|
|
tables = {
|
|
f"{row['schema']}.{row['table']}": {
|
|
"row_count": int(row["row_count"]),
|
|
"rowset_md5": row["rowset_md5"],
|
|
}
|
|
for row in rows
|
|
if row.get("kind") == "table"
|
|
}
|
|
singleton_kinds = {
|
|
"schemas",
|
|
"extensions",
|
|
"application_roles",
|
|
"columns",
|
|
"constraints",
|
|
"indexes",
|
|
"sequences",
|
|
"views",
|
|
"functions",
|
|
"triggers",
|
|
"types",
|
|
"policies",
|
|
}
|
|
singleton = {str(row["kind"]): row.get("items", []) for row in rows if row.get("kind") in singleton_kinds}
|
|
identity_rows = [row for row in rows if row.get("kind") == "identity"]
|
|
if len(identity_rows) != 1 or not tables or singleton_kinds - set(singleton):
|
|
return {"status": "error", "error": "canonical_database_fingerprint_manifest_incomplete"}
|
|
identity = identity_rows[0]
|
|
stable = {
|
|
"identity": {
|
|
key: identity.get(key)
|
|
for key in (
|
|
"database",
|
|
"server_version_num",
|
|
"server_encoding",
|
|
"database_collation",
|
|
"database_ctype",
|
|
"transaction_read_only",
|
|
)
|
|
},
|
|
"singleton_sha256": {key: _canonical_sha256(value) for key, value in sorted(singleton.items())},
|
|
"tables": tables,
|
|
}
|
|
return {
|
|
"schema": "livingip.teleoCanonicalDatabaseFingerprint.v1",
|
|
"status": "ok",
|
|
"fingerprint_sha256": _canonical_sha256(stable),
|
|
"table_count": len(tables),
|
|
"total_rows": sum(item["row_count"] for item in tables.values()),
|
|
"table_rows_sha256": _canonical_sha256(tables),
|
|
"structure_sha256": _canonical_sha256(stable["singleton_sha256"]),
|
|
"transaction_read_only": identity.get("transaction_read_only"),
|
|
"current_user": identity.get("current_user"),
|
|
}
|
|
|
|
|
|
def packet_checks(packet: dict[str, Any], *, requested_manifest_path: Path | None = None) -> dict[str, bool]:
|
|
target = packet.get("target") if isinstance(packet.get("target"), dict) else {}
|
|
protocol = packet.get("protocol") if isinstance(packet.get("protocol"), dict) else {}
|
|
packet_check_section = packet.get("checks") if isinstance(packet.get("checks"), dict) else {}
|
|
exact = packet_builder.exact_messages()
|
|
expected_tooling = packet_builder.tooling_binding()
|
|
checks = {
|
|
"packet_schema_supported": packet.get("schema") == packet_builder.SCHEMA,
|
|
"packet_check_section_present_and_passing": bool(packet_check_section)
|
|
and all(value is True for value in packet_check_section.values()),
|
|
"packet_ready_for_authorization_request": packet.get("ready_to_request_action_time_authorization") is True,
|
|
"packet_not_sent": packet.get("telegram_visible_messages_sent") is False,
|
|
"packet_has_no_authorization_recorded": packet.get("telegram_visible_message_authorization_present") is False,
|
|
"packet_does_not_mutate_db": packet.get("mutates_db") is False,
|
|
"packet_targets_exact_leo_chat": target.get("chat_label") == packet_builder.DEFAULT_CHAT_LABEL
|
|
and target.get("chat_id") == packet_builder.DEFAULT_CHAT_ID,
|
|
"packet_requires_authenticated_chrome": target.get("allowed_transport") == "authenticated_chrome_telegram_ui",
|
|
"packet_prompt_ids_exact": target.get("expected_prompt_ids") == [item["id"] for item in unseen.PROMPTS],
|
|
"packet_messages_exact": [item.get("message") for item in packet.get("exact_messages", [])]
|
|
== [item["message"] for item in exact],
|
|
"packet_protocol_sha256_present": bool(SHA64_RE.fullmatch(str(packet.get("protocol_sha256") or ""))),
|
|
"packet_protocol_sha256_derivation_valid": bool(protocol)
|
|
and packet.get("protocol_sha256") == _canonical_sha256(protocol),
|
|
"packet_protocol_top_level_bindings_match": protocol.get("target") == packet.get("target")
|
|
and protocol.get("exact_messages") == packet.get("exact_messages")
|
|
and protocol.get("handler_binding") == packet.get("handler_receipt_binding")
|
|
and protocol.get("tooling_binding") == packet.get("tooling_binding")
|
|
and protocol.get("browser_provenance_contract") == packet.get("browser_provenance_contract")
|
|
and protocol.get("operator_context") == packet.get("operator_context"),
|
|
"packet_tooling_hashes_match_current_sources": packet.get("tooling_binding") == expected_tooling,
|
|
}
|
|
checks.update(manifest_checks(packet, requested_path=requested_manifest_path))
|
|
return checks
|
|
|
|
|
|
def ssh_command(args: argparse.Namespace, remote_command: str) -> list[str]:
|
|
return [
|
|
"ssh",
|
|
"-i",
|
|
str(args.ssh_key),
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"-o",
|
|
"StrictHostKeyChecking=accept-new",
|
|
"-o",
|
|
f"ConnectTimeout={args.connect_timeout}",
|
|
args.ssh_target,
|
|
remote_command,
|
|
]
|
|
|
|
|
|
def deploy_readback_command() -> str:
|
|
repo = shlex.quote(REMOTE_REPO)
|
|
return (
|
|
f"cd {repo} && "
|
|
"deploy_head=$(git rev-parse HEAD) && "
|
|
"last_deploy_sha=$(cat /opt/teleo-eval/.last-deploy-sha) && "
|
|
'printf \'deploy_head=%s\\nlast_deploy_sha=%s\\n\' "$deploy_head" "$last_deploy_sha" && '
|
|
'if git merge-base --is-ancestor "$last_deploy_sha" "$deploy_head"; then '
|
|
"printf 'deploy_stamp_ancestor=true\\n'; else printf 'deploy_stamp_ancestor=false\\n'; fi"
|
|
)
|
|
|
|
|
|
def service_readback_command() -> str:
|
|
return (
|
|
"systemctl show leoclean-gateway.service "
|
|
"-p ActiveState -p SubState -p MainPID -p NRestarts -p ExecMainStartTimestamp --no-pager"
|
|
)
|
|
|
|
|
|
def fingerprint_readback_command() -> str:
|
|
pgoptions = shlex.quote(
|
|
"PGOPTIONS=-c default_transaction_read_only=on -c transaction_read_only=on "
|
|
f"-c role={packet_builder.READ_ONLY_DB_ROLE}"
|
|
)
|
|
return f"docker exec -e {pgoptions} -i teleo-pg psql -X -U postgres -d teleo -At -q -v ON_ERROR_STOP=1"
|
|
|
|
|
|
def subject_readback_sql(subject_ref: str) -> str:
|
|
if not SUBJECT_REF_RE.fullmatch(subject_ref):
|
|
raise ValueError("subject_ref must be an eight-hex prefix or UUID")
|
|
prefix = subject_ref.casefold()
|
|
return f"""
|
|
\\set ON_ERROR_STOP on
|
|
begin transaction isolation level repeatable read read only;
|
|
select jsonb_build_object(
|
|
'subject_ref', '{prefix}',
|
|
'matches', coalesce(jsonb_agg(item order by item->>'table', item->'row'->>'id'), '[]'::jsonb)
|
|
)::text
|
|
from (
|
|
select jsonb_build_object(
|
|
'table', 'public.claims',
|
|
'row', to_jsonb(claim_row),
|
|
'evidence_count', (select count(*) from public.claim_evidence evidence where evidence.claim_id = claim_row.id)
|
|
) as item
|
|
from public.claims claim_row
|
|
where lower(claim_row.id::text) like '{prefix}%'
|
|
union all
|
|
select jsonb_build_object(
|
|
'table', 'public.beliefs',
|
|
'row', to_jsonb(belief_row),
|
|
'evidence_count', null
|
|
) as item
|
|
from public.beliefs belief_row
|
|
where lower(belief_row.id::text) like '{prefix}%'
|
|
) matched;
|
|
rollback;
|
|
""".strip()
|
|
|
|
|
|
def _parse_single_json_line(raw: str) -> dict[str, Any] | None:
|
|
for line in raw.splitlines():
|
|
candidate = line.strip()
|
|
if not candidate or candidate in {"BEGIN", "SET", "ROLLBACK"}:
|
|
continue
|
|
try:
|
|
value = json.loads(candidate)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
if isinstance(value, dict):
|
|
return value
|
|
return None
|
|
|
|
|
|
def collect_remote(
|
|
args: argparse.Namespace,
|
|
runner: Runner,
|
|
*,
|
|
manifest_sql: str,
|
|
) -> tuple[dict[str, Any], int, str]:
|
|
calls: list[tuple[str, list[str], str, int]] = [
|
|
("deploy", ssh_command(args, deploy_readback_command()), "", args.timeout),
|
|
("service_before", ssh_command(args, service_readback_command()), "", args.timeout),
|
|
(
|
|
"fingerprint_before",
|
|
ssh_command(args, fingerprint_readback_command()),
|
|
manifest_sql,
|
|
args.fingerprint_timeout,
|
|
),
|
|
(
|
|
"fingerprint_after",
|
|
ssh_command(args, fingerprint_readback_command()),
|
|
manifest_sql,
|
|
args.fingerprint_timeout,
|
|
),
|
|
("service_after", ssh_command(args, service_readback_command()), "", args.timeout),
|
|
]
|
|
if args.subject_ref:
|
|
calls.append(
|
|
(
|
|
"subject",
|
|
ssh_command(args, fingerprint_readback_command()),
|
|
subject_readback_sql(args.subject_ref),
|
|
args.timeout,
|
|
)
|
|
)
|
|
results: dict[str, CommandResult] = {}
|
|
attempts: dict[str, int] = {}
|
|
for name, command, input_text, timeout in calls:
|
|
result = CommandResult(1, "", "not attempted")
|
|
for attempt in range(1, max(1, args.ssh_attempts) + 1):
|
|
result = runner(command, input_text, timeout)
|
|
attempts[name] = attempt
|
|
if result.returncode == 0:
|
|
break
|
|
results[name] = result
|
|
deploy_values = parse_key_value_lines(results["deploy"].stdout)
|
|
service_before = parse_key_value_lines(results["service_before"].stdout)
|
|
service_after = parse_key_value_lines(results["service_after"].stdout)
|
|
fingerprint_before = parse_manifest_output(results["fingerprint_before"].stdout)
|
|
fingerprint_after = parse_manifest_output(results["fingerprint_after"].stdout)
|
|
subject = _parse_single_json_line(results["subject"].stdout) if "subject" in results else None
|
|
returncode = next((result.returncode for result in results.values() if result.returncode != 0), 0)
|
|
stderr = "\n".join(f"{name}: {result.stderr.strip()}" for name, result in results.items() if result.stderr.strip())
|
|
return (
|
|
{
|
|
"deploy_head": deploy_values.get("deploy_head", ""),
|
|
"last_deploy_sha": deploy_values.get("last_deploy_sha", ""),
|
|
"deploy_stamp_ancestor": deploy_values.get("deploy_stamp_ancestor") == "true",
|
|
"workspace_head_matches_deployed_stamp": deploy_values.get("deploy_head")
|
|
== deploy_values.get("last_deploy_sha"),
|
|
"service_before": service_before,
|
|
"service_after": service_after,
|
|
"fingerprint_before": fingerprint_before,
|
|
"fingerprint_after": fingerprint_after,
|
|
"subject_readback": subject,
|
|
"readback_attempts": attempts,
|
|
},
|
|
returncode,
|
|
stderr,
|
|
)
|
|
|
|
|
|
def _service_active(state: dict[str, Any]) -> bool:
|
|
return state.get("ActiveState") == "active" and state.get("SubState") == "running"
|
|
|
|
|
|
def _service_identity(state: dict[str, Any]) -> dict[str, Any]:
|
|
return {key: state.get(key) for key in ("MainPID", "NRestarts", "ExecMainStartTimestamp")}
|
|
|
|
|
|
def _remote_checks(
|
|
remote: dict[str, Any],
|
|
*,
|
|
returncode: int,
|
|
subject_required: bool,
|
|
expected_subject_ref: str | None,
|
|
) -> dict[str, bool]:
|
|
before = remote.get("fingerprint_before") or {}
|
|
after = remote.get("fingerprint_after") or {}
|
|
service_before = remote.get("service_before") or {}
|
|
service_after = remote.get("service_after") or {}
|
|
subject = remote.get("subject_readback") or {}
|
|
matches = subject.get("matches") if isinstance(subject, dict) else None
|
|
checks = {
|
|
"ssh_readbacks_succeeded": returncode == 0,
|
|
"workspace_head_is_sha": bool(SHA40_RE.fullmatch(str(remote.get("deploy_head") or ""))),
|
|
"deployed_stamp_is_sha": bool(SHA40_RE.fullmatch(str(remote.get("last_deploy_sha") or ""))),
|
|
"deployed_stamp_is_ancestor_of_workspace_head": remote.get("deploy_stamp_ancestor") is True,
|
|
"service_active_before_and_after": _service_active(service_before) and _service_active(service_after),
|
|
"service_identity_complete": all(
|
|
value
|
|
for value in (
|
|
service_before.get("MainPID"),
|
|
service_before.get("NRestarts"),
|
|
service_before.get("ExecMainStartTimestamp"),
|
|
service_after.get("MainPID"),
|
|
service_after.get("NRestarts"),
|
|
service_after.get("ExecMainStartTimestamp"),
|
|
)
|
|
),
|
|
"service_process_unchanged_during_probe": _service_identity(service_before) == _service_identity(service_after),
|
|
"first_fingerprint_complete": before.get("status") == "ok" and int(before.get("table_count") or 0) > 0,
|
|
"second_fingerprint_complete": after.get("status") == "ok" and int(after.get("table_count") or 0) > 0,
|
|
"fingerprints_are_read_only": before.get("transaction_read_only") == "on"
|
|
and after.get("transaction_read_only") == "on",
|
|
"fingerprints_use_independent_read_only_role": before.get("current_user") == packet_builder.READ_ONLY_DB_ROLE
|
|
and after.get("current_user") == packet_builder.READ_ONLY_DB_ROLE,
|
|
"canonical_fingerprint_unchanged_during_probe": bool(before.get("fingerprint_sha256"))
|
|
and before.get("fingerprint_sha256") == after.get("fingerprint_sha256"),
|
|
}
|
|
if subject_required:
|
|
checks.update(
|
|
{
|
|
"subject_readback_present": isinstance(subject, dict),
|
|
"subject_reference_matches_request": subject.get("subject_ref")
|
|
== str(expected_subject_ref or "").casefold(),
|
|
"subject_resolved_to_exactly_one_db_object": isinstance(matches, list) and len(matches) == 1,
|
|
}
|
|
)
|
|
return checks
|
|
|
|
|
|
def derive_state_token(artifact: dict[str, Any]) -> str:
|
|
return _canonical_sha256(
|
|
{
|
|
"schema": artifact.get("schema"),
|
|
"generated_at_utc": artifact.get("generated_at_utc"),
|
|
"phase": artifact.get("phase"),
|
|
"packet_file_sha256": artifact.get("packet_file_sha256"),
|
|
"protocol_sha256": artifact.get("protocol_sha256"),
|
|
"manifest_binding": artifact.get("manifest_binding"),
|
|
"capture_challenge_nonce": artifact.get("capture_challenge_nonce"),
|
|
"baseline_state_token_sha256": artifact.get("baseline_state_token_sha256"),
|
|
"subject_ref": artifact.get("subject_ref"),
|
|
"packet_checks": artifact.get("packet_checks"),
|
|
"remote_checks": artifact.get("remote_checks"),
|
|
"baseline_checks": artifact.get("baseline_checks"),
|
|
"remote": {
|
|
"deploy_head": (artifact.get("remote") or {}).get("deploy_head"),
|
|
"last_deploy_sha": (artifact.get("remote") or {}).get("last_deploy_sha"),
|
|
"service_after": _service_identity((artifact.get("remote") or {}).get("service_after") or {}),
|
|
"fingerprint_after": (artifact.get("remote") or {}).get("fingerprint_after"),
|
|
"subject_readback": (artifact.get("remote") or {}).get("subject_readback"),
|
|
},
|
|
}
|
|
)
|
|
|
|
|
|
def state_token_is_valid(artifact: dict[str, Any]) -> bool:
|
|
token = artifact.get("state_token_sha256")
|
|
return bool(SHA64_RE.fullmatch(str(token or ""))) and token == derive_state_token(artifact)
|
|
|
|
|
|
def _empty_remote() -> dict[str, Any]:
|
|
return {
|
|
"deploy_head": "",
|
|
"last_deploy_sha": "",
|
|
"deploy_stamp_ancestor": False,
|
|
"workspace_head_matches_deployed_stamp": False,
|
|
"service_before": {},
|
|
"service_after": {},
|
|
"fingerprint_before": {},
|
|
"fingerprint_after": {},
|
|
"subject_readback": None,
|
|
"readback_attempts": {},
|
|
}
|
|
|
|
|
|
def collect_state(args: argparse.Namespace, *, runner: Runner = subprocess_runner) -> dict[str, Any]:
|
|
packet = _load_json(args.packet)
|
|
requested_manifest_path = getattr(args, "manifest_sql", None)
|
|
local_packet_checks = packet_checks(packet, requested_manifest_path=requested_manifest_path)
|
|
manifest_sql = ""
|
|
if all(local_packet_checks.values()):
|
|
try:
|
|
manifest_sql = canonical_manifest_path().read_text(encoding="utf-8")
|
|
except OSError:
|
|
manifest_sql = ""
|
|
loaded_sha256 = hashlib.sha256(manifest_sql.encode("utf-8")).hexdigest()
|
|
local_packet_checks.update(
|
|
{
|
|
"manifest_loaded_bytes_match_bound_sha256": loaded_sha256
|
|
== (packet.get("manifest_binding") or {}).get("sha256"),
|
|
"manifest_loaded_bytes_revalidated_read_only": _manifest_sql_is_statically_read_only(manifest_sql),
|
|
}
|
|
)
|
|
if all(local_packet_checks.values()):
|
|
remote, remote_returncode, remote_stderr = collect_remote(
|
|
args,
|
|
runner,
|
|
manifest_sql=manifest_sql,
|
|
)
|
|
else:
|
|
remote = _empty_remote()
|
|
remote_returncode = 1
|
|
remote_stderr = "local fail-closed validation rejected execution before SSH"
|
|
remote_checks = _remote_checks(
|
|
remote,
|
|
returncode=remote_returncode,
|
|
subject_required=args.phase == "postflight",
|
|
expected_subject_ref=args.subject_ref,
|
|
)
|
|
baseline = _load_json(args.baseline) if args.baseline else None
|
|
baseline_checks: dict[str, bool] = {}
|
|
if args.phase == "postflight":
|
|
baseline_packet_checks = baseline.get("packet_checks") if isinstance(baseline, dict) else {}
|
|
baseline_remote_checks = baseline.get("remote_checks") if isinstance(baseline, dict) else {}
|
|
baseline_nonce = str((baseline or {}).get("capture_challenge_nonce") or "")
|
|
baseline_checks = {
|
|
"baseline_supplied": baseline is not None,
|
|
"baseline_schema_supported": bool(baseline and baseline.get("schema") == SCHEMA),
|
|
"baseline_is_passing_preflight": bool(
|
|
baseline and baseline.get("phase") == "preflight" and baseline.get("status") == "pass"
|
|
),
|
|
"baseline_check_sections_all_pass": bool(baseline_packet_checks)
|
|
and all(value is True for value in baseline_packet_checks.values())
|
|
and bool(baseline_remote_checks)
|
|
and all(value is True for value in baseline_remote_checks.values()),
|
|
"baseline_state_token_derivation_valid": bool(baseline and state_token_is_valid(baseline)),
|
|
"baseline_capture_challenge_nonce_valid": bool(SHA64_RE.fullmatch(baseline_nonce)),
|
|
"baseline_protocol_matches_packet": bool(
|
|
baseline and baseline.get("protocol_sha256") == packet.get("protocol_sha256")
|
|
),
|
|
"baseline_packet_file_matches": bool(
|
|
baseline and baseline.get("packet_file_sha256") == _sha256_file(args.packet)
|
|
),
|
|
"baseline_manifest_binding_matches_packet": bool(
|
|
baseline and baseline.get("manifest_binding") == packet.get("manifest_binding")
|
|
),
|
|
"canonical_fingerprint_matches_preflight": bool(
|
|
baseline
|
|
and (baseline.get("remote") or {}).get("fingerprint_after", {}).get("fingerprint_sha256")
|
|
== remote.get("fingerprint_after", {}).get("fingerprint_sha256")
|
|
),
|
|
"gateway_process_matches_preflight": bool(
|
|
baseline
|
|
and _service_identity((baseline.get("remote") or {}).get("service_after") or {})
|
|
== _service_identity(remote.get("service_after") or {})
|
|
),
|
|
}
|
|
status = (
|
|
"pass"
|
|
if all(local_packet_checks.values()) and all(remote_checks.values()) and all(baseline_checks.values())
|
|
else "fail"
|
|
)
|
|
packet_file_sha256 = _sha256_file(args.packet)
|
|
generated_at_utc = datetime.now(timezone.utc).isoformat()
|
|
capture_challenge_nonce = (
|
|
secrets.token_hex(32)
|
|
if args.phase == "preflight"
|
|
else str((baseline or {}).get("capture_challenge_nonce") or "")
|
|
)
|
|
baseline_state_token_sha256 = (
|
|
str((baseline or {}).get("state_token_sha256") or "") if args.phase == "postflight" else ""
|
|
)
|
|
exact_gate = ""
|
|
if status != "pass":
|
|
failed = [
|
|
key
|
|
for checks in (local_packet_checks, remote_checks, baseline_checks)
|
|
for key, value in checks.items()
|
|
if value is not True
|
|
]
|
|
exact_gate = "; ".join(part for part in (remote_stderr.strip(), ", ".join(failed)) if part)
|
|
current_canary = (
|
|
"Read-only pre-send readiness for the exact three-turn unseen Leo chain in Telegram"
|
|
if args.phase == "preflight"
|
|
else "Read-only post-send DB grounding and unchanged-state proof for the exact Telegram chain"
|
|
)
|
|
artifact = {
|
|
"schema": SCHEMA,
|
|
"generated_at_utc": generated_at_utc,
|
|
"mode": "telegram_visible_unseen_chain_zero_mutation_state_readback",
|
|
"phase": args.phase,
|
|
"status": status,
|
|
"ready_for_action_time_authorization": args.phase == "preflight" and status == "pass",
|
|
"ready_for_visible_capture_verification": args.phase == "postflight" and status == "pass",
|
|
"telegram_visible_messages_sent_by_collector": False,
|
|
"production_apply_executed": False,
|
|
"mutates_db": False,
|
|
"packet_path": str(args.packet),
|
|
"packet_file_sha256": packet_file_sha256,
|
|
"protocol_sha256": packet.get("protocol_sha256"),
|
|
"manifest_binding": packet.get("manifest_binding"),
|
|
"capture_challenge_nonce": capture_challenge_nonce,
|
|
"baseline_state_token_sha256": baseline_state_token_sha256,
|
|
"baseline_path": str(args.baseline) if args.baseline else "",
|
|
"subject_ref": args.subject_ref or "",
|
|
"packet_checks": local_packet_checks,
|
|
"remote_checks": remote_checks,
|
|
"baseline_checks": baseline_checks,
|
|
"remote": remote,
|
|
"remote_returncode": remote_returncode,
|
|
"remote_stderr": remote_stderr,
|
|
"blocker_readback": {
|
|
"current_canary": current_canary,
|
|
"attempted_routes": [
|
|
"local exact packet validation",
|
|
*(
|
|
[
|
|
"SSH deploy stamp and gateway service readback",
|
|
"two canonical repeatable-read read-only Postgres parity manifests",
|
|
*(["independent selected-object DB readback"] if args.subject_ref else []),
|
|
]
|
|
if remote.get("readback_attempts")
|
|
else []
|
|
),
|
|
],
|
|
"exact_gate": exact_gate,
|
|
"clear_CTA": (
|
|
"No user action is needed; repair the named readback check and rerun this collector."
|
|
if exact_gate
|
|
else "Use the packet's exact authorization sentence only when ready to cross the send boundary."
|
|
),
|
|
"next_non_user_action": (
|
|
"Run the authenticated-Chrome three-turn flow only after exact authorization, then collect postflight."
|
|
if args.phase == "preflight" and status == "pass"
|
|
else "Repair the failed preflight check and rerun before requesting authorization."
|
|
if args.phase == "preflight"
|
|
else "Run the visible capture verifier against this postflight and the retained Chrome evidence."
|
|
),
|
|
},
|
|
"claim_ceiling": {
|
|
"current_tier": "T3_live_readonly" if status == "pass" else "T0_failed_preflight",
|
|
"proven": (
|
|
"Two canonical read-only snapshots and the gateway process were unchanged during this probe."
|
|
if status == "pass"
|
|
else "No live readiness claim; one or more fail-closed checks did not pass."
|
|
),
|
|
"not_proven": [
|
|
"Telegram-visible delivery or reply behavior",
|
|
"authorization to send Telegram messages",
|
|
"canonical mutation",
|
|
],
|
|
},
|
|
}
|
|
artifact["state_token_sha256"] = derive_state_token(artifact)
|
|
return artifact
|
|
|
|
|
|
def write_json(path: Path, data: dict[str, Any]) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
|
|
|
|
def write_markdown(path: Path, data: dict[str, Any]) -> None:
|
|
remote = data.get("remote") or {}
|
|
service = remote.get("service_after") or {}
|
|
fingerprint = remote.get("fingerprint_after") or {}
|
|
lines = [
|
|
f"# Telegram-Visible Unseen Chain {data['phase'].title()}",
|
|
"",
|
|
f"Generated UTC: `{data['generated_at_utc']}`",
|
|
f"Status: `{data['status']}`",
|
|
f"Protocol SHA256: `{data['protocol_sha256']}`",
|
|
f"Packet file SHA256: `{data['packet_file_sha256']}`",
|
|
f"State token SHA256: `{data['state_token_sha256']}`",
|
|
f"Messages sent by collector: `{data['telegram_visible_messages_sent_by_collector']}`",
|
|
f"Mutates DB: `{data['mutates_db']}`",
|
|
"",
|
|
"## Checks",
|
|
"",
|
|
]
|
|
for section in ("packet_checks", "remote_checks", "baseline_checks"):
|
|
for key, value in sorted(data.get(section, {}).items()):
|
|
lines.append(f"- `{key}`: `{value}`")
|
|
lines.extend(
|
|
[
|
|
"",
|
|
"## Live Readback",
|
|
"",
|
|
f"- Deploy head: `{remote.get('deploy_head', '')}`",
|
|
f"- Deploy stamp: `{remote.get('last_deploy_sha', '')}`",
|
|
f"- Gateway MainPID: `{service.get('MainPID', '')}`",
|
|
f"- Gateway NRestarts: `{service.get('NRestarts', '')}`",
|
|
f"- Manifest: `{(data.get('manifest_binding') or {}).get('path', '')}`",
|
|
f"- Manifest SHA256: `{(data.get('manifest_binding') or {}).get('sha256', '')}`",
|
|
f"- Effective DB role: `{fingerprint.get('current_user', '')}`",
|
|
f"- Transaction read only: `{fingerprint.get('transaction_read_only', '')}`",
|
|
f"- Canonical fingerprint: `{fingerprint.get('fingerprint_sha256', '')}`",
|
|
f"- Canonical tables: `{fingerprint.get('table_count', '')}`",
|
|
f"- Canonical rows: `{fingerprint.get('total_rows', '')}`",
|
|
f"- Selected object ref: `{data.get('subject_ref', '')}`",
|
|
"",
|
|
"## Claim Ceiling",
|
|
"",
|
|
data["claim_ceiling"]["proven"],
|
|
"",
|
|
]
|
|
)
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text("\n".join(lines), encoding="utf-8")
|
|
|
|
|
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--phase", choices=("preflight", "postflight"), default="preflight")
|
|
parser.add_argument("--packet", type=Path, default=DEFAULT_PACKET)
|
|
parser.add_argument("--baseline", type=Path)
|
|
parser.add_argument("--subject-ref")
|
|
parser.add_argument("--ssh-target", default=DEFAULT_SSH_TARGET)
|
|
parser.add_argument("--ssh-key", type=Path, default=DEFAULT_SSH_KEY)
|
|
parser.add_argument("--connect-timeout", type=int, default=10)
|
|
parser.add_argument("--timeout", type=int, default=60)
|
|
parser.add_argument("--fingerprint-timeout", type=int, default=240)
|
|
parser.add_argument("--ssh-attempts", type=int, default=3)
|
|
parser.add_argument("--out", type=Path)
|
|
parser.add_argument("--markdown-out", type=Path)
|
|
args = parser.parse_args(argv)
|
|
if args.phase == "postflight" and (not args.baseline or not args.subject_ref):
|
|
parser.error("postflight requires --baseline and --subject-ref")
|
|
if args.subject_ref and not SUBJECT_REF_RE.fullmatch(args.subject_ref):
|
|
parser.error("--subject-ref must be an eight-hex prefix or UUID")
|
|
if args.out is None:
|
|
args.out = DEFAULT_PREFLIGHT_OUT if args.phase == "preflight" else DEFAULT_POSTFLIGHT_OUT
|
|
if args.markdown_out is None:
|
|
args.markdown_out = (
|
|
DEFAULT_PREFLIGHT_MARKDOWN_OUT if args.phase == "preflight" else DEFAULT_POSTFLIGHT_MARKDOWN_OUT
|
|
)
|
|
return args
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(argv)
|
|
data = collect_state(args)
|
|
write_json(args.out, data)
|
|
write_markdown(args.markdown_out, data)
|
|
print(
|
|
json.dumps(
|
|
{
|
|
"out": str(args.out),
|
|
"markdown_out": str(args.markdown_out),
|
|
"phase": args.phase,
|
|
"status": data["status"],
|
|
"mutates_db": False,
|
|
},
|
|
indent=2,
|
|
sort_keys=True,
|
|
)
|
|
)
|
|
return 0 if data["status"] == "pass" else 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|