"""Derive HUX entity ids from server-owned context and subject files.""" from __future__ import annotations import hashlib import hmac import os import re import stat from pathlib import Path DOMAIN = "hux.context.id.v1" KEY_BYTES = 32 RAW_ID = re.compile(r"^[A-Za-z0-9._:@+-]{1,200}$") SLOT = re.compile(r"^slot-[0-9]{1,3}$") SUBJECT = re.compile(r"^usr_[0-9a-f]{64}$") PAIRS = frozenset({("ses", "session"), ("conv", "conversation"), ("prj", "project"), ("run", "run")}) class ContextUnavailable(ValueError): """Trusted runtime context cannot be constructed.""" def _read_owned_key(path: str | Path) -> bytes: """Read one single-link, owner-only 32-byte context key without following links.""" flags = os.O_RDONLY | getattr(os, "O_CLOEXEC", 0) | getattr(os, "O_NOFOLLOW", 0) try: descriptor = os.open(Path(path), flags) except OSError as exc: raise ContextUnavailable("HUX context key is unavailable") from exc try: info = os.fstat(descriptor) if ( not stat.S_ISREG(info.st_mode) or info.st_uid != os.geteuid() or stat.S_IMODE(info.st_mode) != 0o600 or info.st_nlink != 1 or info.st_size != KEY_BYTES ): raise ContextUnavailable("HUX context key is unsafe") value = os.read(descriptor, KEY_BYTES + 1) except ContextUnavailable: raise except OSError as exc: raise ContextUnavailable("HUX context key is unavailable") from exc finally: os.close(descriptor) if len(value) != KEY_BYTES: raise ContextUnavailable("HUX context key is invalid") return value def derive_hux_id( key: bytes, prefix: str, purpose: str, slot: str, subject: str, raw: str, ) -> str: """Match WebUI's public ``hux.context.id.v1`` derivation exactly.""" if (prefix, purpose) not in PAIRS: raise ContextUnavailable("unsupported HUX id purpose") if not SLOT.fullmatch(slot) or not SUBJECT.fullmatch(subject): raise ContextUnavailable("HUX identity is malformed") if not isinstance(raw, str) or not RAW_ID.fullmatch(raw): raise ContextUnavailable("runtime context id is malformed") if not isinstance(key, bytes) or len(key) != KEY_BYTES: raise ContextUnavailable("HUX context key is invalid") message = "\0".join((DOMAIN, purpose, slot, subject, raw)).encode("utf-8") digest = hmac.new(key, message, hashlib.sha256).hexdigest()[:32] return f"{prefix}_{digest}" class ContextIds: """Stable HUX ids for one Hermes tool call.""" def __init__(self, key_file: str | Path, slot: str, subject: str) -> None: if not SLOT.fullmatch(slot) or not SUBJECT.fullmatch(subject): raise ContextUnavailable("HUX identity is malformed") self._key = _read_owned_key(key_file) self._slot = slot self._subject = subject def conversation(self, raw_session_id: str) -> str: """Map the persisted Hermes/WebUI session to its shared conversation id.""" return derive_hux_id( self._key, "conv", "conversation", self._slot, self._subject, raw_session_id ) def session(self, raw_session_id: str) -> str: """Map the persisted Hermes/WebUI session to its shared session id.""" return derive_hux_id( self._key, "ses", "session", self._slot, self._subject, raw_session_id ) def project(self, project_source: str) -> str: """Map the server-selected project source to its shared project id.""" return derive_hux_id( self._key, "prj", "project", self._slot, self._subject, project_source ) def run(self, raw_turn_id: str) -> str: """Map one stable Hermes turn to its shared HUX run id.""" return derive_hux_id(self._key, "run", "run", self._slot, self._subject, raw_turn_id)