@githolon/dsl 0.107.4 → 0.108.1
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 +24 -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 +10634 -6122
- package/dart/lib/src/runner/offline-install.mjs +4187 -2716
- 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 +373 -282
- package/dart/lib/src/webview_bridge.dart +2146 -725
- package/dart/pubspec.lock +17 -1
- package/dart/pubspec.yaml +3 -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 +17 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +11 -0
- 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 +17945 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +933 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pb.dart +245 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/progress.pbenum.dart +160 -0
- 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.lock +16 -0
- package/dart_types/pubspec.yaml +3 -1
- package/dart_types/test/surfaced_test.dart +2 -1
- package/package.json +1 -1
- package/runtime/generated-application-host.js +5760 -1879
- package/src/authoring.ts +3 -2
- package/src/birth_meter_ensure.ts +2 -0
- package/src/births.ts +21 -0
- package/src/build_package.ts +30 -0
- package/src/business_birth.ts +41 -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 +392 -46
- package/src/directive.ts +19 -4
- package/src/engine_entry.ts +45 -4
- package/src/framework/application_home.ts +4 -0
- package/src/framework/auth_provider_law.ts +296 -20
- package/src/framework/birth_certificate_law.ts +24 -0
- package/src/framework/home.ts +83 -40
- package/src/framework/owned_custody.ts +25 -12
- package/src/framework/owned_custody_value.ts +51 -0
- package/src/framework/relations.ts +47 -21
- package/src/framework/signing_identity.ts +100 -17
- package/src/framework/workspaces.ts +39 -10
- 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 +22 -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 +44 -42
- package/src/stable_ids.ts +37 -2
- 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/src/framework/home.ts
CHANGED
|
@@ -31,11 +31,16 @@ import { sha256Digest, t } from "../fields.js";
|
|
|
31
31
|
import { Lww } from "../drivers.js";
|
|
32
32
|
import { directive } from "../directive.js";
|
|
33
33
|
import { ensure } from "../authoring.js";
|
|
34
|
-
import { set, type PlannedOp } from "../ops.js";
|
|
34
|
+
import { set, withMarker, type PlannedOp } from "../ops.js";
|
|
35
35
|
import { query } from "../query.js";
|
|
36
36
|
import { birth } from "../births.js";
|
|
37
|
-
import {
|
|
38
|
-
|
|
37
|
+
import {
|
|
38
|
+
authProvider as authProviderQuery,
|
|
39
|
+
authProviderCertificates as authProviderCertificatesQuery,
|
|
40
|
+
authProviderKeys as authProviderKeysQuery,
|
|
41
|
+
readAuthProviderVerifyingConfiguration,
|
|
42
|
+
seedAuthProvider,
|
|
43
|
+
} from "./auth_provider_law.js";
|
|
39
44
|
import { readRef, readSet, workspaceInvariant, refAs } from "./workspace_invariant.js";
|
|
40
45
|
import { identityRelations } from "./signing_identity.js";
|
|
41
46
|
import { RelationTuple, writeTuple } from "./relations.js";
|
|
@@ -47,6 +52,7 @@ import {
|
|
|
47
52
|
DomainInstallExecutor,
|
|
48
53
|
domainInstallGenesisOffer,
|
|
49
54
|
} from "./domain_lifecycle.js";
|
|
55
|
+
import { birthCertificatePayload } from "./birth_certificate_law.js";
|
|
50
56
|
|
|
51
57
|
// `nomos.sha256` — the TRUSTLESS, pure hash computed IN the githolon (Rust sha2 bound in the sealed engine;
|
|
52
58
|
// see deterministic-rquickjs). Available as a global to plans + invariant predicates (like `nomos.mint`); it
|
|
@@ -354,18 +360,9 @@ export function birthReceivingGroupForDomain(
|
|
|
354
360
|
// THE PARENT'S DECLARED VERIFIER. This captured read supplies the inherited provider seed.
|
|
355
361
|
// Birth-certificate verification and founding-signer enrollment belong to the kernel-owned
|
|
356
362
|
// attestation step, so neither fact is duplicated in this tenant plan or payload.
|
|
357
|
-
const
|
|
358
|
-
?
|
|
359
|
-
:
|
|
360
|
-
const apRaw = Array.isArray(apRows) && apRows.length > 0 ? apRows[0] : undefined;
|
|
361
|
-
// Projected rows are `{type,id,data:{…fields}}`; folded shapes may be flat — accept both.
|
|
362
|
-
const ap = (apRaw && typeof apRaw === "object" && typeof (apRaw as { data?: unknown }).data === "object"
|
|
363
|
-
? (apRaw as { data: Record<string, unknown> }).data
|
|
364
|
-
: apRaw) as Record<string, unknown> | undefined;
|
|
365
|
-
const apStr = (k: string): string | undefined => {
|
|
366
|
-
const v = ap?.[k];
|
|
367
|
-
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
368
|
-
};
|
|
363
|
+
const inheritedProvider = p.inheritAuthProvider === false
|
|
364
|
+
? undefined
|
|
365
|
+
: readAuthProviderVerifyingConfiguration();
|
|
369
366
|
const genesisChain: Array<{ domain: string; directiveId: string; actor: string; payload: Record<string, unknown>; domainHash?: string }> = [
|
|
370
367
|
// THE ORDER-DECLARED LAW INSTALL: two typed closure identities. The kernel resolves each
|
|
371
368
|
// closure from the parent's custody when it folds the birth; law bytes never ride the intent.
|
|
@@ -424,23 +421,14 @@ export function birthReceivingGroupForDomain(
|
|
|
424
421
|
// (the up-front captured read above) AS DATA into the child genesis. The child owns the copy as its
|
|
425
422
|
// own law-state; nothing is inherited-by-reference. Opt out: `inheritAuthProvider: false`.
|
|
426
423
|
if (p.inheritAuthProvider !== false) {
|
|
427
|
-
|
|
428
|
-
const issuer = str("issuer");
|
|
429
|
-
if (issuer !== undefined) {
|
|
424
|
+
if (inheritedProvider !== undefined) {
|
|
430
425
|
genesisChain.push({
|
|
431
426
|
domain: groupLawDomain,
|
|
432
427
|
directiveId: "seedAuthProvider",
|
|
433
428
|
actor: founder,
|
|
434
429
|
payload: {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
...(str("keys") !== undefined ? { keys: str("keys") } : {}),
|
|
438
|
-
...(str("audience") !== undefined ? { audience: str("audience") } : {}),
|
|
439
|
-
...(str("principalClaim") !== undefined ? { principalClaim: str("principalClaim") } : {}),
|
|
440
|
-
...(str("rootCert") !== undefined ? { rootCert: str("rootCert") } : {}),
|
|
441
|
-
...(str("deviceBindingClaim") !== undefined ? { deviceBindingClaim: str("deviceBindingClaim") } : {}),
|
|
442
|
-
...(str("requireDeviceBinding") !== undefined ? { requireDeviceBinding: str("requireDeviceBinding") } : {}),
|
|
443
|
-
label: str("label") ?? "inherited-from-parent",
|
|
430
|
+
...inheritedProvider,
|
|
431
|
+
label: "inherited-from-parent",
|
|
444
432
|
setBy: p.owner,
|
|
445
433
|
setAt: 1, // genesis position — deterministic, like enrollSigner's grantedAt
|
|
446
434
|
},
|
|
@@ -454,6 +442,8 @@ export function birthReceivingGroupForDomain(
|
|
|
454
442
|
return [];
|
|
455
443
|
})
|
|
456
444
|
.reads(authProviderQuery) // the parent-side AuthProvider capture the inheritance copies (the #58 lane)
|
|
445
|
+
.reads(authProviderKeysQuery)
|
|
446
|
+
.reads(authProviderCertificatesQuery)
|
|
457
447
|
.births(); // BIRTH IS A DECLARED KERNEL PRIMITIVE — emits the `nomosBirthGate` era key; the kernel folds the recipe
|
|
458
448
|
return {
|
|
459
449
|
...birthDirective,
|
|
@@ -581,6 +571,7 @@ export const sharedByObject = query("sharedByObject").key("object").returns(Shar
|
|
|
581
571
|
* verify doors); this aggregate is the human-readable mirror.
|
|
582
572
|
*/
|
|
583
573
|
export const BIRTH_ATTESTATION_ID = "birth-attestation:self";
|
|
574
|
+
export const BIRTH_ATTESTATION_LINK_PREFIX = "birth-attestation-link:";
|
|
584
575
|
export const BirthAttestation = aggregate("BirthAttestation", {
|
|
585
576
|
/** This home's self-id (`home-<sha256(founder)>`) — the cert's `child`. */
|
|
586
577
|
child: t.string().merge(Lww),
|
|
@@ -594,12 +585,29 @@ export const BirthAttestation = aggregate("BirthAttestation", {
|
|
|
594
585
|
parentKey: t.string().merge(Lww),
|
|
595
586
|
/** The parent's SignerEpoch at the attestation (informational; the cert is judged at birth-time validity). */
|
|
596
587
|
epoch: t.int().merge(Lww),
|
|
597
|
-
/**
|
|
598
|
-
*
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
588
|
+
/** Legacy replay slot for histories authored before `BirthAttestationLink`. New histories write the
|
|
589
|
+
* travelling chain only as ordered, typed link records below. */
|
|
590
|
+
certChain: t.string().merge(Lww).optional(),
|
|
591
|
+
}).public();
|
|
592
|
+
|
|
593
|
+
/** One exact link in the travelling certificate chain. The zero-padded index is custody order; every
|
|
594
|
+
* certificate member is a separate typed USDA property. `certChain` above remains only as a replay fallback
|
|
595
|
+
* for histories authored before this aggregate existed. */
|
|
596
|
+
export const BirthAttestationLink = aggregate("BirthAttestationLink", {
|
|
597
|
+
index: t.int().merge(Lww),
|
|
598
|
+
child: t.string().merge(Lww),
|
|
599
|
+
founder: t.string().merge(Lww),
|
|
600
|
+
verified: t.bool().merge(Lww),
|
|
601
|
+
parentKey: t.string().merge(Lww),
|
|
602
|
+
parentId: t.string().merge(Lww),
|
|
603
|
+
epoch: t.int().merge(Lww),
|
|
604
|
+
signature: t.string().merge(Lww),
|
|
605
|
+
verifiedVia: t.string().merge(Lww).optional(),
|
|
606
|
+
idpToken: t.string().merge(Lww).optional(),
|
|
607
|
+
capturedClock: t.int().merge(Lww).optional(),
|
|
608
|
+
authProviderIssuer: t.string().merge(Lww).optional(),
|
|
609
|
+
authProviderJwks: t.string().merge(Lww).optional(),
|
|
610
|
+
authProviderAudience: t.string().merge(Lww).optional(),
|
|
603
611
|
}).public();
|
|
604
612
|
|
|
605
613
|
/** Audit read: this home's birth attestation (who attested it + the verified verdict). */
|
|
@@ -611,7 +619,7 @@ export const birthAttestation = query("birthAttestation").key("child").returns(B
|
|
|
611
619
|
* the [`BirthAttestation`] singleton so the home's own state carries its travelling cert. A CONTROL-PLANE
|
|
612
620
|
* write seeded once at birth (the open session lane structurally refuses it by directive id). The values
|
|
613
621
|
* are the parent's GATE VERDICT (`founder`/`verified` lowered from the captured port — CAS-as-law, never
|
|
614
|
-
* the offer payload) + the signed
|
|
622
|
+
* the offer payload) + the typed signed links; the home's verify doors re-derive trust from the chain.
|
|
615
623
|
*/
|
|
616
624
|
export const seedBirthAttestation = directive("seedBirthAttestation")
|
|
617
625
|
.ensures(BirthAttestation)
|
|
@@ -625,21 +633,56 @@ export const seedBirthAttestation = directive("seedBirthAttestation")
|
|
|
625
633
|
parentId: z.string(),
|
|
626
634
|
parentKey: z.string().min(1), // base64 K_P
|
|
627
635
|
epoch: z.number().int().nonnegative(),
|
|
628
|
-
|
|
636
|
+
links: z.array(birthCertificatePayload).min(1),
|
|
637
|
+
/** Law-declared target roles for the verified immediate parent workspace. The kernel injects this
|
|
638
|
+
* value from the admitted birth recipe and supplies `parentId`; no host chooses either fact. */
|
|
639
|
+
parentWorkspaceRoles: z.array(z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*$/)).optional(),
|
|
640
|
+
}).superRefine((payload, ctx) => {
|
|
641
|
+
if ((payload.parentWorkspaceRoles?.length ?? 0) > 0 && payload.parentId.length === 0) {
|
|
642
|
+
ctx.addIssue({ code: "custom", message: "parent workspace roles require a verified non-root parentId" });
|
|
643
|
+
}
|
|
629
644
|
}),
|
|
630
645
|
)
|
|
631
646
|
.plan((p) => {
|
|
632
647
|
const h = instance(BirthAttestation, BIRTH_ATTESTATION_ID);
|
|
633
|
-
|
|
648
|
+
const header = [
|
|
634
649
|
set(h, "child", p.child),
|
|
635
650
|
set(h, "founder", p.founder),
|
|
636
651
|
set(h, "verified", p.verified),
|
|
637
652
|
set(h, "parentId", p.parentId),
|
|
638
653
|
set(h, "parentKey", p.parentKey),
|
|
639
654
|
set(h, "epoch", p.epoch),
|
|
640
|
-
set(h, "certChain", p.certChain),
|
|
641
655
|
];
|
|
642
|
-
|
|
656
|
+
const links = p.links.flatMap((certificate, index) => {
|
|
657
|
+
const link = instance(BirthAttestationLink, `${BIRTH_ATTESTATION_LINK_PREFIX}${String(index).padStart(8, "0")}`);
|
|
658
|
+
return [
|
|
659
|
+
withMarker(set(link, "index", index), "ensures"),
|
|
660
|
+
withMarker(set(link, "child", certificate.child), "ensures"),
|
|
661
|
+
withMarker(set(link, "founder", certificate.founder), "ensures"),
|
|
662
|
+
withMarker(set(link, "verified", certificate.verified), "ensures"),
|
|
663
|
+
withMarker(set(link, "parentKey", certificate.parentKey), "ensures"),
|
|
664
|
+
withMarker(set(link, "parentId", certificate.parentId), "ensures"),
|
|
665
|
+
withMarker(set(link, "epoch", certificate.epoch), "ensures"),
|
|
666
|
+
withMarker(set(link, "signature", certificate.signature), "ensures"),
|
|
667
|
+
...(certificate.verifiedVia ? [withMarker(set(link, "verifiedVia", certificate.verifiedVia), "ensures")] : []),
|
|
668
|
+
...(certificate.idpToken ? [withMarker(set(link, "idpToken", certificate.idpToken), "ensures")] : []),
|
|
669
|
+
...(certificate.capturedClock === undefined ? [] : [withMarker(set(link, "capturedClock", certificate.capturedClock), "ensures")]),
|
|
670
|
+
...(certificate.authProviderIssuer ? [withMarker(set(link, "authProviderIssuer", certificate.authProviderIssuer), "ensures")] : []),
|
|
671
|
+
...(certificate.authProviderJwks ? [withMarker(set(link, "authProviderJwks", certificate.authProviderJwks), "ensures")] : []),
|
|
672
|
+
...(certificate.authProviderAudience ? [withMarker(set(link, "authProviderAudience", certificate.authProviderAudience), "ensures")] : []),
|
|
673
|
+
];
|
|
674
|
+
});
|
|
675
|
+
const parentAuthority = (p.parentWorkspaceRoles ?? []).flatMap((relation) => writeTuple({
|
|
676
|
+
object: "workspace:self",
|
|
677
|
+
relation,
|
|
678
|
+
subject: `workspace:${p.parentId}`,
|
|
679
|
+
grantedBy: `workspace:${p.parentId}`,
|
|
680
|
+
grantedAt: String(p.epoch),
|
|
681
|
+
}));
|
|
682
|
+
return [...header, ...links, ...parentAuthority];
|
|
683
|
+
})
|
|
684
|
+
.alsoEnsures(BirthAttestationLink)
|
|
685
|
+
.alsoEnsures(RelationTuple);
|
|
643
686
|
|
|
644
687
|
/**
|
|
645
688
|
* THE ERA DECLARATION (the `nomosParentAttestedBirth` USD key). Exporting this sentinel from the home law
|
|
@@ -652,8 +695,8 @@ export const seedBirthAttestation = directive("seedBirthAttestation")
|
|
|
652
695
|
export const parentAttestedBirth: ParentAttestedBirthMarker = {
|
|
653
696
|
...requireParentAttestedBirth,
|
|
654
697
|
// DECLARE the owner + cert locators so the KERNEL reads them (it names neither): the home owner lives at
|
|
655
|
-
// the `HomeOwner` singleton's `principal
|
|
656
|
-
//
|
|
698
|
+
// the `HomeOwner` singleton's `principal`. `certObject`/`certField` retain the legacy chain locator for
|
|
699
|
+
// old histories; current histories are discovered through the typed `BirthAttestationLink` aggregate.
|
|
657
700
|
ownerObject: RECEIVING_GROUP_ID,
|
|
658
701
|
ownerField: "principal",
|
|
659
702
|
certObject: BIRTH_ATTESTATION_ID,
|
|
@@ -19,10 +19,11 @@ import {
|
|
|
19
19
|
writeWrap,
|
|
20
20
|
} from "./encryption_scope.js";
|
|
21
21
|
import { GenesisWarrant, seedGenesis } from "./genesis.js";
|
|
22
|
-
import { BirthAttestation, seedBirthAttestation } from "./home.js";
|
|
22
|
+
import { BirthAttestation, BirthAttestationLink, seedBirthAttestation } from "./home.js";
|
|
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) => {
|
|
@@ -430,6 +438,7 @@ const CUSTODY_RELATIONS_EXPORT = "__nomos_owned_custody_relations_7c1f5e";
|
|
|
430
438
|
const WARRANT_EXPORT = "__nomos_owned_genesis_warrant_7c1f5e";
|
|
431
439
|
const SEED_EXPORT = "__nomos_owned_seed_genesis_7c1f5e";
|
|
432
440
|
const BIRTH_ATTESTATION_EXPORT = "__nomos_owned_birth_attestation_7c1f5e";
|
|
441
|
+
const BIRTH_ATTESTATION_LINK_EXPORT = "__nomos_owned_birth_attestation_link_7c1f5e";
|
|
433
442
|
const BIRTH_ATTESTATION_SEED_EXPORT = "__nomos_owned_seed_birth_attestation_7c1f5e";
|
|
434
443
|
const ENROLL_EXPORT = "__nomos_owned_enroll_signer_7c1f5e";
|
|
435
444
|
const INITIALIZE_EXPORT = "__nomos_owned_initialize_identity_7c1f5e";
|
|
@@ -452,6 +461,7 @@ const RECOVERY_CAPABILITY_EXPORT = "__nomos_owned_recovery_capability_7c1f5e";
|
|
|
452
461
|
const ownedGenesisWarrant = hidden(GenesisWarrant);
|
|
453
462
|
const ownedSeedGenesis = hidden(seedGenesis);
|
|
454
463
|
const ownedBirthAttestation = hidden(BirthAttestation);
|
|
464
|
+
const ownedBirthAttestationLink = hidden(BirthAttestationLink);
|
|
455
465
|
const ownedSeedBirthAttestation = hidden(seedBirthAttestation);
|
|
456
466
|
const ownedEnrollSigner = hidden(enrollSigner);
|
|
457
467
|
const ownedIdentityRelations = hidden(identityRelations);
|
|
@@ -543,6 +553,7 @@ export function withOwnedCustodyExports<M extends Record<string, unknown>>(mod:
|
|
|
543
553
|
[WARRANT_EXPORT, ownedGenesisWarrant],
|
|
544
554
|
[SEED_EXPORT, ownedSeedGenesis],
|
|
545
555
|
[BIRTH_ATTESTATION_EXPORT, ownedBirthAttestation],
|
|
556
|
+
[BIRTH_ATTESTATION_LINK_EXPORT, ownedBirthAttestationLink],
|
|
546
557
|
[BIRTH_ATTESTATION_SEED_EXPORT, ownedSeedBirthAttestation],
|
|
547
558
|
[ENROLL_EXPORT, ownedEnrollSigner],
|
|
548
559
|
] as const) {
|
|
@@ -574,6 +585,7 @@ export function withOwnedCustodyExports<M extends Record<string, unknown>>(mod:
|
|
|
574
585
|
[GenesisWarrant, ownedGenesisWarrant],
|
|
575
586
|
[seedGenesis, ownedSeedGenesis],
|
|
576
587
|
[BirthAttestation, ownedBirthAttestation],
|
|
588
|
+
[BirthAttestationLink, ownedBirthAttestationLink],
|
|
577
589
|
[seedBirthAttestation, ownedSeedBirthAttestation],
|
|
578
590
|
[enrollSigner, ownedEnrollSigner],
|
|
579
591
|
] as const) {
|
|
@@ -639,6 +651,7 @@ export function withOwnedCustodyExports<M extends Record<string, unknown>>(mod:
|
|
|
639
651
|
[WARRANT_EXPORT]: ownedGenesisWarrant,
|
|
640
652
|
[SEED_EXPORT]: ownedSeedGenesis,
|
|
641
653
|
[BIRTH_ATTESTATION_EXPORT]: ownedBirthAttestation,
|
|
654
|
+
[BIRTH_ATTESTATION_LINK_EXPORT]: ownedBirthAttestationLink,
|
|
642
655
|
[BIRTH_ATTESTATION_SEED_EXPORT]: ownedSeedBirthAttestation,
|
|
643
656
|
[ENROLL_EXPORT]: ownedEnrollSigner,
|
|
644
657
|
};
|
|
@@ -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,70 @@ 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
|
+
|
|
195
214
|
export const RelationTuple = aggregate("RelationTuple", {
|
|
196
215
|
object: t.string().merge(Lww),
|
|
197
216
|
relation: t.string().merge(Lww),
|
|
198
|
-
subjects: t.map(
|
|
217
|
+
subjects: t.map(RelationshipSubjectValue.field()).merge(MapOf(Lww)),
|
|
199
218
|
}).public();
|
|
200
219
|
export type RelationTupleHandle = typeof RelationTuple;
|
|
201
220
|
|
|
202
221
|
/** The per-subject leaf the `subjects` map stores as a typed kernel value. */
|
|
203
222
|
export interface RelationSubjectLeaf {
|
|
204
|
-
status: RelationStatus;
|
|
205
|
-
grantedBy: string;
|
|
206
|
-
grantedAt: string;
|
|
207
|
-
revokedBy?: string;
|
|
208
|
-
revokedAt?: string;
|
|
223
|
+
readonly status: RelationStatus;
|
|
224
|
+
readonly grantedBy: string;
|
|
225
|
+
readonly grantedAt: string;
|
|
226
|
+
readonly revokedBy?: string;
|
|
227
|
+
readonly revokedAt?: string;
|
|
209
228
|
/**
|
|
210
229
|
* VA-core (signer-provenance) — the SignerEpoch this leaf was authored under. OMITTED when absent so
|
|
211
230
|
* every pre-VA chain (and every non-signer relation) stays byte-identical: `compact()` strips an
|
|
212
231
|
* `undefined`. A revoke (`unrelateSubject`) backdates by bumping the SignerEpoch; a key whose
|
|
213
232
|
* `validFromEpoch` predates the current epoch is stale (the full backdate gate is phase 4).
|
|
214
233
|
*/
|
|
215
|
-
validFromEpoch?: number;
|
|
234
|
+
readonly validFromEpoch?: number;
|
|
216
235
|
/**
|
|
217
236
|
* VA-core (signer-provenance) — the IdP/issuer that VOUCHED for the enrolling principal at enroll time
|
|
218
237
|
* (e.g. the `AuthProvider.issuer`). The kernel's `verify_captured_principal` DERIVES `principal_verified`
|
|
219
238
|
* from THIS leaf field (provenance, not the wire): an enrollment vouched by a real IdP reads verified on
|
|
220
239
|
* every lane. OMITTED when absent (legacy / unverified enrollment) so existing chains stay byte-identical.
|
|
221
240
|
*/
|
|
222
|
-
verifiedVia?: string;
|
|
241
|
+
readonly verifiedVia?: string;
|
|
223
242
|
/**
|
|
224
243
|
* Secure-by-default owned-data device handoff. Framework-reserved and omitted from every ordinary
|
|
225
|
-
* relation leaf; the
|
|
244
|
+
* relation leaf; the typed encrypted handoff is independently verified by the kernel before it may land.
|
|
226
245
|
*/
|
|
227
|
-
ownedCustody?:
|
|
246
|
+
readonly ownedCustody?: OwnedCustody;
|
|
228
247
|
}
|
|
229
248
|
|
|
230
249
|
/** Strip undefined keys so the typed leaf has one stable optional-field shape. */
|
|
231
|
-
function compact(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
250
|
+
function compact(leaf: RelationSubjectLeaf): RelationSubjectLeaf {
|
|
251
|
+
return {
|
|
252
|
+
status: leaf.status,
|
|
253
|
+
grantedBy: leaf.grantedBy,
|
|
254
|
+
grantedAt: leaf.grantedAt,
|
|
255
|
+
...(leaf.revokedBy === undefined ? {} : { revokedBy: leaf.revokedBy }),
|
|
256
|
+
...(leaf.revokedAt === undefined ? {} : { revokedAt: leaf.revokedAt }),
|
|
257
|
+
...(leaf.validFromEpoch === undefined ? {} : { validFromEpoch: leaf.validFromEpoch }),
|
|
258
|
+
...(leaf.verifiedVia === undefined ? {} : { verifiedVia: leaf.verifiedVia }),
|
|
259
|
+
...(leaf.ownedCustody === undefined ? {} : { ownedCustody: leaf.ownedCustody }),
|
|
260
|
+
};
|
|
235
261
|
}
|
|
236
262
|
|
|
237
263
|
interface WriteTupleInput {
|
|
@@ -261,7 +287,7 @@ interface WriteTupleInput {
|
|
|
261
287
|
/** VA-core: the IdP/issuer that vouched for the enrolling principal (signer relations only; omitted otherwise). */
|
|
262
288
|
verifiedVia?: string;
|
|
263
289
|
/** Framework-internal encrypted owned-data handoff (owned device relation only). */
|
|
264
|
-
ownedCustody?:
|
|
290
|
+
ownedCustody?: OwnedCustody;
|
|
265
291
|
}
|
|
266
292
|
|
|
267
293
|
/** Lower a tuple write to plan ops. The first op MATERIALIZES the (object, relation)
|
|
@@ -297,13 +323,13 @@ function lowerTuple(
|
|
|
297
323
|
status: p.status,
|
|
298
324
|
grantedBy: grantedByRaw,
|
|
299
325
|
grantedAt: p.grantedAt,
|
|
300
|
-
revokedBy: revokedByRaw,
|
|
301
|
-
revokedAt: p.revokedAt,
|
|
326
|
+
...(revokedByRaw === undefined ? {} : { revokedBy: revokedByRaw }),
|
|
327
|
+
...(p.revokedAt === undefined ? {} : { revokedAt: p.revokedAt }),
|
|
302
328
|
// VA-core signer-provenance leaf fields — `compact` strips an `undefined`, so a non-signer relation
|
|
303
329
|
// (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,
|
|
330
|
+
...(p.validFromEpoch === undefined ? {} : { validFromEpoch: p.validFromEpoch }),
|
|
331
|
+
...(p.verifiedVia === undefined ? {} : { verifiedVia: p.verifiedVia }),
|
|
332
|
+
...(p.ownedCustody === undefined ? {} : { ownedCustody: p.ownedCustody }),
|
|
307
333
|
});
|
|
308
334
|
const subjectEntry = {
|
|
309
335
|
...setEntry(rt, "subjects", subjectRaw, leaf),
|