Version: 1.0.1 (Latest)

## Gate Packages Overview

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](https://docs.kamiwaza.ai/federation/execution-gates).

## When to Use Gate Packages

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

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

│

└─ atomic in-place

(no unbound-gate window)
```

Each lifecycle action is an SDK call:

| Action              | SDK call                                                          | Required permission |
| ------------------- | ----------------------------------------------------------------- | ------------------- |
| Install             | `kz.gates.packages.install(spec, hash_digest=..., index_url=...)`| admin               |
| List installed      | `kz.gates.packages.list()`                                       | admin               |
| Get one            | `kz.gates.packages.get(name)`                                    | admin               |
| Atomic replace      | `kz.gates.packages.replace(name, new_spec, hash_digest=..., index_url=...)` | admin  |
| Uninstall           | `kz.gates.packages.uninstall(name)`                             | admin               |
| Discover a class    | `kz.gates.discover(classpath)`                                   | viewer or above     |
| Bind as ExecutionGate | `kz.cluster.set_execution_gate(type=..., config=...)`       | admin               |
| Bind as AttributeGate | `kz.datasets.set_gate(urn=..., type=..., config=...)`        | admin               |

## Installing a Gate Package

```python
from kamiwaza_sdk import KamiwazaClient

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

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', ...]
```

## Discovering and Binding

Once installed, the gate classpath is reachable via discover:

```python
gate = kz.gates.discover("acme_gates.gate.AcmeAttributeGate")

print(gate.name, gate.kind, gate.config_schema)
```

## 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.

```python
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",
)
```

## Hash-mismatch Refusal

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

```python
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"])
```

## Uninstall

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

## 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.
- **NetworkPolicy isolation**: optional configurations that provide additional security measures for gate code execution.

## 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**.
