orchestrata_ultimate.promotion.identity_scope
Reference › orchestrata_ultimate › promotion › identity_scope
The data types and parsing helpers of the identity verifier, plus the systemd discovery it starts from. It also defines the constants that set the scope of the check, including the unit-name globs and property list.
The types are VersionRecord (commit, ref, repo and deployed time parsed from VERSION), UnitFacts (a discovered unit with its scope, systemctl show properties and optional tools-tree root) and IdentityReport. The report accumulates units, mismatches (something differs), errors (something could not be inspected) and skipped entries. proven counts the judged units, exit_code is 2 when there are errors, else 1 when there are mismatches, else 0, render() gives the text form (IDENTITY OK with the short sha, or one line per problem), and as_dict() the JSON form.
parse_version reads the space-separated key value lines that the installer writes and returns nothing when the commit or deployed stamp is missing. parse_timestamp accepts ISO-8601 and systemd's default timestamp format. read_tools_version parses a tools tree's own VERSION and appends the cause to the error list when it fails. systemd_unescape and instance_of decode a template instance name without a subprocess, so the instance parameter stays data. show runs systemctl show for a unit and parse_show parses the output. discover lists units with systemctl list-units --all --plain and keeps those matching the globs and an optional regular expression.
At a glance
| Source | src/orchestrata_ultimate/promotion/identity_scope.py |
| Lines of code | 326 |
| Classes | 3 |
| Functions | 8 |
| Imports (internal) | none |
| Imported by | orchestrata_ultimate.promotion.identity, orchestrata_ultimate.promotion.identity_checks, orchestrata_ultimate.promotion.identity_core, orchestrata_ultimate.promotion.identity_skips |
| Tests that import it | tests/promotion/test_identity_tool_tree_instances.py, tests/scripts/test_identity_verifier_during_publish.py |
Class diagram
Dependency diagram
API
Classes
VersionRecord (dataclass)
Decorators: dataclass(frozen=True) · Line 115
| Attribute | Type | Default |
|---|---|---|
commit |
str |
|
ref |
str |
|
repo |
str |
|
deployed |
datetime |
UnitFacts (dataclass)
Decorators: dataclass · Line 123
| Attribute | Type | Default |
|---|---|---|
name |
str |
|
scope |
str |
|
properties |
dict[str, str] |
|
root |
Path | None |
None |
IdentityReport (dataclass)
Decorators: dataclass · Line 132
| Attribute | Type | Default |
|---|---|---|
root |
Path |
|
sha |
str |
'' |
units |
list[UnitFacts] |
field(default_factory=list) |
mismatches |
list[str] |
field(default_factory=list) |
errors |
list[str] |
field(default_factory=list) |
skipped |
list[str] |
field(default_factory=list) |
| Method | Signature | Summary |
|---|---|---|
proven property |
proven() -> int |
Every discovered unit is judged (D2) -- at exit 0 all of them proven. |
exit_code property |
exit_code() -> int |
D5: cannot-inspect always outranks a found mismatch. |
render |
render() -> str |
|
as_dict |
as_dict() -> dict[str, object] |
Functions
| Function | Signature | Summary |
|---|---|---|
systemd_unescape |
systemd_unescape(text: str) -> str |
Pure systemd-escape --unescape: - is / and \xHH is the byte HH (no subprocess -- the instance parameter is data, ADR-0067 R1). |
instance_of |
instance_of(name: str) -> str |
The raw %i of a template-instance unit name, "" for any other. |
read_tools_version |
read_tools_version(tools_root: Path, errors: list[str]) -> VersionRecord | None |
<tools_root>/VERSION parsed, or None with the cause appended to errors -- never a silent pass (the tool tree is its own deploy record). |
parse_version |
parse_version(text: str) -> VersionRecord | None |
Space-separated key value lines, as install-orchestrata.sh's printf writes them (D3 -- NOT key=value). |
parse_timestamp |
parse_timestamp(value: str) -> datetime | None |
Either an ISO-8601 stamp (VERSION) or systemd's default show format. |
parse_show |
parse_show(output: str) -> dict[str, str] |
A property that repeats (two ExecStartPre= lines, EnvironmentFiles= per file) is CONCATENATED, not overwritten (confirmed, systemd 257). |
show |
show(run: RunFn, scope: str, unit: str, properties: Sequence[str]) -> tuple[dict[str, str], str | None] |
|
discover |
discover(run: RunFn, scope: str, globs: Sequence[str], pattern: re.Pattern[str] | None) -> tuple[list[str], str | None] |
Every unit (any type) whose NAME matches one of globs. |
Source
"""Scope, identity records and discovery for ``identity_core.py`` (U-T6c).
Split out 2026-09-26 so ``identity_core.py`` stays under its own 300-line
budget (RUL-021): the module-level scoping constants (unit globs, resolved
``systemctl show`` property names, path-directive names), the identity
dataclasses (``VersionRecord``/``UnitFacts``/``IdentityReport``), the
``VERSION``/timestamp/``systemctl show`` parsers, and the subprocess-shaped
``show``/``discover`` calls (still driven by the caller's injected ``run``,
never a module-level subprocess of their own) all live here.
``identity_core.py`` imports every public name straight back, so the split
is invisible to every importer (``identity.py``,
``scripts/check_prod_tree_separation.py``,
``scripts/verify_deployment_identity.py``, every test) -- see that module's
own docstring for the full picture.
"""
from __future__ import annotations
import re
import subprocess
from collections.abc import Callable, Sequence
from dataclasses import dataclass, field
from datetime import UTC, datetime
from pathlib import Path
#: The deployed tool tree (ADR-0067 P3): template instances whose ExecStart
#: argv0 lives here are judged against THIS root, not the product ROOT.
TOOLS_ROOT = Path("/opt/orchestrata-tools")
# Prefixes a path-like directive token may legitimately live under besides
# ROOT itself. Anything under /home is NEVER on this list (amendment D2).
SYSTEM_PREFIXES = (
"/usr",
"/bin",
"/sbin",
"/etc",
"/run",
"/var",
"/tmp",
"/dev",
"/proc",
"/sys",
str(TOOLS_ROOT),
)
# Resolved values only -- drop-ins and `\`-continued directives are already
# merged into these by systemd itself (confirmed, systemd 257).
UNIT_PROPERTIES = (
"FragmentPath",
"DropInPaths",
"ExecStart",
"ExecStartPre",
"ExecStartPost",
"WorkingDirectory",
"EnvironmentFiles",
"Environment",
"Type",
"ActiveState",
"SubState",
"Result",
"MainPID",
"ExecMainStartTimestamp",
"NRestarts",
"StandardOutput",
"StandardError",
"StandardInput",
)
# Directives whose value is a path into a checkout. Canonical home of this
# constant -- scripts/check_prod_tree_separation.py imports it FROM here
# (moved 2026-09-23, amendment finding 4: identity.py must not need
# `scripts` importable at all to run).
#
# `Environment=` is here because of a real miss: orchestrata-history-mining
# carried `Environment=PYTHONPATH=<dev tree>/src`. An exported PYTHONPATH
# OUTRANKS a checkout's own .pth, so that one line makes a unit execute the
# development tree's source no matter how correct its WorkingDirectory and
# ExecStart are. `StandardOutput`/`StandardError`/`StandardInput`/
# `RootDirectory` are here for the same reason: each can carry (or redirect
# through) a path into the dev tree without touching WorkingDirectory or any
# Exec* directive at all.
PATH_DIRECTIVES = (
"WorkingDirectory",
"ExecStart",
"ExecStartPre",
"ExecStartPost",
"ExecStop",
"ExecReload",
"EnvironmentFile",
"Environment",
"StandardOutput",
"StandardError",
"StandardInput",
"RootDirectory",
)
# orchestrata-*/orchgraph-* services, plus every timer regardless of prefix.
# NOTE this is deliberately BROAD for check_prod_tree_separation.py's own
# purpose (examine every timer's own path directives, of which it has none
# in practice, so nothing is ever mistaken for "not checked"). identity.py's
# SCOPING rule is narrower and lives separately as SERVICE_GLOBS below --
# identity.py never treats "matches DEFAULT_GLOBS" alone as in-scope.
DEFAULT_GLOBS = ("orchestrata-*.service", "orchgraph-*.service", "*.timer")
# identity.py's actual scope (amendment finding 1): orchestrata-*/orchgraph-*
# SERVICES, and -- separately, per discovered service -- ONLY that service's
# OWN sibling `.timer` (same base name). A bare host timer (logrotate.timer,
# man-db.timer, ...) is never in scope just because it matches "*.timer".
SERVICE_GLOBS = tuple(g for g in DEFAULT_GLOBS if g.endswith(".service"))
RunFn = Callable[[Sequence[str]], "subprocess.CompletedProcess[str]"]
@dataclass(frozen=True)
class VersionRecord:
commit: str
ref: str
repo: str
deployed: datetime
@dataclass
class UnitFacts:
name: str
scope: str # "user" | "system"
properties: dict[str, str]
#: Set only for a tool-tree template instance: the root its verdicts used.
root: Path | None = None
@dataclass
class IdentityReport:
root: Path
sha: str = ""
units: list[UnitFacts] = field(default_factory=list)
mismatches: list[str] = field(default_factory=list)
errors: list[str] = field(default_factory=list)
skipped: list[str] = field(default_factory=list)
@property
def proven(self) -> int:
"""Every discovered unit is judged (D2) -- at exit 0 all of them proven."""
return len(self.units)
@property
def exit_code(self) -> int:
"""D5: cannot-inspect always outranks a found mismatch."""
if self.errors:
return 2
if self.mismatches:
return 1
return 0
def render(self) -> str:
if not self.errors and not self.mismatches:
lines = [f"IDENTITY OK {self.sha[:12]} · {self.proven} unit(s) proven"]
else:
lines = [f"cannot inspect: {problem}" for problem in self.errors]
lines.extend(self.mismatches)
lines.extend(self.skipped)
lines.extend(
f"judged {u.name} against {u.root} "
"(ADR-0067 R1: instance parameter is data)"
for u in self.units
if u.root is not None
)
return "\n".join(lines)
def as_dict(self) -> dict[str, object]:
return {
"root": str(self.root),
"sha": self.sha,
"exit_code": self.exit_code,
"proven": self.proven,
"units": [
{
"name": u.name,
"scope": u.scope,
"properties": u.properties,
"judged_against": str(u.root) if u.root else None,
}
for u in self.units
],
"mismatches": list(self.mismatches),
"errors": list(self.errors),
"skipped": list(self.skipped),
}
def systemd_unescape(text: str) -> str:
"""Pure ``systemd-escape --unescape``: ``-`` is ``/`` and ``\\xHH`` is the
byte HH (no subprocess -- the instance parameter is data, ADR-0067 R1)."""
out = bytearray()
i = 0
while i < len(text):
hex_pair = text[i + 2 : i + 4]
if text[i : i + 2] == "\\x" and re.fullmatch(r"[0-9a-fA-F]{2}", hex_pair):
out.append(int(hex_pair, 16))
i += 4
continue
out += b"/" if text[i] == "-" else text[i].encode()
i += 1
return out.decode("utf-8", errors="replace")
def instance_of(name: str) -> str:
"""The raw ``%i`` of a template-instance unit name, ``""`` for any other."""
return name.split(".service")[0].partition("@")[2]
def read_tools_version(tools_root: Path, errors: list[str]) -> VersionRecord | None:
"""``<tools_root>/VERSION`` parsed, or None with the cause appended to
``errors`` -- never a silent pass (the tool tree is its own deploy record)."""
path = tools_root / "VERSION"
try:
text = path.read_text(encoding="utf-8")
except OSError as exc:
errors.append(f"cannot read {path}: {exc}")
return None
record = parse_version(text)
if record is None:
errors.append(f"{path} is missing or not in the `key value` format")
return record
def parse_version(text: str) -> VersionRecord | None:
"""Space-separated `key value` lines, as install-orchestrata.sh's printf
writes them (D3 -- NOT key=value)."""
fields: dict[str, str] = {}
for raw in text.splitlines():
line = raw.strip()
if not line or line.startswith("#"):
continue
key, _, value = line.partition(" ")
fields[key.strip()] = value.strip()
commit = fields.get("commit", "")
deployed_raw = fields.get("deployed", "")
if not commit or not deployed_raw:
return None
deployed = parse_timestamp(deployed_raw)
if deployed is None:
return None
return VersionRecord(
commit=commit,
ref=fields.get("ref", ""),
repo=fields.get("repo", ""),
deployed=deployed,
)
def parse_timestamp(value: str) -> datetime | None:
"""Either an ISO-8601 stamp (VERSION) or systemd's default show format."""
text = value.strip()
if not text or text in ("n/a", "0"):
return None
try:
parsed = datetime.fromisoformat(text)
except ValueError:
pass
else:
return parsed if parsed.tzinfo else parsed.replace(tzinfo=UTC)
match = re.match(
r"^\w{3}\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+(\S+)$", text
)
if not match:
return None
date_part, time_part, _tz = match.groups()
try:
parsed = datetime.strptime(f"{date_part} {time_part}", "%Y-%m-%d %H:%M:%S")
except ValueError:
return None
return parsed.replace(tzinfo=UTC)
def parse_show(output: str) -> dict[str, str]:
"""A property that repeats (two ExecStartPre= lines, EnvironmentFiles=
per file) is CONCATENATED, not overwritten (confirmed, systemd 257)."""
properties: dict[str, str] = {}
for line in output.splitlines():
if "=" not in line:
continue
key, _, value = line.partition("=")
key, value = key.strip(), value.strip()
properties[key] = f"{properties[key]} {value}" if key in properties else value
return properties
def show(
run: RunFn, scope: str, unit: str, properties: Sequence[str]
) -> tuple[dict[str, str], str | None]:
flags = ["--user"] if scope == "user" else []
result = run(["systemctl", *flags, "show", "-p", ",".join(properties), unit])
if result.returncode != 0:
problem = (result.stderr or result.stdout or "").strip()
return {}, f"systemctl show {scope} {unit} failed: {problem}"
return parse_show(result.stdout), None
def discover(
run: RunFn, scope: str, globs: Sequence[str], pattern: re.Pattern[str] | None
) -> tuple[list[str], str | None]:
"""Every unit (any type) whose NAME matches one of `globs`.
`--plain` is load-bearing: without it, systemctl prefixes a "● " marker
on units in a "bad" state, shifting `line.split()[0]` off the unit name
(discovered against this host's real, already-failed orchestrata units).
"""
import fnmatch
flags = ["--user"] if scope == "user" else []
result = run(["systemctl", *flags, "list-units", "--all", "--no-legend", "--plain"])
if result.returncode != 0:
problem = (result.stderr or result.stdout or "").strip()
return [], f"systemctl list-units ({scope}) failed: {problem}"
names: list[str] = []
for raw in result.stdout.splitlines():
line = raw.strip()
if not line:
continue
unit = line.split()[0]
if not any(fnmatch.fnmatch(unit, glob) for glob in globs):
continue
if pattern is not None and not pattern.match(unit):
continue
names.append(unit)
return names, None