fix: harden preprocessing state and child capabilities

This commit is contained in:
2026-08-11 14:30:16 +02:00
parent a5916f6177
commit ee5ac381b3
12 changed files with 323 additions and 104 deletions
+8 -8
View File
@@ -20,12 +20,11 @@ from tht.vectorstore.embeddings import EmbeddingsError
preprocess_app = typer.Typer(help="Materialize versioned preprocessing artifacts")
logger = logging.getLogger(__name__)
def _require_writer_capability() -> None:
"""Mutating children opt into the backend-owned fd capability contract."""
import os
if os.environ.get("THOTH_WORKSPACE_CAPABILITY_REQUIRED") == "1":
from tht.workspace_writer_lock import require_workspace_writer_capability
require_workspace_writer_capability()
def _require_writer_capability(*, workspace_id: str | None = None, revision: str | None = None) -> None:
# Authorization is unconditional: an environment marker is attacker-controlled
# and must never turn a mutating direct invocation into an authorized child.
from tht.workspace_writer_lock import require_workspace_writer_capability
require_workspace_writer_capability(workspace_id=workspace_id, revision=revision)
_PREPROCESS_EXPECTED_ERRORS = (
OSError, RuntimeError, ValueError, TypeError, KeyError,
@@ -36,7 +35,6 @@ _PREPROCESS_EXPECTED_ERRORS = (
def run_dwh_from_config(
config: Path, *, steps: tuple[str, ...], resume: str | None = None,
):
_require_writer_capability()
from tht.cli.lsh_cmd import build_lsh_artifacts
from tht.cli.schema_cmd import _load_config_or_exit, refresh_catalog
from tht.jobs.dwh_pipeline import (
@@ -45,6 +43,7 @@ def run_dwh_from_config(
)
cfg = _load_config_or_exit(config)
_require_writer_capability(workspace_id=getattr(getattr(cfg, "runtime_identity", None), "workspace_id", None), revision=getattr(getattr(cfg, "runtime_identity", None), "workspace_revision", None))
binding = config_dwh_binding(cfg)
workspace_root = cfg.paths.artifacts.parent
lsh_names = (
@@ -82,7 +81,6 @@ def _parse_dwh_steps(value: str) -> tuple[str, ...]:
def run_from_config(config: Path, *, dry_run: bool = False, resume: str | None = None):
_require_writer_capability()
from tht.adapters.factory import build_evidence_sources, build_vector_store
from tht.cli.vector_cmd import make_embedder
from tht.corpus.chunk import ChunkPolicy
@@ -90,6 +88,7 @@ def run_from_config(config: Path, *, dry_run: bool = False, resume: str | None =
from tht.corpus.store import CorpusStore
cfg = _load_config_or_exit(config)
_require_writer_capability(workspace_id=getattr(getattr(cfg, "runtime_identity", None), "workspace_id", None), revision=getattr(getattr(cfg, "runtime_identity", None), "workspace_revision", None))
if cfg.embeddings is None:
raise RuntimeError("embeddings are not configured")
corpus_root = cfg.paths.artifacts.parent / "corpus"
@@ -123,6 +122,7 @@ def gc_from_config(config: Path, *, dry_run: bool = False):
from tht.corpus.store import CorpusStore
cfg = _load_config_or_exit(config)
_require_writer_capability(workspace_id=getattr(getattr(cfg, "runtime_identity", None), "workspace_id", None), revision=getattr(getattr(cfg, "runtime_identity", None), "workspace_revision", None))
if cfg.embeddings is None:
raise RuntimeError("embeddings are not configured")
corpus_root = cfg.paths.artifacts.parent / "corpus"