Governed repository execution
The original backend contract remains unchanged. An opt-in strict OCI subprocess-deny profile adds a separate worker protocol; v1 static adapters retain their original guarantees.
Available in published stable 0.3.0.
Repository exposure now supports direct, governed local-process and governed OCI REST/MCP bundles. The requested backend must be explicitly compatible with every selected Exposure Record. Generation never ranks backends or falls back to direct. The existing Readiness eligibility restrictions remain in force.
| Mode | Invocation boundary | State between calls |
|---|---|---|
| Direct | Trusted transport interpreter | Globals, imports and mutable defaults persist |
| Governed local | Fresh Python process and copied invocation directory | Reset each invocation |
| Governed OCI | Fresh container and read-only copied bundle | Reset each invocation |
Independent contracts
apizr.repository-runtime/v1 binds the complete Repository Interface and digest,
Exposure Plan digest, source-universe digest, exact capability ID, shared
InvocationContract and digest, bound effect snapshot, ExecutionPolicy v1 and digest.
The source-universe digest covers the canonical source manifest: each relative
source/bundle path, module, package identity, size and content hash.
apizr.repository-runtime/v2 wraps that worker contract with ExecutionPolicy v2
and digest, explicit RuntimeImage and worker digest. The worker's execution context
records which exposure backend authorized it; it does not claim kernel enforcement.
A v1 worker authorized for OCI cannot be invoked as a local plan.
apizr.repository-execution-bundle/v1 and /v2 independently bind these plans to
REST/MCP manifests. They record the repository/exposure/transport-contract digests,
policy artifact, capability-ID-to-plan mapping and exact artifact hashes. v2 also
binds provider and runtime image. Plan filenames are SHA-256 hashes of capability
IDs. Public names are presentation; capability IDs authorize invocation.
The existing single-source runtime/bridge contracts and repository transport v1 contracts are unchanged. Omitting the execution policy produces the exact previous direct artifacts. Committed schemas:
Static generation and integrity
Generation consumes validated catalog, graph, readiness, exposure and interface
evidence. It does not execute project code, probe Docker, check runtime image
availability, inspect installed dependencies, run Git or connect to the network.
Unknown effects required by effects.require_known refuse planning; the execution
planner consumes the bound effect snapshot without inferring effects.
The generator prepares all plans and artifacts before staging and atomic output publication. Output must be new or empty. Artifacts contain no build-host path, PID, timestamp or hostname. Source/capability ordering and repository relocation do not change the bytes.
At startup, the transport checks its manifest, all artifact hashes, repository interface, upstream evidence, exposure policy, execution bridge/policy, each plan, source manifest, source bytes and backend availability. It never installs the project importer or imports selected/support project modules.
Before every call, it rereads the manifest against the startup digest anchor, revalidates all emitted artifacts and their cross-bindings, then uses the verified bytes to populate a fresh invocation root. No cached startup plan bypasses these checks. A modified source, policy, plan, interface, worker or bridge produces a sanitized failure; restoring the original bytes permits later calls. Hash binding is integrity within the trusted bundle, not publisher authentication against an attacker who replaces an entire bundle before startup.
Worker and importer
Only the fresh worker installs the existing RepositoryLoader. It verifies the
exact source universe and imports the selected module using verified bytes,
including real package initializers, relative imports and synthetic namespace
parents. Undeclared local children and unchecked filesystem/bytecode fallbacks
remain refused. Unrelated modules are checked without executing their source.
An unexposed helper remains private even when selected code calls it.
The worker uses the shared framed protocol, input bounds, verify_binding,
arguments, sync/async invocation and finite JSON output rules. Source exceptions,
stderr, internal paths and daemon errors are not returned to clients. Public
results reuse apizr.execution-result/v1 and /v2.
| Outcome | REST | MCP message |
|---|---|---|
| Success | 200 | JSON result |
| Invalid input | 422 | Invalid tool arguments |
| Timeout | 504 | Tool execution timed out |
| OCI resource limit | 503 | Tool execution resource limit exceeded |
| Other failure | 500 | Tool execution failed |
Local and OCI boundaries
Local execution reuses wall timeout, input/output limits, clean or allowlisted environment, fresh working directory and process-group cleanup. It provides no network denial, filesystem sandbox or absolute subprocess prohibition. Unsupported controls fail closed. A missing local backend refuses startup/invocation.
OCI uses the same reviewed Docker launch implementation as single-source execution:
network none, read-only root, UID/GID 65532, dropped capabilities, no-new-privileges,
seccomp, private IPC/cgroup namespace, memory/CPU/PID limits, bounded scratch,
read-only /bundle and unconditional teardown. Only the fixed repository entrypoint
differs. There is no second security-flag list or generic Docker argument/mount/device
escape. This is container isolation, not a VM. PID limits do not prohibit subprocesses;
subprocess_deny remains refused under this original contract.
Repository OCI startup and invocation verify the immutable sha256:<64hex> image
ID, explicit Linux platform, existing worker compatibility and the additional label
org.apizr.repository.worker.protocol=apizr.repository-runtime/v1. An old image
with only the single-source label is insufficient. The development worker image
advertises both. There is no tag resolution, automatic pull or local fallback.
The dedicated entrypoint clears image environment, retains only explicitly allowed
names, changes to /bundle and starts the repository worker.
OOM is classified as resource_limit only when the provider reports a terminal
container with positive OOM evidence. Exit 137 alone is not sufficient. Timeout
classification and unconditional cleanup retain the existing semantics.
Deployment responsibility
Standalone generated transports embed only the reviewed runtime dependency closure;
installation of outerspace-apizr in the transport environment is unnecessary.
They do not embed analyzers, repository scanners, graph builders, notebook conversion
or legacy generators. Local workers use that Python environment; OCI workers use
the explicitly selected image. External application dependencies must already be
installed there. Missing dependencies fail sanitized; nothing installs them for you.
Only Python source is packaged. Data/resources remain an explicit deployment concern. Bundling the scanned source universe does not prove complete runtime dependency closure or transitive safety. Code and dependencies must still be trusted.
Verification
Tests pin every direct artifact hash against baseline #91 and preserve all existing single-source goldens. Four governed manifest/bridge goldens cover local/OCI REST/MCP. Audit hooks guard generation and actual REST, MCP stdio and MCP Streamable HTTP transport processes. Real workers verify fresh globals/defaults/support-module state, private helpers, package imports, tamper recovery, crash and timeout containment. The mandatory OCI job runs those transports and reuses reviewed kernel isolation, ordinary/detached descendant and repeated OOM probes. Both new packages have separate 90% branch-aware coverage floors and strict Pyright checks.
This completes the intended core 0.3 runtime flow. The repository is in its release-candidate and product pass; publication remains a separate deliberate action.