@githolon/dsl 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@githolon/dsl",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "description": "Nomos 2 domain-authoring DSL: aggregates + directives in TS, executed and encoded to the Rust kernel's wire shapes.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
package/src/attested.ts CHANGED
@@ -28,6 +28,8 @@ import { NomosSourceFrontier } from "./attested_core.js";
28
28
 
29
29
  export {
30
30
  trustSource,
31
+ trustParent,
32
+ PARENT_TRUST_SENTINEL,
31
33
  foreignQuery,
32
34
  isTrustSourceDecl,
33
35
  NomosSourceFrontier,
@@ -94,6 +94,27 @@ export function trustSource(workspace: string, opts: TrustSourceOptions): TrustS
94
94
  return decl;
95
95
  }
96
96
 
97
+ /** K2's genesis sentinel — `trustParent()` declares `genesis: "@parent"`; the kernel derives
98
+ * identity from the reader's OWN birth certificate (kernel mirror:
99
+ * `nomos_executor::PARENT_TRUST_SENTINEL`). */
100
+ export const PARENT_TRUST_SENTINEL = "@parent";
101
+
102
+ /**
103
+ * DECLARE trust in the reader's PARENT (K2, stage 4 — §1.3): the accepted attestor set is
104
+ * DERIVED from the reader's own birth certificate (`BirthAttestation.parentKey`, seeded at
105
+ * birth by the parent's offer-effect) plus any delegation cert the envelope carries that the
106
+ * parent key signed — zero hand-pinned keys, one-hop vouching by construction. `workspace`
107
+ * remains the ROUTABLE name (transport must know whom to ask); identity is the derived chain,
108
+ * never the name. Rotation = the source serves a fresh parent-signed delegation — no reader
109
+ * law change ever.
110
+ */
111
+ export function trustParent(
112
+ workspace: string,
113
+ opts: Omit<TrustSourceOptions, "genesis" | "keyHashes" | "roster"> = {},
114
+ ): TrustSourceDecl {
115
+ return trustSource(workspace, { ...opts, genesis: PARENT_TRUST_SENTINEL, roster: "derived" });
116
+ }
117
+
97
118
  /** Shape predicate for compile discovery (the `isQueryDecl` pattern). */
