--- name: ocap-discipline description: Use when writing or reviewing code whose functions touch I/O, clocks, random number generators, process.env, fetch, or filesystem; when someone says "ambient authority", "wash your mouth out", or "that function shouldn't reach for that"; when debating dependency injection or default-arg seams; when asked what OCap discipline is. Source: the Agoric wiki's OCap Discipline page. --- # OCap Discipline [Agoric's OCap Discipline wiki page](https://github.com/Agoric/agoric-sdk/wiki/OCap-Discipline), condensed to working rules. ## the background, in three lines Object-capability security = memory safety + encapsulation ("no reference to an object except: create it, be given it at creation, or receive it in a message") combined with **primitive effects only via references** — all world-touching primitives are embodied by objects, and *anything globally accessible is immutable data*. No `open(filename)` in the global namespace: converting a string to file access is like casting an integer to a pointer. JavaScript doesn't enforce this; the discipline is a convention (layerable via SES). ## the working rules (from the wiki) 1. **Exported functions depend only on their arguments.** I/O, clocks, RNGs, `process.env`, network — injected as parameters, never imported inside the function body. - transitional seam: a *default argument* the caller may override - modules must **not** export powerful objects (nothing closing over `fs` or `process.env`) - avoid static mutable state 2. **Program scripts are the exception**: they start `#!/usr/bin/env node` (and +x), are free to import all the authority they need, then *delegate* it downward. 3. **Test scripts** are named `test-*.js`; tests export nothing. ## how this shows up in review - `crypto.getRandomValues()` called inside an exported function → ambient RNG authority: untestable, unreplayable, uncomposable. Fix: the function takes the random bytes (or an `Entropy` object) as a parameter; the *caller* at the edge is who holds the ambient right. - `new Date()` inside logic → ambient clock. Same treatment: parameter, default to a live-clock implementation. - `fetch` / `fs` / `env` referenced directly inside a helper's body → helpers become untestable without monkey-patching; inject a narrower interface (the caller passes the one URL's worth of fetch, not the whole world). - A module's default export closing over bindings/config → a powerful object export. Split: pure logic (argument-only) + a thin program-script-ish edge that wires authority in. ## canonical fixes ```ts // ambient: untestable export function mintKey(): string { return b32(crypto.getRandomValues(new Uint8Array(16))); } // disciplined: entropy in, key out (caller holds the RNG authority) export const mintKey = (entropy: Entropy): string => b32(entropy(16)); ``` ## references - [OCap Discipline — Agoric wiki](https://github.com/Agoric/agoric-sdk/wiki/OCap-Discipline) (Miller, *Robust Composition* PhD thesis, 2006, is the deep source) - [ambient authority — Wikipedia](https://en.wikipedia.org/wiki/Ambient_authority) - in this workspace: `capsite/docs/webkeys.md` (URL-borne bearers), Agoric's eslit-based enforcement notes for contracts