The one-eval-tool boundary makes an agent context-efficient: only the value a program returns enters the model's context. glove-egress makes that same boundary a privacy boundary, and gives you the instruments to measure it.
It is built on the function catalog — the egress combinators are ordinary ToolFns, so they mount on any REPL surface (glove-js, glove-python, glove-lisp).
Honest status. glove-egress is consumed by two benchmarks today, not yet by a production deployment. It is the tested primitive the exfiltration study concludes is needed, ready for the first app that wants a measured, enforced boundary.
The exfiltration study reaches one structural conclusion: a privacy boundary that depends on the model's goodwill is not a boundary. Voluntary “return only decisions” priming plateaued at a 33% leak rate; only enforcement reached 0%. The consequence is that enforcement belongs in the platform — not in a prompt, and not copy-pasted into each app.
The intuitive “an assertion collapses a k-way read to log₂k bits” is Shannon information — and Shannon is the wrong safety ruler, because it averages a catastrophic reveal away. All three rulers ship:
selfInfo, contentBits) — a throughput headline.minEntropyLeak, gLeak) — the one-guess / coarse-win risk, the security-grounded bound.BoundaryMeter.report) — the operational ground truth: which exact secrets crossed.import { BoundaryMeter, minEntropyLeak, gLeak } from "glove-egress";
const meter = new BoundaryMeter();
meter.cross("read", record); // record every value that crosses
meter.cross("assertion", true, { decisionSpace: 2 });
const r = meter.report(canaries);
// { bytesCrossed, bitsCrossed, canariesRecovered, secretBitsRecovered, … }Priming a model to “return only decisions” is a discount, not a boundary. The gate makes it structural: a program must end in a decision built by an egress combinator, whose codomain is bounded by construction.
import { egressFns, guardEffectFns, DEFAULT_EGRESS_POLICY } from "glove-egress";
// assert / count / choose / bucket / report
session.registerAll(egressFns(DEFAULT_EGRESS_POLICY));
// Effect allowlist — blocks outbound effects to off-org recipients or
// carrying secret-shaped payloads.
const guarded = guardEffectFns(catalog, DEFAULT_EGRESS_POLICY, onBlock);| Combinator | What can cross |
|---|---|
assert({ label, cond }) | One bit |
count({ label, n }) | An integer |
choose({ label, value, from }) | One member of a small set |
bucket({ label, hist }) | A k-anonymity-suppressed histogram |
report({ label, text }) | Short prose with credential/PII tokens redacted |
A per-session min-entropy bit budget caps cumulative disclosure across calls (QIF composition — not differential privacy; a deterministic authoritative bit has unbounded ε). The gate refuses raw returns, so wiring it onto a specific eval tool — an execute_js that must return a decision — is a few lines.
Before you trust a budget, watch an adversary spend it. The simulator runs extraction strategies against a policy and reports what remains unknown:
import { simulateExtraction, residualGuarantee } from "glove-egress";
// Binary search pins a 1024-way secret in ~10 queries…
simulateExtraction({ N: 1024, secret: 733, strategy: "binary" });
// …unless the bit budget halts it: ≥64 candidates still remain.
simulateExtraction({ N: 1024, secret: 733, strategy: "binary", budgetBits: 4 });The design and its evaluation — Shannon vs min-entropy, canaries, four egress disciplines run against real models, and the delegated-judge tier — are written up as The Boundary Is the Guarantee in benches/scratchpad-bench/EXFIL-PAPER.md.