@adastracomputing/ink 0.3.0 → 0.5.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.
@@ -60,18 +60,18 @@ export const InkAuditEventTypeSchema = z.enum([
60
60
  ]);
61
61
  // ── INK Audit Event (hash-chained, signed) ──
62
62
  export const InkAuditEventSchema = z.object({
63
- id: z.string().min(1),
63
+ id: z.string().min(1).max(256),
64
64
  version: z.literal("ink-audit/1"),
65
- agentId: z.string().min(1),
66
- agentSignature: z.string().min(1),
65
+ agentId: z.string().min(1).max(512),
66
+ agentSignature: z.string().min(1).max(256),
67
67
  sequence: z.number().int().positive(),
68
68
  previousEventHash: z.string().regex(/^[0-9a-f]{64}$/).nullable(),
69
69
  eventType: InkAuditEventTypeSchema,
70
70
  timestamp: z.string().datetime(),
71
- messageId: z.string().min(1).optional(),
72
- correlationId: z.string().min(1).optional(),
73
- counterpartyId: z.string().min(1).optional(),
74
- signingKeyId: z.string().min(1).optional(),
71
+ messageId: z.string().min(1).max(256).optional(),
72
+ correlationId: z.string().min(1).max(256).optional(),
73
+ counterpartyId: z.string().min(1).max(512).optional(),
74
+ signingKeyId: z.string().min(1).max(128).optional(),
75
75
  data: z.record(z.string(), z.unknown()).optional(),
76
76
  });
77
77
  // ── Receipt (INK Auditability §1) ──
