@veritasacta/artifacts 0.1.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/SPEC.md ADDED
@@ -0,0 +1,107 @@
1
+ # Veritas Acta Signed Artifacts — Specification v1
2
+
3
+ A minimal standard for creating and verifying Ed25519-signed JSON artifacts.
4
+
5
+ ## Overview
6
+
7
+ A **signed artifact** is a JSON object with:
8
+ - A typed **payload** (the content being attested)
9
+ - A deterministic **canonical form** (for signing)
10
+ - An Ed25519 **signature** (for verification)
11
+
12
+ The envelope is the same for all artifact types. The payload schema varies by type.
13
+
14
+ ## Envelope Format
15
+
16
+ Every signed artifact is a JSON object with these top-level fields:
17
+
18
+ | Field | Type | Required | Description |
19
+ |-------|------|----------|-------------|
20
+ | `v` | number | yes | Schema version. Currently `1`. |
21
+ | `type` | string | yes | Artifact type identifier (e.g., `scopeblind:decision`, `acta:anchor`). |
22
+ | `timestamp` | string | yes | ISO-8601 timestamp of creation. |
23
+ | `signature` | string | yes | Hex-encoded Ed25519 signature over the canonical form (excluding this field). |
24
+
25
+ All other fields are payload-specific and defined by the artifact type.
26
+
27
+ ## Canonical Serialization
28
+
29
+ To produce a deterministic byte string for signing:
30
+
31
+ 1. Remove the `signature` field from the artifact object.
32
+ 2. Serialize with `JSON.stringify(obj, Object.keys(obj).sort())` (JCS-style sorted keys).
33
+ 3. Encode the resulting string as UTF-8 bytes.
34
+
35
+ This is the message that gets signed and verified.
36
+
37
+ ## Signing
38
+
39
+ ```
40
+ message = utf8_encode(canonical_json(artifact_without_signature))
41
+ signature = ed25519_sign(message, private_key)
42
+ ```
43
+
44
+ The signature is stored as a lowercase hex string (128 hex chars = 64 bytes).
45
+
46
+ ## Verification
47
+
48
+ ```
49
+ message = utf8_encode(canonical_json(artifact_without_signature))
50
+ valid = ed25519_verify(hex_decode(signature), message, hex_decode(public_key))
51
+ ```
52
+
53
+ ## Registered Artifact Types
54
+
55
+ ### `scopeblind:decision`
56
+ An enforcement decision by a ScopeBlind verifier.
57
+
58
+ | Field | Type | Description |
59
+ |-------|------|-------------|
60
+ | `slug` | string | Tenant identifier |
61
+ | `decision` | string | `allow`, `challenge`, `deny`, `payment_required` |
62
+ | `risk` | number | Risk score (0-100) |
63
+ | `reason` | string | Machine-readable reason |
64
+ | `identity_class` | string | `fingerprint`, `dbsc`, `dpop`, `agent_key` |
65
+ | `identity_hash` | string | Truncated identity hash (privacy-safe) |
66
+ | `country` | string? | 2-letter country code |
67
+ | `epoch` | number? | Epoch window |
68
+ | `ts` | number | Unix timestamp (ms) |
69
+
70
+ ### `acta:anchor`
71
+ A signed checkpoint of all topic chain heads in an Acta instance.
72
+
73
+ | Field | Type | Description |
74
+ |-------|------|-------------|
75
+ | `merkle_root` | string | SHA-256 Merkle root of chain heads |
76
+ | `chain_heads` | array | `[{ topic, chain_head_hash, chain_length }]` |
77
+ | `topic_count` | number | Number of topics anchored |
78
+ | `public_key` | string | Hex-encoded Ed25519 public key |
79
+ | `charter_hash` | string | Protocol charter hash |
80
+ | `protocol_spec_hash` | string | Protocol spec hash |
81
+ | `protocol_version` | string | Protocol version |
82
+ | `policy_hash` | string | Instance policy hash |
83
+
84
+ ### `acta:resolution`
85
+ A prediction resolution outcome (future — when extracted from Acta).
86
+
87
+ | Field | Type | Description |
88
+ |-------|------|-------------|
89
+ | `topic` | string | Topic slug |
90
+ | `prediction_id` | string | Entry ID of the prediction |
91
+ | `resolution_type` | string | `confirmed`, `refuted`, `partially_confirmed`, `unresolvable` |
92
+ | `outcome` | string | Human-readable outcome description |
93
+ | `source` | object | Source evidence envelope |
94
+
95
+ ### `delegation:grant` (reserved, not yet implemented)
96
+ A delegation of authority from principal to agent.
97
+
98
+ ## Test Vectors
99
+
100
+ See `test-vectors.json` for canonical payloads, expected hashes, and signatures using the test keypair:
101
+
102
+ ```
103
+ Private key: 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
104
+ Public key: d75a980182b10ab7d54bfed3c964073a0ee172f3daa3f4a18446b0b8d183f8e3
105
+ ```
106
+
107
+ (This is the RFC 8032 test vector #1 keypair — deterministic and well-known.)
package/index.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @veritasacta/artifacts — Type definitions
3
+ */
4
+
5
+ /** Produce a deterministic JSON string with sorted keys (JCS-style). */
6
+ export function canonicalize(obj: Record<string, unknown>): string;
7
+
8
+ /** SHA-256 hash of the canonical form. Returns lowercase hex. */
9
+ export function canonicalHash(obj: Record<string, unknown>): string;
10
+
11
+ /** Sign result from signArtifact. */
12
+ export interface SignResult {
13
+ artifact: Record<string, unknown> & { signature: string };
14
+ signature: string;
15
+ hash: string;
16
+ }
17
+
18
+ /** Sign an artifact payload with Ed25519. Payload must include v, type, timestamp. */
19
+ export function signArtifact(
20
+ payload: Record<string, unknown> & { v: number; type: string; timestamp: string },
21
+ privateKeyHex: string,
22
+ ): SignResult;
23
+
24
+ /** Verification result from verifyArtifact. */
25
+ export interface VerifyResult {
26
+ valid: boolean;
27
+ hash?: string;
28
+ error?: string;
29
+ }
30
+
31
+ /** Verify an artifact's Ed25519 signature. */
32
+ export function verifyArtifact(
33
+ artifact: Record<string, unknown> & { signature: string },
34
+ publicKeyHex: string,
35
+ ): VerifyResult;
36
+
37
+ /** Generate a new Ed25519 keypair. Returns hex-encoded strings. */
38
+ export function generateKeypair(): { privateKey: string; publicKey: string };
39
+
40
+ /** Derive public key from private key. */
41
+ export function getPublicKey(privateKeyHex: string): string;
42
+
43
+ export { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
44
+ export { sha256 } from '@noble/hashes/sha256';
package/index.js ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @veritasacta/artifacts — Signed Artifact Envelope
3
+ *
4
+ * Canonical serialization, Ed25519 signing, and verification
5
+ * for typed JSON payloads (receipts, anchors, resolutions, etc.)
6
+ *
7
+ * Both ScopeBlind and Veritas Acta use this shared primitive.
8
+ *
9
+ * @license MIT
10
+ */
11
+
12
+ import { ed25519 } from '@noble/curves/ed25519';
13
+ import { sha256 } from '@noble/hashes/sha256';
14
+ import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
15
+
16
+ // ── Canonical Serialization ─────────────────────────────────────
17
+
18
+ /**
19
+ * Produce a deterministic JSON string from an object.
20
+ * Keys are sorted lexicographically at every level (JCS-style).
21
+ *
22
+ * @param {Record<string, unknown>} obj
23
+ * @returns {string}
24
+ */
25
+ export function canonicalize(obj) {
26
+ return JSON.stringify(obj, (_key, value) => {
27
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
28
+ const sorted = {};
29
+ for (const k of Object.keys(value).sort()) {
30
+ sorted[k] = value[k];
31
+ }
32
+ return sorted;
33
+ }
34
+ return value;
35
+ });
36
+ }
37
+
38
+ /**
39
+ * SHA-256 hash of the canonical form of an object.
40
+ * Returns lowercase hex.
41
+ *
42
+ * @param {Record<string, unknown>} obj
43
+ * @returns {string}
44
+ */
45
+ export function canonicalHash(obj) {
46
+ return bytesToHex(sha256(utf8ToBytes(canonicalize(obj))));
47
+ }
48
+
49
+ // ── Signing ─────────────────────────────────────────────────────
50
+
51
+ /**
52
+ * Sign an artifact payload with Ed25519.
53
+ *
54
+ * @param {Record<string, unknown>} payload - Must include `v`, `type`, `timestamp`.
55
+ * @param {string} privateKeyHex - 64-char hex-encoded Ed25519 private key.
56
+ * @returns {{ artifact: Record<string, unknown>, signature: string, hash: string }}
57
+ */
58
+ export function signArtifact(payload, privateKeyHex) {
59
+ if (!payload.v || !payload.type || !payload.timestamp) {
60
+ throw new Error('Artifact must include v, type, and timestamp fields');
61
+ }
62
+
63
+ const message = utf8ToBytes(canonicalize(payload));
64
+ const signature = bytesToHex(ed25519.sign(message, hexToBytes(privateKeyHex)));
65
+ const hash = bytesToHex(sha256(message));
66
+
67
+ return {
68
+ artifact: { ...payload, signature },
69
+ signature,
70
+ hash,
71
+ };
72
+ }
73
+
74
+ // ── Verification ────────────────────────────────────────────────
75
+
76
+ /**
77
+ * Verify an artifact's Ed25519 signature.
78
+ *
79
+ * @param {Record<string, unknown>} artifact - Signed artifact (with `signature` field).
80
+ * @param {string} publicKeyHex - 64-char hex-encoded Ed25519 public key.
81
+ * @returns {{ valid: boolean, hash?: string, error?: string }}
82
+ */
83
+ export function verifyArtifact(artifact, publicKeyHex) {
84
+ try {
85
+ if (!artifact.signature) {
86
+ return { valid: false, error: 'missing_signature' };
87
+ }
88
+
89
+ // Strip signature to reconstruct the signed message
90
+ const { signature, ...payload } = artifact;
91
+ const message = utf8ToBytes(canonicalize(payload));
92
+ const hash = bytesToHex(sha256(message));
93
+
94
+ const valid = ed25519.verify(
95
+ hexToBytes(signature),
96
+ message,
97
+ hexToBytes(publicKeyHex),
98
+ );
99
+
100
+ return valid
101
+ ? { valid: true, hash }
102
+ : { valid: false, hash, error: 'invalid_signature' };
103
+ } catch (err) {
104
+ return { valid: false, error: 'verification_error' };
105
+ }
106
+ }
107
+
108
+ // ── Keypair Utilities ───────────────────────────────────────────
109
+
110
+ /**
111
+ * Generate a new Ed25519 keypair.
112
+ * @returns {{ privateKey: string, publicKey: string }}
113
+ */
114
+ export function generateKeypair() {
115
+ const privateKey = ed25519.utils.randomPrivateKey();
116
+ const publicKey = ed25519.getPublicKey(privateKey);
117
+ return {
118
+ privateKey: bytesToHex(privateKey),
119
+ publicKey: bytesToHex(publicKey),
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Derive the public key from a private key.
125
+ * @param {string} privateKeyHex
126
+ * @returns {string}
127
+ */
128
+ export function getPublicKey(privateKeyHex) {
129
+ return bytesToHex(ed25519.getPublicKey(hexToBytes(privateKeyHex)));
130
+ }
131
+
132
+ // ── Re-exports for convenience ──────────────────────────────────
133
+
134
+ export { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
135
+ export { sha256 } from '@noble/hashes/sha256';
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@veritasacta/artifacts",
3
+ "version": "0.1.0",
4
+ "description": "Signed artifact envelope — canonical serialization, Ed25519 signing, and verification for typed JSON payloads.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "index.js",
8
+ "types": "index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./index.js",
12
+ "types": "./index.d.ts"
13
+ },
14
+ "./test-vectors": "./test-vectors.json"
15
+ },
16
+ "files": [
17
+ "index.js",
18
+ "index.d.ts",
19
+ "test-vectors.json",
20
+ "SPEC.md"
21
+ ],
22
+ "dependencies": {
23
+ "@noble/curves": "^1.8.0",
24
+ "@noble/hashes": "^1.7.0"
25
+ },
26
+ "devDependencies": {},
27
+ "scripts": {
28
+ "test": "node --test test.js",
29
+ "vectors": "node generate-vectors.js"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ },
34
+ "keywords": [
35
+ "ed25519",
36
+ "signed-artifacts",
37
+ "receipts",
38
+ "canonical-json",
39
+ "verification",
40
+ "veritasacta",
41
+ "scopeblind",
42
+ "voprf",
43
+ "audit"
44
+ ],
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/VeritasActa/artifacts"
48
+ },
49
+ "homepage": "https://veritasacta.com"
50
+ }
@@ -0,0 +1,193 @@
1
+ {
2
+ "_meta": {
3
+ "spec_version": 1,
4
+ "generated_at": "2026-03-13T00:00:00.000Z",
5
+ "keypair": {
6
+ "private_key": "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
7
+ "public_key": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
8
+ "note": "RFC 8032 Test Vector #1 — do NOT use in production"
9
+ }
10
+ },
11
+ "vectors": [
12
+ {
13
+ "name": "ScopeBlind Decision Receipt (deny)",
14
+ "payload": {
15
+ "v": 1,
16
+ "type": "scopeblind:decision",
17
+ "slug": "test-tenant",
18
+ "decision": "deny",
19
+ "risk": 78,
20
+ "reason": "repeat_visitor",
21
+ "identity_class": "fingerprint",
22
+ "identity_hash": "a3f8c1b2",
23
+ "country": "US",
24
+ "epoch": 7,
25
+ "timestamp": "2026-03-13T12:00:00.000Z",
26
+ "ts": 1773496800000
27
+ },
28
+ "canonical": "{\"country\":\"US\",\"decision\":\"deny\",\"epoch\":7,\"identity_class\":\"fingerprint\",\"identity_hash\":\"a3f8c1b2\",\"reason\":\"repeat_visitor\",\"risk\":78,\"slug\":\"test-tenant\",\"timestamp\":\"2026-03-13T12:00:00.000Z\",\"ts\":1773496800000,\"type\":\"scopeblind:decision\",\"v\":1}",
29
+ "hash": "ae52a4305dff01a97d382bb06bf69f70de6e70ceec9c4174db02b0b9ae9bd787",
30
+ "signature": "f7b235bcfd8261a00c99694c817b76491eb99d03e7ddcb3b3da625533c520235380c76b4a5457a7baf60d2a45b1129016eeb4f8f75e6fa8d14e79e8c39b22807",
31
+ "signed_artifact": {
32
+ "v": 1,
33
+ "type": "scopeblind:decision",
34
+ "slug": "test-tenant",
35
+ "decision": "deny",
36
+ "risk": 78,
37
+ "reason": "repeat_visitor",
38
+ "identity_class": "fingerprint",
39
+ "identity_hash": "a3f8c1b2",
40
+ "country": "US",
41
+ "epoch": 7,
42
+ "timestamp": "2026-03-13T12:00:00.000Z",
43
+ "ts": 1773496800000,
44
+ "signature": "f7b235bcfd8261a00c99694c817b76491eb99d03e7ddcb3b3da625533c520235380c76b4a5457a7baf60d2a45b1129016eeb4f8f75e6fa8d14e79e8c39b22807"
45
+ },
46
+ "verification": {
47
+ "valid": true,
48
+ "hash": "ae52a4305dff01a97d382bb06bf69f70de6e70ceec9c4174db02b0b9ae9bd787"
49
+ }
50
+ },
51
+ {
52
+ "name": "Acta Anchor",
53
+ "payload": {
54
+ "v": 1,
55
+ "type": "acta:anchor",
56
+ "timestamp": "2026-03-13T00:00:00.000Z",
57
+ "merkle_root": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
58
+ "chain_heads": [
59
+ {
60
+ "topic": "ai-trust",
61
+ "chain_head_hash": "1111111111111111111111111111111111111111111111111111111111111111",
62
+ "chain_length": 5
63
+ },
64
+ {
65
+ "topic": "climate",
66
+ "chain_head_hash": "2222222222222222222222222222222222222222222222222222222222222222",
67
+ "chain_length": 3
68
+ }
69
+ ],
70
+ "topic_count": 2,
71
+ "public_key": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
72
+ "charter_hash": "3a0f734d0000000000000000000000000000000000000000000000000000000",
73
+ "protocol_spec_hash": "d1f905770000000000000000000000000000000000000000000000000000000",
74
+ "protocol_version": "1.0.0",
75
+ "policy_hash": "3f1e07340000000000000000000000000000000000000000000000000000000"
76
+ },
77
+ "canonical": "{\"chain_heads\":[{\"chain_head_hash\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"chain_length\":5,\"topic\":\"ai-trust\"},{\"chain_head_hash\":\"2222222222222222222222222222222222222222222222222222222222222222\",\"chain_length\":3,\"topic\":\"climate\"}],\"charter_hash\":\"3a0f734d0000000000000000000000000000000000000000000000000000000\",\"merkle_root\":\"abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789\",\"policy_hash\":\"3f1e07340000000000000000000000000000000000000000000000000000000\",\"protocol_spec_hash\":\"d1f905770000000000000000000000000000000000000000000000000000000\",\"protocol_version\":\"1.0.0\",\"public_key\":\"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a\",\"timestamp\":\"2026-03-13T00:00:00.000Z\",\"topic_count\":2,\"type\":\"acta:anchor\",\"v\":1}",
78
+ "hash": "ddd17188699186d30a27fc87c7dff27e3cdef0b2ee351b783ddc389b78ea7817",
79
+ "signature": "a8e9823474d93895f45f6c0e33ea4ef3d83abee292980eb4e0ec575b227d117be5ffedc5dbe6799a64a4599119cb983a05373426e96de07b2893d489ced2660f",
80
+ "signed_artifact": {
81
+ "v": 1,
82
+ "type": "acta:anchor",
83
+ "timestamp": "2026-03-13T00:00:00.000Z",
84
+ "merkle_root": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
85
+ "chain_heads": [
86
+ {
87
+ "topic": "ai-trust",
88
+ "chain_head_hash": "1111111111111111111111111111111111111111111111111111111111111111",
89
+ "chain_length": 5
90
+ },
91
+ {
92
+ "topic": "climate",
93
+ "chain_head_hash": "2222222222222222222222222222222222222222222222222222222222222222",
94
+ "chain_length": 3
95
+ }
96
+ ],
97
+ "topic_count": 2,
98
+ "public_key": "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
99
+ "charter_hash": "3a0f734d0000000000000000000000000000000000000000000000000000000",
100
+ "protocol_spec_hash": "d1f905770000000000000000000000000000000000000000000000000000000",
101
+ "protocol_version": "1.0.0",
102
+ "policy_hash": "3f1e07340000000000000000000000000000000000000000000000000000000",
103
+ "signature": "a8e9823474d93895f45f6c0e33ea4ef3d83abee292980eb4e0ec575b227d117be5ffedc5dbe6799a64a4599119cb983a05373426e96de07b2893d489ced2660f"
104
+ },
105
+ "verification": {
106
+ "valid": true,
107
+ "hash": "ddd17188699186d30a27fc87c7dff27e3cdef0b2ee351b783ddc389b78ea7817"
108
+ }
109
+ },
110
+ {
111
+ "name": "Prediction Resolution (confirmed)",
112
+ "payload": {
113
+ "v": 1,
114
+ "type": "acta:resolution",
115
+ "timestamp": "2026-03-13T18:00:00.000Z",
116
+ "topic": "ai-trust",
117
+ "prediction_id": "pred_001",
118
+ "resolution_type": "confirmed",
119
+ "outcome": "The prediction was confirmed by the resolution source.",
120
+ "source": {
121
+ "source_url": "https://example.com/result",
122
+ "retrieved_at": "2026-03-13T17:30:00.000Z",
123
+ "content_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
124
+ }
125
+ },
126
+ "canonical": "{\"outcome\":\"The prediction was confirmed by the resolution source.\",\"prediction_id\":\"pred_001\",\"resolution_type\":\"confirmed\",\"source\":{\"content_hash\":\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"retrieved_at\":\"2026-03-13T17:30:00.000Z\",\"source_url\":\"https://example.com/result\"},\"timestamp\":\"2026-03-13T18:00:00.000Z\",\"topic\":\"ai-trust\",\"type\":\"acta:resolution\",\"v\":1}",
127
+ "hash": "e84e52c522cb28100ae14b72ed0030c567fca6bd92b7ca522b27339395f292ae",
128
+ "signature": "4d1112711310b67bf432d0fc1285d7d91558864a856d48c41540593147c98b5e7efcc954e843285d4f86a97e15853ed9e3a16f102d344107db574dc157c3d300",
129
+ "signed_artifact": {
130
+ "v": 1,
131
+ "type": "acta:resolution",
132
+ "timestamp": "2026-03-13T18:00:00.000Z",
133
+ "topic": "ai-trust",
134
+ "prediction_id": "pred_001",
135
+ "resolution_type": "confirmed",
136
+ "outcome": "The prediction was confirmed by the resolution source.",
137
+ "source": {
138
+ "source_url": "https://example.com/result",
139
+ "retrieved_at": "2026-03-13T17:30:00.000Z",
140
+ "content_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
141
+ },
142
+ "signature": "4d1112711310b67bf432d0fc1285d7d91558864a856d48c41540593147c98b5e7efcc954e843285d4f86a97e15853ed9e3a16f102d344107db574dc157c3d300"
143
+ },
144
+ "verification": {
145
+ "valid": true,
146
+ "hash": "e84e52c522cb28100ae14b72ed0030c567fca6bd92b7ca522b27339395f292ae"
147
+ }
148
+ },
149
+ {
150
+ "name": "Delegation Grant (placeholder)",
151
+ "payload": {
152
+ "v": 1,
153
+ "type": "delegation:grant",
154
+ "timestamp": "2026-03-13T10:00:00.000Z",
155
+ "principal": "org:example-corp",
156
+ "delegate": "agent:finance-bot-01",
157
+ "scope": [
158
+ "read:transactions",
159
+ "write:reports"
160
+ ],
161
+ "budget": {
162
+ "max_requests": 1000,
163
+ "window_hours": 24
164
+ },
165
+ "expires": "2026-03-14T10:00:00.000Z"
166
+ },
167
+ "canonical": "{\"budget\":{\"max_requests\":1000,\"window_hours\":24},\"delegate\":\"agent:finance-bot-01\",\"expires\":\"2026-03-14T10:00:00.000Z\",\"principal\":\"org:example-corp\",\"scope\":[\"read:transactions\",\"write:reports\"],\"timestamp\":\"2026-03-13T10:00:00.000Z\",\"type\":\"delegation:grant\",\"v\":1}",
168
+ "hash": "02a01dd7d11ebfedc10b5d286399b8b8787ed5b293a04b1daf1181c01cd80e5d",
169
+ "signature": "28a33cc114328941ccf364b8e1ab81992605880f6af4cdc9188b8e67e2ffe51451328629ff0223159667d64f891f4b780ee05f9bbafe93b0e89e16add32e4d08",
170
+ "signed_artifact": {
171
+ "v": 1,
172
+ "type": "delegation:grant",
173
+ "timestamp": "2026-03-13T10:00:00.000Z",
174
+ "principal": "org:example-corp",
175
+ "delegate": "agent:finance-bot-01",
176
+ "scope": [
177
+ "read:transactions",
178
+ "write:reports"
179
+ ],
180
+ "budget": {
181
+ "max_requests": 1000,
182
+ "window_hours": 24
183
+ },
184
+ "expires": "2026-03-14T10:00:00.000Z",
185
+ "signature": "28a33cc114328941ccf364b8e1ab81992605880f6af4cdc9188b8e67e2ffe51451328629ff0223159667d64f891f4b780ee05f9bbafe93b0e89e16add32e4d08"
186
+ },
187
+ "verification": {
188
+ "valid": true,
189
+ "hash": "02a01dd7d11ebfedc10b5d286399b8b8787ed5b293a04b1daf1181c01cd80e5d"
190
+ }
191
+ }
192
+ ]
193
+ }