From 20097cccaaa01a24c31a31370eb5d1d2de479245 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Wed, 7 Oct 2026 01:41:13 -0500 Subject: [PATCH] Add ocap-discipline skill --- ocap-discipline/SKILL.md | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 ocap-discipline/SKILL.md diff --git a/ocap-discipline/SKILL.md b/ocap-discipline/SKILL.md new file mode 100644 index 0000000..d8aba58 --- /dev/null +++ b/ocap-discipline/SKILL.md @@ -0,0 +1,71 @@ +--- +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 \ No newline at end of file