Skip to content

container

container

Bottom-up container→state_dir resolution via podman annotations.

Shielded containers are launched with a terok.shield.state_dir annotation that points at the per-container state directory written by pre_start(). The OCI hook already reads that annotation out of the runtime-provided OCI state JSON (see resources/hook_entrypoint.py). This module does the same lookup for consumers that only have a container name and no in-process ShieldConfig — the clearance hub's verdict path, ad-hoc CLI invocations against a live container, anything that enters from the podman side of the handoff rather than from terok's task orchestration.

The annotation is the single source of truth for a shielded container's state directory: both the OCI hook (via crun's stdin) and the CLI (via this module) converge on the same string. In-process callers (terok-sandbox.make_shield) supply state_dir at construction and don't need to do a lookup.

On hosts where podman inspect isn't reachable (no podman on PATH, no rootless user namespace, container simply doesn't exist), the resolver returns None and callers fall back to whatever legacy behaviour they had.

ShieldAnnotations(version, state_dir) dataclass

Every shield-owned OCI annotation read off one container in one inspect.

Each field carries the same "cannot determine" None its single-annotation resolver uses; a failed inspect (podman missing, container absent) yields a record with every field None.

version instance-attribute

Bundle version — see resolve_shield_version.

state_dir instance-attribute

State directory — see resolve_state_dir.

resolve_state_dir(container)

Return the per-container state_dir from podman annotations, or None.

Reads the terok.shield.state_dir annotation out of the container's config. Any failure — podman missing, container absent, annotation not set, non-absolute, JSON malformed — collapses to None so callers can fall through.

Parameters:

Name Type Description Default
container str

Container name or ID (short or full) as podman knows it.

required

Returns:

Type Description
Path | None

The resolved Path if the annotation is present and absolute,

Path | None

otherwise None.

Source code in src/terok_shield/container.py
def resolve_state_dir(container: str) -> Path | None:
    """Return the per-container ``state_dir`` from podman annotations, or ``None``.

    Reads the ``terok.shield.state_dir`` annotation out of the container's
    config.  Any failure — podman missing, container absent, annotation not
    set, non-absolute, JSON malformed — collapses to ``None`` so callers can
    fall through.

    Args:
        container: Container name or ID (short or full) as podman knows it.

    Returns:
        The resolved ``Path`` if the annotation is present and absolute,
        otherwise ``None``.
    """
    annotations = _annotations(_inspect_records(container))
    return None if annotations is None else _state_dir_from(annotations)

resolve_shield_version(container)

Return the bundle version a container was prepared with, or None.

Reads the terok.shield.version OCI annotation stamped by Shield.pre_start. None when the container is absent, unshielded, or the annotation is missing / non-integer — callers treat that as "cannot determine" and fall through rather than block. An orchestrator compares this against BUNDLE_VERSION to refuse restarting a container whose bundle predates the installed shield (fail-fast, re-create).

Source code in src/terok_shield/container.py
def resolve_shield_version(container: str) -> int | None:
    """Return the bundle version a container was prepared with, or ``None``.

    Reads the ``terok.shield.version`` OCI annotation stamped by
    [`Shield.pre_start`][terok_shield.Shield.pre_start].  ``None`` when the
    container is absent, unshielded, or the annotation is missing / non-integer
    — callers treat that as "cannot determine" and fall through rather than
    block.  An orchestrator compares this against
    [`BUNDLE_VERSION`][terok_shield.state.BUNDLE_VERSION] to refuse restarting a
    container whose bundle predates the installed shield (fail-fast, re-create).
    """
    annotations = _annotations(_inspect_records(container))
    return None if annotations is None else _version_from(annotations)

resolve_annotations(container)

Read the shield annotations off container in a single podman inspect.

A sweep that needs both the bundle version and the state dir pays one inspect here instead of one per resolve_shield_version / resolve_state_dir call. Per-field failures collapse to None exactly as in the single resolvers.

Parameters:

Name Type Description Default
container str

Container name or ID (short or full) as podman knows it.

required

Returns:

Type Description
ShieldAnnotations
ShieldAnnotations

record; all fields are None when the inspect itself fails.

Source code in src/terok_shield/container.py
def resolve_annotations(container: str) -> ShieldAnnotations:
    """Read the shield annotations off *container* in a single ``podman inspect``.

    A sweep that needs both the bundle version and the state dir pays one
    inspect here instead of one per
    [`resolve_shield_version`][terok_shield.container.resolve_shield_version] /
    [`resolve_state_dir`][terok_shield.container.resolve_state_dir] call.
    Per-field failures collapse to ``None`` exactly as in the single
    resolvers.

    Args:
        container: Container name or ID (short or full) as podman knows it.

    Returns:
        A [`ShieldAnnotations`][terok_shield.container.ShieldAnnotations]
        record; all fields are ``None`` when the inspect itself fails.
    """
    annotations = _annotations(_inspect_records(container))
    if annotations is None:
        return ShieldAnnotations(version=None, state_dir=None)
    return ShieldAnnotations(
        version=_version_from(annotations),
        state_dir=_state_dir_from(annotations),
    )