98
119
  export function isTrustSourceDecl(v: unknown): v is TrustSourceDecl {
99
120
  return (
@@ -0,0 +1,185 @@
1
+ // NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
2
+ // remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
3
+ // wasm32-wasip1 artifact {kernel · projection · embedded QuickJS engine}.
4
+ // If a file isn't this / hosting this / authoring for this / proving this — it's gone.
5
+
6
+ /**
7
+ * framework/attested_receipts.ts — ATTESTATION RECEIPTS + EQUIVOCATION ACCOUNTABILITY
8
+ * (attested reads slice B, stage 3 — `architecture/attested_reads_design.md` §5).
9
+ *
10
+ * The SOURCE's ledger must REMEMBER what it vouched: every attested-read signature the
11
+ * read-attestor emits is leaf-accumulated (`sha256hex(attested_read_signed_bytes)` — derived,
12
+ * never carried) and SEALED into the source's own chain as one {@link NomosAttestationReceipt}
13
+ * per batch — one write covers up to 1024 reads. The seal is an ORDINARY OFFER
14
+ * ({@link recordAttestationBatch}, authored AS the enrolled `workspace:read-attestor` through
15
+ * the one gate); the kernel's `attestation_ledger_violation` arm DEMANDS the receipt's
16
+ * `batchRoot` be the Merkle root of its carried leaves on every door, so a chain-committed
17
+ * receipt is always internally consistent. The adapter only SCHEDULES the seal (the debounced-
18
+ * admission-alarm precedent: scheduling affects WHEN, never WHAT or WHETHER).
19
+ *
20
+ * The READER's half: {@link reportSourceEquivocation} folds a {@link NomosSourceDishonor} fact
21
+ * from two mutually-incriminating signed envelopes (same source key, same fold_root, same
22
+ * (queryId, args), DIVERGENT canonical results). The kernel verifies the evidence — pure
23
+ * carried bytes, the xwi posture — at author, edge admission, AND verify_chain; a forged or
24
+ * non-contradictory report is a typed refusal (`EquivocationEvidenceInvalid`). Once folded,
25
+ * a trust edge declaring `onDishonor: "refuse"` refuses every LATER attestation from that
26
+ * `(genesis, keyHash)` — position-aware like revocation, never retroactive.
27
+ */
28
+ import { z } from "zod";
29
+ import { aggregate, instance } from "../aggregate.js";
30
+ import { t } from "../fields.js";
31
+ import { Lww } from "../drivers.js";
32
+ import { directive } from "../directive.js";
33
+ import { set } from "../ops.js";
34
+ import { query } from "../query.js";
35
+
36
+ /** The framework-reserved receipt instance-id prefix (kernel mirror:
37
+ * `nomos_executor::ATTESTATION_RECEIPT_PREFIX`). */
38
+ export const ATTESTATION_RECEIPT_PREFIX = "attest-receipt:";
39
+
40
+ /** The receipt instance id for a batch. */
41
+ export function attestationReceiptId(batchId: string): string {
42
+ return ATTESTATION_RECEIPT_PREFIX + batchId;
43
+ }
44
+
45
+ /** The framework-reserved dishonor instance-id prefix (kernel mirror:
46
+ * `nomos_executor::SOURCE_DISHONOR_PREFIX`). */
47
+ export const SOURCE_DISHONOR_PREFIX = "source-dishonor:";
48
+
49
+ /** The dishonor instance id for a proven `(sourceGenesis, keyHash)`. */
50
+ export function sourceDishonorId(sourceGenesis: string, keyHash: string): string {
51
+ return `${SOURCE_DISHONOR_PREFIX}${sourceGenesis}:${keyHash}`;
52
+ }
53
+
54
+ /**
55
+ * NomosAttestationReceipt (§5.1) — ONE sealed batch of attested-read receipt leaves in the
56
+ * SOURCE's own chain. Each leaf = `sha256hex(attested_read_signed_bytes)` — it commits to the
57
+ * FULL attestation (query, args, canonical result, head, fold_root, epoch, time) with zero
58
+ * extra signed bytes and NO reader identity (least-disclosure). `receiptPath` (the kernel
59
+ * query op) serves Merkle inclusion `{batchRoot, index, path}` from the folded `leaves`.
60
+ */
61
+ export const NomosAttestationReceipt = aggregate("NomosAttestationReceipt", {
62
+ /** `"<epoch>-<nSealedReceiptsInFold>-<keyHash8>"` — chain-derived mint (collision-free across remounts); promised on every envelope's `receipt_hint`. */
63
+ batchId: t.string().merge(Lww),
64
+ /** sha256 Merkle root over the batch's leaf hashes (kernel-verified == root(leaves) at the gate). */
65
+ batchRoot: t.string().merge(Lww),
66
+ /** The leaf count (kernel-verified == leaves.length at the gate). */
67
+ leafCount: t.int().merge(Lww),
68
+ /** The leaf hashes, in serve order (JSON array) — what `receiptPath` serves from. */
69
+ leaves: t.json().merge(Lww),
70
+ /** The `attested_at_ms` range the batch covers. */
71
+ firstAt: t.int().merge(Lww),
72
+ lastAt: t.int().merge(Lww),
73
+ /** The source chain position when the batch sealed. */
74
+ headAtSeal: t.string().merge(Lww),
75
+ foldRootAtSeal: t.string().merge(Lww),
76
+ /** Which read-attestor key signed every member of this batch (`sha256hex(pubkey)`). */
77
+ signerKeyHash: t.string().merge(Lww),
78
+ });
79
+
80
+ /** Audit read: the source's sealed receipt stream, by batch id. */
81
+ export const attestationReceiptByBatch = query("attestationReceiptByBatch")
82
+ .key("batchId")
83
+ .returns(NomosAttestationReceipt);
84
+
85
+ /**
86
+ * `recordAttestationBatch` (§5.2) — THE SEAL. One authored intent per batch, authored AS the
87
+ * enrolled `workspace:read-attestor` through the NORMAL offer gate (the key is an enrolled
88
+ * signer precisely so it can do this — §1.2's "one key, two duties"). The payload is the
89
+ * kernel's own `attestationOpenBatch` snapshot, carried verbatim by the adapter; the gate
90
+ * re-derives the Merkle root and refuses any drift (`AttestationReceiptInvalid`) — the
91
+ * adapter carries, never computes. Seal triggers (leafCount ≥ 1024 / age ≥ 60s / epoch change
92
+ * / unmount) are the kernel's verdict on the same snapshot; a failed seal retries, never
93
+ * drops silently.
94
+ */
95
+ export const recordAttestationBatch = directive("recordAttestationBatch")
96
+ .ensures(NomosAttestationReceipt)
97
+ .payload(
98
+ z.object({
99
+ batchId: z.string().min(1),
100
+ batchRoot: z.string().min(1),
101
+ leafCount: z.number().int().positive(),
102
+ leaves: z.array(z.string().min(1)).min(1),
103
+ firstAt: z.number().int().nonnegative(),
104
+ lastAt: z.number().int().nonnegative(),
105
+ headAtSeal: z.string(),
106
+ foldRootAtSeal: z.string(),
107
+ signerKeyHash: z.string().min(1),
108
+ }),
109
+ )
110
+ .plan((p) => {
111
+ const r = instance(NomosAttestationReceipt, attestationReceiptId(p.batchId));
112
+ return [
113
+ set(r, "batchId", p.batchId),
114
+ set(r, "batchRoot", p.batchRoot),
115
+ set(r, "leafCount", p.leafCount),
116
+ set(r, "leaves", JSON.stringify(p.leaves)),
117
+ set(r, "firstAt", p.firstAt),
118
+ set(r, "lastAt", p.lastAt),
119
+ set(r, "headAtSeal", p.headAtSeal),
120
+ set(r, "foldRootAtSeal", p.foldRootAtSeal),
121
+ set(r, "signerKeyHash", p.signerKeyHash),
122
+ ];
123
+ });
124
+
125
+ /**
126
+ * NomosSourceDishonor (§5.3) — a PROVEN equivocation, folded in the READER's (or a
127
+ * governor's) chain at `source-dishonor:<sourceGenesis>:<keyHash>`. The full evidence (the
128
+ * two signed envelopes) rides the committed intent's payload — the fact aggregate carries
129
+ * the identity tuple + when it was proven. Once folded, `onDishonor: "refuse"` trust edges
130
+ * refuse every LATER attestation from this `(genesis, keyHash)` (position-aware).
131
+ */
132
+ export const NomosSourceDishonor = aggregate("NomosSourceDishonor", {
133
+ /** The equivocating source chain's genesis oid (its true identity). */
134
+ sourceGenesis: t.string().merge(Lww),
135
+ /** The equivocating read-attestor key (`sha256hex(pubkey)`). */
136
+ keyHash: t.string().merge(Lww),
137
+ /** The fold-root BOTH envelopes pinned — the state the source spoke of with two tongues. */
138
+ foldRoot: t.string().merge(Lww),
139
+ /** When the reporter proved it (ms — the reporter's clock, informational). */
140
+ provenAtMs: t.int().merge(Lww),
141
+ });
142
+
143
+ /** Audit read: the folded dishonor facts, by source genesis. */
144
+ export const sourceDishonorByGenesis = query("sourceDishonorByGenesis")
145
+ .key("sourceGenesis")
146
+ .returns(NomosSourceDishonor);
147
+
148
+ /** One carried evidence envelope (an attested read exactly as captured — §5.3 walk 1). */
149
+ const evidenceEnvelope = z.object({
150
+ queryId: z.string().min(1),
151
+ args: z.record(z.string(), z.string()).default({}),
152
+ result: z.unknown(),
153
+ attestation: z.record(z.string(), z.unknown()),
154
+ });
155
+
156
+ /**
157
+ * `reportSourceEquivocation` (§5.3) — carry TWO signed attested-read envelopes; the KERNEL
158
+ * gate verifies (pure carried bytes): both ed25519 signatures, same source key, same
159
+ * `(source_genesis, fold_root, queryId, canonical args)`, DIVERGENT canonical results — and
160
+ * that the folded fact's id names exactly the proven `(genesis, keyHash)`. Forged or
161
+ * non-contradictory evidence is `EquivocationEvidenceInvalid` on every door. The plan trusts
162
+ * the payload's identity mirrors ONLY because the gate re-derives them from the evidence.
163
+ */
164
+ export const reportSourceEquivocation = directive("reportSourceEquivocation")
165
+ .ensures(NomosSourceDishonor)
166
+ .payload(
167
+ z.object({
168
+ a: evidenceEnvelope,
169
+ b: evidenceEnvelope,
170
+ /** Mirror of the proven identity (kernel-checked against the verified evidence). */
171
+ sourceGenesis: z.string().min(1),
172
+ keyHash: z.string().min(1),
173
+ foldRoot: z.string().min(1),
174
+ provenAtMs: z.number().int().nonnegative(),
175
+ }),
176
+ )
177
+ .plan((p) => {
178
+ const d = instance(NomosSourceDishonor, sourceDishonorId(p.sourceGenesis, p.keyHash));
179
+ return [
180
+ set(d, "sourceGenesis", p.sourceGenesis),
181
+ set(d, "keyHash", p.keyHash),
182
+ set(d, "foldRoot", p.foldRoot),
183
+ set(d, "provenAtMs", p.provenAtMs),
184
+ ];
185
+ });
@@ -0,0 +1,96 @@
1
+ // NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
2
+ // remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
3
+ // wasm32-wasip1 artifact {kernel · projection · embedded QuickJS engine}.
4
+ // If a file isn't this / hosting this / authoring for this / proving this — it's gone.
5
+
6
+ /**
7
+ * framework/source_roster.ts — K3 TRACKED ROSTERS (attested reads slice B, stage 5 —
8
+ * `architecture/attested_reads_design.md` §1.5).
9
+ *
10
+ * The reader's chain maintains a {@link NomosSourceRoster} per trusted source: a reader-side
11
+ * mirror of the SOURCE's own read-attestor signer roster, delivered as ATTESTED READS of the
12
+ * source's signer chain itself (the kernel-native `attestorRoster` query — the roster rides
13
+ * the SAME attested-read machinery as any foreign premise). `roster: "tracked"` on the trust
14
+ * edge activates the folded roster inside `accepted_keys`; rotation then needs ZERO reader
15
+ * law deploys, and a source-side revocation propagates on the reader's next sync.
16
+ *
17
+ * THE GROUNDED RECURSION (§1.5.2): a roster read's envelope is verified against the accepted
18
+ * set AS ALREADY FOLDED in the reader's prefix — the FIRST sync therefore verifies against
19
+ * the bootstrap trust (the K1 pins or the K2 birth-cert-derived key), which exists by
20
+ * construction before any roster does; every later sync verifies against the roster its own
21
+ * prefix folded. Deterministic at replay: the roster is reader-chain state.
22
+ */
23
+ import { z } from "zod";
24
+ import { aggregate, instance } from "../aggregate.js";
25
+ import { t } from "../fields.js";
26
+ import { Lww } from "../drivers.js";
27
+ import { directive, type Directive } from "../directive.js";
28
+ import { set } from "../ops.js";
29
+ import { read } from "../read.js";
30
+ import { foreignQuery } from "../attested_core.js";
31
+
32
+ /** The framework-reserved roster instance-id prefix (kernel mirror:
33
+ * `nomos_executor::SOURCE_ROSTER_PREFIX`). */
34
+ export const SOURCE_ROSTER_PREFIX = "source-roster:";
35
+
36
+ /** The roster instance id for a source genesis. */
37
+ export function sourceRosterId(sourceGenesis: string): string {
38
+ return SOURCE_ROSTER_PREFIX + sourceGenesis;
39
+ }
40
+
41
+ /** The kernel-native source-side roster query id (`nomos_executor::ATTESTOR_ROSTER_QUERY`). */
42
+ export const ATTESTOR_ROSTER_QUERY_ID = "attestorRoster";
43
+
44
+ /**
45
+ * NomosSourceRoster (§1.5) — the reader's folded mirror of one source's read-attestor
46
+ * roster, keyed by the source's TRUE identity (`source-roster:<genesis>`). `rosterJson` is
47
+ * the `keyHash → leaf` map exactly as the source's signer chain folds it (a key is ACCEPTED
48
+ * iff its leaf's `status == "active"` — the position-aware revocation mirror); the kernel's
49
+ * `accepted_keys` reads THIS under `roster: "tracked"`.
50
+ */
51
+ export const NomosSourceRoster = aggregate("NomosSourceRoster", {
52
+ /** The routable source name the edge was declared under (diagnostics). */
53
+ source: t.string().merge(Lww),
54
+ /** The `keyHash → enrollment leaf` map, JSON — as attested by the source's own chain. */
55
+ rosterJson: t.json().merge(Lww),
56
+ /** The source's `SignerEpoch` at the attested sync. */
57
+ currentEpoch: t.int().merge(Lww),
58
+ });
59
+
60
+ /** One roster row as the source's `attestorRoster` query answers it. */
61
+ interface AttestorRosterRow {
62
+ id: string;
63
+ data: {
64
+ genesis: string;
65
+ currentEpoch: number;
66
+ keys: Record<string, unknown>;
67
+ };
68
+ }
69
+
70
+ /**
71
+ * BUILD the reader-side `syncSourceRoster` framework directive for ONE tracked source
72
+ * (§1.5.1) — export the result from the law that declares `trustSource(sourceWs, { roster:
73
+ * "tracked", … })`. Its plan performs `read(attestorRoster, {}, { from: sourceWs })` (an
74
+ * ordinary attested read — verified by the five-step arm against the accepted set the reader's
75
+ * PREFIX already folds: pins/birth-cert first, then the roster itself) and folds the answer
76
+ * into {@link NomosSourceRoster}. One directive per tracked source per law (the id embeds
77
+ * nothing — a law tracking several sources exports several via distinct ids).
78
+ */
79
+ export function sourceRosterSync(sourceWs: string, directiveId = "syncSourceRoster"): Directive<Record<string, never>> {
80
+ const rosterQuery = foreignQuery(ATTESTOR_ROSTER_QUERY_ID);
81
+ return directive(directiveId)
82
+ .ensures(NomosSourceRoster)
83
+ .payload(z.object({}).strict())
84
+ .plan(() => {
85
+ const rows = read<AttestorRosterRow[]>(rosterQuery, {}, { from: sourceWs });
86
+ if (rows.length === 0) throw new Error("attestor-roster-empty");
87
+ const d = rows[0]!.data;
88
+ const r = instance(NomosSourceRoster, sourceRosterId(d.genesis));
89
+ return [
90
+ set(r, "source", sourceWs),
91
+ set(r, "rosterJson", JSON.stringify(d.keys ?? {})),
92
+ set(r, "currentEpoch", d.currentEpoch ?? 0),
93
+ ];
94
+ })
95
+ .readsFrom(sourceWs, rosterQuery) as Directive<Record<string, never>>;
96
+ }
package/src/index.ts CHANGED
@@ -124,6 +124,8 @@ export { read } from "./read.js";
124
124
  // surface (`trustSource`), the foreign query reference, and the reader-side SourceFrontier.
125
125
  export {
126
126
  trustSource,
127
+ trustParent,
128
+ PARENT_TRUST_SENTINEL,
127
129
  foreignQuery,
128
130
  NomosSourceFrontier,
129
131
  sourceFrontierId,
@@ -133,6 +135,31 @@ export {
133
135
  type TrustSourceOptions,
134
136
  type ForeignQueryRef,
135
137
  } from "./attested.js";
138
+ // ATTESTATION RECEIPTS + EQUIVOCATION ACCOUNTABILITY (slice B stage 3 — §5): the source-side
139
+ // sealed receipt batch + the reader-side proven-dishonor fact, both ordinary law the kernel's
140
+ // `attestation_ledger_violation` arm guards on every door.
141
+ export {
142
+ NomosAttestationReceipt,
143
+ recordAttestationBatch,
144
+ attestationReceiptByBatch,
145
+ attestationReceiptId,
146
+ ATTESTATION_RECEIPT_PREFIX,
147
+ NomosSourceDishonor,
148
+ reportSourceEquivocation,
149
+ sourceDishonorByGenesis,
150
+ sourceDishonorId,
151
+ SOURCE_DISHONOR_PREFIX,
152
+ } from "./framework/attested_receipts.js";
153
+ // K3 TRACKED ROSTERS (slice B stage 5 — §1.5): the reader-side folded source roster + the
154
+ // per-source sync directive factory (roster updates ride the attested-read machinery itself,
155
+ // grounded on the pins/birth-cert bootstrap).
156
+ export {
157
+ NomosSourceRoster,
158
+ sourceRosterSync,
159
+ sourceRosterId,
160
+ SOURCE_ROSTER_PREFIX,
161
+ ATTESTOR_ROSTER_QUERY_ID,
162
+ } from "./framework/source_roster.js";
136
163
  // THE BIRTH EFFECT (G3 — birth is a declared kernel primitive): a `.births()` directive's plan records the
137
164
  // child's genesis recipe via `birth(recipe)` (reuse `buildGenesisChain`); it rides the committed intent as a
138
165
  // declared output, and the kernel — reading the `.births` declaration off the law manifest (the