@lapxo/bound 0.2.3 → 0.2.4
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/.github/workflows/release.yml +67 -0
- package/.github/workflows/test.yml +35 -0
- package/CITATION.cff +19 -0
- package/TARGET.bound +71 -20
- package/dist/cli/accept.js +103 -71
- package/dist/cli/place.js +3 -2
- package/dist/cli/standing-result.js +3 -5
- package/dist/cli/takes.js +4 -3
- package/dist/cli/verb.js +2 -1
- package/dist/engine/ports.d.ts +6 -1
- package/dist/engine/ports.js +23 -9
- package/dist/fold/claims.d.ts +29 -0
- package/dist/fold/claims.js +2 -1
- package/dist/fold/closed.js +2 -1
- package/dist/fold/digests.js +7 -7
- package/dist/host/admitted-place.js +3 -31
- package/dist/host/call-context.d.ts +20 -0
- package/dist/host/call-context.js +14 -0
- package/dist/host/capsule-process.js +2 -1
- package/dist/host/capsule.js +7 -4
- package/dist/host/commit-result.d.ts +6 -0
- package/dist/host/commit-result.js +5 -0
- package/dist/host/contextual-place.js +9 -14
- package/dist/host/host-controls.js +19 -0
- package/dist/host/in-process-act.d.ts +82 -0
- package/dist/host/in-process-act.js +84 -0
- package/dist/host/in-process-context.d.ts +46 -0
- package/dist/host/in-process-context.js +91 -0
- package/dist/host/in-process-root.d.ts +117 -0
- package/dist/host/in-process-root.js +84 -0
- package/dist/host/in-process.d.ts +41 -0
- package/dist/host/in-process.js +55 -0
- package/dist/host/index.d.ts +15 -0
- package/dist/host/index.js +7 -0
- package/dist/host/native-receiver.d.ts +19 -0
- package/dist/host/native-receiver.js +25 -0
- package/dist/host/place-act-transaction.d.ts +24 -0
- package/dist/host/place-act-transaction.js +78 -0
- package/dist/host/place-clock-selection.d.ts +55 -0
- package/dist/host/place-clock-selection.js +1 -1
- package/dist/host/ports/local-act.d.ts +44 -0
- package/dist/host/ports/local-act.js +13 -2
- package/dist/host/ports/place-act.d.ts +49 -0
- package/dist/host/ports/place-act.js +19 -54
- package/dist/host/ports/publication.d.ts +10 -0
- package/dist/host/root-clock-source.d.ts +86 -0
- package/dist/host/root-clock-source.js +7 -5
- package/dist/host/signature-era.js +12 -0
- package/dist/host/signing.js +1 -1
- package/dist/host/source-content.js +2 -1
- package/dist/host/standing-carrier.js +18 -0
- package/dist/host/standing-projection.js +50 -0
- package/dist/land/act.js +60 -26
- package/dist/observe/run.js +2 -1
- package/dist/observe/runner.js +16 -8
- package/docs/contracts/native-host.md +21 -0
- package/docs/contracts/native-host.publication.json +25 -0
- package/docs/contracts/native-publication.inventory.json +2 -1
- package/docs/contracts/publication.inventory.json +29 -0
- package/docs/guide.md +10 -0
- package/docs/reference.md +39 -0
- package/docs/why.md +109 -0
- package/package.json +32 -2
- package/receipts.bound +10369 -9723
- package/samples/native-host/no.json +10 -0
- package/samples/native-host/yes.json +20 -0
- package/vectors/native/native-host.vector.json +62 -0
package/dist/cli/accept.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { BoundRefusal } from "../host/refusals.js";
|
|
2
|
+
import { assertHostNotCancelled as notCancelled } from "../host/host-controls.js";
|
|
3
|
+
import { authorityAtEpoch } from '@lapxo/topos/wire';
|
|
4
|
+
import { requireEffects } from "../host/read-only.js";
|
|
5
|
+
import { hostOutput } from "../host/call-context.js";
|
|
1
6
|
import { assertProspectiveRootClockSelection } from "../host/root-clock-source.js";
|
|
2
7
|
import { hasContextualPlace, landContextualPlace } from "../host/contextual-place.js";
|
|
3
8
|
import { commitLocalAct, intactLocalAct, committedLocalActs } from "../host/ports/local-act.js";
|
|
@@ -78,8 +83,8 @@ function refusesWidening(signed, standing) {
|
|
|
78
83
|
const widened = widenedWithoutWitness(signed, standing, isCeiling, wordOf(standing, 'at-classes', 'witness'));
|
|
79
84
|
if (widened.length) {
|
|
80
85
|
for (const line of widened)
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
hostOutput('error', `WIDER ${fieldOf(line, 'scope')} · ${fieldOf(line, 'value')}\n`);
|
|
87
|
+
hostOutput('error', `${selfName()}: REFUSE·widening nothing landed · a ceiling that admits more than it did gives back freedom the lock had spent: sign it under a witness that names what made the old ceiling wrong\n`);
|
|
83
88
|
return true;
|
|
84
89
|
}
|
|
85
90
|
return false;
|
|
@@ -91,26 +96,26 @@ function refusesAdmission(root, signed, standing) {
|
|
|
91
96
|
const store = storeOf(root);
|
|
92
97
|
const outside = outsideTheirCapsule(root, standing, signed);
|
|
93
98
|
for (const line of outside)
|
|
94
|
-
|
|
99
|
+
hostOutput('error', `OUTSIDE ${fieldOf(line, 'scope')} · ${fieldOf(line, 'value')}\n`);
|
|
95
100
|
if (outside.length) {
|
|
96
|
-
|
|
101
|
+
hostOutput('error', `${selfName()}: REFUSE·outside nothing landed · a reader or a capsule lands in the capsule of its world, never in the wire or the instrument\n`);
|
|
97
102
|
return true;
|
|
98
103
|
}
|
|
99
104
|
const forks = forksJoinedBy(store, signed).filter(fork => forksItself(fork.lines) || fork.lines.filter(line => signed.includes(line)).length > 1);
|
|
100
105
|
if (forks.length) {
|
|
101
106
|
for (const fork of forks) {
|
|
102
|
-
|
|
107
|
+
hostOutput('error', `FORK ${fork.key}\n`);
|
|
103
108
|
for (const line of fork.lines)
|
|
104
|
-
|
|
109
|
+
hostOutput('error', ` ${signed.includes(line) ? 'new ' : 'held'} by=${fieldOf(line, 'by')} ${sideOf(line, lockLines(store))}\n`);
|
|
105
110
|
}
|
|
106
|
-
|
|
111
|
+
hostOutput('error', `${selfName()}: REFUSE·fork nothing landed · one signer never forks itself, and one act never forks itself: withdraw the held side in this batch, or sign the other reading under its own scope\n`);
|
|
107
112
|
return true;
|
|
108
113
|
}
|
|
109
114
|
const held = aliasPinned(root, signed);
|
|
110
115
|
if (held.length) {
|
|
111
116
|
for (const one of held)
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
hostOutput('error', `ALIAS ${one.scope} · ${one.digest} · ${one.by.join(' ')}\n`);
|
|
118
|
+
hostOutput('error', `${selfName()}: REFUSE·alias nothing landed · an alias is withdrawn only when no pin names its digest\n`);
|
|
114
119
|
return true;
|
|
115
120
|
}
|
|
116
121
|
return false;
|
|
@@ -121,24 +126,24 @@ function refusesStandingPins(root, lines) {
|
|
|
121
126
|
return false;
|
|
122
127
|
}
|
|
123
128
|
catch (error) {
|
|
124
|
-
|
|
129
|
+
hostOutput('error', `${selfName()}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
125
130
|
return true;
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
/** A batch signed as accept signs it: at the next epoch, by the key it names, each line read by the wire it would land under. */
|
|
129
|
-
export async function signedBatch(root, args, files) {
|
|
130
|
-
const byFile = files.map((file) => [placesOfFile(file), claimLinesIn(file)]);
|
|
134
|
+
export async function signedBatch(root, args, files, host) {
|
|
135
|
+
const byFile = host ? [[[], host.records]] : files.map((file) => [placesOfFile(file), claimLinesIn(file)]);
|
|
131
136
|
const proposed = byFile.flatMap(([, lines]) => lines);
|
|
132
137
|
if (!proposed.length) {
|
|
133
|
-
|
|
138
|
+
hostOutput('error', `${selfName()}: a batch is a file with claim lines\n`);
|
|
134
139
|
return { kind: 'exit', code: 2 };
|
|
135
140
|
}
|
|
136
141
|
const drafted = proposed.filter((line) => !fieldOf(line, 'sig'));
|
|
137
142
|
const bare = drafted.filter((line) => fieldOf(line, 'epoch') || !['', 'target'].includes(fieldOf(line, 'by')));
|
|
138
143
|
if (bare.length) {
|
|
139
144
|
for (const line of bare)
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
hostOutput('error', `BARE ${fieldOf(line, 'scope')} · by=${fieldOf(line, 'by') || '∅'}${fieldOf(line, 'epoch') ? ` epoch=${fieldOf(line, 'epoch')}` : ''}\n`);
|
|
146
|
+
hostOutput('error', `${selfName()}: REFUSE·bare nothing landed · ${bare.length} lines name a signer or an epoch they were not signed with: a drafted line proposes by=target and sign signs it, a line a lane signed carries its sig\n`);
|
|
142
147
|
return { kind: 'exit', code: 1 };
|
|
143
148
|
}
|
|
144
149
|
const store = storeOf(root);
|
|
@@ -146,7 +151,7 @@ export async function signedBatch(root, args, files) {
|
|
|
146
151
|
const rooted = lockStanding(store);
|
|
147
152
|
const stray = destinations.filter(one => !placesIn(root).includes(one) && !foundableIn(root, rooted, wordOf(rooted, 'families', 'region'), one));
|
|
148
153
|
if (stray.length) {
|
|
149
|
-
|
|
154
|
+
hostOutput('error', `${selfName()}: REFUSE·place nothing landed · ${stray.join(', ')} is no place of the tree, and no line of the root founds it: --place names the own lock a line lands in\n`);
|
|
150
155
|
return { kind: 'exit', code: 2 };
|
|
151
156
|
}
|
|
152
157
|
const local = localActLines(root, destinations);
|
|
@@ -157,8 +162,8 @@ export async function signedBatch(root, args, files) {
|
|
|
157
162
|
: []))(fieldOf(line, 'by'), publicKeyOf(root, fieldOf(line, 'by'))));
|
|
158
163
|
if (stale.length) {
|
|
159
164
|
for (const one of stale)
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
hostOutput('error', `SIGNED ${one}\n`);
|
|
166
|
+
hostOutput('error', `${selfName()}: REFUSE·signed nothing landed · ${stale.length} lines a lane signed stand at no epoch this act\n`);
|
|
162
167
|
return { kind: 'exit', code: 1 };
|
|
163
168
|
}
|
|
164
169
|
const [named, places] = [namedPlaces(args), placesIn(root)];
|
|
@@ -177,7 +182,7 @@ export async function signedBatch(root, args, files) {
|
|
|
177
182
|
await admittedObjects(root, [...signedOwnerLines(store), ...proposed]);
|
|
178
183
|
}
|
|
179
184
|
catch (e) {
|
|
180
|
-
|
|
185
|
+
hostOutput('error', `${selfName()}: ${String(e)} · nothing landed\n`);
|
|
181
186
|
return { kind: 'exit', code: 1 };
|
|
182
187
|
}
|
|
183
188
|
}
|
|
@@ -186,30 +191,39 @@ export async function signedBatch(root, args, files) {
|
|
|
186
191
|
const outdated = proposed.filter(line => fieldOf(line, 'sig') && isConfig(line) && Number(fieldOf(line, 'epoch')) !== epoch);
|
|
187
192
|
if (outdated.length) {
|
|
188
193
|
for (const line of outdated)
|
|
189
|
-
|
|
190
|
-
|
|
194
|
+
hostOutput('error', `SIGNED ${fieldOf(line, 'scope')} · signed by ${fieldOf(line, 'by')} at epoch ${fieldOf(line, 'epoch')}, and this act lands at ${epoch}: sign it again\n`);
|
|
195
|
+
hostOutput('error', `${selfName()}: REFUSE·signed nothing landed · ${outdated.length} lines a lane signed stand at no epoch this act\n`);
|
|
191
196
|
return { kind: 'exit', code: 1 };
|
|
192
197
|
}
|
|
193
|
-
const keyFile = valueOf(args, FLAGS.keyFile);
|
|
194
|
-
const keyId = valueOf(args, FLAGS.key) ?? ownerOf(store);
|
|
198
|
+
const keyFile = host ? undefined : valueOf(args, FLAGS.keyFile);
|
|
199
|
+
const keyId = host?.keyId ?? valueOf(args, FLAGS.key) ?? ownerOf(store);
|
|
195
200
|
const publicKey = drafted.length ? publicKeyOf(root, keyId) : undefined;
|
|
196
201
|
if (drafted.length && !publicKey) {
|
|
197
|
-
|
|
202
|
+
hostOutput('error', `${selfName()}: unknown key ${keyId} — name it in ${selfName()}.keys\n`);
|
|
198
203
|
return { kind: 'exit', code: 2 };
|
|
199
204
|
}
|
|
200
205
|
let signed = proposed.filter((line) => fieldOf(line, 'sig'));
|
|
206
|
+
if (host && drafted.length) {
|
|
207
|
+
notCancelled(host.signal);
|
|
208
|
+
const selected = authorityFor(lockLines(store), rootSigner(root), signaturesOf(store).admitted).admitted.find(s => s.id === keyId);
|
|
209
|
+
for (const line of drafted) {
|
|
210
|
+
const parsed = parse(line);
|
|
211
|
+
if (parsed.kind !== 'fact' || !selected || authorityAtEpoch({ ...parsed.value.fields, by: keyId, epoch: String(epoch) }, selected, epoch, fieldOf(line, 'scope'), 'authority').kind !== 'admitted')
|
|
212
|
+
throw new BoundRefusal('host/receiver-authority', 'REFUSE·host/receiver-authority');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
201
215
|
if (drafted.length) {
|
|
202
216
|
let invoke;
|
|
203
217
|
try {
|
|
204
|
-
invoke = signerFor([...lockStanding(store), ...destinations.flatMap(place => ownLockOf(root, place))], keyId, valueOf(args, FLAGS.signer), keyFile);
|
|
218
|
+
invoke = signerFor([...lockStanding(store), ...destinations.flatMap(place => ownLockOf(root, place))], keyId, host?.signer.name ?? valueOf(args, FLAGS.signer), keyFile, host?.signer);
|
|
205
219
|
}
|
|
206
220
|
catch (error) {
|
|
207
|
-
|
|
221
|
+
hostOutput('error', `${selfName()}: ${error instanceof Error ? error.message : 'REFUSE·signer unavailable'}\n`);
|
|
208
222
|
return { kind: 'exit', code: 1 };
|
|
209
223
|
}
|
|
210
224
|
const got = await signConsentLot(drafted, { epoch, by: keyId, algorithm: signaturesOf(store).era }, publicKey, signaturesOf(store).admitted, invoke);
|
|
211
225
|
if (got.kind === 'refuse') {
|
|
212
|
-
|
|
226
|
+
hostOutput('error', `${selfName()}: ${got.why}\n`);
|
|
213
227
|
return { kind: 'exit', code: 1 };
|
|
214
228
|
}
|
|
215
229
|
let index = 0;
|
|
@@ -222,7 +236,7 @@ export async function signedBatch(root, args, files) {
|
|
|
222
236
|
for (const line of signed) {
|
|
223
237
|
const got = admittedLine(line, wireLines, Number(fieldOf(line, 'epoch')), foldClaims(signedOwnerLines(store)).standing.filter(one => fieldOf(one, 'scope').startsWith('uses/') && fieldOf(one, 'value') !== 'withdraw'));
|
|
224
238
|
if (got.kind !== 'fact') {
|
|
225
|
-
|
|
239
|
+
hostOutput('error', `${selfName()}: REFUSE·wire ${fieldOf(line, 'scope')} · ${got.why} · nothing landed\n`);
|
|
226
240
|
return { kind: 'exit', code: 1 };
|
|
227
241
|
}
|
|
228
242
|
}
|
|
@@ -231,33 +245,33 @@ export async function signedBatch(root, args, files) {
|
|
|
231
245
|
const taken = retractedTogether(signed);
|
|
232
246
|
if (taken.length) {
|
|
233
247
|
for (const pair of taken) {
|
|
234
|
-
|
|
235
|
-
|
|
248
|
+
hostOutput('error', `WITHDRAW ${sayingOf(pair.withdraw)}\n`);
|
|
249
|
+
hostOutput('error', `NEW ${sayingOf(pair.line)}\n`);
|
|
236
250
|
}
|
|
237
|
-
|
|
251
|
+
hostOutput('error', `${selfName()}: REFUSE·withdraw nothing landed · a withdraw matches a new line in this batch, so it takes that line too\n`);
|
|
238
252
|
return { kind: 'exit', code: 1 };
|
|
239
253
|
}
|
|
240
254
|
const authority = authorityFor([...lockLines(store), ...signed], rootSigner(root), signaturesOf(store).admitted);
|
|
241
255
|
const ownership = ownedOf(root, signed, line => authority.of(line).kind === 'admitted', by => authority.admitted.find(one => one.id === by)?.coverage ?? [], named, line => { const heads = byFile.flatMap(([heads, lines]) => lines.some(draft => sayingOf(draft) === sayingOf(line)) ? heads : []); return heads.length ? heads : undefined; });
|
|
242
256
|
if (ownership.nowhere.length) {
|
|
243
257
|
for (const line of ownership.nowhere)
|
|
244
|
-
|
|
258
|
+
hostOutput('error', `${selfName()}: REFUSE·signer ${keyId} uncovered ${fieldOf(line, 'scope')} · nothing signed for delivery\n`);
|
|
245
259
|
return { kind: 'exit', code: 1 };
|
|
246
260
|
}
|
|
247
261
|
if (ownership.forked.length) {
|
|
248
262
|
for (const one of ownership.forked)
|
|
249
|
-
|
|
250
|
-
|
|
263
|
+
hostOutput('error', `FORKED ${one.place}/${LOCK} · ${one.key} · the lot would leave two standing claims where the lock had one\n`);
|
|
264
|
+
hostOutput('error', `${selfName()}: REFUSE·forked nothing landed · ${ownership.forked.length} claims an own lot would fork: a lot for many places lands the same lines at each, and lines meant for one place go in a lot of their own\n`);
|
|
251
265
|
return { kind: 'exit', code: 1 };
|
|
252
266
|
}
|
|
253
267
|
if (refusesAdmission(root, ownership.ledger, lockStanding(store)))
|
|
254
268
|
return { kind: 'exit', code: 1 };
|
|
255
269
|
if (signed.some(line => !isConfig(line))) {
|
|
256
270
|
try {
|
|
257
|
-
await admittedObjects(root, [...signedOwnerLines(store), ...signed], text =>
|
|
271
|
+
await admittedObjects(root, [...signedOwnerLines(store), ...signed], text => hostOutput('out', text + '\n'));
|
|
258
272
|
}
|
|
259
273
|
catch (e) {
|
|
260
|
-
|
|
274
|
+
hostOutput('error', `${selfName()}: ${e instanceof Error ? e.message : String(e)} · nothing landed\n`);
|
|
261
275
|
return { kind: 'exit', code: 1 };
|
|
262
276
|
}
|
|
263
277
|
}
|
|
@@ -265,7 +279,7 @@ export async function signedBatch(root, args, files) {
|
|
|
265
279
|
admissionEvidence(root, ownership.ledger, lockStanding(store));
|
|
266
280
|
}
|
|
267
281
|
catch (error) {
|
|
268
|
-
|
|
282
|
+
hostOutput('error', `${selfName()}: ${error instanceof Error ? error.message : String(error)} · nothing landed\n`);
|
|
269
283
|
return { kind: 'exit', code: 1 };
|
|
270
284
|
}
|
|
271
285
|
return { kind: 'signed', signed, epoch, base, ...(drafted.length ? { keyId } : {}) };
|
|
@@ -285,12 +299,26 @@ export async function accept(root, args) {
|
|
|
285
299
|
if (namedPlaces(args).length || valueOf(args, FLAGS.key) || valueOf(args, FLAGS.keyFile) || valueOf(args, FLAGS.signer))
|
|
286
300
|
throw Error('REFUSE·contextual place accepts one already signed context envelope');
|
|
287
301
|
const result = await landContextualPlace(root, contextFiles[0]);
|
|
288
|
-
|
|
302
|
+
hostOutput('out', JSON.stringify({ profile: 'place-act-context@1', identity: result.identity, status: result.kind, context: result.context, receipts: result.receipts }) + '\n');
|
|
289
303
|
return 0;
|
|
290
304
|
}
|
|
291
305
|
const named = namedPlaces(args);
|
|
292
306
|
const placed = positionalsOf(args).reduce((all, file) => ((places) => (places.length ? claimLinesIn(file).reduce((got, line) => got.set(sayingOf(line), [...new Set([...(got.get(sayingOf(line)) ?? []), ...places])]), all) : all))(placesOfFile(file)), new Map());
|
|
293
307
|
const batch = await signedBatch(root, args, positionalsOf(args));
|
|
308
|
+
return finishAcceptance(root, args, batch, placed, started);
|
|
309
|
+
}
|
|
310
|
+
/** Root host boundary, sharing signing, wire, coverage, takes and publication
|
|
311
|
+
* with accept(). No file is manufactured for a batch of already-owned bytes. */
|
|
312
|
+
export async function acceptRootRecords(root, host) {
|
|
313
|
+
requireEffects('Root lot admission');
|
|
314
|
+
notCancelled(host.signal);
|
|
315
|
+
if (!actResultProfile(lockStanding(storeOf(root))))
|
|
316
|
+
throw new BoundRefusal('host/act', 'REFUSE·Root host requires the admitted atomic result profile');
|
|
317
|
+
const started = Date.now(), batch = await signedBatch(root, [], [], host);
|
|
318
|
+
return finishAcceptance(root, [], batch, new Map(), started, host);
|
|
319
|
+
}
|
|
320
|
+
async function finishAcceptance(root, args, batch, placed, started, host) {
|
|
321
|
+
const named = namedPlaces(args);
|
|
294
322
|
if (batch.kind === 'exit')
|
|
295
323
|
return batch.code;
|
|
296
324
|
if (batch.kind === 'once')
|
|
@@ -300,8 +328,8 @@ export async function accept(root, args) {
|
|
|
300
328
|
const owned = ownedOf(root, batch.signed, (line) => authority.of(line).kind === 'admitted', (by) => authority.admitted.find((one) => one.id === by)?.coverage ?? [], named, (line) => placed.get(sayingOf(line)));
|
|
301
329
|
if (owned.nowhere.length) {
|
|
302
330
|
for (const line of owned.nowhere)
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
hostOutput('error', `NOWHERE ${fieldOf(line, 'scope')} · ${((got) => (got.kind === 'admitted' ? 'admitted' : got.why))(authority.of(line))}, and ${named.length ? `no place of ${named.join(', ')} takes it` : 'no one own lock it covers takes it: name the places with --place'}\n`);
|
|
332
|
+
hostOutput('error', `${selfName()}: REFUSE·nowhere nothing landed · ${owned.nowhere.length} lines neither the ledger nor an own lock admits: a line is landed signed where it stands, never written in bare\n`);
|
|
305
333
|
return 1;
|
|
306
334
|
}
|
|
307
335
|
const places = placesIn(root);
|
|
@@ -310,18 +338,18 @@ export async function accept(root, args) {
|
|
|
310
338
|
const every = pinned.length === 0 && touched.length === places.length && places.length > 0;
|
|
311
339
|
const regions = (pinned.length ? pinned : every ? places : touched).filter((one) => places.includes(one));
|
|
312
340
|
const where = every ? 'every place · then the root, where the places join' : regions.length ? regions.map((one) => `${one}/`).join(' · ') : 'the root alone, where the places join';
|
|
313
|
-
|
|
341
|
+
hostOutput('out', `ACTS ${where} · ${owned.ledger.length} lines to the ledger${[...owned.own].map(([place, lines]) => ` · ${lines.length} to ${place}'s own lock`).join('')} · before the lock\n`);
|
|
314
342
|
const asked = Date.now();
|
|
315
343
|
// Root-ledger writes may change grants or inherited policies. Child-only
|
|
316
344
|
// lots read that authority through publication, without excluding siblings.
|
|
317
345
|
const rootWrite = owned.ledger.length > 0 || every || regions.length === 0;
|
|
318
346
|
const enter = rootWrite ? underTheRegions : underAuthorityRegions;
|
|
319
|
-
return enter(store, rootWrite ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger, records: batch.signed }, started, owned, asked, regions));
|
|
347
|
+
return enter(store, rootWrite ? [...regions, ''] : regions, 'land', () => acceptNow(root, { ...batch, signed: owned.ledger, records: batch.signed }, started, owned, asked, regions, host));
|
|
320
348
|
}
|
|
321
|
-
async function acceptNow(root, batch, started, owned, asked = started, descent = []) {
|
|
349
|
+
async function acceptNow(root, batch, started, owned, asked = started, descent = [], host) {
|
|
322
350
|
const waited = Date.now() - asked;
|
|
323
351
|
if (waited > 60000)
|
|
324
|
-
|
|
352
|
+
hostOutput('out', `RED wait ${Math.round(waited / 1000)} s for the lock before the act · a wait over a minute is a red\n`);
|
|
325
353
|
const spent = [`sign ${asked - started}`, `wait ${waited}`];
|
|
326
354
|
const took = timing(spent);
|
|
327
355
|
const { signed, epoch } = batch;
|
|
@@ -330,14 +358,14 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
330
358
|
const store = storeOf(root);
|
|
331
359
|
const currentBase = fullDigest(store, localActLines(root, batch.base.places));
|
|
332
360
|
if (currentBase !== batch.base.digest) {
|
|
333
|
-
|
|
361
|
+
hostOutput('error', `${selfName()}: REFUSE·base moved ${batch.base.digest} → ${currentBase} · nothing landed\n`);
|
|
334
362
|
return 1;
|
|
335
363
|
}
|
|
336
364
|
try {
|
|
337
365
|
assertProspectiveRootClockSelection(root, signed);
|
|
338
366
|
}
|
|
339
367
|
catch (error) {
|
|
340
|
-
|
|
368
|
+
hostOutput('error', `${selfName()}: ${error instanceof Error ? error.message : String(error)} · nothing landed\n`);
|
|
341
369
|
return 1;
|
|
342
370
|
}
|
|
343
371
|
if (signed.some(line => !isConfig(line))) {
|
|
@@ -345,7 +373,7 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
345
373
|
await admittedObjects(root, [...signedOwnerLines(store), ...signed]);
|
|
346
374
|
}
|
|
347
375
|
catch (e) {
|
|
348
|
-
|
|
376
|
+
hostOutput('error', `${selfName()}: ${e instanceof Error ? e.message : String(e)} · nothing landed\n`);
|
|
349
377
|
return 1;
|
|
350
378
|
}
|
|
351
379
|
}
|
|
@@ -357,7 +385,7 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
357
385
|
evidence = took('evidence', () => admissionEvidence(root, signed, snapshot.standing));
|
|
358
386
|
}
|
|
359
387
|
catch (error) {
|
|
360
|
-
|
|
388
|
+
hostOutput('error', `${selfName()}: ${error instanceof Error ? error.message : String(error)} · nothing landed\n`);
|
|
361
389
|
return 1;
|
|
362
390
|
}
|
|
363
391
|
const fold = evidence.fold;
|
|
@@ -365,7 +393,7 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
365
393
|
for (const line of signed) {
|
|
366
394
|
const verdict = authority.of(line);
|
|
367
395
|
if (verdict.kind !== 'admitted') {
|
|
368
|
-
|
|
396
|
+
hostOutput('error', `${selfName()}: ${verdict.kind === 'refuse' ? verdict.why : `REFUSE·authority grey: ${verdict.why}`}\n`);
|
|
369
397
|
return 1;
|
|
370
398
|
}
|
|
371
399
|
}
|
|
@@ -384,13 +412,13 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
384
412
|
const refused = judged.filter((one) => one.kind === 'refused' || (one.kind === 'judged' && one.verdict.kind === 'refuse'));
|
|
385
413
|
if (refused.length) {
|
|
386
414
|
for (const one of refused)
|
|
387
|
-
|
|
388
|
-
|
|
415
|
+
hostOutput('out', `REFUSE take ${one.scope} · ${one.kind === 'judged' ? one.verdict.why : one.kind === 'refused' ? one.why : ''}\n`);
|
|
416
|
+
hostOutput('error', `${selfName()}: REFUSE·takes nothing landed · ${refused.length} takes refused: a batch lands whole or not at all\n`);
|
|
389
417
|
return 1;
|
|
390
418
|
}
|
|
391
419
|
const told = took('told', () => standingOf(root, undefined, signed));
|
|
392
420
|
for (const name of openedBy(descent, told.standing, signed))
|
|
393
|
-
|
|
421
|
+
hostOutput('error', `OPEN ${name}\n`);
|
|
394
422
|
if (owned.own.size)
|
|
395
423
|
layReleases(store, root, told.standing, store);
|
|
396
424
|
const resultProfile = actResultProfile(snapshot.standing);
|
|
@@ -405,13 +433,17 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
405
433
|
throw Error('REFUSE·act native result requires a declared fold identity');
|
|
406
434
|
const receipt = canonical({ scope: resultProfile.scope, at: resultProfile.context, role: 'writes', form: 'alphabet', measure: 'digest', value: identity, by: folder, epoch: String(epoch) });
|
|
407
435
|
const bundle = { records: records.join('\n') + '\n', receipt: receipt + '\n', placements: placements.map(p => ({ place: p.place, records: p.records.join('\n') + '\n' })) };
|
|
408
|
-
|
|
436
|
+
notCancelled(host?.signal);
|
|
437
|
+
host?.prepared?.(identity);
|
|
438
|
+
notCancelled(host?.signal);
|
|
439
|
+
const committed = await commitLocalAct(store, identity, descent, async () => bundle, intactLocalAct, host?.storage, host?.signal);
|
|
440
|
+
host?.committed?.(identity, committed, committed.kind);
|
|
409
441
|
// Commitment precedes projections and conformance work. If either fails,
|
|
410
442
|
// this public identity still names the admitted act; it is never rollback.
|
|
411
|
-
|
|
443
|
+
hostOutput('out', `ACT ${identity} · epoch ${epoch} · admitted · conformance not asserted\n`);
|
|
412
444
|
for (const line of records)
|
|
413
|
-
|
|
414
|
-
|
|
445
|
+
hostOutput('out', line + '\n');
|
|
446
|
+
hostOutput('out', receipt + '\n');
|
|
415
447
|
landed = [{ at: 'committed act ' + identity, appended: records.length }];
|
|
416
448
|
}
|
|
417
449
|
else
|
|
@@ -442,7 +474,7 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
442
474
|
}
|
|
443
475
|
landCoordinates(store, new Map(), coordinates.taken, `withdraw:epoch-${epoch}`);
|
|
444
476
|
for (const path of coordinates.taken)
|
|
445
|
-
|
|
477
|
+
hostOutput('out', `GONE ${path} · withdrawn by its line\n`);
|
|
446
478
|
}
|
|
447
479
|
for (const one of judged)
|
|
448
480
|
landTake(root, one, []);
|
|
@@ -453,31 +485,31 @@ async function acceptNow(root, batch, started, owned, asked = started, descent =
|
|
|
453
485
|
land(store, 'judge', late);
|
|
454
486
|
for (const line of late)
|
|
455
487
|
if (fieldOf(line, 'measure') !== 'cone')
|
|
456
|
-
|
|
488
|
+
hostOutput('out', `LATE ${fieldOf(line, 'scope')} · answered from the landed cone\n`);
|
|
457
489
|
}
|
|
458
490
|
if (signed.some((line) => fieldOf(line, 'scope').startsWith(`${basename(ownRoot())}/`) && !fieldOf(line, 'scope').split('/').slice(1, -1).includes('uses'))) {
|
|
459
491
|
const code = instrumentOf(store, entryOf(root), told.standing, root, ownLock());
|
|
460
492
|
if (instrumentKept(store) !== code) {
|
|
461
493
|
const open = await closeReceipts(root, store);
|
|
462
494
|
instrumentKeep(store, code);
|
|
463
|
-
|
|
495
|
+
hostOutput('error', `open by instrument · ${open.length}\n`);
|
|
464
496
|
}
|
|
465
497
|
}
|
|
466
498
|
const rendered = took('render', () => renderInto(root, store));
|
|
467
|
-
|
|
499
|
+
hostOutput('out', `RENDER 0\n`);
|
|
468
500
|
const own = new Set([...owned.own.values()].flat()).size;
|
|
469
501
|
const perLine = Math.round((Date.now() - started) / Math.max(1, signed.length + own));
|
|
470
502
|
const folder = writerFor(fold.standing, 'fold');
|
|
471
503
|
const beat = (ms, at) => canonical({ scope: 'beat/accept', role: 'writes', form: 'interval', measure: 'ms', value: `${ms}..${ms}`, by: folder ?? '', at: `place:${at}` });
|
|
472
504
|
if (folder !== undefined)
|
|
473
505
|
land(store, folder, [beat(perLine, 'line'), beat(1, fold.kept ? 'kept' : 'folded'), beat(Date.now() - started, 'total')]);
|
|
474
|
-
|
|
506
|
+
hostOutput('out', `FACT signed ${signed.length + own} · landed ${landed.length ? landed.map((one) => `${one.appended} in ${one.at}`).join(' · ') : '0'}${[...owned.own].map(([place, lines]) => ` · ${lines.length} in ${place}/${LOCK}`).join('')} · epoch ${epoch}\n`
|
|
475
507
|
+ `TARGET ${rendered.standing} claims · ${rendered.forks.length} forks · ${rendered.vacuous.length} vacuous · ${rendered.grey.length} grey · ${rendered.refused.length} refused · readings · no encounters computed · ${Date.now() - started} ms\n`
|
|
476
508
|
+ `SPENT ${spent.join(' ms · ')} ms · ${signed.length} lines\n`);
|
|
477
509
|
if (withdrawn) {
|
|
478
|
-
|
|
510
|
+
hostOutput('out', `WITHDRAW ${withdrawn.taken.length} taken · ${withdrawn.exact.length} exact on distributive scales · ${withdrawn.open.length} on open scales, not computed\n`);
|
|
479
511
|
for (const line of withdrawn.inexact)
|
|
480
|
-
|
|
512
|
+
hostOutput('out', `INEXACT ${fieldOf(line, 'scope')} · its scale is not distributive: what the others hold is not what they held without it\n`);
|
|
481
513
|
}
|
|
482
514
|
return 0;
|
|
483
515
|
});
|
|
@@ -490,18 +522,18 @@ function repaid(root, proposed, started) {
|
|
|
490
522
|
const store = storeOf(root);
|
|
491
523
|
const prior = committedLocalActs(store).find(act => { const lines = act.bundle.records.trimEnd().split('\n'); return lines.length === proposed.length && lines.every((line, i) => line === proposed[i]); });
|
|
492
524
|
if (prior) {
|
|
493
|
-
|
|
525
|
+
hostOutput('out', `ACT ${prior.identity} · epoch ${fieldOf(prior.bundle.receipt.trim(), 'epoch')} · admitted · replay\n` + prior.bundle.records + prior.bundle.receipt);
|
|
494
526
|
}
|
|
495
527
|
const asked = new Set(proposed.filter((line) => fieldOf(line, 'role') === 'demands').map((line) => fieldOf(line, 'scope')));
|
|
496
528
|
const view = `${wordOf(foldClaims(signedOwnerLines(store)).standing, 'families', 'view')}/`;
|
|
497
529
|
const standingAsked = signedDemands(store).filter((line) => asked.has(fieldOf(line, 'scope')) && !fieldOf(line, 'scope').startsWith(view));
|
|
498
530
|
const owed = paidOf(store, standingAsked, [], view).missing;
|
|
499
531
|
if (!owed.length) {
|
|
500
|
-
|
|
532
|
+
hostOutput('out', `ONCE ${proposed.length} lines · every one already landed and every demand of it paid: a batch landed twice is one, nothing landed\n`);
|
|
501
533
|
return 0;
|
|
502
534
|
}
|
|
503
535
|
const judged = owed.map((line) => judgeDemand(root, line));
|
|
504
|
-
|
|
536
|
+
hostOutput('out', `ONCE ${proposed.length} lines · every one already landed · ${owed.length} of its demands unpaid, judged again\n`);
|
|
505
537
|
if (judged.every((one) => one.kind === 'owed')) {
|
|
506
538
|
for (const one of judged)
|
|
507
539
|
landTake(root, one, []);
|
|
@@ -510,8 +542,8 @@ function repaid(root, proposed, started) {
|
|
|
510
542
|
const refused = judged.filter((one) => one.kind === 'refused' || (one.kind === 'judged' && one.verdict.kind === 'refuse'));
|
|
511
543
|
if (refused.length) {
|
|
512
544
|
for (const one of refused)
|
|
513
|
-
|
|
514
|
-
|
|
545
|
+
hostOutput('out', `REFUSE take ${one.scope} · ${one.kind === 'judged' ? one.verdict.why : one.kind === 'refused' ? one.why : ''}\n`);
|
|
546
|
+
hostOutput('error', `${selfName()}: REFUSE·takes nothing landed · ${refused.length} takes refused: what a batch left unpaid is paid whole or not at all\n`);
|
|
515
547
|
return 1;
|
|
516
548
|
}
|
|
517
549
|
const standing = standingOf(root);
|
|
@@ -525,11 +557,11 @@ function repaid(root, proposed, started) {
|
|
|
525
557
|
land(store, 'judge', late);
|
|
526
558
|
for (const line of late)
|
|
527
559
|
if (fieldOf(line, 'measure') !== 'cone')
|
|
528
|
-
|
|
560
|
+
hostOutput('out', `LATE ${fieldOf(line, 'scope')} · answered from the landed cone\n`);
|
|
529
561
|
}
|
|
530
562
|
const rendered = renderInto(root, store);
|
|
531
|
-
|
|
532
|
-
|
|
563
|
+
hostOutput('out', `RENDER 0\n`);
|
|
564
|
+
hostOutput('out', `TARGET ${rendered.standing} claims · ${rendered.forks.length} forks · ${rendered.vacuous.length} vacuous · ${rendered.grey.length} grey · ${rendered.refused.length} refused · readings · no encounters computed · ${Date.now() - started} ms\n`);
|
|
533
565
|
return 0;
|
|
534
566
|
}
|
|
535
567
|
function packTake(one) {
|
package/dist/cli/place.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hostOutput } from "../host/call-context.js";
|
|
1
2
|
import { refreshRetainedJudgment } from "./retained-judgment.js";
|
|
2
3
|
import { timing } from "../host/timing.js";
|
|
3
4
|
import { attestationObservations } from "../fold/evidence.js";
|
|
@@ -193,7 +194,7 @@ export function foldPlace(root, entry, under, look = true, given) {
|
|
|
193
194
|
if (closed && given?.free !== true) {
|
|
194
195
|
const prior = keptPlace(store, under, instrumentOf(store, entry, standingOf(root).standing, root, ownLock()));
|
|
195
196
|
if (prior !== undefined) {
|
|
196
|
-
|
|
197
|
+
hostOutput('error', `CLOSED ${under || '.'} · digest ${digestMs} ms\n`);
|
|
197
198
|
return refreshRetainedJudgment(prior, [...prior.standing.filter((line) => isCeiling(line) && !(under && fieldOf(line, 'measure') === 'unread')), ...ownCeilings(under)]);
|
|
198
199
|
}
|
|
199
200
|
}
|
|
@@ -276,6 +277,6 @@ export function foldPlace(root, entry, under, look = true, given) {
|
|
|
276
277
|
};
|
|
277
278
|
if (folder !== undefined && meets(root, store, under ?? '', true, whole.observed))
|
|
278
279
|
took('keep', () => keepFold(store, taken, whole, points, self));
|
|
279
|
-
|
|
280
|
+
hostOutput('error', `SPENT fold ${spent.join(' ms · ')} ms\n`);
|
|
280
281
|
return whole;
|
|
281
282
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { BoundRefusal } from "../host/refusals.js";
|
|
2
1
|
import { hasContextualPlace, contextualStanding } from "../host/contextual-place.js";
|
|
3
2
|
import { publicStandingLines } from "../host/admitted-place.js";
|
|
4
|
-
import {
|
|
3
|
+
import { assertStandingView } from "../host/standing-projection.js";
|
|
5
4
|
import { lockStanding } from "../fold/keys.js";
|
|
6
5
|
import { storeOf } from "../land/ledger.js";
|
|
7
6
|
/** An evidence projection, never a new authority lock. Authentication uses
|
|
@@ -9,9 +8,8 @@ import { storeOf } from "../land/ledger.js";
|
|
|
9
8
|
* supersessions and withdrawals across all declared classes. Storage layout
|
|
10
9
|
* and delivery mode do not enter this public boundary. */
|
|
11
10
|
export function printStandingResult(root) {
|
|
12
|
-
const store = storeOf(root)
|
|
13
|
-
|
|
14
|
-
throw new BoundRefusal('standing/view', 'REFUSE·view standing requires a declared standing view');
|
|
11
|
+
const store = storeOf(root);
|
|
12
|
+
assertStandingView(lockStanding(store));
|
|
15
13
|
if (hasContextualPlace(root)) {
|
|
16
14
|
process.stdout.write(JSON.stringify(contextualStanding(root)) + '\n');
|
|
17
15
|
return;
|
package/dist/cli/takes.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hostOutput } from "../host/call-context.js";
|
|
1
2
|
import { existsSync } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { fullDigest } from "../fold/digests.js";
|
|
@@ -62,7 +63,7 @@ export function landTake(root, one, gates) {
|
|
|
62
63
|
if (one.kind === 'refused')
|
|
63
64
|
return;
|
|
64
65
|
if (one.kind === 'owed') {
|
|
65
|
-
|
|
66
|
+
hostOutput('out', `OWED take ${one.scope} · ${one.why}\n`);
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
69
|
if (one.kind === 'conditioned') {
|
|
@@ -71,7 +72,7 @@ export function landTake(root, one, gates) {
|
|
|
71
72
|
scope, role: 'writes', form: 'alphabet', measure: fieldOf(demand, 'measure') || 'status', value: fieldOf(demand, 'value') || 'present', by: 'judge', at: receipt, condition,
|
|
72
73
|
})]);
|
|
73
74
|
land(store, 'take', [canonical({ scope: `take/${scope}`, role: 'writes', form: 'alphabet', measure: 'status', value: 'fact', by: 'judge', at: receipt })]);
|
|
74
|
-
|
|
75
|
+
hostOutput('out', `FACT take ${scope} · paid by ${receipt} · condition ${condition} · no moved set\n`);
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
const { scope, demand, before, after, ignored, started } = one;
|
|
@@ -102,7 +103,7 @@ export function landTake(root, one, gates) {
|
|
|
102
103
|
canonical({ scope: `take/${scope}`, role: 'writes', form: 'interval', measure: 'ms', value: `${ms}..${ms}`, by: 'judge', at: receipt }),
|
|
103
104
|
]);
|
|
104
105
|
const effects = verdict.envelope.effects.map((e) => `${e.kind}@${e.at || '.'}:${e.admitted ? e.status : `${e.status}·grey`}(${e.ms} ms)`).join(' ') || 'none';
|
|
105
|
-
|
|
106
|
+
hostOutput('out', `${verdict.kind === 'fact' ? 'FACT ' : 'REFUSE'} take ${scope} · ${verdict.why}\n`
|
|
106
107
|
+ `MOVED ${verdict.changed.length} sources: ${verdict.changed.join(' ')}\n`
|
|
107
108
|
+ `IGNORED ${[...ignored].map(([role, n]) => `${n} ${role}`).join(' · ') || 'none'} (not walked, never landed)\n`
|
|
108
109
|
+ `${verdict.notOwned.length ? `NOT OWN ${verdict.notOwned.length} witnesses of other laws, left for them: ${verdict.notOwned.join(' ')}\n` : ''}`
|
package/dist/cli/verb.js
CHANGED
|
@@ -49,7 +49,7 @@ import { placesIn } from "../fold/places.js";
|
|
|
49
49
|
import { isCeiling } from "../fold/configures.js";
|
|
50
50
|
import { readingsIn } from "../land/inert.js";
|
|
51
51
|
import { pass } from "./pass.js";
|
|
52
|
-
import { underTheLock, underTheRegions, underRootAuthority } from "../land/act.js";
|
|
52
|
+
import { installActSignalHandler, underTheLock, underTheRegions, underRootAuthority } from "../land/act.js";
|
|
53
53
|
import { ownerOf, renderInto, signedDemands, targetFile } from "./owner.js";
|
|
54
54
|
import { claimLinesIn, entryOf, namedPlaces, positionalIndexes, positionalsOf, valueOf } from "./args.js";
|
|
55
55
|
import { EXIT, FLAGS, NAMED_VIEWS, helpLines } from "./names.js";
|
|
@@ -672,6 +672,7 @@ const refused = (error) => {
|
|
|
672
672
|
printRefusal(error instanceof BoundRefusal ? error : new BoundRefusal('operation/failed', message));
|
|
673
673
|
process.exitCode = EXIT.refuse;
|
|
674
674
|
};
|
|
675
|
+
installActSignalHandler();
|
|
675
676
|
try {
|
|
676
677
|
const ran = main(process.argv.slice(2));
|
|
677
678
|
if (ran instanceof Promise)
|
package/dist/engine/ports.d.ts
CHANGED
|
@@ -28,5 +28,10 @@ export declare function retainContent(digest: string, bytes: Uint8Array, ports:
|
|
|
28
28
|
readonly digest: string;
|
|
29
29
|
readonly bytes: number;
|
|
30
30
|
}>;
|
|
31
|
-
/** Acquire
|
|
31
|
+
/** Acquire canonical line signatures only. The line protocol is not widened
|
|
32
|
+
* for contextual receipt frames. Both operations share bounds and response checks. */
|
|
32
33
|
export declare function invokeSigningPort(lot: SigningLot, sign: SigningPort, limits: PortLimits, signal?: AbortSignal): Promise<readonly SignatureRecord[]>;
|
|
34
|
+
/** Host-prepared signature frames. This acquires no authority: the contextual
|
|
35
|
+
* transaction validates the frame's domain/receiver and verifies every signature.
|
|
36
|
+
* A carrier cannot select this operation or supply a signature verifier. */
|
|
37
|
+
export declare function invokeFrameSigningPort(lot: SigningLot, sign: SigningPort, limits: PortLimits, signal?: AbortSignal): Promise<readonly SignatureRecord[]>;
|
package/dist/engine/ports.js
CHANGED
|
@@ -57,10 +57,29 @@ export async function retainContent(digest, bytes, ports, limits, signal) {
|
|
|
57
57
|
fail('cancelled'); const result = await ports.retain(digest, copy, s); if (result !== undefined)
|
|
58
58
|
fail('response'); return Object.freeze({ digest, bytes: copy.byteLength }); });
|
|
59
59
|
}
|
|
60
|
-
/** Acquire
|
|
60
|
+
/** Acquire canonical line signatures only. The line protocol is not widened
|
|
61
|
+
* for contextual receipt frames. Both operations share bounds and response checks. */
|
|
61
62
|
export async function invokeSigningPort(lot, sign, limits, signal) {
|
|
63
|
+
validateSigningLot(lot, limits);
|
|
64
|
+
let request;
|
|
65
|
+
try {
|
|
66
|
+
request = signerRequestBytes(lot);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return fail('response');
|
|
70
|
+
}
|
|
71
|
+
return invokeDeclaredSignatures(lot, request, sign, limits, signal);
|
|
72
|
+
}
|
|
73
|
+
/** Host-prepared signature frames. This acquires no authority: the contextual
|
|
74
|
+
* transaction validates the frame's domain/receiver and verifies every signature.
|
|
75
|
+
* A carrier cannot select this operation or supply a signature verifier. */
|
|
76
|
+
export async function invokeFrameSigningPort(lot, sign, limits, signal) {
|
|
77
|
+
validateSigningLot(lot, limits);
|
|
78
|
+
return invokeDeclaredSignatures(lot, JSON.stringify(lot) + '\n', sign, limits, signal);
|
|
79
|
+
}
|
|
80
|
+
function validateSigningLot(lot, limits) {
|
|
62
81
|
limitsOf(limits);
|
|
63
|
-
if (!lot || !Array.isArray(lot.bytes))
|
|
82
|
+
if (!lot || !Array.isArray(lot.bytes) || !lot.bytes.length || typeof lot.keyId !== 'string' || !lot.keyId || typeof lot.algorithm !== 'string' || !lot.algorithm)
|
|
64
83
|
fail('budget');
|
|
65
84
|
let size = 0;
|
|
66
85
|
for (const bytes of lot.bytes) {
|
|
@@ -70,13 +89,8 @@ export async function invokeSigningPort(lot, sign, limits, signal) {
|
|
|
70
89
|
if (size > limits.maxBytes)
|
|
71
90
|
fail('budget');
|
|
72
91
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
request = signerRequestBytes(lot);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return fail('response');
|
|
79
|
-
}
|
|
92
|
+
}
|
|
93
|
+
function invokeDeclaredSignatures(lot, request, sign, limits, signal) {
|
|
80
94
|
if (encoded(request).length > limits.maxBytes)
|
|
81
95
|
fail('budget');
|
|
82
96
|
const input = Object.freeze({ ...lot, bytes: Object.freeze([...lot.bytes]) });
|