@githolon/dsl 0.108.0 → 0.108.2
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/dart/CHANGELOG.md +5 -0
- package/dart/lib/src/application_framework_calls.dart +299 -0
- package/dart/lib/src/generated_call.dart +96 -0
- package/dart/lib/src/nms_generated_runtime.dart +29 -0
- package/dart/lib/src/node_runner_transport.dart +77 -1
- package/dart/lib/src/result_usda.dart +487 -0
- package/dart/lib/src/runner/node-entry.mjs +6401 -2102
- package/dart/lib/src/runner/offline-install.mjs +1538 -163
- package/dart/lib/src/runtime_v2_gateway.dart +54 -0
- package/dart/lib/src/runtime_v2_transport.dart +117 -0
- package/dart/lib/src/session_bound.dart +7 -2
- package/dart/lib/src/test_harness.dart +193 -112
- package/dart/lib/src/webview_bridge.dart +2120 -569
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_credentials_test.dart +1 -1
- package/dart/test/acquire_session_test.dart +68 -14
- package/dart/test/adv_realm_interference_test.dart +6 -4
- package/dart/test/cluster_births_test.dart +11 -8
- package/dart/test/cluster_local_autosync_test.dart +1 -1
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +52 -35
- package/dart/test/first_app_gauntlet_test.dart +1 -1
- package/dart/test/local_sync_inert_test.dart +23 -26
- package/dart/test/offer_authentication_proof_test.dart +193 -0
- package/dart/test/result_usda_test.dart +49 -0
- package/dart/test/session_bound_test.dart +8 -11
- package/dart/test/stale_bundle_test.dart +18 -3
- package/dart/test/test_harness_test.dart +14 -27
- package/dart/test/webview_bridge_test.dart +504 -83
- package/dart_types/CHANGELOG.md +7 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +3 -2
- package/dart_types/lib/src/custody.dart +64 -19
- package/dart_types/lib/src/local_work_rejected.dart +30 -45
- package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pb.dart +169 -0
- package/dart_types/lib/src/proto/nomos/custody/v1/snapshot.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +2021 -234
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +175 -8
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +8664 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +369 -0
- package/dart_types/lib/src/surfaced.dart +30 -25
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +2 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +5736 -2047
- package/src/authoring.ts +3 -2
- package/src/births.ts +21 -0
- package/src/build_package.ts +30 -0
- package/src/business_birth.ts +39 -35
- package/src/codegen_call_usda.ts +1111 -0
- package/src/codegen_dart.ts +207 -282
- package/src/codegen_dart_application.ts +195 -252
- package/src/codegen_dart_client.ts +1978 -593
- package/src/codegen_dart_framework_calls.ts +291 -0
- package/src/codegen_dart_scenario.ts +7 -0
- package/src/codegen_proof.ts +3 -4
- package/src/codegen_provider_dart.ts +21 -7
- package/src/codegen_query_usda.ts +214 -0
- package/src/codegen_result_usda.ts +685 -0
- package/src/codegen_ts.ts +3945 -1111
- package/src/compile_package_main.ts +360 -32
- package/src/directive.ts +19 -4
- package/src/engine_entry.ts +45 -4
- package/src/framework/application_home.ts +2 -0
- package/src/framework/auth_provider_law.ts +262 -20
- package/src/framework/home.ts +32 -28
- package/src/framework/owned_custody.ts +19 -11
- package/src/framework/owned_custody_value.ts +51 -0
- package/src/framework/relations.ts +57 -21
- package/src/framework/signing_identity.ts +34 -0
- package/src/framework/workspaces.ts +31 -9
- package/src/framework_call_definitions.ts +53 -0
- package/src/framework_read_definitions.ts +45 -0
- package/src/identity_frontier.ts +58 -0
- package/src/index.ts +20 -1
- package/src/intent.ts +216 -16
- package/src/law_check.ts +19 -1
- package/src/law_graph.ts +57 -8
- package/src/law_graph_usda.ts +73 -5
- package/src/law_value_type.ts +106 -19
- package/src/read.ts +47 -0
- package/src/read_projection_namespace.ts +7 -0
- package/src/shape_compat.ts +38 -39
- package/src/stable_ids.ts +37 -2
- package/src/stable_ids_for_module.ts +64 -4
- package/src/test_call_usda.ts +83 -0
- package/src/wire.ts +36 -2
- package/src/wire_encode.ts +45 -1
- package/src/workspace_sharing.ts +27 -10
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbjson.dart +0 -6096
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbserver.dart +0 -50
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbjson.dart +0 -178
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pb.dart +0 -828
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbenum.dart +0 -465
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbjson.dart +0 -573
- package/dart_types/lib/src/proto/nomos/runtime/v1/runtime.pbserver.dart +0 -50
|
@@ -23,6 +23,7 @@ import { BirthAttestation, BirthAttestationLink, seedBirthAttestation } from "./
|
|
|
23
23
|
import { RelationTuple, __qualifyAggregateType, removeTuple, writeTuple } from "./relations.js";
|
|
24
24
|
import { SIGNER_RELATION, enrollSigner, signerObject, identityRelations } from "./signing_identity.js";
|
|
25
25
|
import { impureCapability, impureCapabilityAggregate } from "./impure_capability.js";
|
|
26
|
+
import { OwnedCustodyValue, OwnedDeviceRequestValue } from "./owned_custody_value.js";
|
|
26
27
|
|
|
27
28
|
/** Obscure stable law id: deliberately unavailable as a generated tenant directive method. */
|
|
28
29
|
export const OWNED_DEVICE_APPROVAL_DIRECTIVE = "nomos_internal_approve_owned_device_7c1f5e";
|
|
@@ -68,7 +69,7 @@ export const OwnedRecoveryPolicy = aggregate("NomosOwnedRecoveryPolicy", {
|
|
|
68
69
|
const recoveryOrderParams = {
|
|
69
70
|
ownerPrincipal: t.string().merge(Lww),
|
|
70
71
|
recoveryPrincipal: t.string().merge(Lww),
|
|
71
|
-
deviceRequest:
|
|
72
|
+
deviceRequest: OwnedDeviceRequestValue.merge(Lww),
|
|
72
73
|
};
|
|
73
74
|
|
|
74
75
|
/**
|
|
@@ -81,14 +82,14 @@ const recoveryOrderParams = {
|
|
|
81
82
|
* the last thing that should degrade quietly.
|
|
82
83
|
*/
|
|
83
84
|
const recoveryReceipt = {
|
|
84
|
-
ownedCustody:
|
|
85
|
+
ownedCustody: OwnedCustodyValue.zod,
|
|
85
86
|
};
|
|
86
87
|
|
|
87
88
|
/** Order fields ⊕ the receipt field, so the aggregate can hold what the Receipt writes. */
|
|
88
89
|
const recoveryOrderFields = {
|
|
89
90
|
...recoveryOrderParams,
|
|
90
91
|
// Absent until the recovery principal's Receipt lands (the read-decode discipline).
|
|
91
|
-
ownedCustody:
|
|
92
|
+
ownedCustody: OwnedCustodyValue.merge(Lww).optional(),
|
|
92
93
|
};
|
|
93
94
|
|
|
94
95
|
/** Private framework Order -> Receipt journal. Generated business clients never expose this aggregate. */
|
|
@@ -105,7 +106,7 @@ const ownedRecoveryLifecycle = impureCapability({
|
|
|
105
106
|
paramsSchema: {
|
|
106
107
|
ownerPrincipal: principal,
|
|
107
108
|
recoveryPrincipal: principal,
|
|
108
|
-
deviceRequest:
|
|
109
|
+
deviceRequest: OwnedDeviceRequestValue.zod,
|
|
109
110
|
},
|
|
110
111
|
writeParams: (id, p) => [
|
|
111
112
|
set(instance(OwnedRecoveryOrder, id), "ownerPrincipal", p.ownerPrincipal),
|
|
@@ -114,7 +115,7 @@ const ownedRecoveryLifecycle = impureCapability({
|
|
|
114
115
|
],
|
|
115
116
|
receiptSchema: recoveryReceipt,
|
|
116
117
|
writeReceipt: (id, r) => [
|
|
117
|
-
set(instance(OwnedRecoveryOrder, id), "ownedCustody", r.ownedCustody
|
|
118
|
+
set(instance(OwnedRecoveryOrder, id), "ownedCustody", OwnedCustodyValue.zod.parse(r.ownedCustody)),
|
|
118
119
|
],
|
|
119
120
|
// This is intentionally unaddressed: opening the designated principal's ordinary generated
|
|
120
121
|
// client is the provider. Declaring the capability still matters because every durable Order must
|
|
@@ -132,6 +133,13 @@ const hiddenRecoveryLifecycle = {
|
|
|
132
133
|
deadLetter: hidden(ownedRecoveryLifecycle.deadLetter),
|
|
133
134
|
reschedule: hidden(ownedRecoveryLifecycle.reschedule),
|
|
134
135
|
};
|
|
136
|
+
|
|
137
|
+
/** Compiler-contract handles for the two generated recovery calls used by the
|
|
138
|
+
* host. They remain framework-private directives; exporting the authored
|
|
139
|
+
* objects here lets code generation derive the cross-language signatures
|
|
140
|
+
* instead of maintaining another payload mirror. */
|
|
141
|
+
export const requestOwnedRecovery = hiddenRecoveryLifecycle.obligation;
|
|
142
|
+
export const completeOwnedRecovery = hiddenRecoveryLifecycle.complete;
|
|
135
143
|
const ownedRecoveryCapability = {
|
|
136
144
|
...ownedRecoveryLifecycle,
|
|
137
145
|
// Capability expansion deliberately re-seats the lifecycle quartet after application-language
|
|
@@ -152,7 +160,7 @@ export const ownedRecoveryOrdersByRecoveryPrincipal = query("nomosOwnedRecoveryO
|
|
|
152
160
|
.returns(OwnedRecoveryOrder);
|
|
153
161
|
|
|
154
162
|
/** Opt in once. Retrying the same policy is idempotent; replacing the principal is refused by the kernel. */
|
|
155
|
-
const protectOwnedIdentity = Object.assign(
|
|
163
|
+
export const protectOwnedIdentity = Object.assign(
|
|
156
164
|
directive(OWNED_RECOVERY_PROTECT_DIRECTIVE)
|
|
157
165
|
.ensures(OwnedRecoveryPolicy)
|
|
158
166
|
.payload(z.object({
|
|
@@ -196,13 +204,13 @@ const protectOwnedIdentity = Object.assign(
|
|
|
196
204
|
{ businessBirthInternal: true as const, ownedAuthoringInternal: true as const },
|
|
197
205
|
);
|
|
198
206
|
|
|
199
|
-
const acceptOwnedRecovery = Object.assign(
|
|
207
|
+
export const acceptOwnedRecovery = Object.assign(
|
|
200
208
|
directive(OWNED_RECOVERY_ACCEPT_DIRECTIVE)
|
|
201
209
|
.ensures(RelationTuple)
|
|
202
210
|
.payload(z.object({
|
|
203
211
|
requestId: z.string().min(1),
|
|
204
212
|
newKeySubject: keySubject,
|
|
205
|
-
ownedCustody:
|
|
213
|
+
ownedCustody: OwnedCustodyValue.zod,
|
|
206
214
|
recoveredAt: z.string().min(1),
|
|
207
215
|
}))
|
|
208
216
|
.plan((p, ctx) => {
|
|
@@ -249,7 +257,7 @@ const approval = directive(OWNED_DEVICE_APPROVAL_DIRECTIVE)
|
|
|
249
257
|
.ensures(RelationTuple)
|
|
250
258
|
.payload(z.object({
|
|
251
259
|
newKeySubject: keySubject,
|
|
252
|
-
ownedCustody:
|
|
260
|
+
ownedCustody: OwnedCustodyValue.zod,
|
|
253
261
|
verifiedVia: z.string().min(1),
|
|
254
262
|
validFromEpoch: z.number().int().nonnegative(),
|
|
255
263
|
approvedAt: z.string().min(1),
|
|
@@ -287,12 +295,12 @@ export const approveOwnedDevice = Object.assign(approval, {
|
|
|
287
295
|
});
|
|
288
296
|
|
|
289
297
|
/** Establish this principal's shared owned-data root before another principal shares to them. */
|
|
290
|
-
const initializeOwnedIdentity = Object.assign(
|
|
298
|
+
export const initializeOwnedIdentity = Object.assign(
|
|
291
299
|
directive(OWNED_INITIALIZE_DIRECTIVE)
|
|
292
300
|
.ensures(RelationTuple)
|
|
293
301
|
.payload(z.object({
|
|
294
302
|
newKeySubject: keySubject,
|
|
295
|
-
ownedCustody:
|
|
303
|
+
ownedCustody: OwnedCustodyValue.zod,
|
|
296
304
|
initializedAt: z.string().min(1),
|
|
297
305
|
}))
|
|
298
306
|
.plan((p, ctx) => {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// NOMOS — framework-owned typed custody values shared by relation facts, recovery Orders,
|
|
2
|
+
// generated clients and the kernel. USDA is the canonical semantic representation; these
|
|
3
|
+
// descriptors are the authored source from which TS/Dart encoders and decoders are generated.
|
|
4
|
+
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { valueObject, type ValueObjectValue } from "../value_object.js";
|
|
7
|
+
|
|
8
|
+
const base64 = z.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
|
|
9
|
+
const sha256 = z.string().regex(/^[0-9a-f]{64}$/);
|
|
10
|
+
|
|
11
|
+
export const OwnedDeviceRequestValue = valueObject("OwnedDeviceRequest", {
|
|
12
|
+
v: z.number().int().min(1).max(255),
|
|
13
|
+
workspace: z.string().min(1),
|
|
14
|
+
principal: z.string().min(1),
|
|
15
|
+
scope: z.string().min(1),
|
|
16
|
+
signingPublic: base64,
|
|
17
|
+
wrappingPublic: base64,
|
|
18
|
+
signature: base64,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export type OwnedDeviceRequest = ValueObjectValue<typeof OwnedDeviceRequestValue>;
|
|
22
|
+
|
|
23
|
+
export const OwnedCustodyValue = valueObject("OwnedCustody", {
|
|
24
|
+
v: z.number().int().min(1).max(255),
|
|
25
|
+
request: OwnedDeviceRequestValue,
|
|
26
|
+
hpkeEpk: base64,
|
|
27
|
+
ct: base64,
|
|
28
|
+
keyCommitment: sha256,
|
|
29
|
+
principalWrappingPublic: base64,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type OwnedCustody = ValueObjectValue<typeof OwnedCustodyValue>;
|
|
33
|
+
|
|
34
|
+
/** Deterministic compile/proof value. It is structurally valid but intentionally has no
|
|
35
|
+
* cryptographic authority; the live executor will reject it if it ever reaches admission. */
|
|
36
|
+
export const OWNED_CUSTODY_PROBE: OwnedCustody = {
|
|
37
|
+
v: 1,
|
|
38
|
+
request: {
|
|
39
|
+
v: 1,
|
|
40
|
+
workspace: "nomos-probe",
|
|
41
|
+
principal: "user:nomos-probe",
|
|
42
|
+
scope: "nomos-probe",
|
|
43
|
+
signingPublic: "AA==",
|
|
44
|
+
wrappingPublic: "AA==",
|
|
45
|
+
signature: "AA==",
|
|
46
|
+
},
|
|
47
|
+
hpkeEpk: "AA==",
|
|
48
|
+
ct: "AA==",
|
|
49
|
+
keyCommitment: "0".repeat(64),
|
|
50
|
+
principalWrappingPublic: "AA==",
|
|
51
|
+
};
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
*/
|
|
42
42
|
import { z } from "zod";
|
|
43
43
|
import { aggregate, instance } from "../aggregate.js";
|
|
44
|
-
import { t
|
|
44
|
+
import { t } from "../fields.js";
|
|
45
45
|
import { Lww, MapOf } from "../drivers.js";
|
|
46
46
|
import { directive } from "../directive.js";
|
|
47
47
|
import { set, setEntry, withMarker, type PlannedOp } from "../ops.js";
|
|
@@ -49,6 +49,8 @@ import { query } from "../query.js";
|
|
|
49
49
|
import { endorsementOf, type AttributionPrincipal, type EndorsedAuthority } from "../principal_evidence.js";
|
|
50
50
|
import { isRelationObjectToken, qualifyRelationType, type RelationObjectToken } from "../relation_schema.js";
|
|
51
51
|
import { qualifyReadProjectionType } from "../read_projection_namespace.js";
|
|
52
|
+
import { valueObject } from "../value_object.js";
|
|
53
|
+
import { OwnedCustodyValue, type OwnedCustody } from "./owned_custody_value.js";
|
|
52
54
|
|
|
53
55
|
// ─────────────────────────────── the dispatch context (relation-namespacing) ───────────────────────────────
|
|
54
56
|
//
|
|
@@ -192,46 +194,80 @@ export function relationTupleInstanceId(object: string, relation: string): strin
|
|
|
192
194
|
* - relation Lww (the edge label)
|
|
193
195
|
* - subjects MapOf(Lww) — subject → typed `RelationshipSubject` runtime leaf
|
|
194
196
|
*/
|
|
197
|
+
/** The exact law-level value stored for one subject in a relation tuple.
|
|
198
|
+
*
|
|
199
|
+
* This used to be `t.json()` and forced every generated reader to recover an
|
|
200
|
+
* unstructured object after the kernel had already proved a typed
|
|
201
|
+
* `RelationshipSubject`. Keeping the descriptor here makes the DSL graph the
|
|
202
|
+
* one authority for TS, Dart, USDA and the kernel's map leaf. */
|
|
203
|
+
export const RelationshipSubjectValue = valueObject("RelationshipSubject", {
|
|
204
|
+
status: z.enum(["active", "revoked"]),
|
|
205
|
+
grantedBy: z.string(),
|
|
206
|
+
grantedAt: z.string(),
|
|
207
|
+
revokedBy: z.string().optional(),
|
|
208
|
+
revokedAt: z.string().optional(),
|
|
209
|
+
validFromEpoch: z.number().int().nonnegative().optional(),
|
|
210
|
+
verifiedVia: z.string().optional(),
|
|
211
|
+
ownedCustody: OwnedCustodyValue.optional(),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Historical RelationTuple law declared this map value as `t.json()`, but the
|
|
215
|
+
// kernel has always admitted and stored only the nominal RelationshipSubject
|
|
216
|
+
// palette for this framework field. Keep that exact prior declaration as the
|
|
217
|
+
// source of the explicit evolution edge: this is a type-disclosure refinement
|
|
218
|
+
// of existing structured custody, not a JSON parser or a business-data cast.
|
|
219
|
+
const LegacyRelationshipSubjects = t.map(t.json()).merge(MapOf(Lww));
|
|
220
|
+
|
|
195
221
|
export const RelationTuple = aggregate("RelationTuple", {
|
|
196
222
|
object: t.string().merge(Lww),
|
|
197
223
|
relation: t.string().merge(Lww),
|
|
198
|
-
subjects: t
|
|
224
|
+
subjects: t
|
|
225
|
+
.map(RelationshipSubjectValue.field())
|
|
226
|
+
.merge(MapOf(Lww))
|
|
227
|
+
.evolvesFrom(LegacyRelationshipSubjects),
|
|
199
228
|
}).public();
|
|
200
229
|
export type RelationTupleHandle = typeof RelationTuple;
|
|
201
230
|
|
|
202
231
|
/** The per-subject leaf the `subjects` map stores as a typed kernel value. */
|
|
203
232
|
export interface RelationSubjectLeaf {
|
|
204
|
-
status: RelationStatus;
|
|
205
|
-
grantedBy: string;
|
|
206
|
-
grantedAt: string;
|
|
207
|
-
revokedBy?: string;
|
|
208
|
-
revokedAt?: string;
|
|
233
|
+
readonly status: RelationStatus;
|
|
234
|
+
readonly grantedBy: string;
|
|
235
|
+
readonly grantedAt: string;
|
|
236
|
+
readonly revokedBy?: string;
|
|
237
|
+
readonly revokedAt?: string;
|
|
209
238
|
/**
|
|
210
239
|
* VA-core (signer-provenance) — the SignerEpoch this leaf was authored under. OMITTED when absent so
|
|
211
240
|
* every pre-VA chain (and every non-signer relation) stays byte-identical: `compact()` strips an
|
|
212
241
|
* `undefined`. A revoke (`unrelateSubject`) backdates by bumping the SignerEpoch; a key whose
|
|
213
242
|
* `validFromEpoch` predates the current epoch is stale (the full backdate gate is phase 4).
|
|
214
243
|
*/
|
|
215
|
-
validFromEpoch?: number;
|
|
244
|
+
readonly validFromEpoch?: number;
|
|
216
245
|
/**
|
|
217
246
|
* VA-core (signer-provenance) — the IdP/issuer that VOUCHED for the enrolling principal at enroll time
|
|
218
247
|
* (e.g. the `AuthProvider.issuer`). The kernel's `verify_captured_principal` DERIVES `principal_verified`
|
|
219
248
|
* from THIS leaf field (provenance, not the wire): an enrollment vouched by a real IdP reads verified on
|
|
220
249
|
* every lane. OMITTED when absent (legacy / unverified enrollment) so existing chains stay byte-identical.
|
|
221
250
|
*/
|
|
222
|
-
verifiedVia?: string;
|
|
251
|
+
readonly verifiedVia?: string;
|
|
223
252
|
/**
|
|
224
253
|
* Secure-by-default owned-data device handoff. Framework-reserved and omitted from every ordinary
|
|
225
|
-
* relation leaf; the
|
|
254
|
+
* relation leaf; the typed encrypted handoff is independently verified by the kernel before it may land.
|
|
226
255
|
*/
|
|
227
|
-
ownedCustody?:
|
|
256
|
+
readonly ownedCustody?: OwnedCustody;
|
|
228
257
|
}
|
|
229
258
|
|
|
230
259
|
/** Strip undefined keys so the typed leaf has one stable optional-field shape. */
|
|
231
|
-
function compact(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
260
|
+
function compact(leaf: RelationSubjectLeaf): RelationSubjectLeaf {
|
|
261
|
+
return {
|
|
262
|
+
status: leaf.status,
|
|
263
|
+
grantedBy: leaf.grantedBy,
|
|
264
|
+
grantedAt: leaf.grantedAt,
|
|
265
|
+
...(leaf.revokedBy === undefined ? {} : { revokedBy: leaf.revokedBy }),
|
|
266
|
+
...(leaf.revokedAt === undefined ? {} : { revokedAt: leaf.revokedAt }),
|
|
267
|
+
...(leaf.validFromEpoch === undefined ? {} : { validFromEpoch: leaf.validFromEpoch }),
|
|
268
|
+
...(leaf.verifiedVia === undefined ? {} : { verifiedVia: leaf.verifiedVia }),
|
|
269
|
+
...(leaf.ownedCustody === undefined ? {} : { ownedCustody: leaf.ownedCustody }),
|
|
270
|
+
};
|
|
235
271
|
}
|
|
236
272
|
|
|
237
273
|
interface WriteTupleInput {
|
|
@@ -261,7 +297,7 @@ interface WriteTupleInput {
|
|
|
261
297
|
/** VA-core: the IdP/issuer that vouched for the enrolling principal (signer relations only; omitted otherwise). */
|
|
262
298
|
verifiedVia?: string;
|
|
263
299
|
/** Framework-internal encrypted owned-data handoff (owned device relation only). */
|
|
264
|
-
ownedCustody?:
|
|
300
|
+
ownedCustody?: OwnedCustody;
|
|
265
301
|
}
|
|
266
302
|
|
|
267
303
|
/** Lower a tuple write to plan ops. The first op MATERIALIZES the (object, relation)
|
|
@@ -297,13 +333,13 @@ function lowerTuple(
|
|
|
297
333
|
status: p.status,
|
|
298
334
|
grantedBy: grantedByRaw,
|
|
299
335
|
grantedAt: p.grantedAt,
|
|
300
|
-
revokedBy: revokedByRaw,
|
|
301
|
-
revokedAt: p.revokedAt,
|
|
336
|
+
...(revokedByRaw === undefined ? {} : { revokedBy: revokedByRaw }),
|
|
337
|
+
...(p.revokedAt === undefined ? {} : { revokedAt: p.revokedAt }),
|
|
302
338
|
// VA-core signer-provenance leaf fields — `compact` strips an `undefined`, so a non-signer relation
|
|
303
339
|
// (these never set) stays byte-identical to a pre-VA leaf (the era rule on the wire shape).
|
|
304
|
-
validFromEpoch: p.validFromEpoch,
|
|
305
|
-
verifiedVia: p.verifiedVia,
|
|
306
|
-
ownedCustody: p.ownedCustody,
|
|
340
|
+
...(p.validFromEpoch === undefined ? {} : { validFromEpoch: p.validFromEpoch }),
|
|
341
|
+
...(p.verifiedVia === undefined ? {} : { verifiedVia: p.verifiedVia }),
|
|
342
|
+
...(p.ownedCustody === undefined ? {} : { ownedCustody: p.ownedCustody }),
|
|
307
343
|
});
|
|
308
344
|
const subjectEntry = {
|
|
309
345
|
...setEntry(rt, "subjects", subjectRaw, leaf),
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
RelationTuple,
|
|
59
59
|
relationTupleInstanceId,
|
|
60
60
|
} from "./relations.js";
|
|
61
|
+
import { MODEL_LIFECYCLE_AUTHORITY } from "./domain_lifecycle.js";
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
64
|
* The framework `identity` relation schema — the `signer` relation, LEAF-ONLY
|
|
@@ -84,6 +85,9 @@ export function signerObject(principal: string): string {
|
|
|
84
85
|
/** The `signer` relation name — the relation a device key holds on its `identity:<principal>` object. */
|
|
85
86
|
export const SIGNER_RELATION = "signer";
|
|
86
87
|
|
|
88
|
+
/** Reserved workload principal for external admission receipts. It is never the business actor. */
|
|
89
|
+
export const ADMISSION_CHECKPOINT_PRINCIPAL = "workspace:admission-peer";
|
|
90
|
+
|
|
87
91
|
const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/, "expected lowercase sha256 hex (the device pubkey hash)");
|
|
88
92
|
|
|
89
93
|
// ─────────────────────────────── PHASE 0 — SignerEpoch ───────────────────────────────
|
|
@@ -240,6 +244,36 @@ export const enrollSigner = directive("enrollSigner")
|
|
|
240
244
|
});
|
|
241
245
|
});
|
|
242
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Enrol the runtime workload key which signs causal admission checkpoints. This is ordinary law:
|
|
249
|
+
* lifecycle authority chooses the public key, the relation is visible/revocable, and possession of
|
|
250
|
+
* the secret alone grants nothing. The key certifies that this workspace admitted an exact causal
|
|
251
|
+
* transition; the target directive's independent Zanzibar requirement remains business authority.
|
|
252
|
+
*/
|
|
253
|
+
export const enrollAdmissionCheckpointSigner = directive(
|
|
254
|
+
"_nmsEffectEnrollAdmissionCheckpointSigner",
|
|
255
|
+
)
|
|
256
|
+
.ensures(RelationTuple)
|
|
257
|
+
.payload(
|
|
258
|
+
z.object({
|
|
259
|
+
keyHash: sha256Hex,
|
|
260
|
+
validFromEpoch: z.number().int().nonnegative(),
|
|
261
|
+
grantedAt: z.string().min(1),
|
|
262
|
+
}).strict(),
|
|
263
|
+
)
|
|
264
|
+
.plan((p, ctx) =>
|
|
265
|
+
writeTuple({
|
|
266
|
+
object: signerObject(ADMISSION_CHECKPOINT_PRINCIPAL),
|
|
267
|
+
relation: SIGNER_RELATION,
|
|
268
|
+
subject: `key:${p.keyHash}`,
|
|
269
|
+
grantedBy: ctx.principal().asAttribution(),
|
|
270
|
+
grantedAt: p.grantedAt,
|
|
271
|
+
validFromEpoch: p.validFromEpoch,
|
|
272
|
+
verifiedVia: "workspace-law",
|
|
273
|
+
}),
|
|
274
|
+
)
|
|
275
|
+
.requires(MODEL_LIFECYCLE_AUTHORITY, { object: "workspace:self" });
|
|
276
|
+
|
|
243
277
|
/**
|
|
244
278
|
* THE SELF-BIND ENROLL (VA-1 / B2). A device enrolls a key for ITS OWN verified identity — the bound
|
|
245
279
|
* principal is `ctx.principal()` (the captured authoring principal), NOT a payload field, so a device can
|
|
@@ -1397,16 +1397,37 @@ export const setPlatformPool = directive("setPlatformPool")
|
|
|
1397
1397
|
// here verbatim — the public surface and the law bytes are unchanged.
|
|
1398
1398
|
export {
|
|
1399
1399
|
AUTH_PROVIDER_ID,
|
|
1400
|
+
AUTH_PROVIDER_KEY_ID_PREFIX,
|
|
1401
|
+
AUTH_PROVIDER_CERTIFICATE_ID_PREFIX,
|
|
1400
1402
|
AuthProvider,
|
|
1403
|
+
AuthProviderKey,
|
|
1404
|
+
AuthProviderCertificate,
|
|
1405
|
+
authProviderPublicKey,
|
|
1406
|
+
authProviderKeySet,
|
|
1407
|
+
authProviderConfiguration,
|
|
1408
|
+
type AuthProviderConfiguration,
|
|
1409
|
+
writeAuthProviderConfiguration,
|
|
1410
|
+
writeAuthProviderKeySet,
|
|
1401
1411
|
seedAuthProvider,
|
|
1402
1412
|
authProviderSeedBeforeWarrant,
|
|
1403
1413
|
setAuthProvider,
|
|
1404
1414
|
authProvider,
|
|
1415
|
+
authProviderKeys,
|
|
1416
|
+
authProviderCertificates,
|
|
1417
|
+
} from "./auth_provider_law.js";
|
|
1418
|
+
import {
|
|
1419
|
+
AUTH_PROVIDER_ID,
|
|
1420
|
+
AuthProvider,
|
|
1421
|
+
AuthProviderCertificate,
|
|
1422
|
+
AuthProviderKey,
|
|
1423
|
+
authProvider,
|
|
1424
|
+
authProviderKeySet,
|
|
1425
|
+
setAuthProvider,
|
|
1426
|
+
writeAuthProviderKeySet,
|
|
1405
1427
|
} from "./auth_provider_law.js";
|
|
1406
|
-
import { AUTH_PROVIDER_ID, AuthProvider, setAuthProvider, authProvider } from "./auth_provider_law.js";
|
|
1407
1428
|
|
|
1408
1429
|
/**
|
|
1409
|
-
* rotateKeys — TRUSTLESS key rotation. Replace
|
|
1430
|
+
* rotateKeys — TRUSTLESS key rotation. Replace the typed key epoch with public keys whose every key
|
|
1410
1431
|
* carries an `x5c` cert chain. DELIBERATELY UNGATED at the relation level — the KERNEL is the authority: it
|
|
1411
1432
|
* admits this ONLY if each key's x5c chain validates (RFC 5280) to the law-pinned {@link AuthProvider.rootCert}
|
|
1412
1433
|
* and each key matches its CA-vouched leaf cert (the bind). So a bailiff (host on a cron, or anyone) may
|
|
@@ -1417,17 +1438,18 @@ export const rotateKeys = directive("rotateKeys")
|
|
|
1417
1438
|
.ensures(AuthProvider)
|
|
1418
1439
|
.payload(
|
|
1419
1440
|
z.object({
|
|
1420
|
-
/** The new
|
|
1421
|
-
|
|
1441
|
+
/** The new typed keys; EACH key MUST carry an `x5c` the kernel validates to the pinned root. */
|
|
1442
|
+
keySet: authProviderKeySet,
|
|
1422
1443
|
rotatedAt: epochMs,
|
|
1423
|
-
}),
|
|
1444
|
+
}).strict(),
|
|
1424
1445
|
)
|
|
1425
1446
|
.plan((p) => {
|
|
1426
1447
|
const ap = instance(AuthProvider, AUTH_PROVIDER_ID);
|
|
1427
|
-
// The kernel's x5c admission gate authorizes this
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1448
|
+
// The kernel's x5c admission gate authorizes this; the plan stores the CA-vouched typed key epoch.
|
|
1449
|
+
return [withMarker(set(ap, "keyEpoch", p.keySet.epoch), "ensures"), ...writeAuthProviderKeySet(p.keySet), set(ap, "setAt", p.rotatedAt)];
|
|
1450
|
+
})
|
|
1451
|
+
.alsoEnsuresMany(AuthProviderKey)
|
|
1452
|
+
.alsoEnsuresMany(AuthProviderCertificate);
|
|
1431
1453
|
|
|
1432
1454
|
/**
|
|
1433
1455
|
* HostnameClaim — a CUSTOM HOSTNAME bound to a platform: `api.acme.com` serving
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Stable framework operation names, bound to their authored directive schemas.
|
|
2
|
+
//
|
|
3
|
+
// This is deliberately metadata, not a second schema language. Every payload
|
|
4
|
+
// type and encoder is derived from the Directive object carried here. Both the
|
|
5
|
+
// TypeScript and Dart compilers consume this same list, so adding or changing a
|
|
6
|
+
// framework field cannot leave a handwritten host mirror behind.
|
|
7
|
+
|
|
8
|
+
import type { Directive } from "./directive.js";
|
|
9
|
+
import { APPLICATION_HOME_DOMAIN } from "./framework/application_home_domain.js";
|
|
10
|
+
import { setAuthProvider } from "./framework/auth_provider_law.js";
|
|
11
|
+
import { seedBirthPolicy } from "./framework/birth_meter.js";
|
|
12
|
+
import { installDomain, promoteDomain, rollbackDomain, stageDomain } from "./framework/domain_lifecycle.js";
|
|
13
|
+
import { seedGenesis } from "./framework/genesis.js";
|
|
14
|
+
import {
|
|
15
|
+
birthReceivingGroupForDomain,
|
|
16
|
+
seedBirthAttestation,
|
|
17
|
+
seedReceivingGroup,
|
|
18
|
+
seedReceivingGroupPolicy,
|
|
19
|
+
} from "./framework/home.js";
|
|
20
|
+
import { enrollDelegation, enrollFirstDevice, enrollSigner } from "./framework/signing_identity.js";
|
|
21
|
+
|
|
22
|
+
export interface FixedFrameworkCallDefinition {
|
|
23
|
+
readonly group: "identity" | "lifecycle" | "birth";
|
|
24
|
+
readonly kind: string;
|
|
25
|
+
readonly directive: Directive<any>;
|
|
26
|
+
/** Needed by the pre-bind Dart birth ceremony before an application exists. */
|
|
27
|
+
readonly dartBirth?: true;
|
|
28
|
+
/** A composed application-home law must contain this operation. */
|
|
29
|
+
readonly requiredApplicationHome?: true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const receivingGroupBirth = birthReceivingGroupForDomain(
|
|
33
|
+
APPLICATION_HOME_DOMAIN,
|
|
34
|
+
0,
|
|
35
|
+
[APPLICATION_HOME_DOMAIN],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export const fixedFrameworkCallDefinitions: readonly FixedFrameworkCallDefinition[] = [
|
|
39
|
+
{ group: "identity", kind: "enrollFirstDevice", directive: enrollFirstDevice, dartBirth: true, requiredApplicationHome: true },
|
|
40
|
+
{ group: "identity", kind: "enrollSigner", directive: enrollSigner, dartBirth: true, requiredApplicationHome: true },
|
|
41
|
+
{ group: "identity", kind: "setAuthProvider", directive: setAuthProvider, requiredApplicationHome: true },
|
|
42
|
+
{ group: "identity", kind: "birthReceivingGroup", directive: receivingGroupBirth, dartBirth: true, requiredApplicationHome: true },
|
|
43
|
+
{ group: "lifecycle", kind: "installDomain", directive: installDomain },
|
|
44
|
+
{ group: "lifecycle", kind: "stageDomain", directive: stageDomain },
|
|
45
|
+
{ group: "lifecycle", kind: "promoteDomain", directive: promoteDomain },
|
|
46
|
+
{ group: "lifecycle", kind: "rollbackDomain", directive: rollbackDomain },
|
|
47
|
+
{ group: "birth", kind: "seedBirthPolicy", directive: seedBirthPolicy, dartBirth: true },
|
|
48
|
+
{ group: "birth", kind: "enrollDelegation", directive: enrollDelegation, dartBirth: true },
|
|
49
|
+
{ group: "birth", kind: "seedBirthAttestation", directive: seedBirthAttestation, dartBirth: true },
|
|
50
|
+
{ group: "birth", kind: "seedGenesis", directive: seedGenesis, dartBirth: true },
|
|
51
|
+
{ group: "birth", kind: "seedReceivingGroupPolicy", directive: seedReceivingGroupPolicy, dartBirth: true },
|
|
52
|
+
{ group: "birth", kind: "seedReceivingGroup", directive: seedReceivingGroup, dartBirth: true },
|
|
53
|
+
];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Stable framework reads bound to their authored aggregate/query declarations.
|
|
2
|
+
//
|
|
3
|
+
// This is metadata, not another schema language. The compiler consumes the
|
|
4
|
+
// AggregateHandle/QueryDecl objects directly, so the public host contract,
|
|
5
|
+
// canonical query USDA and result decoder all change together with the DSL.
|
|
6
|
+
|
|
7
|
+
import type { AggregateHandle } from "./aggregate.js";
|
|
8
|
+
import type { QueryDecl } from "./query.js";
|
|
9
|
+
import { EncKey, encKeyByPrincipal } from "./framework/encryption_identity.js";
|
|
10
|
+
import {
|
|
11
|
+
KeyScope,
|
|
12
|
+
WrappedKey,
|
|
13
|
+
keyScopeByScope,
|
|
14
|
+
wrappedKeysByRecipient,
|
|
15
|
+
} from "./framework/encryption_scope.js";
|
|
16
|
+
import {
|
|
17
|
+
OwnedRecoveryOrder,
|
|
18
|
+
OwnedRecoveryPolicy,
|
|
19
|
+
ownedRecoveryOrdersByRecoveryPrincipal,
|
|
20
|
+
} from "./framework/owned_custody.js";
|
|
21
|
+
import { RelationTuple, relationTuplesByObject } from "./framework/relations.js";
|
|
22
|
+
import { SignerEpoch } from "./framework/signing_identity.js";
|
|
23
|
+
|
|
24
|
+
export interface FixedFrameworkReadDefinition {
|
|
25
|
+
readonly kind:
|
|
26
|
+
| "encKey"
|
|
27
|
+
| "keyScope"
|
|
28
|
+
| "ownedRecoveryOrder"
|
|
29
|
+
| "ownedRecoveryPolicy"
|
|
30
|
+
| "relationTuple"
|
|
31
|
+
| "signerEpoch"
|
|
32
|
+
| "wrappedKey";
|
|
33
|
+
readonly aggregate: AggregateHandle;
|
|
34
|
+
readonly query?: QueryDecl;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const fixedFrameworkReadDefinitions: readonly FixedFrameworkReadDefinition[] = [
|
|
38
|
+
{ kind: "encKey", aggregate: EncKey, query: encKeyByPrincipal },
|
|
39
|
+
{ kind: "keyScope", aggregate: KeyScope, query: keyScopeByScope },
|
|
40
|
+
{ kind: "ownedRecoveryOrder", aggregate: OwnedRecoveryOrder, query: ownedRecoveryOrdersByRecoveryPrincipal },
|
|
41
|
+
{ kind: "ownedRecoveryPolicy", aggregate: OwnedRecoveryPolicy },
|
|
42
|
+
{ kind: "relationTuple", aggregate: RelationTuple, query: relationTuplesByObject },
|
|
43
|
+
{ kind: "signerEpoch", aggregate: SignerEpoch },
|
|
44
|
+
{ kind: "wrappedKey", aggregate: WrappedKey, query: wrappedKeysByRecipient },
|
|
45
|
+
];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
export type IdentityFrontierSource = Readonly<{
|
|
6
|
+
source: string;
|
|
7
|
+
custody: "git-head" | "working-file";
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Read the identity frontier which is already part of custody.
|
|
12
|
+
*
|
|
13
|
+
* The working identity layer is candidate output: it may contain identities minted
|
|
14
|
+
* by an earlier successful local compile which have never been committed or shipped.
|
|
15
|
+
* Git HEAD is therefore the historical evolution frontier whenever the package is in
|
|
16
|
+
* Git custody. Outside Git, the checked-in/working file remains the portable fallback.
|
|
17
|
+
*/
|
|
18
|
+
export function readHistoricalIdentityFrontier(
|
|
19
|
+
lockPath: string,
|
|
20
|
+
): IdentityFrontierSource | undefined {
|
|
21
|
+
const directory = path.dirname(lockPath);
|
|
22
|
+
let repositoryRoot: string;
|
|
23
|
+
try {
|
|
24
|
+
repositoryRoot = realpathSync(execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
25
|
+
cwd: directory,
|
|
26
|
+
encoding: "utf8",
|
|
27
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
28
|
+
}).trim());
|
|
29
|
+
} catch {
|
|
30
|
+
return existsSync(lockPath)
|
|
31
|
+
? { source: readFileSync(lockPath, "utf8"), custody: "working-file" }
|
|
32
|
+
: undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const canonicalLockPath = path.join(realpathSync(directory), path.basename(lockPath));
|
|
36
|
+
const relative = path.relative(repositoryRoot, canonicalLockPath);
|
|
37
|
+
if (relative === "" || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
38
|
+
return existsSync(lockPath)
|
|
39
|
+
? { source: readFileSync(lockPath, "utf8"), custody: "working-file" }
|
|
40
|
+
: undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
return {
|
|
45
|
+
source: execFileSync("git", ["show", `HEAD:${relative.split(path.sep).join("/")}`], {
|
|
46
|
+
cwd: repositoryRoot,
|
|
47
|
+
encoding: "utf8",
|
|
48
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
49
|
+
}),
|
|
50
|
+
custody: "git-head",
|
|
51
|
+
};
|
|
52
|
+
} catch {
|
|
53
|
+
// A repository with no committed identity has no historical frontier. Treating
|
|
54
|
+
// an untracked or candidate-only file as history would make local experimentation
|
|
55
|
+
// irreversible before it ever entered custody.
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -254,7 +254,7 @@ export { repair, repairsOf, isRepairDecl, type RepairDecl, type RepairMatcher, t
|
|
|
254
254
|
// NOT YET DEPLOYABLE (#57): the sealed wasm engine does not provide `nomos.read` yet, so
|
|
255
255
|
// `nomos-compile` REFUSES law that references read() (a named compile error, never a runtime halt).
|
|
256
256
|
// The captured-reads wave (task #58) wires the capability in and lifts the refusal.
|
|
257
|
-
export { read } from "./read.js";
|
|
257
|
+
export { read, readFrameworkDeclaredRows } from "./read.js";
|
|
258
258
|
// RELEASE COHERENCE — standard OSGi Contract Namespace requirements derived from law shape.
|
|
259
259
|
export {
|
|
260
260
|
requiredKernelContracts,
|
|
@@ -319,12 +319,14 @@ export {
|
|
|
319
319
|
// `nomosBirthGate` era key, never domain state) — spawns + self-validates the child after the offer admits.
|
|
320
320
|
export {
|
|
321
321
|
birth,
|
|
322
|
+
workspaceRole,
|
|
322
323
|
workspaceNameForKey,
|
|
323
324
|
WORKSPACE_NAME_SEP,
|
|
324
325
|
birthKeyed,
|
|
325
326
|
birthSingleton,
|
|
326
327
|
birthInstance,
|
|
327
328
|
type BirthRecipe,
|
|
329
|
+
type WorkspaceRole,
|
|
328
330
|
type GenesisChainOrBuilder,
|
|
329
331
|
type BusinessBirthDecl,
|
|
330
332
|
type BusinessBirthSpec,
|
|
@@ -505,6 +507,8 @@ export {
|
|
|
505
507
|
identityRelations,
|
|
506
508
|
signerObject,
|
|
507
509
|
SIGNER_RELATION,
|
|
510
|
+
ADMISSION_CHECKPOINT_PRINCIPAL,
|
|
511
|
+
enrollAdmissionCheckpointSigner,
|
|
508
512
|
enrollSigner,
|
|
509
513
|
// VA-core — SignerEpoch + self-bind enroll/revoke + the strict-increment & verified-enroll gates.
|
|
510
514
|
SignerEpoch,
|
|
@@ -535,11 +539,26 @@ export {
|
|
|
535
539
|
// the provider explicitly, without re-declaring the kernel's security schema in tenant code.
|
|
536
540
|
export {
|
|
537
541
|
AUTH_PROVIDER_ID,
|
|
542
|
+
AUTH_PROVIDER_KEY_ID_PREFIX,
|
|
543
|
+
AUTH_PROVIDER_CERTIFICATE_ID_PREFIX,
|
|
538
544
|
AuthProvider,
|
|
545
|
+
AuthProviderKey,
|
|
546
|
+
AuthProviderCertificate,
|
|
547
|
+
authProviderPublicKey,
|
|
548
|
+
authProviderKeySet,
|
|
549
|
+
authProviderConfiguration,
|
|
550
|
+
type AuthProviderConfiguration,
|
|
551
|
+
type AuthProviderVerifyingConfiguration,
|
|
552
|
+
readAuthProviderConfiguration,
|
|
553
|
+
readAuthProviderVerifyingConfiguration,
|
|
554
|
+
writeAuthProviderConfiguration,
|
|
555
|
+
writeAuthProviderKeySet,
|
|
539
556
|
seedAuthProvider,
|
|
540
557
|
authProviderSeedBeforeWarrant,
|
|
541
558
|
setAuthProvider,
|
|
542
559
|
authProvider,
|
|
560
|
+
authProviderKeys,
|
|
561
|
+
authProviderCertificates,
|
|
543
562
|
} from "./framework/auth_provider_law.js";
|
|
544
563
|
// THE RECEIVING-GROUP HOLON — the framework-private shared signer roster + access manifest.
|
|
545
564
|
export {
|