nervur 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,25 +1,24 @@
1
1
  {
2
2
  "name": "nervur",
3
- "version": "0.13.0",
4
- "description": "A distributed system of sovereign beings — one door, one question: by whose authority.",
3
+ "version": "0.15.0",
4
+ "description": "Nervur grown on the Quo kit: the graph, the organs and the floor, with @quo-systems/js as the whole of the protocol",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Razvan Gherghina",
7
- "homepage": "https://nervur.org",
8
7
  "type": "module",
8
+ "sideEffects": false,
9
9
  "exports": {
10
- ".": "./index.mjs"
10
+ ".": "./src/index.js"
11
11
  },
12
- "files": [
13
- "*.mjs",
14
- "GETTING-STARTED.md",
15
- "NOTICE",
16
- "SECURITY.md"
17
- ],
18
12
  "engines": {
19
13
  "node": ">=22"
20
14
  },
21
15
  "dependencies": {
22
- "@noble/curves": "2.3.0",
23
- "graphql": "^17.0.2"
24
- }
16
+ "@quo-systems/js": "0.0.3",
17
+ "graphql": "17.0.2"
18
+ },
19
+ "files": [
20
+ "src",
21
+ "LICENSE",
22
+ "README.md"
23
+ ]
25
24
  }
package/src/being.js ADDED
@@ -0,0 +1,125 @@
1
+ import { Refusal, parse, recordsOf, encode, decodeAll } from '@quo-systems/js';
2
+ import { contract as read } from './contract.js';
3
+ import { program } from './program.js';
4
+ import { project } from './projection.js';
5
+
6
+ const refuse = (code, detail) => {
7
+ throw new Refusal(code, detail);
8
+ };
9
+
10
+ // A being is a contract and its resolvers, and nothing else. The contract is a
11
+ // GraphQL schema — authoring, and authoring only — projected to a Quo blueprint
12
+ // before anything holds it, so the warden gets the notation it has always got
13
+ // and a stranger implementing Quo elsewhere reads a blueprint like any other.
14
+ //
15
+ // A being is compiled by `program`, which compiles organs too and cannot tell
16
+ // which it is holding. What this file adds is the wire skin the kit leaves
17
+ // open: bytes in and bytes out, and the object the warden dispatches from.
18
+ //
19
+ // Two things are Nervur's to keep, and the law is what asks for them. "What a
20
+ // blueprint does not declare does not exist," and "granting a being is granting
21
+ // every field it declares" — together they make the declared set the scope of a
22
+ // grant. A held object with a method the blueprint never named would answer
23
+ // callers anyway, which is a grant that grants more than its digest says.
24
+ //
25
+ // A being is raised rather than built: the organs it declared with
26
+ // `schema @needs(organs: [...])` are supplied by the ground at `hold`, and the
27
+ // resolvers are closed over them. Reach is a fact about a being, not about a
28
+ // call, so it arrives once — and that is what makes the refusal structural. An
29
+ // organ the being did not declare is not absent from a check; it is absent from
30
+ // the object, and there is nothing to police.
31
+ //
32
+ // `contacts` arrives the same way, as the resolver factory's second argument.
33
+ // What is closed over is a **reader**, not a value: reach is fixed at raise and
34
+ // contacts are not, so `list()` goes to the warden's record at the moment it is
35
+ // called and there is nothing here to go stale.
36
+ export function being(sdl, resolvers) {
37
+ const held = read(sdl);
38
+ const text = project(held);
39
+ const blueprint = parse(text);
40
+ const records = recordsOf(blueprint);
41
+
42
+ const raise = (supplied = {}, contacts = null) =>
43
+ object(held, blueprint, records, resolvers, supplied, contacts);
44
+ // A being that cannot stand should say so at once rather than at a hold. But
45
+ // only where saying so costs nothing: a resolver factory may allocate, or
46
+ // register itself somewhere, and running it speculatively would do that twice
47
+ // for a being with no organs and once for a being with them. So a plain set
48
+ // of resolvers is checked here, and a factory is run exactly once, at hold.
49
+ if (held.needs.length === 0 && typeof resolvers !== 'function') raise();
50
+
51
+ return Object.freeze({
52
+ contract: text,
53
+ organs: Object.freeze([...held.needs]),
54
+ raise,
55
+ schema: sdl,
56
+ });
57
+ }
58
+
59
+ function object(held, blueprint, records, resolvers, supplied, contacts) {
60
+ // The ground offers; the being takes what its schema declared. Two
61
+ // narrowings, and an organ offered but not needed is simply not taken — a
62
+ // ground handing the same tray to every being it holds is the ordinary case.
63
+ const reach = {};
64
+ for (const alias of held.needs) {
65
+ if (!supplied[alias]) refuse('ORGAN_MISSING', alias);
66
+ reach[alias] = supplied[alias];
67
+ }
68
+ const organs = Object.freeze(reach);
69
+
70
+ const built = typeof resolvers === 'function' ? resolvers(organs, contacts) : resolvers;
71
+
72
+ // Both halves of the mismatch are refused, and refused here rather than at a
73
+ // call: a being that stands and then falls over on one field is a being whose
74
+ // digest lied to everyone who read it. `hasOwn`, not a bare lookup — a
75
+ // contract declaring `toString` would otherwise be satisfied by
76
+ // `Object.prototype`'s, and a resolver is a thing an author wrote, not a
77
+ // thing a prototype happens to supply.
78
+ for (const field of Object.keys(held.fields)) {
79
+ if (!Object.hasOwn(built, field) || typeof built[field] !== 'function') {
80
+ refuse('NO_RESOLVER', field);
81
+ }
82
+ }
83
+ // A resolver the contract does not declare is unreachable by design. Silence
84
+ // would hide the likelier cause — a name that does not match — so it is an
85
+ // error instead.
86
+ for (const name of Object.keys(built)) {
87
+ if (!Object.hasOwn(held.fields, name)) refuse('UNDECLARED_RESOLVER', name);
88
+ }
89
+
90
+ const one = program(held, built);
91
+
92
+ // No prototype, and this is the gate rather than a tidiness. The kit
93
+ // dispatches with `object[name]` and `typeof === 'function'`, and an
94
+ // inherited method passes both — a plain object here answers a sealed ask for
95
+ // `constructor`, because `Object` is a function and hands back the very bytes
96
+ // it was called with, which is exactly what the warden accepts as an answer.
97
+ // Freezing does not help: freezing does not remove a prototype.
98
+ const table = Object.create(null);
99
+ for (const field of blueprint.fields) {
100
+ const types = field.args.map((argument) => argument.type);
101
+ const names = field.args.map((argument) => argument.name);
102
+
103
+ // The kit's signature: bytes in, bytes or nothing out. A resolver sees
104
+ // neither. It sees its arguments by the names its contract gave them, and
105
+ // its leash — never a caller, never an address, because a being learns
106
+ // neither and Nervur does not soften it.
107
+ table[field.name] = async (bytes, leash) => {
108
+ const values = decodeAll(types, bytes, records);
109
+ const answered = await one.answer(
110
+ field.name,
111
+ Object.fromEntries(names.map((name, at) => [name, values[at]])),
112
+ leash,
113
+ );
114
+ // A field declaring no answer answers nothing, and that is an answer: the
115
+ // door sends an envelope with no data in it.
116
+ if (!field.answer) return undefined;
117
+ // An answer that does not fit its declared type never leaves. `encode`
118
+ // refuses it, the warden's global catch turns that into silence, and
119
+ // silence is the only refusal a door can express.
120
+ return encode(field.answer, answered, records);
121
+ };
122
+ }
123
+
124
+ return Object.freeze(table);
125
+ }
@@ -0,0 +1,161 @@
1
+ import { parse as parseGraphQL } from 'graphql';
2
+ import { Refusal } from '@quo-systems/js';
3
+
4
+ // What a thing answers, read once from its own text. A being's contract and an
5
+ // organ's contract are the same file under the same name, because they are one
6
+ // kind of code — so there is one reader here and no second one anywhere.
7
+ //
8
+ // The contract is a GraphQL schema. That is authoring and nothing else: it
9
+ // never reaches the wire, is never an answer, and is never a describe. Every
10
+ // implementer of Quo speaks the notation and has never heard of Nervur, so a
11
+ // contract is projected to a Quo blueprint at the ground's edge and that
12
+ // projection is the only boundary.
13
+ //
14
+ // A contract carries no identity of its own. The being's name is the digest of
15
+ // its projected blueprint, which the warden takes, and a second identity taken
16
+ // over the GraphQL bytes would be a second truth about the same thing.
17
+
18
+ const refuse = (code, detail) => {
19
+ throw new Refusal(code, detail);
20
+ };
21
+
22
+ const OPERATIONS = Object.freeze(['query', 'mutation', 'subscription']);
23
+
24
+ const named = (node) => node.name.value;
25
+
26
+ // A field's type, flattened to the name it ends at, whether it is a list, and
27
+ // whether the caller must give it. GraphQL is nullable by default and says so
28
+ // by omission; the projection is where that becomes `?`, not here.
29
+ const inner = (node) => (node.kind === 'NamedType' ? named(node) : inner(node.type));
30
+
31
+ const typed = (node) =>
32
+ node.kind === 'NonNullType'
33
+ ? { ...typed(node.type), required: true }
34
+ : node.kind === 'ListType'
35
+ ? { list: true, of: inner(node.type), required: false, element: typed(node.type) }
36
+ : { of: named(node), required: false };
37
+
38
+ // What a contract says it needs to work, written in the schema itself:
39
+ //
40
+ // schema @needs(organs: ["book", "far"]) { query: Clerk }
41
+ //
42
+ // It is a need and never a binding. A contract is written to stand on any
43
+ // ground, so it names what it must reach and says nothing about whose
44
+ // implementation answers.
45
+ //
46
+ // Undeclared is unreachable, and never by a check: a contract that never asked
47
+ // for an organ does not have it and cannot learn that it exists, because the
48
+ // object it is handed has no such key.
49
+ const needed = (definition) => {
50
+ for (const directive of definition?.directives ?? []) {
51
+ if (directive.name.value !== 'needs') continue;
52
+ const organs = (directive.arguments ?? []).find((one) => one.name.value === 'organs');
53
+ if (organs?.value?.kind !== 'ListValue') refuse('BAD_NEEDS', 'organs must be a list of names');
54
+ return organs.value.values.map((one) => {
55
+ if (one.kind !== 'StringValue') refuse('BAD_NEEDS', 'an organ is named by a string');
56
+ return one.value;
57
+ });
58
+ }
59
+ return [];
60
+ };
61
+
62
+ // Which type is the root of each operation. GraphQL's own `schema` block names
63
+ // them, and that is where the being's name comes from — `schema { query: Clerk }`
64
+ // makes a class block called `Clerk`. With no `schema` block the conventional
65
+ // type names stand in, which is the plain form every engineer already writes.
66
+ const rooted = (definition) => {
67
+ const roots = { query: 'Query', mutation: 'Mutation', subscription: 'Subscription' };
68
+ for (const operation of definition?.operationTypes ?? []) {
69
+ roots[operation.operation] = named(operation.type);
70
+ }
71
+ return roots;
72
+ };
73
+
74
+ const fieldsOf = (definition) =>
75
+ Object.fromEntries(
76
+ (definition.fields ?? []).map((field) => [
77
+ named(field),
78
+ Object.freeze({
79
+ args: Object.freeze(
80
+ Object.fromEntries(
81
+ (field.arguments ?? []).map((one) => [named(one), Object.freeze(typed(one.type))]),
82
+ ),
83
+ ),
84
+ answers: Object.freeze(typed(field.type)),
85
+ }),
86
+ ]),
87
+ );
88
+
89
+ export function contract(sdl) {
90
+ if (typeof sdl !== 'string') refuse('NOT_A_SCHEMA', typeof sdl);
91
+ let document;
92
+ try {
93
+ document = parseGraphQL(sdl);
94
+ } catch (error) {
95
+ refuse('UNREADABLE_SCHEMA', error.message);
96
+ }
97
+
98
+ const schema = document.definitions.find(
99
+ (one) => one.kind === 'SchemaDefinition' || one.kind === 'SchemaExtension',
100
+ );
101
+ const roots = rooted(schema);
102
+ const rootNames = new Set(Object.values(roots));
103
+
104
+ const objects = new Map();
105
+ const inputs = new Map();
106
+ for (const definition of document.definitions) {
107
+ if (definition.kind === 'ObjectTypeDefinition') {
108
+ if (objects.has(named(definition))) refuse('DUPLICATE_TYPE', named(definition));
109
+ objects.set(named(definition), definition);
110
+ continue;
111
+ }
112
+ if (definition.kind === 'InputObjectTypeDefinition') {
113
+ if (inputs.has(named(definition))) refuse('DUPLICATE_TYPE', named(definition));
114
+ inputs.set(named(definition), definition);
115
+ continue;
116
+ }
117
+ if (definition.kind === 'SchemaDefinition' || definition.kind === 'SchemaExtension') continue;
118
+ // Everything else — interfaces, unions, enums, fragments, an operation —
119
+ // is a thing the notation has no word for, and a contract carrying one is
120
+ // refused where it is read rather than guessed at where it is projected.
121
+ refuse('NO_WORD', definition.kind);
122
+ }
123
+
124
+ // Every root field, flat, with the operation it stands under. This is what a
125
+ // caller may ask, and there is no register of fields anywhere else.
126
+ // Resolvers are flat under the contract, not nested under a root: one
127
+ // contract is one class block in the notation, so a field name is unique
128
+ // across the whole contract and a second layer would name nothing.
129
+ const fields = {};
130
+ for (const operation of OPERATIONS) {
131
+ const definition = objects.get(roots[operation]);
132
+ if (!definition) continue;
133
+ for (const [name, one] of Object.entries(fieldsOf(definition))) {
134
+ if (Object.hasOwn(fields, name)) refuse('DUPLICATE_FIELD', name);
135
+ fields[name] = Object.freeze({ ...one, operation });
136
+ }
137
+ }
138
+ if (Object.keys(fields).length === 0) refuse('NO_FIELDS', 'a contract that answers nothing');
139
+
140
+ // The name a being wears: the query root's, or the mutation root's where a
141
+ // contract only writes. One contract is one class block, so one name.
142
+ const name = objects.has(roots.query)
143
+ ? roots.query
144
+ : objects.has(roots.mutation)
145
+ ? roots.mutation
146
+ : roots.subscription;
147
+
148
+ const records = {};
149
+ for (const [typeName, definition] of [...objects, ...inputs]) {
150
+ if (rootNames.has(typeName)) continue;
151
+ records[typeName] = Object.freeze(fieldsOf(definition));
152
+ }
153
+
154
+ return Object.freeze({
155
+ fields: Object.freeze(fields),
156
+ name,
157
+ needs: Object.freeze([...new Set(needed(schema))]),
158
+ records: Object.freeze(records),
159
+ sdl,
160
+ });
161
+ }
package/src/ground.js ADDED
@@ -0,0 +1,273 @@
1
+ import { Warden, commitment, readAnswer, reach, signingPair } from '@quo-systems/js';
2
+ import { serve } from '@quo-systems/js/door';
3
+
4
+ const same = (a, b) => a.length === b.length && a.every((byte, at) => byte === b[at]);
5
+
6
+ // The kit keys both of its records by a pk's hex, and does not export the
7
+ // spelling. Zero dependencies and no host API: this is the whole of it.
8
+ const hex = (bytes) => [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('');
9
+
10
+ // One process, many beings, exactly one door. The kit's `Warden` is the door
11
+ // and everything behind it, so a ground is a closure over one warden and
12
+ // nothing else — Nervur adds no protocol of its own, ever.
13
+ //
14
+ // The instance is closed over rather than returned. Nervur is written
15
+ // Crockford: factory functions, no `this` at a call site, frozen surfaces. A
16
+ // warden handed to a caller is a warden whose methods must be called bound,
17
+ // which puts the kit's class idiom in Nervur's hands. It stops here.
18
+ //
19
+ // Every seed and every draw of randomness arrives as an argument, the way the
20
+ // kit takes them. A ground that reached for entropy would be a ground the
21
+ // bench cannot replay.
22
+ export async function ground({ nameSeed, padlockSeed, heirSeed, hints = [], limit = 0n }) {
23
+ const warden = await Warden.open({ nameSeed, padlockSeed, heirSeed, hints, limit });
24
+
25
+ // The half of an invitation the kit's record has nowhere to keep: the heir
26
+ // this ground rotates to the first time it spends the relation. An
27
+ // invitation hands over the heir keys and nothing else, so the holder's
28
+ // first ask must arrive as a rotation carrying a commitment to a successor
29
+ // the granting house has never seen. Meeting that once is a trap; meeting it
30
+ // at every call site is the trap kept. So `remember` derives the successor
31
+ // and `ask` spends it, and a caller never learns the difference.
32
+ const rotations = new Map();
33
+
34
+ // A relation is named by the being that spends it and the house it stands
35
+ // at, never by a handle: a handle passed around is a relation one being can
36
+ // spend on another's behalf, and the whole point of the outbound record is
37
+ // that it cannot.
38
+ const relationFor = (from, at) => {
39
+ for (const row of warden.relationsOf(from)) if (same(row.warden, at)) return row;
40
+ return null;
41
+ };
42
+
43
+ // Contacts: who reaches this being, and the two acts that change it. Quo
44
+ // keeps the record and refuses to have an opinion about it — it has no word
45
+ // for member, owner or guest and must not, because a protocol a stranger
46
+ // implements in another language cannot carry meanings. The opinion is
47
+ // Nervur's, and this is where it lives. Nothing is copied: `list` walks the
48
+ // warden's own inbound record at the moment it is asked, `admit` is the
49
+ // warden's `grant`, `remove` is the warden's `amend`.
50
+ //
51
+ // The warden holds the record the other way round — voice to beings — so the
52
+ // inverse is derived here rather than kept, because a second index is a
53
+ // second truth and the first one moves without telling it.
54
+ //
55
+ // The scoping is the design, and it is structural rather than a check that
56
+ // refuses: `beingPk` is shut inside this closure at mint, so a being's
57
+ // context has no address it could name to reach another's row. The same
58
+ // shape `errand` uses for a relation.
59
+ const contacts = (beingPk) => {
60
+ const at = hex(beingPk);
61
+ const reaches = (row) => row.beings.has(at);
62
+ return Object.freeze({
63
+ // The voices that reach this being, read live.
64
+ list: () => [...warden.inbound.values()].filter(reaches).map((row) => row.voice),
65
+
66
+ // Mint a voice against this being and hand back the invitation. A being
67
+ // still cannot mint standing — it has no warden — and this does not give
68
+ // it one: what it has is a reach the ground minted for it, at one being.
69
+ admit: (keys) => warden.grant(beingPk, keys),
70
+
71
+ // Take this being out of that voice's row, and nothing else. A voice that
72
+ // does not reach this being is not amended at all — the warden would
73
+ // answer true to a removal that removed nothing, and a being told its act
74
+ // succeeded when it touched no record has been told a lie.
75
+ remove: (voicePk) => {
76
+ const row = warden.standing(voicePk);
77
+ if (!row || !reaches(row)) return false;
78
+ return warden.amend(voicePk, { remove: [beingPk] });
79
+ },
80
+ });
81
+ };
82
+
83
+ // The one act of spending a relation, written once. `ask` below is a caller
84
+ // of it and so is every organ minted by `errand`, because they are the same
85
+ // act: seal down a relation, move it, read what came back. A second copy of
86
+ // this would be a second place the first-ask rotation could be forgotten.
87
+ // `leash`, where there is one, is the call this ask is made in the course of.
88
+ // It is handed to the kit rather than read here: what may go onward is the
89
+ // difference between when the message arrived and this moment, and only the
90
+ // kit can take the second reading at the moment of sealing. A being holding a
91
+ // leash therefore cannot widen an allowance even by mistake, which is the
92
+ // whole reason the remainder never passes through the being as a number.
93
+ const spend = async ({ from, at, being, method, allowance, leash = null, random }) => {
94
+ const row = relationFor(from, at);
95
+ if (!row) throw new Error('no relation');
96
+ const rotation = rotations.get(row) ?? null;
97
+ // Spent whether or not an answer comes back: the far door rotates the
98
+ // standing at step three, long before it decides whether to speak.
99
+ rotations.delete(row);
100
+ const envelope = await warden.ask(row, {
101
+ seq: row.seq + 1n,
102
+ commitment: rotation,
103
+ allowance,
104
+ leash,
105
+ being,
106
+ method,
107
+ random,
108
+ });
109
+ // An ask that could not be judged is not made, and the kit says so by
110
+ // handing back nothing to send.
111
+ if (!envelope) return null;
112
+ const answer = await reach(row.hints, envelope);
113
+ if (answer === null) return null;
114
+ return readAnswer({
115
+ envelope: answer,
116
+ padlockSecret: warden.padlock.secret,
117
+ wardenPk: row.warden,
118
+ });
119
+ };
120
+
121
+ return Object.freeze({
122
+ // What a stranger holds: the door's name, its commitment, its padlock and
123
+ // where to reach it. No voice, so it opens nothing.
124
+ card: () => warden.card(),
125
+
126
+ // The door's own being — the one every voice reaches, holders included.
127
+ publicBeing: () => warden.publicBeing(),
128
+
129
+ // Hold a being: what is held is a contract and its resolvers together,
130
+ // never an object and a blueprint that happen to have been handed in at
131
+ // the same moment. Those two can disagree, and the whole meaning of a
132
+ // digest rests on their not disagreeing.
133
+ //
134
+ // The organs are supplied here, which is what "scoped by the ground"
135
+ // means: the ground decides what is on the tray, and the being takes only
136
+ // what it declared. A being cannot reach for one that was never offered,
137
+ // and cannot be given one it never asked for.
138
+ // The contacts context is minted here, per hold, over this being's own
139
+ // name — derived from the seed the way the kit derives it, because the
140
+ // being must be shut inside its own context and the hold that names it has
141
+ // not happened yet.
142
+ hold: async (one, { seed, heirSeed, cells, organs }) => {
143
+ const pk = (await signingPair(seed)).pk;
144
+ return warden.hold(one.raise(organs, contacts(pk)), {
145
+ seed,
146
+ heirSeed,
147
+ cells,
148
+ blueprint: one.contract,
149
+ });
150
+ },
151
+
152
+ // Mint a voice and write the standing that lets it reach one being.
153
+ grant: (being, keys) => warden.grant(being, keys),
154
+
155
+ // The other direction: record an invitation this ground was handed, and say
156
+ // which of its beings spends it. A relation nobody here owns belongs to the
157
+ // ground itself and travels nowhere, which is a different thing and not
158
+ // this; a being is named, always.
159
+ //
160
+ // What comes back reads the relation and cannot move it. `seq` is the count
161
+ // kept against that far door, which is the one fact a caller has any
162
+ // business watching.
163
+ remember: async (invitation, { being, heirSeed }) => {
164
+ const row = warden.remember(invitation, { being });
165
+ const next = await signingPair(heirSeed);
166
+ rotations.set(row, await commitment(invitation.warden, next.pk));
167
+ return Object.freeze({
168
+ warden: invitation.warden,
169
+ being,
170
+ hints: () => [...row.hints],
171
+ seq: () => row.seq,
172
+ });
173
+ },
174
+
175
+ // Spend a relation: seal the ask down it, post it at the hints the
176
+ // invitation carried, and read the answer back. All four acts are the
177
+ // kit's — `ask`, `carry` under it, `reach`, `readAnswer` — and what is
178
+ // Nervur's is only which relation, which number, and the padlock secret
179
+ // staying inside this closure.
180
+ //
181
+ // The number is this ground's to pick, not a caller's: a relation that
182
+ // repeats one is a relation the far door refuses, and a caller counting by
183
+ // hand is a caller that will eventually get it wrong.
184
+ //
185
+ // `random` is the **drawn bytes** for this one seal, as at `judge` and
186
+ // unlike `listen`, because here the ground has one message and not a
187
+ // stream of them.
188
+ //
189
+ // Silence and unreachable are told apart, and they must be: silence is
190
+ // `null`, the far house having judged and said nothing, while a road that
191
+ // does not answer throws out of the carriage. Only one of the two means the
192
+ // far house said no. A being spending a relation it does not hold is
193
+ // neither — it is a fault at home, and it throws before a byte moves.
194
+ ask: ({ from, at, being = null, method = null, allowance, random }) =>
195
+ spend({ from, at, being, method, allowance, random }),
196
+
197
+ // The organ that crosses. Everything above is the ground talking to another
198
+ // house; this is how a being does it, and a being may do it no other way.
199
+ //
200
+ // What is minted is a reach and nothing more. The being does not hold the
201
+ // warden, does not hold the relation, and cannot name a house: `from` and
202
+ // `at` are shut inside this closure at mint, so an organ is one road to one
203
+ // house on behalf of one being. That is what keeps it unspendable twice —
204
+ // there is nothing here to copy and point elsewhere.
205
+ //
206
+ // Which being at the far house is still the caller's to name, because the
207
+ // standing the invitation carries is what decides that, and it is judged
208
+ // over there. An organ that filtered it here would be this house guessing
209
+ // at the far house's answer.
210
+ //
211
+ // The leash is handed in rather than chosen, and it is the one the door
212
+ // gave the resolver. The being passes it on whole and never a number read
213
+ // off it: what may go onward is this door's dwell subtracted at the moment
214
+ // of sealing, which is later than any moment the being could have read. So
215
+ // the remainder reaches the far house without the being having chosen it,
216
+ // and a being cannot hand its organ a wider allowance than it received. The
217
+ // ground adds no arithmetic of its own — a leash that has run out is
218
+ // refused by the kit at the moment of carrying, and shows up here as
219
+ // silence.
220
+ //
221
+ // `random` is a **function** the organ draws from per message, as at
222
+ // `listen` and unlike `judge`: an organ has a stream of messages ahead of
223
+ // it and not one, and a seal that reused its bytes would be a seal.
224
+ errand: ({ from, at, random }) =>
225
+ Object.freeze({
226
+ ask: (leash, { being = null, method = null } = {}) =>
227
+ spend({ from, at, being, method, leash, random: random() }),
228
+ }),
229
+
230
+ // The whole of the door: bytes in, bytes or silence out.
231
+ //
232
+ // `random` here is the **drawn bytes** for this one judgment. At `listen`
233
+ // below it is a **function** the host draws from, once per message. The
234
+ // two differ because the drawing belongs to whoever has messages to count,
235
+ // and that is the host — so a ground handed a function here would meet
236
+ // every ask with silence, the warden's global catch turning the fault into
237
+ // the same refusal as everything else.
238
+ judge: (envelope, terrain) => warden.judge(envelope, terrain),
239
+
240
+ // Listen. This is the one place the warden is handed anywhere, and it goes
241
+ // to the kit's own host adapter rather than out of this closure — the door
242
+ // is the half of the carriage that cannot be portable, so a ground that
243
+ // only calls out never reaches this and never imports a host's API.
244
+ //
245
+ // The hint that comes back is the whole address: Quo posts to it exactly
246
+ // as given. `close` drops the listening socket and the keep-alive
247
+ // connections with it, because a door that waits for the last idle caller
248
+ // to time out is a door that never shuts.
249
+ // A door holds callers to the limit its warden publishes, because a
250
+ // published limit nobody enforces is worse than none — a caller can
251
+ // compute it before sending and would be told the truth about a door that
252
+ // then reads anything. A warden that publishes zero has stated no limit,
253
+ // so nothing is enforced; the kit's door spells that `null`.
254
+ //
255
+ // A ground that declared no hints publishes the one it just got: an
256
+ // ephemeral port is not knowable before the socket is open, so a ground
257
+ // could otherwise only ever grant invitations with no road in them. A
258
+ // ground that declared its own hints keeps them — it knows where it is
259
+ // reachable from and a loopback address appended to that would be a road
260
+ // no stranger can take.
261
+ listen: async ({ clock, random, host, port, limit } = {}) => {
262
+ const door = await serve(warden, {
263
+ clock,
264
+ random,
265
+ host,
266
+ port,
267
+ limit: limit ?? (warden.limit > 0n ? warden.limit : null),
268
+ });
269
+ if (warden.hints.length === 0) warden.hints = [door.hint];
270
+ return door;
271
+ },
272
+ });
273
+ }
package/src/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { ground } from './ground.js';
2
+ export { being } from './being.js';
3
+ export { contract } from './contract.js';
4
+ export { program } from './program.js';
5
+ export { project } from './projection.js';