@@ -85,34 +85,38 @@ export const InkReceiptDispositionSchema = z.enum([
85
85
  export const InkReceiptSchema = z.object({
86
86
  protocol: z.literal("ink/0.1"),
87
87
  type: z.literal("network.tulpa.receipt"),
88
- from: z.string(),
89
- to: z.string(),
90
- messageId: z.string(),
88
+ from: z.string().max(512),
89
+ to: z.string().max(512),
90
+ messageId: z.string().max(256),
91
91
  disposition: InkReceiptDispositionSchema,
92
92
  dispositionAt: z.string().datetime(),
93
93
  note: z.string().max(500).optional(),
94
- messageHash: z.string(),
95
- nonce: z.string(),
94
+ messageHash: z.string().max(256),
95
+ nonce: z.string().max(256),
96
96
  timestamp: z.string().datetime(),
97
- signature: z.string(),
97
+ signature: z.string().max(256),
98
98
  });
99
99
  // ── Audit Query (INK Auditability §3) ──
100
100
  export const InkAuditQuerySchema = z.object({
101
101
  protocol: z.literal("ink/0.1"),
102
102
  type: z.literal("network.tulpa.audit_query"),
103
- from: z.string(),
104
- to: z.string(),
105
- messageId: z.string(),
106
- nonce: z.string(),
103
+ from: z.string().max(512),
104
+ to: z.string().max(512),
105
+ messageId: z.string().max(256),
106
+ nonce: z.string().max(256),
107
107
  timestamp: z.string().datetime(),
108
108
  });
109
109
  // ── Audit Response (INK Auditability §3) ──
110
110
  export const InkAuditResponseSchema = z.object({
111
111
  protocol: z.literal("ink/0.1"),
112
112
  type: z.literal("network.tulpa.audit_response"),
113
- messageId: z.string(),
114
- events: z.array(InkAuditEventSchema),
115
- responseSignature: z.string(),
113
+ messageId: z.string().max(256),
114
+ // Bound both the event count and (via InkAuditEventSchema's per-field caps)
115
+ // each event, so an audit response from an untrusted witness cannot force
116
+ // unbounded buffering. A single response that needs more than this should
117
+ // page rather than return one giant array.
118
+ events: z.array(InkAuditEventSchema).max(1000),
119
+ responseSignature: z.string().max(256),
116
120
  });
117
121
  // ── Third-Party Audit Submit (INK Auditability §7.2) ──
118
122
  export const InkAuditSubmitSchema = z.object({
@@ -128,10 +132,10 @@ export const InkAuditSubmitSchema = z.object({
128
132
  export const InkAuditInclusionSchema = z.object({
129
133
  protocol: z.literal("ink/0.1"),
130
134
  type: z.literal("network.tulpa.audit_inclusion"),
131
- eventId: z.string(),
135
+ eventId: z.string().max(256),
132
136
  treeSize: z.number().int().positive(),
133
137
  leafIndex: z.number().int().min(0),
134
- rootHash: z.string(),
138
+ rootHash: z.string().max(128),
135
139
  /** Optional Merkle inclusion proof — array of 64-character lowercase hex
136
140
  * hash siblings on the path from the leaf to the root. Consumers that
137
141
  * verify proofs (third-party auditor clients) read this field; consumers
@@ -141,7 +145,7 @@ export const InkAuditInclusionSchema = z.object({
141
145
  * force the parser to allocate megabytes of garbage proof data. */
142
146
  inclusionProof: z.array(z.string().regex(/^[0-9a-f]{64}$/)).max(64).optional(),
143
147
  timestamp: z.string().datetime(),
144
- serviceSignature: z.string(),
148
+ serviceSignature: z.string().max(256),
145
149
  });
146
150
  // ── Introduction Receipt (INK Introduction Receipts Extension §4) ──
147
151
  export const InkIntroductionReceiptStatusSchema = z.enum([
@@ -154,22 +158,22 @@ export const InkIntroductionReceiptStatusSchema = z.enum([
154
158
  export const InkIntroductionReceiptSchema = z.object({
155
159
  protocol: z.literal("ink/0.1"),
156
160
  type: z.literal("network.tulpa.introduction_receipt"),
157
- id: z.string(),
158
- correlationId: z.string(),
159
- from: z.string(),
160
- to: z.string(),
161
- requesterDid: z.string(),
162
- introducerDid: z.string(),
163
- beneficiaryDid: z.string(),
164
- targetDid: z.string(),
161
+ id: z.string().max(256),
162
+ correlationId: z.string().max(256),
163
+ from: z.string().max(512),
164
+ to: z.string().max(512),
165
+ requesterDid: z.string().max(512),
166
+ introducerDid: z.string().max(512),
167
+ beneficiaryDid: z.string().max(512),
168
+ targetDid: z.string().max(512),
165
169
  status: InkIntroductionReceiptStatusSchema,
166
170
  purpose: z.string().min(1).max(500),
167
- nonce: z.string(),
171
+ nonce: z.string().max(256),
168
172
  timestamp: z.string().datetime(),
169
- relatedIntentId: z.string().optional(),
170
- relatedResolutionId: z.string().optional(),
173
+ relatedIntentId: z.string().max(256).optional(),
174
+ relatedResolutionId: z.string().max(256).optional(),
171
175
  note: z.string().max(500).optional(),
172
- contextHash: z.string().optional(),
173
- authorizationChainRef: z.string().optional(),
176
+ contextHash: z.string().max(256).optional(),
177
+ authorizationChainRef: z.string().max(512).optional(),
174
178
  expiresAt: z.string().datetime().optional(),
175
179
  });
@@ -32,12 +32,12 @@ export const ChallengeTypeSchema = z.enum([
32
32
  export const InkChallengeSchema = z.object({
33
33
  protocol: z.literal("ink/0.1"),
34
34
  type: z.literal("network.tulpa.challenge"),
35
- intentRef: z.string(),
35
+ intentRef: z.string().max(256),
36
36
  challengeType: ChallengeTypeSchema,
37
- fields: z.array(z.string()).optional(),
38
- availableWindows: z.array(z.string()).optional(),
39
- contextFields: z.array(z.string()).optional(),
40
- nonce: z.string(),
37
+ fields: z.array(z.string().max(256)).max(32).optional(),
38
+ availableWindows: z.array(z.string().max(64)).max(32).optional(),
39
+ contextFields: z.array(z.string().max(256)).max(32).optional(),
40
+ nonce: z.string().max(256),
41
41
  timestamp: z.string().datetime(),
42
42
  });
43
43
  // ── Rejection (network.tulpa.rejection) — Stage 2b ──
@@ -58,12 +58,12 @@ export const RejectionReasonSchema = z.enum([
58
58
  export const InkRejectionSchema = z.object({
59
59
  protocol: z.literal("ink/0.1"),
60
60
  type: z.literal("network.tulpa.rejection"),
61
- intentRef: z.string(),
61
+ intentRef: z.string().max(256),
62
62
  reason: RejectionReasonSchema,
63
63
  detail: z.string().max(500).optional(),
64
- retryAfter: z.string().optional(),
64
+ retryAfter: z.string().max(64).optional(),
65
65
  backoffHint: InkBackoffHintSchema.optional(),
66
- nonce: z.string(),
66
+ nonce: z.string().max(256),
67
67
  timestamp: z.string().datetime(),
68
68
  });
69
69
  // ── Resolution (network.tulpa.resolution) — Stage 3 ──
@@ -74,16 +74,16 @@ export const ResolutionOutcomeSchema = z.enum([
74
74
  "expired",
75
75
  ]);
76
76
  export const ResolutionDetailsSchema = z.object({
77
- scheduledAt: z.string().optional(),
78
- duration: z.string().optional(),
77
+ scheduledAt: z.string().max(64).optional(),
78
+ duration: z.string().max(64).optional(),
79
79
  }).passthrough();
80
80
  export const InkResolutionSchema = z.object({
81
81
  protocol: z.literal("ink/0.1"),
82
82
  type: z.literal("network.tulpa.resolution"),
83
- intentRef: z.string(),
83
+ intentRef: z.string().max(256),
84
84
  outcome: ResolutionOutcomeSchema,
85
85
  details: ResolutionDetailsSchema.optional(),
86
- counterpartyDid: z.string().optional(),
87
- nonce: z.string(),
86
+ counterpartyDid: z.string().max(512).optional(),
87
+ nonce: z.string().max(256),
88
88
  timestamp: z.string().datetime(),
89
89
  });
@@ -223,14 +223,14 @@ export declare const MessageProvenanceSchema: z.ZodOptional<z.ZodObject<{
223
223
  */
224
224
  export declare const INK_PROTOCOL_VERSIONS: readonly ["ink/0.1", "ink/0.2"];
225
225
  export declare const ProtocolVersionSchema: z.ZodEnum<{
226
- "ink/0.1": "ink/0.1";
227
226
  "ink/0.2": "ink/0.2";
227
+ "ink/0.1": "ink/0.1";
228
228
  }>;
229
229
  export type ProtocolVersion = z.infer<typeof ProtocolVersionSchema>;
230
230
  export declare const MessageEnvelopeSchema: z.ZodObject<{
231
231
  protocol: z.ZodEnum<{
232
- "ink/0.1": "ink/0.1";
233
232
  "ink/0.2": "ink/0.2";
233
+ "ink/0.1": "ink/0.1";
234
234
  }>;
235
235
  id: z.ZodString;
236
236
  correlationId: z.ZodString;
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { ProfileSnapshotSchema } from "./profile.js";
3
+ import { isWithinBounds } from "../crypto/sign.js";
3
4
  // --- Intent Types ---
4
5
  export const IntentTypeSchema = z.enum([
5
6
  "schedule_meeting",
@@ -191,6 +192,14 @@ export const MessageEnvelopeSchema = z.object({
191
192
  * Returns the validated message or throws a ZodError.
192
193
  */
193
194
  export function validateMessage(raw) {
195
+ // Bound the raw object's complexity BEFORE Zod walks it. A strict-mode parse
196
+ // must enumerate every key to reject unknowns, so a million-key object would
197
+ // otherwise burn hundreds of ms of CPU before being rejected. This also
198
+ // rejects JCS-unsafe numbers so a validated envelope is always one a
199
+ // canonicalizer can sign unambiguously.
200
+ if (!isWithinBounds(raw)) {
201
+ throw new Error("message exceeds complexity bounds");
202
+ }
194
203
  const envelope = MessageEnvelopeSchema.parse(raw);
195
204
  const payloadSchema = payloadSchemas[envelope.intent];
196
205
  // Validate payload strictly — reject unknown fields
package/docs/maturity.md CHANGED
@@ -28,7 +28,13 @@
28
28
 
29
29
  These hold across major version 0 (`ink/0.1` and `ink/0.2`):
30
30
 
31
- - Envelope structure (fields, canonicalization with JCS / RFC 8785)
31
+ - Envelope structure (fields, canonicalization with JCS / RFC 8785).
32
+ Signed bodies are restricted to JSON numbers that every conforming
33
+ canonicalizer serializes identically: non-finite values, negative zero,
34
+ and values whose shortest form uses exponential notation (for example
35
+ `1e21` or `1e-7`) are rejected at sign and verify time. INK payloads
36
+ carry only small integers and plain decimals, so this keeps the signed
37
+ bytes unambiguous across implementations.
32
38
  - Ed25519 signing base: `ink/0.1\nMETHOD\nPATH\nrecipientDid\nJCS(body)\ntimestamp`
33
39
  - Agent Card schema for `keys.signing` and `keys.encryption`
34
40
  - Key rotation authority rule (see `key-rotation-rule.md`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adastracomputing/ink",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Library and specification for the INK (Inter-agent Networking Kernel) protocol",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "Ad Astra Computing Inc.",
@@ -51,7 +51,8 @@
51
51
  "check:pack": "./scripts/check-pack.sh",
52
52
  "gen:body-vectors": "tsx scripts/gen-body-signature-vectors.ts",
53
53
  "prepack": "npm run build",
54
- "prepublishOnly": "npm run build"
54
+ "prepublishOnly": "npm run build",
55
+ "audit:all": "bash scripts/audit-npm-projects.sh"
55
56
  },
56
57
  "dependencies": {
57
58
  "@noble/curves": "^2.2.0",
@@ -61,14 +62,14 @@
61
62
  "zod": "^4.4.3"
62
63
  },
63
64
  "devDependencies": {
64
- "@cloudflare/workers-types": "^4.20260604.1",
65
- "@types/node": "^24.12.4",
66
- "@typescript-eslint/eslint-plugin": "^8.60.1",
67
- "@typescript-eslint/parser": "^8.60.0",
65
+ "@cloudflare/workers-types": "^4.20260610.1",
66
+ "@types/node": "^24.13.1",
67
+ "@typescript-eslint/eslint-plugin": "^8.61.0",
68
+ "@typescript-eslint/parser": "^8.61.0",
68
69
  "eslint": "^10.4.1",
69
70
  "tsx": "^4.22.4",
70
71
  "typescript": "^6.0.3",
71
- "vitest": "^4.1.7"
72
+ "vitest": "^4.1.8"
72
73
  },
73
74
  "keywords": [
74
75
  "ink",