∪ Audiences
Who can open a value.
An audience is the set of identities that can read a value. It is not a list checked at the door: it is the set of keys that can decrypt it.
Kernel: this.me 4.2.1 (npm) ⓘ
Theory: Encrypted Audiences and Cryptographic Set-Chemistry. This page is language-neutral; each implementation is linked at the end.
Three operations
| Operation | Opens for | How |
|---|---|---|
OR a ∪ b |
any one member | one content key, wrapped once per member |
AND a ∩ b |
only the whole coalition | the content key split into XOR shares, one per member |
minus A \ x |
everyone in A except x |
resolved at seal time; revoking later means resealing with a new key |
K = s₁ ⊕ s₂ ⊕ … ⊕ sₙ AND: every share needed
W_i = wrap(pub_i, K) OR: any one wrap opens K
There is no NOT and no threshold-k (Shamir). A key, once read, cannot be un-read: removal works forward, by resealing.
The smallest audience: {self}
A secret scope is an audience of one. The owner reads; a guest gets nothing.
import { ME } from "this.me";
const me = new ME() as any;
me.wallet["_"]("vault-key");
me.wallet.note("private");
me("wallet.note") // "private"
me.as(null)("wallet.note") // undefined (guest)
OR from npm 4.2.1
The npm package exports the wrapping primitive, so an OR audience can be built by hand: wrap one content key for each member.
const a = await ME.generateP256KeyPair();
const b = await ME.generateP256KeyPair();
const out = await ME.generateP256KeyPair();
const S = "content-key-123";
const Wa = await ME.wrapSecretV1({ secret: S, recipientPublicKey: a.publicKey, kid: "a", class: "identity-key" });
const Wb = await ME.wrapSecretV1({ secret: S, recipientPublicKey: b.publicKey, kid: "b", class: "identity-key" });
await ME.unwrapSecretV1(Wa, a.privateKey, "utf8"); // "content-key-123"
await ME.unwrapSecretV1(Wb, b.privateKey, "utf8"); // "content-key-123"
await ME.unwrapSecretV1(Wa, out.privateKey, "utf8"); // throws OperationError
Run on Node with npm this.me 4.2.1; the outputs above are the real ones. Each wrap is { version, class, kid, publicKey, encryption, policy }. P-256 key pairs are generated randomly, not derived from the seed. Getting each member’s public key to the sender is not solved by the kernel.
Live: choose the audience
Click a circle to add or remove that identity. Each change wraps a fresh content key K for every member with ME.wrapSecretV1, then every party tries ME.unwrapSecretV1, here, on this.me 4.2.1 from npm. Members get K; the others get the kernel’s real error.
AND and minus
Not exported by npm 4.2.1, so not run here. They live in the 4.2.1 source, audience.ts, tested by audience-algebra.test.ts (16/16).
K (splitXor). Minus: drops every coalition containing the member, once, when sealing (minus L101–107); seal and open: L207–247.Status by implementation
| OR / AND / minus | Wrapped secret (one recipient) | |
|---|---|---|
| TypeScript this.me 4.2.1 | implemented and tested in the source; not exported by the npm package | exported (ME.wrapSecretV1, ME.unwrapSecretV1) |
| Rust rust-v0.3.3 | none | implemented and tested |
| Python | no implementation yet | no implementation yet |
The sealed format is an envelope tree, { version, audience, epoch, ciphertext, tree }, whose nodes are leaf (one wrap), or and and.
Implementations
TypeScriptSecretsSecret scopes in this.me 4.2.1: what ["_"] seals and who can read it.
TypeScriptIdentity-Bound SecretsSeed, identity hash and keys bound to an identity.
RustRust API (rust-v0.3.3)Wrapped secrets; no audience algebra.
Python: no implementation yet.
See also
- Algebra of Contexts: Spaces inside spaces: path, predicate, plural and audience as one space (T, A, C, P).
- SEED: The one root every .me identity grows from — what it is, what it derives, what it never protects.
- Axioms: The invariants the kernel guarantees, with runtime proof.
- Encrypted Audiences: Theory: two islands, one shared space.
- Cryptographic Set-Chemistry: Theory: union, intersection and difference over encrypted audiences.