@kindgi/specs 0.1.4-rc.5 → 0.1.5-rc.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/README.md CHANGED
@@ -42,7 +42,7 @@ Schemas reference each other by `$id` (e.g. `pack.schema.json` →
42
42
  | Schema | Purpose |
43
43
  |---|---|
44
44
  | `agent.schema.json` | Agent — instructions + capabilities + tools + memory, declaratively composed |
45
- | `audit-bundle.schema.json` | HITL audit bundle — signed export of approvals + decisions + provenance refs |
45
+ | `audit-bundle.schema.json` | Audit bundle — the signed body of an approval's audit bundle: who decided, when, why, and the evidence |
46
46
  | `capability.schema.json` | Capability declaration — model requirements as constraints |
47
47
  | `compliance-evidence.schema.json` | Compliance evidence record emitted by the runtime |
48
48
  | `discoverable.schema.json` | Discoverable entity marker — cross-history retrieval |
@@ -57,6 +57,7 @@ Schemas reference each other by `$id` (e.g. `pack.schema.json` →
57
57
  | `policy.schema.json` | Tenant policy — access control and scope rules |
58
58
  | `provenance.schema.json` | Causal DAG — signed, portable, verifiable outside the runtime |
59
59
  | `run-event.schema.json` | Kernel run lifecycle events (SSE stream) |
60
+ | `signed-export.schema.json` | Signed export — the envelope of every signed export (audit bundle, provenance, compliance evidence): the signed bytes, the signature, the key |
60
61
  | `tool.schema.json` | Tool definition — MCP-compatible, transport-agnostic |
61
62
 
62
63
  ## Rules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kindgi/specs",
3
- "version": "0.1.4-rc.5",
3
+ "version": "0.1.5-rc.0",
4
4
  "description": "Canonical JSON Schemas (Draft 2020-12) for every Kindgi artifact kind — flow, agent, tool, capability, guardrail, policy, event, run event, memory, provenance, pack, compliance evidence, audit bundle, eval suite, discoverable entity. The wire contracts every SDK, runtime, and external tool validates against. Each schema is importable by name as `@kindgi/specs/<name>.schema.json`.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "./*.schema.json": "./schemas/*.schema.json",
25
25
  "./examples/*.json": "./examples/*.json",
26
+ "./test-vectors/*": "./test-vectors/*",
26
27
  "./package.json": "./package.json"
27
28
  },
28
29
  "files": [
@@ -30,6 +31,7 @@
30
31
  "src",
31
32
  "schemas",
32
33
  "examples",
34
+ "test-vectors",
33
35
  "README.md",
34
36
  "type-manifest.json"
35
37
  ],
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://kindgi.com/schemas/v1/agent.schema.json",
4
- "$comment": "schema-version: 1.3.0",
4
+ "$comment": "schema-version: 1.6.0",
5
5
  "title": "Agent",
6
6
  "description": "An agent definition. Declarative composition of instructions + capabilities + tools + memory retrieval + guardrails + conversation policy. The same definition is durable across restarts and versioned like flows — provenance references agents by (id, version).",
7
7
  "type": "object",
@@ -71,7 +71,44 @@
71
71
  "retrieval": {
72
72
  "type": "array",
73
73
  "items": { "$ref": "#/$defs/RetrievalIntent" },
74
- "description": "Fact-retrieval intents run before every turn to ground the model's context."
74
+ "description": "Fact-retrieval intents run before every turn to ground the model's context. Each sees only what the run may (its project and org, the user it acts for, its conversation and that conversation's end user, and tenant-wide facts); retrieved facts reach the model as a labelled data block, never as instructions. Schema-version 1.4.0 adds the `same-user` scope and `memory`; 1.5.0 adds `memory.remember`; 1.6.0 adds `source: conversations` and `same-segment`."
75
+ },
76
+ "memory": {
77
+ "type": "object",
78
+ "additionalProperties": false,
79
+ "description": "How the agent uses what it retrieves.",
80
+ "properties": {
81
+ "instructionTypes": {
82
+ "type": "array",
83
+ "items": { "type": "string", "minLength": 1 },
84
+ "description": "Fact types that are instructions for this agent: a retrieved fact of one of these types that a person verified goes into the system message under 'Policies (verified)'. Default: none, every retrieved fact is data."
85
+ },
86
+ "remember": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "required": ["types", "scope"],
90
+ "description": "Lets the agent remember: its turns offer the built-in tool `kindgi_remember` (built-in tools are `kindgi_<verb>`; an agent can't list one in `tools`). The model picks the type, the text (up to 2,000 characters), an optional slot `key` and when it stops being true; the scope comes from here and the run. Every remembered fact is `unverified`, attributed to the agent version and the call that wrote it, and expires after `keepDays` unless a person verifies it. A person approves it before any read sees it when the scope is wider than one person (`same-project`, `tenant`) or the text reads like an instruction.",
91
+ "properties": {
92
+ "types": {
93
+ "type": "array",
94
+ "minItems": 1,
95
+ "items": { "type": "string", "minLength": 1 },
96
+ "description": "The fact types it may write."
97
+ },
98
+ "scope": {
99
+ "type": "string",
100
+ "enum": ["same-user", "same-conversation", "same-project", "tenant"],
101
+ "description": "Where its facts go: the conversation's end user (else the user the run acts for), the conversation, the run's project, or the tenant. Each but `tenant` includes the run's project."
102
+ },
103
+ "keepDays": {
104
+ "type": "integer",
105
+ "minimum": 1,
106
+ "maximum": 3650,
107
+ "description": "Days an unverified fact is kept. Default 30."
108
+ }
109
+ }
110
+ }
111
+ }
75
112
  },
76
113
  "guardrails": {
77
114
  "type": "array",
@@ -207,26 +244,56 @@
207
244
  },
208
245
  "RetrievalIntent": {
209
246
  "type": "object",
210
- "required": ["types", "scope"],
247
+ "required": ["scope"],
211
248
  "additionalProperties": false,
212
249
  "properties": {
250
+ "source": {
251
+ "type": "string",
252
+ "enum": ["facts", "conversations"],
253
+ "description": "What it reads: facts (the default), or messages of this agent's earlier conversations, quoted in the turn's `<memory>` block as earlier conversation, never as turns. Schema-version 1.6.0."
254
+ },
213
255
  "types": {
214
256
  "type": "array",
215
257
  "minItems": 1,
216
- "items": { "type": "string", "minLength": 1 }
258
+ "items": { "type": "string", "minLength": 1 },
259
+ "description": "The fact types it retrieves: required for facts, absent for conversations."
260
+ },
261
+ "roles": {
262
+ "type": "array",
263
+ "minItems": 1,
264
+ "uniqueItems": true,
265
+ "items": { "type": "string", "enum": ["user", "agent"] },
266
+ "description": "For conversations: whose messages it recalls. Default [\"user\"], the people's own words. Adding \"agent\" recalls the agent's earlier answers too, which can carry its mistakes: they are quoted as unverified earlier answers, and publishing warns."
217
267
  },
218
268
  "scope": {
219
269
  "type": "string",
220
- "enum": ["same-conversation", "same-project", "tenant"]
270
+ "enum": ["same-conversation", "same-user", "same-segment", "same-project", "tenant"],
271
+ "description": "What the intent selects within what the run may see. Facts: this conversation's; this run's end user's and user's; the run's project's (none without a project); or every fact of the type it may see (`tenant`). Conversations: this person's other conversations with the agent (`same-user`); this conversation's messages older than the history window (`same-conversation`); conversations in the run's segment path (`same-segment`) or its project (`same-project`), whoever had them: those two quote other people's conversations. `same-segment` is for conversations only, `tenant` for facts only."
221
272
  },
222
273
  "limit": {
223
274
  "type": "integer",
224
275
  "minimum": 1,
225
- "description": "Cap on facts per turn. Default 10."
276
+ "description": "Cap on facts (or recalled messages) per turn. Default 10."
226
277
  },
227
278
  "mode": {
228
279
  "type": "string",
229
- "enum": ["keyword", "semantic", "both"]
280
+ "enum": ["keyword", "semantic", "both"],
281
+ "description": "With the user's message as the query: full-text (`keyword`), by meaning (`semantic`, which fails the turn with `semantic-unavailable` on a runtime without embeddings), or both fused by rank (without embeddings, the keyword half). Absent: the newest facts (or messages), no query."
282
+ }
283
+ },
284
+ "if": { "properties": { "source": { "const": "conversations" } }, "required": ["source"] },
285
+ "then": {
286
+ "properties": {
287
+ "types": false,
288
+ "scope": { "enum": ["same-user", "same-conversation", "same-segment", "same-project"] }
289
+ }
290
+ },
291
+ "else": {
292
+ "required": ["types"],
293
+ "properties": {
294
+ "types": true,
295
+ "roles": false,
296
+ "scope": { "enum": ["same-conversation", "same-user", "same-project", "tenant"] }
230
297
  }
231
298
  }
232
299
  },
@@ -1,173 +1,108 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://kindgi.com/schemas/v1/audit-bundle.schema.json",
4
- "$comment": "schema-version: 1.0.0",
5
- "title": "HITL audit bundle",
6
- "description": "A signed export bundling one or more HITL approvals + their decisions + references to the provenance DAGs those approvals concern. Produced by the Kindgi runtime's HITL audit export. Ed25519-signed with the same key material used for provenance signing. Verifiable outside the runtime by an external auditor with only the public key.",
4
+ "$comment": "schema-version: 2.0.0",
5
+ "title": "Audit bundle",
6
+ "description": "The signed body of an approval's audit bundle: what a `signed-export.schema.json` envelope of kind `audit-bundle` carries in `bundle` (decoded). Who decided a human review, when, why, and the evidence, signed by the deployment. 2.0.0: this describes the bundle the API exports (one approval, a detached signature over these bytes); 1.0.0 described a multi-approval format with an inline signature that the runtime never produced.",
7
7
  "type": "object",
8
8
  "required": [
9
- "id",
9
+ "bundleSchemaVersion",
10
+ "approvalId",
10
11
  "tenantId",
11
- "generatedAt",
12
- "approvals",
13
- "decisions",
14
- "provenanceRefs",
15
- "signature"
12
+ "subjectKind",
13
+ "subjectRef",
14
+ "requiredRole",
15
+ "status",
16
+ "createdAt",
17
+ "decision",
18
+ "evidence",
19
+ "exportedAt"
16
20
  ],
17
21
  "additionalProperties": false,
18
22
  "properties": {
19
- "id": {
23
+ "bundleSchemaVersion": {
24
+ "const": "2.0.0"
25
+ },
26
+ "approvalId": {
20
27
  "type": "string",
21
- "minLength": 1,
22
- "description": "Unique bundle identifier (UUID). Stable across re-reads."
28
+ "minLength": 1
23
29
  },
24
30
  "tenantId": {
25
31
  "type": "string",
26
- "minLength": 1,
27
- "description": "Sovereignty boundary. All included approvals + decisions belong to this tenant."
32
+ "minLength": 1
28
33
  },
29
- "generatedAt": {
34
+ "subjectKind": {
30
35
  "type": "string",
31
- "format": "date-time",
32
- "description": "ISO 8601 UTC timestamp when the bundle was materialized."
36
+ "description": "What the review was of: `tool-call:pending`, a session gate, a deployment…"
33
37
  },
34
- "approvals": {
35
- "type": "array",
36
- "description": "The approval records included in this bundle. Ordering is not guaranteed; consumers must key by `id`.",
37
- "items": { "$ref": "#/$defs/approval" }
38
+ "subjectRef": {
39
+ "type": "object",
40
+ "description": "The reviewed thing's own reference, by subject kind."
38
41
  },
39
- "decisions": {
40
- "type": "array",
41
- "description": "The decision records for approvals in this bundle. Some approvals may not yet have a decision (bundle exported mid-flight); a consumer joins by `approvalId`.",
42
- "items": { "$ref": "#/$defs/reviewDecision" }
42
+ "requiredRole": {
43
+ "enum": ["standard", "senior", "admin"]
43
44
  },
44
- "provenanceRefs": {
45
- "type": "array",
46
- "description": "Back-references to the provenance DAGs the approvals concern. Only present when an approval carried a `provenanceRef`. Consumers fetch the DAGs from the provenance API separately — they are NOT embedded here to keep bundles small.",
47
- "items": {
48
- "type": "object",
49
- "required": ["runId"],
50
- "additionalProperties": false,
51
- "properties": {
52
- "runId": { "type": "string", "minLength": 1 },
53
- "provenanceId": { "type": "string", "minLength": 1 }
54
- }
55
- }
45
+ "status": {
46
+ "enum": ["approved", "rejected", "escalated", "expired", "withdrawn"],
47
+ "description": "Only a decided approval is exported."
56
48
  },
57
- "signature": {
58
- "$ref": "#/$defs/signature"
59
- }
60
- },
61
- "$defs": {
62
- "approval": {
63
- "type": "object",
64
- "required": [
65
- "id",
66
- "tenantId",
67
- "subjectKind",
68
- "subjectRef",
69
- "requiredRole",
70
- "status",
71
- "createdAt",
72
- "updatedAt"
73
- ],
74
- "additionalProperties": false,
75
- "properties": {
76
- "id": { "type": "string", "minLength": 1 },
77
- "tenantId": { "type": "string", "minLength": 1 },
78
- "projectId": { "type": "string", "minLength": 1 },
79
- "subjectKind": {
80
- "type": "string",
81
- "minLength": 1,
82
- "description": "Free-form kind identifier (e.g. 'agent-turn:session-hitl-gate', 'supervisor:fix-proposal', 'pack:install'). Callers namespace their kinds."
83
- },
84
- "subjectRef": {
85
- "type": "object",
86
- "description": "Polymorphic payload describing what needs approval. Shape depends on `subjectKind`.",
87
- "additionalProperties": true
49
+ "createdAt": {
50
+ "type": "string",
51
+ "format": "date-time"
52
+ },
53
+ "decidedAt": {
54
+ "type": "string",
55
+ "format": "date-time"
56
+ },
57
+ "decision": {
58
+ "description": "The recorded decision; `null` when the approval ended without one (expired).",
59
+ "oneOf": [
60
+ {
61
+ "type": "null"
88
62
  },
89
- "requiredRole": { "$ref": "#/$defs/reviewerRole" },
90
- "status": { "$ref": "#/$defs/approvalStatus" },
91
- "assignedTo": { "type": "string", "minLength": 1 },
92
- "batchKey": { "type": "string" },
93
- "title": { "type": "string" },
94
- "description": { "type": "string" },
95
- "context": { "type": "object", "additionalProperties": true },
96
- "provenanceRef": {
63
+ {
97
64
  "type": "object",
98
- "required": ["runId"],
65
+ "required": ["kind", "reviewerId", "reviewerRoleAtDecision", "decidedAt"],
99
66
  "additionalProperties": false,
100
67
  "properties": {
101
- "runId": { "type": "string", "minLength": 1 },
102
- "provenanceId": { "type": "string", "minLength": 1 }
68
+ "kind": {
69
+ "type": "string",
70
+ "description": "`approve`, `reject`, `escalate` or `withdraw`."
71
+ },
72
+ "reviewerId": {
73
+ "type": "string"
74
+ },
75
+ "reviewerRoleAtDecision": {
76
+ "enum": ["standard", "senior", "admin"]
77
+ },
78
+ "decidedAt": {
79
+ "type": "string",
80
+ "format": "date-time"
81
+ },
82
+ "rationale": {
83
+ "type": "string"
84
+ }
103
85
  }
104
- },
105
- "waitTokenId": { "type": "string" },
106
- "createdAt": { "type": "string", "format": "date-time" },
107
- "updatedAt": { "type": "string", "format": "date-time" },
108
- "decidedAt": { "type": "string", "format": "date-time" },
109
- "expiresAt": { "type": "string", "format": "date-time" }
110
- }
86
+ }
87
+ ]
111
88
  },
112
- "reviewDecision": {
89
+ "evidence": {
113
90
  "type": "object",
114
- "required": [
115
- "id",
116
- "tenantId",
117
- "approvalId",
118
- "reviewerId",
119
- "decision",
120
- "reviewerRoleAtDecision",
121
- "decidedAt"
122
- ],
123
91
  "additionalProperties": false,
124
92
  "properties": {
125
- "id": { "type": "string", "minLength": 1 },
126
- "tenantId": { "type": "string", "minLength": 1 },
127
- "approvalId": { "type": "string", "minLength": 1 },
128
- "reviewerId": { "type": "string", "minLength": 1 },
129
- "decision": {
130
- "type": "string",
131
- "enum": ["approve", "reject", "escalate", "withdraw"]
93
+ "guardrailResults": {
94
+ "type": "array",
95
+ "description": "The guardrail results that raised the review, when it came from one."
132
96
  },
133
- "rationale": { "type": "string" },
134
- "evidence": { "type": "object", "additionalProperties": true },
135
- "reviewerRoleAtDecision": {
136
- "$ref": "#/$defs/reviewerRole",
137
- "description": "Snapshot of the reviewer's role at the moment of decision. A subsequent role rotation does not rewrite this audit record."
138
- },
139
- "decidedAt": { "type": "string", "format": "date-time" }
97
+ "messages": {
98
+ "type": "array",
99
+ "description": "The conversation messages of the approval's run, when asked for (`includeMessages`)."
100
+ }
140
101
  }
141
102
  },
142
- "reviewerRole": {
143
- "type": "string",
144
- "enum": ["standard", "senior", "admin"],
145
- "description": "Reviewer role class. Ordered by escalation authority: standard < senior < admin. `requiredRole` on an approval is the minimum; a higher-ranked reviewer can always decide a lower-tier approval."
146
- },
147
- "approvalStatus": {
103
+ "exportedAt": {
148
104
  "type": "string",
149
- "enum": [
150
- "pending",
151
- "assigned",
152
- "in_review",
153
- "approved",
154
- "rejected",
155
- "escalated",
156
- "expired",
157
- "withdrawn"
158
- ]
159
- },
160
- "signature": {
161
- "type": "object",
162
- "required": ["algorithm", "keyId", "value", "signedAt"],
163
- "additionalProperties": false,
164
- "description": "Ed25519 signature over the canonical JSON projection of the bundle (all fields except this signature). Same algorithm and canonicalization as signed provenance exports.",
165
- "properties": {
166
- "algorithm": { "type": "string", "enum": ["ed25519"] },
167
- "keyId": { "type": "string", "minLength": 1 },
168
- "value": { "type": "string", "pattern": "^[A-Za-z0-9+/]+={0,2}$" },
169
- "signedAt": { "type": "string", "format": "date-time" }
170
- }
105
+ "format": "date-time"
171
106
  }
172
107
  }
173
108
  }
@@ -58,7 +58,16 @@
58
58
  "agent-promotion-refused",
59
59
  "agent-rollback",
60
60
  "agent-live-unpinned",
61
- "agent-live-pin-inactive"
61
+ "agent-live-pin-inactive",
62
+ "api-key-minted",
63
+ "api-key-revoked",
64
+ "service-account-created",
65
+ "service-account-granted",
66
+ "service-account-ungranted",
67
+ "service-account-unregistered",
68
+ "person-added",
69
+ "person-granted",
70
+ "person-ungranted"
62
71
  ]
63
72
  },
64
73
  "timestamp": {
@@ -37,6 +37,10 @@
37
37
  "additionalProperties": true,
38
38
  "description": "Check-specific configuration. Interpreted by the check implementation."
39
39
  },
40
+ "configSchema": {
41
+ "type": "object",
42
+ "description": "JSON Schema (Draft 2020-12) of the check's config, for a check that is pack code: the pack index's `configSchema`, which a deployment keeps on the guardrails it registers. The runtime checks a guardrail's `config` against it at registration, for any guardrail naming that check, and the pack service checks it on every call. Optional; a runtime-declaration field like `codeArtifactRef`."
43
+ },
40
44
  "action": {
41
45
  "$ref": "#/$defs/Action"
42
46
  },
@@ -244,6 +244,7 @@
244
244
  }
245
245
  },
246
246
  "retrieval": { "type": "array" },
247
+ "memory": { "$ref": "#/$defs/openObject" },
247
248
  "guardrails": { "type": "array", "items": { "type": "string" } },
248
249
  "budget": { "$ref": "#/$defs/openObject" },
249
250
  "parameters": { "type": "array" },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://kindgi.com/schemas/v1/pack-protocol.schema.json",
4
- "$comment": "schema-version: 2.4.1",
4
+ "$comment": "schema-version: 2.5.0",
5
5
  "title": "Pack protocol",
6
6
  "description": "Pack protocol v2 — the messages a runtime exchanges with a pack service to run pack code (tool handlers and guardrail checks). One request, one response. Transport (HTTP): `POST /v1/invoke` takes a request and answers 200 with a response; `GET /v1/info` (token) answers `info`; `GET /healthz` (no token) answers 200 while the process is up; `GET /readyz` (no token) answers 200 once every module the index names has loaded and the service isn't draining. Headers: `kindgi-pack-token` (required on `/v1/invoke` and `/v1/info`; `Authorization` is deliberately not used, since platforms like Cloud Run put their own identity token there), `kindgi-timeout-ms` (relative deadline; default 120000), `kindgi-run-id`, `kindgi-request-id`, `kindgi-protocol`, `traceparent` (W3C Trace Context: the run's trace, so a pack service's records can carry its trace id; optional, protocol 2.4.1); the answer to an invoke carries `kindgi-duration-ms` and `kindgi-artifact-version`. Every outcome of running pack code is a response message with status 200 — a validation failure, a throw, an unknown tool, `deadline-exceeded`, `cancelled` (the caller closed the connection). Non-2xx statuses are transport-level only: 401 (bad token), 404, 405, 413 (body over 10 MiB), 415 (not `application/json`), 503 with `Retry-After` (not ready, draining, or at the concurrency cap — the call provably did not run), 500 (the service itself failed). Process contract: a pack service reads `--index`, `--module-root` and `--host` arguments, `KINDGI_PACK_SERVICE_TOKEN`, `PORT` (`0` picks a free port) and `KINDGI_PACK_SERVICE_MAX_CONCURRENCY`; it removes `KINDGI_PACK_SERVICE_TOKEN` from its process environment before it loads pack code, which never sees it; it loads every module before listening and exits 1 with a `{\"kind\":\"boot-failed\",\"problems\":[…]}` JSON line on stderr when one fails; once listening it writes `{\"kind\":\"listening\",\"port\":N,…}` on stderr; SIGTERM drains in-flight calls (up to 8 s; `/readyz` and new invokes answer 503 meanwhile) and exits 0.",
7
7
  "oneOf": [
@@ -46,12 +46,13 @@
46
46
  },
47
47
  "env": {
48
48
  "type": "object",
49
- "description": "Resolved environment values for the call (open)."
49
+ "additionalProperties": { "type": "string" },
50
+ "description": "The env values the tool declares (`needsSpec.env`), by name, resolved for this call: the project's value, else its org's, else the tenant's, in the env the runtime serves; a schema `default` when no scope sets one (protocol 2.5.0). The runtime records them with the call, so the call re-run after a wait or a retry sends the same values. Absent when the tool declares none, and from an older runtime."
50
51
  },
51
52
  "secrets": { "type": "object", "description": "Resolved secrets for the call (open)." },
52
53
  "config": {
53
54
  "type": "object",
54
- "description": "Resolved configuration for the call (open)."
55
+ "description": "Reserved: no runtime sends it yet (open)."
55
56
  },
56
57
  "settings": {
57
58
  "type": "object",
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kindgi.com/schemas/v1/signed-export.schema.json",
4
+ "$comment": "schema-version: 1.0.0",
5
+ "title": "Signed export",
6
+ "description": "The envelope every signed export answers: an approval's audit bundle (`POST /v1/approvals/{approvalId}/audit-bundle`), a run's provenance (`POST /v1/provenance/{runId}/export`) and compliance evidence (`POST /v1/compliance/evidence/export`). `bundle` is base64 of the exact bytes that were signed: the body, as sorted-key JSON with no whitespace. A verifier checks the Ed25519 `signature` over those bytes and re-serializes nothing. The embedded `publicKey` only proves the bytes weren't changed; pin the deployment's keys (`GET /v1/export-signing-keys`) to know who signed them. The body of an audit bundle is `audit-bundle.schema.json`.",
7
+ "type": "object",
8
+ "required": [
9
+ "bundle",
10
+ "bundleSchemaVersion",
11
+ "algorithm",
12
+ "signingKeyId",
13
+ "signature",
14
+ "publicKey",
15
+ "canonicalization",
16
+ "exportedAt"
17
+ ],
18
+ "properties": {
19
+ "kind": {
20
+ "enum": ["audit-bundle", "provenance", "compliance"],
21
+ "description": "Which export this is. Absent from older servers."
22
+ },
23
+ "approvalId": {
24
+ "type": "string",
25
+ "description": "An audit bundle's approval."
26
+ },
27
+ "runId": {
28
+ "type": "string",
29
+ "description": "A provenance export's run."
30
+ },
31
+ "tenantId": {
32
+ "type": "string",
33
+ "description": "A compliance export's tenant."
34
+ },
35
+ "bundle": {
36
+ "type": "string",
37
+ "contentEncoding": "base64",
38
+ "contentMediaType": "application/json",
39
+ "description": "Base64 of the signed bytes: the body, as sorted-key JSON."
40
+ },
41
+ "bundleSchemaVersion": {
42
+ "type": "string",
43
+ "description": "The body's version, semver: audit bundle 2.0.0, provenance 1.2.0, compliance 1.0.0."
44
+ },
45
+ "algorithm": {
46
+ "enum": ["ed25519", "ecdsa-p256-sha256"],
47
+ "description": "The signing key's algorithm. An `ecdsa-p256-sha256` signature is IEEE P1363 `r‖s`. A verifier refuses an algorithm it doesn't know, naming it."
48
+ },
49
+ "signingKeyId": {
50
+ "type": "string",
51
+ "minLength": 1,
52
+ "description": "The key that signed it: one of `GET /v1/export-signing-keys`."
53
+ },
54
+ "signature": {
55
+ "type": "string",
56
+ "contentEncoding": "base64",
57
+ "description": "Base64 of the 64-byte signature over the `bundle` bytes: Ed25519's, or ECDSA P-256's as IEEE P1363 `r‖s`."
58
+ },
59
+ "publicKey": {
60
+ "type": "string",
61
+ "description": "The signing key's public half, PEM SPKI."
62
+ },
63
+ "canonicalization": {
64
+ "const": "sorted-key-json"
65
+ },
66
+ "exportedAt": {
67
+ "type": "string",
68
+ "format": "date-time",
69
+ "description": "When it was signed: the same instant as the signed body's `exportedAt`."
70
+ }
71
+ },
72
+ "additionalProperties": false
73
+ }
@@ -0,0 +1,13 @@
1
+ # Test vectors
2
+
3
+ Fixed inputs and expected outcomes that every implementation replays: the TypeScript and Python clients in this repository, and any other verifier.
4
+
5
+ ## `signed-export/`
6
+
7
+ A signed export, one file per algorithm. Each `valid` envelope must check out against its file's `publicKeyPem`. Each `tampered` envelope has one byte of its `bundle` changed and must fail.
8
+
9
+ - `ed25519.json` — the default algorithm, for a key in memory or a file.
10
+ - `ecdsa-p256-sha256.json` — ECDSA P-256 with SHA-256, for a KMS without Ed25519. Its signature is IEEE P1363 `r‖s`: 64 bytes, as Web Crypto takes it. A verifier whose library takes DER converts it first.
11
+ - `unknown-algorithm.json` — an export claiming an algorithm no verifier knows. A verifier must refuse it, naming the algorithm, and never fall back to another.
12
+
13
+ The envelopes follow [`signed-export.schema.json`](../schemas/signed-export.schema.json).
@@ -0,0 +1,29 @@
1
+ {
2
+ "description": "A signed export (an audit bundle) signed with ecdsa-p256-sha256. \"valid\" checks out against \"publicKeyPem\"; \"tampered\" changed one byte of the bundle and must fail.",
3
+ "algorithm": "ecdsa-p256-sha256",
4
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXSMY38HR/vZgqsd9R2OPPMO1DfwC\nLoEHSSlI9nVFat5K8kmrIHIaix0KlpiOQHJ4094yY968Wz7c9bsu4jeGPQ==\n-----END PUBLIC KEY-----\n",
5
+ "valid": {
6
+ "approvalId": "0b6e3c1e-2f4f-4a51-9d0e-7a1f2c3d4e5f",
7
+ "kind": "audit-bundle",
8
+ "bundle": "eyJhcHByb3ZhbElkIjoiMGI2ZTNjMWUtMmY0Zi00YTUxLTlkMGUtN2ExZjJjM2Q0ZTVmIiwiYnVuZGxlU2NoZW1hVmVyc2lvbiI6IjIuMC4wIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wN1QxMTo1OTowMC4wMDBaIiwiZGVjaXNpb24iOiJyZWplY3QiLCJyZXZpZXdlcklkIjoicmV2LTEifSwiZXhwb3J0ZWRBdCI6IjIwMjYtMTAtMDdUMTI6MDA6MDAuMDAwWiJ9",
9
+ "bundleSchemaVersion": "2.0.0",
10
+ "algorithm": "ecdsa-p256-sha256",
11
+ "signingKeyId": "ex_sD1K1gmwO-S3BJxR",
12
+ "signature": "k2iNRtXxE31ctj0xbvRYuAmFMHVXCIWl0W+mMFLGBXXvGFnTJVSmUS5EelSTW+AQ2Lt1jzO0VWVgTyJTI5TlTQ==",
13
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXSMY38HR/vZgqsd9R2OPPMO1DfwC\nLoEHSSlI9nVFat5K8kmrIHIaix0KlpiOQHJ4094yY968Wz7c9bsu4jeGPQ==\n-----END PUBLIC KEY-----\n",
14
+ "canonicalization": "sorted-key-json",
15
+ "exportedAt": "2026-10-07T12:00:00.000Z"
16
+ },
17
+ "tampered": {
18
+ "approvalId": "0b6e3c1e-2f4f-4a51-9d0e-7a1f2c3d4e5f",
19
+ "kind": "audit-bundle",
20
+ "bundle": "eyJhcHByb3ZhbElkIjoiMGI2ZTNjMWUtMmY0Zi00YTUxLTlkMGUtN2ExZjJjM2Q0ZTVmIiwiYnVuZGxlU2NoZW1hVmVyc2lvbiI6IjIuMC4wIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wN1QxMTo1OTowMC4wMDBaIiwiZGVjaXNpb24iOiJSZWplY3QiLCJyZXZpZXdlcklkIjoicmV2LTEifSwiZXhwb3J0ZWRBdCI6IjIwMjYtMTAtMDdUMTI6MDA6MDAuMDAwWiJ9",
21
+ "bundleSchemaVersion": "2.0.0",
22
+ "algorithm": "ecdsa-p256-sha256",
23
+ "signingKeyId": "ex_sD1K1gmwO-S3BJxR",
24
+ "signature": "k2iNRtXxE31ctj0xbvRYuAmFMHVXCIWl0W+mMFLGBXXvGFnTJVSmUS5EelSTW+AQ2Lt1jzO0VWVgTyJTI5TlTQ==",
25
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXSMY38HR/vZgqsd9R2OPPMO1DfwC\nLoEHSSlI9nVFat5K8kmrIHIaix0KlpiOQHJ4094yY968Wz7c9bsu4jeGPQ==\n-----END PUBLIC KEY-----\n",
26
+ "canonicalization": "sorted-key-json",
27
+ "exportedAt": "2026-10-07T12:00:00.000Z"
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "description": "A signed export (an audit bundle) signed with ed25519. \"valid\" checks out against \"publicKeyPem\"; \"tampered\" changed one byte of the bundle and must fail.",
3
+ "algorithm": "ed25519",
4
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAebVWLo/mVPlAeLES6KmLp5AfhTrmlb7X4OORC60ElmQ=\n-----END PUBLIC KEY-----\n",
5
+ "valid": {
6
+ "approvalId": "0b6e3c1e-2f4f-4a51-9d0e-7a1f2c3d4e5f",
7
+ "kind": "audit-bundle",
8
+ "bundle": "eyJhcHByb3ZhbElkIjoiMGI2ZTNjMWUtMmY0Zi00YTUxLTlkMGUtN2ExZjJjM2Q0ZTVmIiwiYnVuZGxlU2NoZW1hVmVyc2lvbiI6IjIuMC4wIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wN1QxMTo1OTowMC4wMDBaIiwiZGVjaXNpb24iOiJyZWplY3QiLCJyZXZpZXdlcklkIjoicmV2LTEifSwiZXhwb3J0ZWRBdCI6IjIwMjYtMTAtMDdUMTI6MDA6MDAuMDAwWiJ9",
9
+ "bundleSchemaVersion": "2.0.0",
10
+ "algorithm": "ed25519",
11
+ "signingKeyId": "ex_ZbYGc9btiEvwHCwi",
12
+ "signature": "auHSr03lOnru5Z8gTEuzYRU//5p4NqoeXUTNNM4XPKqcM8PCcMjj3I6ZKByve09LRQLgFfGqpQSIgpirptTuBg==",
13
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAebVWLo/mVPlAeLES6KmLp5AfhTrmlb7X4OORC60ElmQ=\n-----END PUBLIC KEY-----\n",
14
+ "canonicalization": "sorted-key-json",
15
+ "exportedAt": "2026-10-07T12:00:00.000Z"
16
+ },
17
+ "tampered": {
18
+ "approvalId": "0b6e3c1e-2f4f-4a51-9d0e-7a1f2c3d4e5f",
19
+ "kind": "audit-bundle",
20
+ "bundle": "eyJhcHByb3ZhbElkIjoiMGI2ZTNjMWUtMmY0Zi00YTUxLTlkMGUtN2ExZjJjM2Q0ZTVmIiwiYnVuZGxlU2NoZW1hVmVyc2lvbiI6IjIuMC4wIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wN1QxMTo1OTowMC4wMDBaIiwiZGVjaXNpb24iOiJSZWplY3QiLCJyZXZpZXdlcklkIjoicmV2LTEifSwiZXhwb3J0ZWRBdCI6IjIwMjYtMTAtMDdUMTI6MDA6MDAuMDAwWiJ9",
21
+ "bundleSchemaVersion": "2.0.0",
22
+ "algorithm": "ed25519",
23
+ "signingKeyId": "ex_ZbYGc9btiEvwHCwi",
24
+ "signature": "auHSr03lOnru5Z8gTEuzYRU//5p4NqoeXUTNNM4XPKqcM8PCcMjj3I6ZKByve09LRQLgFfGqpQSIgpirptTuBg==",
25
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAebVWLo/mVPlAeLES6KmLp5AfhTrmlb7X4OORC60ElmQ=\n-----END PUBLIC KEY-----\n",
26
+ "canonicalization": "sorted-key-json",
27
+ "exportedAt": "2026-10-07T12:00:00.000Z"
28
+ }
29
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "description": "Signed exports made by Kindgi 0.1.4 (the published @kindgi/api@0.1.4, an embedder's createApp with its own signingKey; the 0.1.4 runtime itself signed none). 0.1.4's audit bundle stamped the envelope's exportedAt separately from the signed one, so about 1 in 10 came out a millisecond apart. A verifier must check all three: \"auditBundle\" (the two stamps equal), \"auditBundleStampsDiffer\" (valid, with \"note\": it reports the signed time), and \"provenance\" (1.1.0, no signed exportedAt). The leniency is for this format only: envelope bundleSchemaVersion 1 over a signed bundleVersion 1.",
3
+ "made": "2026-10-08, @kindgi/api@0.1.4 and @kindgi/crypto@0.1.4 from npm",
4
+ "algorithm": "ed25519",
5
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAkDYWwEzj1U6P8hOFpI4HAm2jzR/BXyCctqdjH8d+i8g=\n-----END PUBLIC KEY-----\n",
6
+ "auditBundle": {
7
+ "approvalId": "00000000-0000-4000-8000-0000000000d4",
8
+ "bundle": "eyJhcHByb3ZhbElkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMGQ0IiwiYnVuZGxlVmVyc2lvbiI6MSwiY3JlYXRlZEF0IjoiMjAyNi0xMC0wMVQxMDowMDowMC4wMDBaIiwiZGVjaWRlZEF0IjoiMjAyNi0xMC0wMVQxMDowNTowMC4wMDBaIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wMVQxMDowNTowMC4wMDBaIiwia2luZCI6ImFwcHJvdmUiLCJyYXRpb25hbGUiOiJXaXRoaW4gdGhlIHJlZnVuZCBsaW1pdC4iLCJyZXZpZXdlcklkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMGMzIiwicmV2aWV3ZXJSb2xlQXREZWNpc2lvbiI6InN0YW5kYXJkIn0sImV2aWRlbmNlIjp7Imd1YXJkcmFpbFJlc3VsdHMiOlt7ImlkIjoiYWNtZS5saW1pdCIsIm9rIjp0cnVlfV19LCJleHBvcnRlZEF0IjoiMjAyNi0xMC0wOFQxMzoxMjoyMS41MzRaIiwicmVxdWlyZWRSb2xlIjoic3RhbmRhcmQiLCJzdGF0dXMiOiJhcHByb3ZlZCIsInN1YmplY3RLaW5kIjoidG9vbC1jYWxsIiwic3ViamVjdFJlZiI6eyJjYWxsSWQiOiJjMSIsInRvb2xJZCI6ImFjbWUucmVmdW5kIn0sInRlbmFudElkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMGExIn0=",
9
+ "bundleSchemaVersion": 1,
10
+ "algorithm": "ed25519",
11
+ "signingKeyId": "acme-export-key-1",
12
+ "signature": "tLStGUncaT7NwAiBRQVQ8rA9F/Ql6YBeAAXBD9ENbymg0LYImzW/692ZAmmBe5Tje3ve6VkexKTi7SGRAXJvDg==",
13
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAkDYWwEzj1U6P8hOFpI4HAm2jzR/BXyCctqdjH8d+i8g=\n-----END PUBLIC KEY-----\n",
14
+ "canonicalization": "sorted-key-json",
15
+ "exportedAt": "2026-10-08T13:12:21.534Z"
16
+ },
17
+ "auditBundleStampsDiffer": {
18
+ "approvalId": "00000000-0000-4000-8000-0000000000d4",
19
+ "bundle": "eyJhcHByb3ZhbElkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMGQ0IiwiYnVuZGxlVmVyc2lvbiI6MSwiY3JlYXRlZEF0IjoiMjAyNi0xMC0wMVQxMDowMDowMC4wMDBaIiwiZGVjaWRlZEF0IjoiMjAyNi0xMC0wMVQxMDowNTowMC4wMDBaIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wMVQxMDowNTowMC4wMDBaIiwia2luZCI6ImFwcHJvdmUiLCJyYXRpb25hbGUiOiJXaXRoaW4gdGhlIHJlZnVuZCBsaW1pdC4iLCJyZXZpZXdlcklkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMGMzIiwicmV2aWV3ZXJSb2xlQXREZWNpc2lvbiI6InN0YW5kYXJkIn0sImV2aWRlbmNlIjp7Imd1YXJkcmFpbFJlc3VsdHMiOlt7ImlkIjoiYWNtZS5saW1pdCIsIm9rIjp0cnVlfV19LCJleHBvcnRlZEF0IjoiMjAyNi0xMC0wOFQxMzoxMjoyMS41MzJaIiwicmVxdWlyZWRSb2xlIjoic3RhbmRhcmQiLCJzdGF0dXMiOiJhcHByb3ZlZCIsInN1YmplY3RLaW5kIjoidG9vbC1jYWxsIiwic3ViamVjdFJlZiI6eyJjYWxsSWQiOiJjMSIsInRvb2xJZCI6ImFjbWUucmVmdW5kIn0sInRlbmFudElkIjoiMDAwMDAwMDAtMDAwMC00MDAwLTgwMDAtMDAwMDAwMDAwMGExIn0=",
20
+ "bundleSchemaVersion": 1,
21
+ "algorithm": "ed25519",
22
+ "signingKeyId": "acme-export-key-1",
23
+ "signature": "8UGQrLv3plY8TdURP/gAUYGpv6nVBzTBndKDNbfF8jslMAg4Y3Jlr5SQ6Ab615prS5BxaJqldkrEVL3rMgsVAQ==",
24
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAkDYWwEzj1U6P8hOFpI4HAm2jzR/BXyCctqdjH8d+i8g=\n-----END PUBLIC KEY-----\n",
25
+ "canonicalization": "sorted-key-json",
26
+ "exportedAt": "2026-10-08T13:12:21.533Z"
27
+ },
28
+ "note": "made by Kindgi 0.1.4, which stamped the envelope's exportedAt separately: the signed export time is 2026-10-08T13:12:21.532Z (the envelope says 2026-10-08T13:12:21.533Z)",
29
+ "provenance": {
30
+ "runId": "run_acme_0001",
31
+ "bundle": "eyJidW5kbGVTY2hlbWFWZXJzaW9uIjoiMS4xLjAiLCJjcmVhdGVkQXQiOiIyMDI2LTEwLTAxVDEwOjAwOjAwLjAwMFoiLCJkYWciOnsiZWRnZXMiOlt7ImZyb20iOiJuMSIsImtpbmQiOiJjYXVzYWwiLCJ0byI6Im4yIn1dLCJub2RlcyI6W3siaWQiOiJuMSIsImtpbmQiOiJpbnB1dCIsInRpbWVzdGFtcCI6IjIwMjYtMTAtMDFUMTA6MDA6MDAuMDAwWiJ9LHsiaWQiOiJuMiIsImtpbmQiOiJtb2RlbC1vdXRwdXQiLCJtb2RlbFZlcnNpb24iOiJhY21lLW1vZGVsIiwidGltZXN0YW1wIjoiMjAyNi0xMC0wMVQxMDowMDowMi4wMDBaIn1dfSwicHJvdmVuYW5jZUlkIjoicHJvdl9hY21lXzAwMDEiLCJydW5JZCI6InJ1bl9hY21lXzAwMDEiLCJ0ZW5hbnRJZCI6IjAwMDAwMDAwLTAwMDAtNDAwMC04MDAwLTAwMDAwMDAwMDBhMSIsInZlcnNpb24iOiIxLjAuMCJ9",
32
+ "bundleSchemaVersion": "1.1.0",
33
+ "algorithm": "ed25519",
34
+ "signingKeyId": "acme-export-key-1",
35
+ "signature": "X5o+gYHP12nU4yGQo4yCazWyGIyEkjIRH2U5wS788XJf5/ZlA9FezqTzYh0Nh5Q4temPZ0ac6pwOyEldlVfFBQ==",
36
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAkDYWwEzj1U6P8hOFpI4HAm2jzR/BXyCctqdjH8d+i8g=\n-----END PUBLIC KEY-----\n",
37
+ "canonicalization": "sorted-key-json",
38
+ "exportedAt": "2026-10-08T13:12:21.594Z"
39
+ }
40
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "description": "An export claiming an algorithm no verifier knows (\"ecdsa-p384-sha384\"): a verifier must refuse it, naming the algorithm, and never fall back to another.",
3
+ "algorithm": "ecdsa-p384-sha384",
4
+ "valid": null,
5
+ "refused": {
6
+ "approvalId": "0b6e3c1e-2f4f-4a51-9d0e-7a1f2c3d4e5f",
7
+ "kind": "audit-bundle",
8
+ "bundle": "eyJhcHByb3ZhbElkIjoiMGI2ZTNjMWUtMmY0Zi00YTUxLTlkMGUtN2ExZjJjM2Q0ZTVmIiwiYnVuZGxlU2NoZW1hVmVyc2lvbiI6IjIuMC4wIiwiZGVjaXNpb24iOnsiZGVjaWRlZEF0IjoiMjAyNi0xMC0wN1QxMTo1OTowMC4wMDBaIiwiZGVjaXNpb24iOiJyZWplY3QiLCJyZXZpZXdlcklkIjoicmV2LTEifSwiZXhwb3J0ZWRBdCI6IjIwMjYtMTAtMDdUMTI6MDA6MDAuMDAwWiJ9",
9
+ "bundleSchemaVersion": "2.0.0",
10
+ "algorithm": "ecdsa-p384-sha384",
11
+ "signingKeyId": "ex_sD1K1gmwO-S3BJxR",
12
+ "signature": "k2iNRtXxE31ctj0xbvRYuAmFMHVXCIWl0W+mMFLGBXXvGFnTJVSmUS5EelSTW+AQ2Lt1jzO0VWVgTyJTI5TlTQ==",
13
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXSMY38HR/vZgqsd9R2OPPMO1DfwC\nLoEHSSlI9nVFat5K8kmrIHIaix0KlpiOQHJ4094yY968Wz7c9bsu4jeGPQ==\n-----END PUBLIC KEY-----\n",
14
+ "canonicalization": "sorted-key-json",
15
+ "exportedAt": "2026-10-07T12:00:00.000Z"
16
+ }
17
+ }