Skip to main content
Version: 1.0.0 (Latest)

Gate Packages

Gate packages let you install custom ExecutionGate and AttributeGate classes onto a paired cluster from a Python package index — without rebuilding the platform image. Shipped in kamiwaza-mesh-v1.0.0.

A gate package is an ordinary Python package whose modules contribute one or more gate classes (subclasses of ExecutionGate or AttributeGate). The cluster's kz.gates.packages API (Python module: kamiwaza_sdk.gates.packages) installs the package into an isolated per-package directory, makes its classes reachable from kz.gates.discover, and persists the install record. The classes are then bindable via the existing cluster.set_execution_gate(...) and datasets.set_gate(...) APIs covered in Execution Gates.

AI-drafted content

This page was drafted with AI assistance and is pending human review. Specific behavioral claims are verifiable against the canonical design at docs/specifications/core/federation-api-and-sdk/design.md §6.2 WS-M5. Procedural code examples are derived directly from the M5 smoke playbook.

When to use this

Use gate packages when:

  • You need to apply policy logic (clearance gating, attribute mapping, tier-based access) that lives in code your team owns
  • You want to ship that policy without forking or rebuilding the Kamiwaza platform image
  • Your gate class implements either the ExecutionGate protocol (cluster-scoped job authorization) or AttributeGate protocol (dataset-scoped attribute resolution)

Don't use gate packages for:

  • General-purpose application code — gate packages are isolated to the gate runtime and don't have access to the broader application context
  • Policy that's already expressible via standard ReBAC relations — use those directly via the subjects and datasets APIs instead

Lifecycle overview

install ─▶ discover ─▶ bind ─▶ (job submit / dataset access) ─▶ replace ─▶ uninstall

└─ atomic in-place
(no unbound-gate window)

Each lifecycle action is an SDK call:

ActionSDK callRequired permission
Installkz.gates.packages.install(spec, hash_digest=..., index_url=...)admin
List installedkz.gates.packages.list()admin
Get onekz.gates.packages.get(name)admin
Atomic replacekz.gates.packages.replace(name, new_spec, hash_digest=..., index_url=...)admin
Uninstallkz.gates.packages.uninstall(name)admin (refused if any active binding references the package's classpaths)
Discover a classkz.gates.discover(classpath)viewer or above
Bind as ExecutionGatekz.cluster.set_execution_gate(type=..., config=...)admin
Bind as AttributeGatekz.datasets.set_gate(urn=..., type=..., config=...)admin

Installing a gate package

from kamiwaza_sdk import KamiwazaClient

kz = KamiwazaClient(base_url="https://kamiwaza.example.com/api")

# Hash-pin to the exact wheel you've verified out-of-band.
# pip refuses installs whose downloaded artifact doesn't match.
result = kz.gates.packages.install(
"acme-gates==1.0.0",
hash_digest="sha256:<sha256-of-the-wheel>",
index_url="https://pypi.example.com/simple",
)
print(result.package.name, result.package.version)
print(result.package.classpaths) # ['acme_gates.gate.AcmeAttributeGate', ...]

The install path:

  1. Resolves the wheel from index_url (any PEP 503 simple index is acceptable, including PyPI, a private mirror, or a local file index served over HTTP)
  2. Validates the SHA-256 digest against the supplied hash_digest via pip install --require-hashes — if pip can't verify, install fails before any files are written
  3. Installs into a per-package directory under the gate-packages PVC (mounted on the Ray head, the scheduler, and the API server pods)
  4. Scans the installed package for ExecutionGate/AttributeGate subclasses and records the resulting classpaths in the cluster_gate_packages table
  5. Extends the API server's classpath allowlist and sys.path so kz.gates.discover sees the new gate immediately

Discovering and binding

Once installed, the gate classpath is reachable via discover:

gate = kz.gates.discover("acme_gates.gate.AcmeAttributeGate")
print(gate.name, gate.kind, gate.config_schema)

Bind as a cluster ExecutionGate (if the class implements that protocol) or as a per-dataset AttributeGate (if the class implements that protocol). The same APIs work whether the class came from a built-in module or an installed gate package — there's no separate binding path for installed gates.

Atomic replace

Replace updates the package in place without leaving a window where the bound gate class is unimportable. The platform installs the new version into a staging sibling directory, validates that the new version's classpath set is a superset of the old version's bound classpaths, then performs a two-rename POSIX swap.

result = kz.gates.packages.replace(
"acme-gates",
"acme-gates==1.0.1",
hash_digest="sha256:<sha256-of-the-new-wheel>",
index_url="https://pypi.example.com/simple",
)

If the new version drops a classpath that's currently bound, the replace is refused with GatePackageClasspathDropError and the live tree is unchanged. This is the classpath-superset guarantee: an actively-bound gate can never be silently removed by an upgrade.

Hash-mismatch refusal

The platform refuses an install or replace whose downloaded artifact doesn't match the supplied hash:

from kamiwaza_sdk.exceptions import GatePackageHashMismatchError

try:
kz.gates.packages.replace(
"acme-gates",
"acme-gates==1.0.1",
hash_digest="sha256:" + "0" * 64, # wrong on purpose
index_url="https://pypi.example.com/simple",
)
except GatePackageHashMismatchError as exc:
print(exc.body["detail"])

The refusal is enforced by pip's --require-hashes mode; the platform doesn't compute or trust a separately-derived hash. The live tree is untouched.

Uninstall

kz.gates.packages.uninstall("acme-gates")

The uninstall path checks for active bindings: any cluster ExecutionGate or per-dataset AttributeGate whose classpath matches one contributed by the package will cause the uninstall to refuse with GatePackageUninstallBlockedError, naming the bound classpath(s). Unbind the gate first, then retry.

Security model

The gate-packages feature ships with several boundaries:

  • Hash pinning is required on every install and replace. The platform does not allow unhashed installs.
  • Admin-only API: the install/replace/uninstall endpoints require the admin role on the local cluster. Federation peers cannot install gate packages remotely.
  • NetworkPolicy isolation: when the optional top-level chart value workerNetworkPolicy.enabled is set to true, the Ray worker pods running gate code are blocked from arbitrary internet egress. The Ray head pod has a similar policy under the top-level rayHeadNetworkPolicy.enabled. Both are opt-in; default off. (Note the chart keys are top-level, not nested under worker.networkPolicy or rayHead.networkPolicy — a common operator override mistake that silently no-ops.)
  • Audit trail: every install/replace/uninstall emits a gate_package_lifecycle audit event with actor, action, name, version, hash_digest, and (for replace) prior_hash.

Limits and trade-offs

  • A gate-package install affects the local cluster only. Federated peers must install the same package on their side if they want to bind it locally.
  • The classpath-superset guarantee means rollback to an older package version is an install (of the older version) plus an unbind/rebind, not an automatic operation. Plan upgrades to be additive when possible.
  • The PVC backing gate-packages-venv ships disabled by default (authz.gatePackages.pvc.enabled: false in the platform chart) — operators must explicitly opt in before any gate-package install will succeed. Once enabled, the PVC falls through to ReadWriteOnce, which is sufficient on single-node clusters. Multi-node clusters must override both authz.gatePackages.pvc.accessModes to [ReadWriteMany] AND authz.gatePackages.pvc.storageClassName to a RWX-Filesystem class (CephFS / NFS / similar) — overriding only one silently no-ops. See your install team's helm overrides documentation.

Reference