Commit the previously-untracked standard directories and catalog them in the
root README alongside proof:
- aac/ — communication cargo class: portable boards + a ten-level
offline-first cascade (Apache-2.0 code / CC-BY-4.0 spec).
- map/ — geospatial cargo class: range-requestable signed tilesets +
an F0–F4 fidelity cascade (Apache-2.0 code / CC-BY-4.0 spec).
- sandbox/— isolated, metered, attested execution across the heterogeneous
compute fabric (Apache-2.0 code / CC-BY-4.0 spec).
- jouleclaw-otel-authz, jouleclaw-trust — new jouleclaw reference crates.
Root README now lists aac, map, sandbox, and proof in the catalog, the tree,
and the contribution guide.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
114 lines
8.2 KiB
Markdown
114 lines
8.2 KiB
Markdown
# The capability-grant model
|
||
|
||
Normative companion to [`SPEC.md`](../SPEC.md) §5. RFC 2119 keywords apply.
|
||
|
||
A **grant set** is the complete, explicit description of what a unit may touch. The model is **deny-by-default**: any access to a resource not named by a grant **MUST** be denied. The grant set is content-addressed before the run (its `grants_id`, SPEC §3) and **MUST NOT** widen during the run (SPEC §1.2).
|
||
|
||
```
|
||
Grants {
|
||
compute : ComputeGrant,
|
||
storage : StorageGrant,
|
||
network : NetworkGrant,
|
||
kv : KvGrant,
|
||
host : HostGrant,
|
||
}
|
||
```
|
||
|
||
The **default grant set is empty**: no devices beyond `Cpu`, no storage, no network, no kv, no host services. Every capability is opt-in.
|
||
|
||
## ComputeGrant — the heterogeneity clause
|
||
|
||
```
|
||
ComputeGrant {
|
||
devices : Set<DeviceClass>, // Cpu | Simd | Gpu | Npu | Tpu | Lpu | Photonic | Quantum
|
||
energy_budget_uj : u64,
|
||
cpu_time_ms : u64,
|
||
memory_bytes : u64,
|
||
}
|
||
```
|
||
|
||
Rules:
|
||
|
||
1. **Dispatch is gated by class.** A unit **MUST NOT** dispatch work to a device class absent from `devices`; the host **MUST** deny it. `Cpu` is implicitly present.
|
||
2. **The host must isolate AND meter every granted class.** A host **MUST** refuse the grant (decline the run) if `devices` names a class it cannot both confine at the tier in force **and** attribute energy to in the receipt (SPEC §6.2). A class the host can run but cannot meter is not grantable — metering is not optional.
|
||
3. **Class minimums bind the tier.** Any class beyond `Cpu`/`Simd` requires ≥ S2; direct device assignment requires ≥ S5; confidential compute requires ≥ S6 (see [`tiers.md`](tiers.md)).
|
||
4. **Budgets are hard.** On exceeding `energy_budget_uj`, `cpu_time_ms`, or `memory_bytes`, the host **MUST** terminate the unit and record the breached bound as `outcome = Terminated{ breach }` in the attestation (SPEC §7.1).
|
||
|
||
The energy budget is denominated in the same unit as the receipt — integer microjoules — so a unit's grant and its account are directly comparable.
|
||
|
||
## StorageGrant
|
||
|
||
```
|
||
StorageGrant {
|
||
read : [Glob], write : [Glob], delete : [Glob],
|
||
max_iops : u64?, // optional sustained-I/O ceiling: ops/sec
|
||
max_read_bps : u64?, // optional sustained-I/O ceiling: bytes/sec
|
||
}
|
||
```
|
||
|
||
A path operation **MUST** match at least one allow-glob for its mode (`read`/`write`/`delete`) or be denied. Globs use `*` (single segment) and `**`/`/*` (prefix) matching; the matching algorithm is fixed in the reference `sandbox-capability` crate and vectored. Write and delete do not imply read. The empty lists deny all storage.
|
||
|
||
### I/O ceilings — the contention lever
|
||
|
||
A grant **MAY** carry sustained-I/O ceilings. A request to sustain a rate is permitted **iff** it stays within every ceiling that is set; an unset ceiling (`null`) does not bound that dimension. This is a deny-by-default rate control, and it is the standard's lever against **storage-contention side channels** (see `SECURITY.md`): a unit whose I/O is capped cannot drive the high-rate, page-cache-busting random-read pattern such a channel needs to read co-tenant activity off shared-device latency. A deployment that colocates a contention-sensitive workload at S0–S2 (shared physical I/O) **MUST** set a ceiling on untrusted neighbors, or raise the floor to **S3**, where device I/O is partitioned rather than shared.
|
||
|
||
The ceilings serialize **only when set** (`skip_serializing_if`): a grant that omits them is byte-identical to one written before they existed, so its content id and the published conformance vectors are unchanged. The reference enforces them in `sandbox-capability` (`Enforcer::may_sustain_io`, `io_ceilings`); a ceiling is a property of the **grant**, so it is bound into `grants_content_id` and attested.
|
||
|
||
## NetworkGrant
|
||
|
||
```
|
||
NetworkGrant { outbound : [Endpoint], inbound : bool }
|
||
```
|
||
|
||
`outbound` is an allowlist of `host:port` strings; an egress to any endpoint not on the list **MUST** be denied. `inbound = false` denies all inbound connections. The empty grant denies all network. Per SPEC §1.4, the host's own operation never depends on network; a denied egress is a unit-level denial, not a host failure.
|
||
|
||
## KvGrant
|
||
|
||
```
|
||
KvGrant { read : bool, write : bool, prefixes : [Bytes] }
|
||
```
|
||
|
||
Key-value access is gated by direction (`read`/`write`) and by key `prefixes`: a key operation **MUST** match a granted prefix and direction or be denied. Empty `prefixes` with `read`/`write` true grants the whole keyspace; the conservative default is all-false.
|
||
|
||
## HostGrant
|
||
|
||
```
|
||
HostGrant { logging : bool, clock : bool, random : bool, inference : bool }
|
||
```
|
||
|
||
- `logging` — the unit may emit log lines to the host sink.
|
||
- `clock` — the unit may read wall-clock time. When false, time calls return a normalized constant (anti-evasion, SPEC §7.5).
|
||
- `random` — the unit may draw host randomness. When a replay journal is recorded (SPEC §8), draws are journaled.
|
||
- `inference` — the unit may call an on-host model endpoint. When granted, the energy of each inference **MUST** be metered to the device class that served it (SPEC §6.2) and **MUST NOT** be hidden inside an opaque host-call cost.
|
||
|
||
## ProcessGrant — shell and code-interpreter
|
||
|
||
```
|
||
ProcessGrant { exec : bool, programs : [String], max_processes : u32 }
|
||
```
|
||
|
||
The substrate for **shell access** and **code-interpreter** capabilities. A unit **MUST NOT** spawn a subprocess unless `exec` is true and the program is on the `programs` allowlist — deny-by-default, so an empty allowlist permits nothing even with `exec` true. `max_processes` bounds concurrency. Granting `exec` lifts the required tier to at least **S2** ([`tiers.md`](tiers.md)): subprocess execution crosses a brokered host boundary. The reference WASM executor brokers this as a `sandbox::exec` host call that consults the `Enforcer` before spawning and journals the subprocess output (a nondeterministic input, SPEC §8).
|
||
|
||
## BrowserGrant — browser and GUI agents
|
||
|
||
```
|
||
BrowserGrant { enabled : bool, origins : [Origin], headless : bool }
|
||
```
|
||
|
||
The substrate for **browser-automation** and **GUI-agent** capabilities. A navigation **MUST** be denied unless `enabled` is true and the target origin (`scheme://host[:port]`) is on the `origins` allowlist. `headless` forbids on-screen rendering. Granting `enabled` lifts the required tier to at least **S2**.
|
||
|
||
## DisplayGrant — desktop and VNC
|
||
|
||
```
|
||
DisplayGrant { enabled : bool, vnc : bool, max_width : u32, max_height : u32 }
|
||
```
|
||
|
||
The substrate for **desktop** capabilities. Framebuffer access **MUST** be denied unless `enabled` is true; a VNC export **MUST** additionally require `vnc`; the surface is bounded by `max_width × max_height`. Granting `enabled` lifts the required tier to at least **S2**.
|
||
|
||
The **filesystem** capability is `storage`; a **code-interpreter** is `process` + `storage`; an **editor/IDE** is `display` + `process` + `storage`. The standard models the underlying resources, never the composed product — so a new agent surface is expressed as a combination of grants, not a new primitive.
|
||
|
||
## Enforcement contract
|
||
|
||
The reference `sandbox-capability` crate exposes an `Enforcer` that, given a `Grants` set, answers each access query (`may_dispatch(class)`, `may_path(mode, path)`, `may_egress(endpoint)`, `may_kv(dir, key)`, `may_host(service)`, `may_exec(program)`, `may_navigate(origin)`, `may_display()`, `may_vnc()`) with allow/deny. The enforcer is **pure and deterministic**: the same grant set and query always yield the same decision, with no float and no ambient state. Conformance vector class **C2** (SPEC §10, [`conformance.md`](conformance.md)) fixes the decisions for a canonical grant set, including the deny-by-default negatives.
|
||
|
||
At tier S2 the enforcement is **live**, not advisory: the reference WASM executor (`sandbox-exec-wasm`) brokers every capability the guest exercises through a host-call boundary — `kv_get`/`kv_put`, `storage_read`/`storage_write`, `egress`, `clock_now`, `random_u64`, `log` — and each call consults the `Enforcer` before acting. A denied call returns an error code to the guest (denial is not a crash), so capability enforcement is exercised through real execution. Each nondeterministic input the guest receives (a kv or storage read, the clock, a random draw, an outbound call) is recorded in the run's replay journal (SPEC §8), so the attestation carries a `journal_root`.
|