@adastracomputing/ink 0.1.0-alpha.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/CHANGELOG.md +63 -0
- package/CODE_OF_CONDUCT.md +42 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +133 -0
- package/SECURITY.md +57 -0
- package/docs/key-rotation-rule.md +108 -0
- package/docs/logo.svg +8 -0
- package/docs/maturity.md +81 -0
- package/docs/threat-model.md +150 -0
- package/package.json +72 -0
- package/specs/ink-agent-containment-and-governance-extension-spec.md +508 -0
- package/specs/ink-auditability.md +652 -0
- package/specs/ink-authorization-chain.md +242 -0
- package/specs/ink-compatibility-policy.md +263 -0
- package/specs/ink-compliance-checklist.md +309 -0
- package/specs/ink-containment-phase1-implementation-spec.md +593 -0
- package/specs/ink-introduction-receipts-extension.md +501 -0
- package/specs/ink-key-rotation-spec.md +535 -0
- package/src/crypto/ink.ts +902 -0
- package/src/crypto/keys.ts +211 -0
- package/src/crypto/multi-key-verify.ts +170 -0
- package/src/crypto/sign.ts +155 -0
- package/src/crypto/verify.ts +1 -0
- package/src/discovery/agent-card.ts +508 -0
- package/src/index.ts +59 -0
- package/src/ink/checkpoint.ts +75 -0
- package/src/ink/discovery-gating.ts +147 -0
- package/src/ink/handshake-budget.ts +413 -0
- package/src/ink/receipts.ts +114 -0
- package/src/ink/transport-auth.ts +96 -0
- package/src/middleware/ink-auth.ts +263 -0
- package/src/models/agent-card.ts +63 -0
- package/src/models/ink-audit.ts +205 -0
- package/src/models/ink-handshake.ts +123 -0
- package/src/models/intent.ts +201 -0
- package/src/models/key-entry.ts +52 -0
- package/src/models/profile.ts +31 -0
- package/test-vectors/README.md +129 -0
- package/test-vectors/encryption.json +90 -0
- package/test-vectors/handshake.json +482 -0
- package/test-vectors/jcs.json +30 -0
- package/test-vectors/key-rotation.json +101 -0
- package/test-vectors/keys.json +32 -0
- package/test-vectors/receipts-and-audit.json +142 -0
- package/test-vectors/replay.json +88 -0
- package/test-vectors/signing.json +61 -0
- package/test-vectors/witness.json +394 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# INK Authorization Chain (draft extension)
|
|
2
|
+
|
|
3
|
+
**Status:** Draft
|
|
4
|
+
**Authors:** Ad Astra Computing
|
|
5
|
+
**Date:** 2026-03-19
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
The current INK v0.1 model supports single-hop delegation: a Tulpa issues a delegation token to an extension, and the extension can act on the Tulpa's behalf. The message envelope carries a `provenance` field that claims origin (`human`, `agent_approved`, `agent_autonomous`), but:
|
|
10
|
+
|
|
11
|
+
1. **No cryptographic proof of delegation**, the `provenance` field is a self-asserted claim. A malicious extension can forge `origin: "human"` on autonomous messages.
|
|
12
|
+
2. **No multi-hop chains**, if Extension A delegates to Service B which calls Service C, the recipient sees only the final hop. There's no way to trace the full authorization path.
|
|
13
|
+
3. **No recipient-verifiable authorization**, the recipient must trust the sender's provenance claim. The delegation token is between Tulpa↔Extension only; the recipient never sees it.
|
|
14
|
+
|
|
15
|
+
## Design
|
|
16
|
+
|
|
17
|
+
### 1. Delegation Proof (replaces self-asserted provenance)
|
|
18
|
+
|
|
19
|
+
Add a `delegationProof` field to the message envelope that cryptographically binds the delegation chain to the message.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
DelegationProofSchema = z.object({
|
|
23
|
+
// The delegation token (existing format: payload.signature)
|
|
24
|
+
delegationToken: z.string(),
|
|
25
|
+
|
|
26
|
+
// The issuing Tulpa's public key (so recipient can verify the token signature)
|
|
27
|
+
issuerPublicKey: z.string(),
|
|
28
|
+
|
|
29
|
+
// Extension's signature over the message ID, binding this proof to this message
|
|
30
|
+
// signatureBase = messageId + "\n" + intent + "\n" + JCS(payload)
|
|
31
|
+
extensionSignature: z.string(),
|
|
32
|
+
|
|
33
|
+
// Extension's public key (from the installation record / manifest)
|
|
34
|
+
extensionPublicKey: z.string(),
|
|
35
|
+
|
|
36
|
+
// Origin assertion, now signed by the extension rather than self-asserted
|
|
37
|
+
origin: ProvenanceOriginSchema,
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Verification by recipient:**
|
|
42
|
+
|
|
43
|
+
1. Decode the delegation token, verify its Ed25519 signature against `issuerPublicKey`
|
|
44
|
+
2. Check that `issuerPublicKey` matches the sender's known public key (from Agent Card or connection store)
|
|
45
|
+
3. Verify `extensionSignature` against `extensionPublicKey` to confirm the extension signed this specific message
|
|
46
|
+
4. Check that `extensionPublicKey` matches the `publicKeyMultibase` in the delegation token payload
|
|
47
|
+
5. Verify the delegation token hasn't expired and permissions/autonomy tier are sufficient for the intent
|
|
48
|
+
|
|
49
|
+
**Result:** the recipient now has cryptographic proof that:
|
|
50
|
+
- The Tulpa owner authorized this extension (delegation token)
|
|
51
|
+
- The extension actually produced this message (extension signature)
|
|
52
|
+
- The origin claim is bound to the extension's key (not forgeable)
|
|
53
|
+
|
|
54
|
+
### 2. Multi-Hop Delegation Chains
|
|
55
|
+
|
|
56
|
+
For cases where Extension A calls Service B which generates the message:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
DelegationChainSchema = z.object({
|
|
60
|
+
// Ordered list of delegation hops, from Tulpa → final actor
|
|
61
|
+
hops: z.array(DelegationHopSchema).min(1).max(5),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
DelegationHopSchema = z.object({
|
|
65
|
+
// Who delegated
|
|
66
|
+
delegator: z.string(), // did:key of delegator
|
|
67
|
+
delegatorPublicKey: z.string(),
|
|
68
|
+
|
|
69
|
+
// Who received delegation
|
|
70
|
+
delegate: z.string(), // did:key or extension ID
|
|
71
|
+
delegatePublicKey: z.string(),
|
|
72
|
+
|
|
73
|
+
// Scoped grant for this hop
|
|
74
|
+
permissions: z.array(PermissionSchema),
|
|
75
|
+
maxAutonomyTier: AutonomyTierSchema,
|
|
76
|
+
constraints: z.object({
|
|
77
|
+
intentTypes: z.array(IntentTypeSchema).optional(),
|
|
78
|
+
targetAgents: z.array(z.string()).optional(),
|
|
79
|
+
expiresAt: z.string().datetime(),
|
|
80
|
+
maxMessages: z.number().int().positive().optional(),
|
|
81
|
+
allowedTransports: z.array(z.enum([
|
|
82
|
+
"ink_http", "ink_ws", "extension_api",
|
|
83
|
+
"voice", "line_phone", "human_review_queue",
|
|
84
|
+
])).optional(),
|
|
85
|
+
}),
|
|
86
|
+
|
|
87
|
+
// Delegator's signature over (delegate + permissions + constraints)
|
|
88
|
+
signature: z.string(),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Chain validation rules:**
|
|
93
|
+
- Each hop's permissions must be a **subset** of the previous hop's permissions (no privilege escalation)
|
|
94
|
+
- Each hop's `maxAutonomyTier` must be **≤** the previous hop's tier
|
|
95
|
+
- Each hop's `expiresAt` must be **≤** the previous hop's expiration
|
|
96
|
+
- Each hop's `allowedTransports` must be a **subset** of the previous hop's transports (transport attenuation, see INK Containment §7)
|
|
97
|
+
- Maximum chain depth: 5 hops (prevents unbounded delegation)
|
|
98
|
+
- The first hop must be signed by the Tulpa owner's key
|
|
99
|
+
|
|
100
|
+
**Transport scoping (INK Containment §7):**
|
|
101
|
+
- If `allowedTransports` is omitted on a v0.3+ token, it defaults to `["ink_http"]` (least privilege)
|
|
102
|
+
- Legacy tokens (no `tokenVersion` field) receive a permissive default of `["ink_http", "extension_api", "voice", "line_phone"]` during a 90-day migration window
|
|
103
|
+
- Messages arriving on a transport not in the token's `allowedTransports` are rejected with `transport_scope_violation`
|
|
104
|
+
|
|
105
|
+
### 3. Envelope Changes
|
|
106
|
+
|
|
107
|
+
Update `MessageEnvelopeSchema` for v0.3:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
MessageEnvelopeSchema = z.object({
|
|
111
|
+
protocol: z.literal("tulpa/0.2"), // bump when implemented
|
|
112
|
+
id: z.string(),
|
|
113
|
+
correlationId: z.string(),
|
|
114
|
+
createdAt: z.string(),
|
|
115
|
+
expiresAt: z.string().optional(),
|
|
116
|
+
from: z.string(),
|
|
117
|
+
to: z.string(),
|
|
118
|
+
intent: IntentTypeSchema,
|
|
119
|
+
payload: z.unknown(),
|
|
120
|
+
signature: z.string(),
|
|
121
|
+
|
|
122
|
+
// v0.3: replaces the old `provenance` field
|
|
123
|
+
delegationProof: DelegationProofSchema.optional(),
|
|
124
|
+
delegationChain: DelegationChainSchema.optional(),
|
|
125
|
+
|
|
126
|
+
// Deprecated, kept for backward compat during migration
|
|
127
|
+
provenance: MessageProvenanceSchema,
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Backward compatibility:**
|
|
132
|
+
- Recipients that don't understand `delegationProof` fall back to `provenance` (existing behavior)
|
|
133
|
+
- Senders include both fields during the migration period
|
|
134
|
+
- After migration period, `provenance` becomes optional and `delegationProof` is required for extension-originated messages
|
|
135
|
+
|
|
136
|
+
### 4. Agent Card Capability Advertisement
|
|
137
|
+
|
|
138
|
+
Extensions that support chain verification advertise it:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
capabilities: {
|
|
142
|
+
intentsAccepted: [...],
|
|
143
|
+
intentsSent: [...],
|
|
144
|
+
delegationProof: true, // "I include delegation proofs on extension messages"
|
|
145
|
+
delegationChainDepth: 2, // "I support up to 2-hop chains"
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 5. Autonomy Tier Enforcement
|
|
150
|
+
|
|
151
|
+
The current `maxAutonomyTier` in the delegation token becomes **enforceable by the recipient**:
|
|
152
|
+
|
|
153
|
+
| Origin | Required Tier | Recipient Can Verify? |
|
|
154
|
+
|--------|--------------|----------------------|
|
|
155
|
+
| `human` | any | Yes, extension signature proves the extension saw user input |
|
|
156
|
+
| `agent_approved` | `social` or lower | Yes, delegation token tier checked |
|
|
157
|
+
| `agent_autonomous` | `transactional` only | Yes, delegation token tier checked |
|
|
158
|
+
|
|
159
|
+
If the delegation proof shows `origin: agent_autonomous` but the delegation token's `maxAutonomyTier` is `personal`, the recipient rejects the message.
|
|
160
|
+
|
|
161
|
+
### 6. Revocation
|
|
162
|
+
|
|
163
|
+
Existing revocation (token hash stored in installation, checked on each request) works for the Tulpa↔Extension hop. For multi-hop chains:
|
|
164
|
+
|
|
165
|
+
- Each delegator maintains a revocation list (set of revoked delegate keys)
|
|
166
|
+
- The `delegationChain` includes a `revocationEndpoint` per hop
|
|
167
|
+
- Recipients can optionally check revocation endpoints (non-blocking, cached)
|
|
168
|
+
- Revocation is **eventually consistent**, a revoked chain may be accepted for up to the cache TTL (default: 5 min, matching the replay window)
|
|
169
|
+
|
|
170
|
+
## Migration Path
|
|
171
|
+
|
|
172
|
+
1. Ship `delegationProof` generation in the extension middleware
|
|
173
|
+
2. Ship `delegationProof` verification in the integrator-side request pipeline (optional, don't reject messages without it yet)
|
|
174
|
+
3. After adoption threshold (e.g. 90% of messages include proofs), make `delegationProof` required for extension-originated messages
|
|
175
|
+
4. Deprecate `provenance` field
|
|
176
|
+
|
|
177
|
+
## Prior Art and Research
|
|
178
|
+
|
|
179
|
+
This design was validated against established delegation protocols. Key influences:
|
|
180
|
+
|
|
181
|
+
### UCAN (User Controlled Authorization Networks)
|
|
182
|
+
UCAN is the closest analog to INK's delegation model. Both use DID-based identities with Ed25519 signatures and chained capability tokens. Key lessons adopted:
|
|
183
|
+
|
|
184
|
+
- **Separate delegation from invocation.** UCAN 1.0 distinguishes "I was granted this capability" from "I am now exercising it." INK adopts this: the `delegationToken` is the grant; the `extensionSignature` over the specific message is the invocation. This prevents confused deputy attacks where a delegation token is accidentally replayed as an action.
|
|
185
|
+
- **Capability attenuation via partial ordering.** UCAN defines a partial order on capabilities where each hop can only narrow scope. INK uses the same approach: permission-subset checking and autonomy tier ≤ comparison. Given INK already has a flat permission enum (`PermissionSchema`), subset checking is straightforward.
|
|
186
|
+
- **CID-referenced vs. inlined proofs.** Early UCANs inlined parent tokens, causing exponential growth. INK avoids this by having each hop carry only its own signature and constraints, the chain is a flat array, not nested tokens.
|
|
187
|
+
- **No built-in revocation.** UCAN relies on short-lived tokens rather than revocation infrastructure. INK should adopt the same stance (see Revocation section update below).
|
|
188
|
+
|
|
189
|
+
### ZCAP-LD (Authorization Capabilities for Linked Data)
|
|
190
|
+
W3C CCG's capability system uses additive caveats instead of UCAN's partial order. Each delegation can only ADD restrictions, never remove them. This is simpler to implement but less expressive. INK's permission-subset model is more aligned with UCAN's approach, which better fits the structured permission enum.
|
|
191
|
+
|
|
192
|
+
### OAuth 2.0 Token Exchange (RFC 8693)
|
|
193
|
+
The `act` (actor) and `may_act` (pre-authorization) claims provide a clean representation of "who is acting on behalf of whom." INK adopts this pattern:
|
|
194
|
+
- The delegation proof's `origin` + `extensionPublicKey` serves as the `act` claim
|
|
195
|
+
- The delegation token itself serves as the `may_act` pre-authorization
|
|
196
|
+
- RFC 8693 supports nested `act` claims for multi-hop, which maps to INK's `hops` array
|
|
197
|
+
|
|
198
|
+
Key difference: RFC 8693 requires a centralized STS (Security Token Service). INK is decentralized, verification is self-contained using the chain signatures.
|
|
199
|
+
|
|
200
|
+
### SPIFFE/SPIRE
|
|
201
|
+
SPIFFE separates identity from authorization entirely. Relevant pattern: **short-lived credentials with automatic rotation** avoid the revocation problem. SPIFFE SVIDs typically expire in hours, not days. INK should adopt this for delegation tokens (see updated recommendation below).
|
|
202
|
+
|
|
203
|
+
### W3C DID + Verifiable Credentials
|
|
204
|
+
INK already uses `did:key` for agent identity, which is the right choice, self-certifying, no resolution infrastructure needed. The DID Document's `capabilityDelegation` verification relationship was designed to support exactly this kind of delegation. INK could optionally reference it for interop with the broader DID ecosystem.
|
|
205
|
+
|
|
206
|
+
### ActivityPub / AT Protocol
|
|
207
|
+
Neither has meaningful delegation chains. ActivityPub bots are independent actors with no protocol-level delegation proof. AT Protocol has scoped app passwords and PDS-mediated service auth (single-hop). INK's delegation chain is a significant advancement over both.
|
|
208
|
+
|
|
209
|
+
### Design Decisions Informed by Research
|
|
210
|
+
|
|
211
|
+
| Decision | Rationale | Prior Art |
|
|
212
|
+
|----------|-----------|-----------|
|
|
213
|
+
| Flat hop array (not nested tokens) | Avoids exponential size growth | UCAN 1.0 CID-referenced proofs |
|
|
214
|
+
| Permission subset checking | Fits INK's flat enum model | UCAN partial order |
|
|
215
|
+
| Separate delegation from invocation | Prevents confused deputy | UCAN 1.0 delegation/invocation split |
|
|
216
|
+
| `origin` bound to extension signature | Prevents origin forgery | RFC 8693 `act` claim binding |
|
|
217
|
+
| Short-lived tokens over revocation lists | Simpler, more reliable in decentralized systems | SPIFFE short-lived SVIDs |
|
|
218
|
+
| Max 5 hops | 2-3 is typical in practice; 5 allows headroom | No protocol sets a hard limit |
|
|
219
|
+
|
|
220
|
+
## Updated Recommendations (Post-Research)
|
|
221
|
+
|
|
222
|
+
### Shorter Token TTLs
|
|
223
|
+
Based on SPIFFE's approach and UCAN's lesson that revocation is unsolved in pure capability models:
|
|
224
|
+
- **Default TTL: 1-4 hours** (not 48 hours as currently allowed)
|
|
225
|
+
- Extensions should auto-renew tokens before expiry
|
|
226
|
+
- The `ttlHours` parameter in the token endpoint should cap at 24 hours for standard extensions
|
|
227
|
+
- 48-hour tokens should require elevated review status
|
|
228
|
+
|
|
229
|
+
### Invocation Binding
|
|
230
|
+
Adopt UCAN 1.0's delegation/invocation separation explicitly:
|
|
231
|
+
- The `delegationToken` proves "I was granted this capability"
|
|
232
|
+
- The `extensionSignature` over `messageId + intent + JCS(payload)` proves "I am now exercising it on this specific message"
|
|
233
|
+
- Recipients MUST verify both, a valid delegation token alone is not sufficient
|
|
234
|
+
|
|
235
|
+
## Security Considerations
|
|
236
|
+
|
|
237
|
+
- **Key rotation:** if a Tulpa rotates keys, outstanding delegation tokens become invalid. Extensions must re-request tokens after key rotation.
|
|
238
|
+
- **Stolen extension keys:** the delegation token is scoped (permissions, layers, expiry). A stolen key can only act within the granted scope until the token expires or is revoked. With the recommended 1-4 hour TTL, the exposure window is small.
|
|
239
|
+
- **Chain depth attacks:** capped at 5 hops. Each hop strictly narrows scope. No established protocol requires more than 3 in practice.
|
|
240
|
+
- **Clock skew:** delegation token expiry uses the same ±5 min window as INK replay protection.
|
|
241
|
+
- **Confused deputy:** the delegation/invocation separation (per UCAN 1.0) prevents a delegation token from being misused as a direct action. The extension must produce a fresh signature for each message.
|
|
242
|
+
- **Replay of delegation proofs:** the `extensionSignature` binds to a specific `messageId`, so replaying a delegation proof on a different message fails verification.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# INK Compatibility and Versioning Policy
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
Draft, v1 stabilization
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
This document defines how INK versions its wire protocol, when changes require version bumps, how optional capabilities are advertised, and how implementations handle unknown fields and message types.
|
|
9
|
+
|
|
10
|
+
This is the normative compatibility contract. Any change to the INK wire format MUST be evaluated against this policy before merging.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Protocol Version
|
|
15
|
+
|
|
16
|
+
INK uses a single protocol version string in every message envelope, receipt, audit event and handshake message.
|
|
17
|
+
|
|
18
|
+
Current version: `ink/0.1`
|
|
19
|
+
|
|
20
|
+
The version string appears in the `protocol` field of every top-level INK object and in the first line of every signature base.
|
|
21
|
+
|
|
22
|
+
### 1.1 Version Format
|
|
23
|
+
|
|
24
|
+
`ink/<major>.<minor>`
|
|
25
|
+
|
|
26
|
+
- **Major**: incremented for incompatible wire changes
|
|
27
|
+
- **Minor**: incremented for backward-compatible additions
|
|
28
|
+
|
|
29
|
+
Implementations MUST reject messages with an unrecognized major version. Implementations SHOULD accept messages with a recognized major version and an unrecognized minor version by ignoring unknown optional fields.
|
|
30
|
+
|
|
31
|
+
### 1.2 Audit Version
|
|
32
|
+
|
|
33
|
+
Audit events use a separate version string: `ink-audit/1`
|
|
34
|
+
|
|
35
|
+
This version follows the same policy but is independent of the transport protocol version. An audit version bump does not require a transport version bump or vice versa.
|
|
36
|
+
|
|
37
|
+
### 1.3 Wire-namespace prefix (`network.tulpa.*`)
|
|
38
|
+
|
|
39
|
+
Message `type` fields throughout INK v0.1 carry the prefix
|
|
40
|
+
`network.tulpa.*` (e.g. `network.tulpa.encrypted`, `network.tulpa.challenge`).
|
|
41
|
+
This is a **historical artifact** of INK's origin at Tulpa and is *not*
|
|
42
|
+
intended to imply Tulpa ownership of the protocol, Ad Astra Computing
|
|
43
|
+
stewards INK; Tulpa is one product built on it.
|
|
44
|
+
|
|
45
|
+
The prefix is preserved in v0.x for wire compatibility with deployed
|
|
46
|
+
implementations. A future major version MAY introduce a vendor-neutral
|
|
47
|
+
prefix (e.g. `network.ink.*`) and define a transition policy. Until then,
|
|
48
|
+
conforming implementations MUST emit and accept `network.tulpa.*` types as
|
|
49
|
+
specified.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 2. Compatibility Rules
|
|
54
|
+
|
|
55
|
+
### 2.1 Breaking Changes (Major Version Bump Required)
|
|
56
|
+
|
|
57
|
+
The following changes MUST trigger a major version bump:
|
|
58
|
+
|
|
59
|
+
| Change | Reason |
|
|
60
|
+
|--------|--------|
|
|
61
|
+
| Signature base format change | Breaks all existing verification |
|
|
62
|
+
| Required field added to existing message type | Old implementations reject new messages |
|
|
63
|
+
| Required field removed from existing message type | New implementations reject old messages |
|
|
64
|
+
| Field type changed (e.g. string → number) | Deserialization breaks |
|
|
65
|
+
| Cryptographic algorithm change (signing, encryption, hashing) | Interop breaks silently |
|
|
66
|
+
| JCS canonicalization behavior change | Signature verification breaks |
|
|
67
|
+
| Replay protection parameter change (window size, nonce format) | Messages rejected incorrectly |
|
|
68
|
+
| Auth header scheme change | Transport auth breaks |
|
|
69
|
+
| Key status semantics change | Verification behavior changes |
|
|
70
|
+
|
|
71
|
+
### 2.2 Backward-Compatible Changes (Minor Version Bump)
|
|
72
|
+
|
|
73
|
+
The following changes MAY be made under the same major version:
|
|
74
|
+
|
|
75
|
+
| Change | Constraint |
|
|
76
|
+
|--------|-----------|
|
|
77
|
+
| New optional field on existing message type | Receivers MUST ignore unknown fields |
|
|
78
|
+
| New intent type | Receivers respond with `unsupported_intent` rejection |
|
|
79
|
+
| New receipt disposition | Receivers MUST accept unknown dispositions gracefully |
|
|
80
|
+
| New audit event type | Processors MUST ignore unknown event types |
|
|
81
|
+
| New handshake challenge type | Receivers respond with appropriate rejection |
|
|
82
|
+
| New key algorithm added to Agent Card | Receivers skip keys with unknown algorithms |
|
|
83
|
+
| New optional capability in Agent Card | Receivers ignore unknown capability blocks |
|
|
84
|
+
|
|
85
|
+
### 2.3 Non-Version Changes
|
|
86
|
+
|
|
87
|
+
The following changes do not require a version bump:
|
|
88
|
+
|
|
89
|
+
- Documentation clarifications that do not change wire behavior
|
|
90
|
+
- New test vectors for existing behavior
|
|
91
|
+
- Implementation bug fixes that bring behavior into spec compliance
|
|
92
|
+
- New optional Agent Card metadata fields outside `keys` and `capabilities`
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Unknown Fields and Types
|
|
97
|
+
|
|
98
|
+
### 3.1 Unknown Fields
|
|
99
|
+
|
|
100
|
+
Implementations MUST preserve unknown fields during canonicalization (JCS handles this correctly). Implementations MUST NOT reject messages containing unknown fields.
|
|
101
|
+
|
|
102
|
+
### 3.2 Unknown Message Types
|
|
103
|
+
|
|
104
|
+
When a receiver encounters an unknown `type` value in a handshake or protocol message, it MUST respond with a `network.tulpa.rejection` with reason `unsupported_intent`.
|
|
105
|
+
|
|
106
|
+
When a receiver encounters an unknown `intent` in a message envelope, it MUST respond with a rejection and SHOULD send a `received` receipt if receipt support is advertised.
|
|
107
|
+
|
|
108
|
+
### 3.3 Unknown Key Algorithms
|
|
109
|
+
|
|
110
|
+
When building a candidate key set for verification, implementations MUST skip keys with unrecognized `algorithm` values. Verification proceeds with the remaining candidates.
|
|
111
|
+
|
|
112
|
+
### 3.4 Unknown Audit Event Types
|
|
113
|
+
|
|
114
|
+
Audit chain processors MUST NOT break on unknown event types. Unknown events MUST be included in hash chain computation to preserve chain integrity.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 4. Capability Advertisement
|
|
119
|
+
|
|
120
|
+
### 4.1 Mechanism
|
|
121
|
+
|
|
122
|
+
Agent Cards are the canonical mechanism for capability advertisement. The `capabilities` block declares what an agent supports.
|
|
123
|
+
|
|
124
|
+
Implementations MUST NOT assume capabilities that are not advertised.
|
|
125
|
+
|
|
126
|
+
### 4.2 Required Capabilities
|
|
127
|
+
|
|
128
|
+
Every base INK v0.1 agent MUST support:
|
|
129
|
+
- Transport signing (`INK-Ed25519` auth header)
|
|
130
|
+
- Replay protection (timestamp + nonce)
|
|
131
|
+
- At least one intent type
|
|
132
|
+
- Agent Card discovery at `GET /ink/v1/{agentId}/agent.json`
|
|
133
|
+
|
|
134
|
+
### 4.3 Optional Capabilities
|
|
135
|
+
|
|
136
|
+
The following are advertised per-agent and MUST NOT be assumed:
|
|
137
|
+
|
|
138
|
+
| Capability | Agent Card field | Default if absent |
|
|
139
|
+
|-----------|-----------------|-------------------|
|
|
140
|
+
| Receipt support | `capabilities.receipts` | No receipts |
|
|
141
|
+
| Bilateral audit exchange | `capabilities.auditExchange` | Not supported |
|
|
142
|
+
| Third-party witness | `capabilities.thirdPartyAudit` | Not supported |
|
|
143
|
+
| Encryption | Presence of encryption keys in `keys.encryption` | Encryption not supported |
|
|
144
|
+
| Key rotation | Presence of `keys.signing` array | Legacy single-key mode |
|
|
145
|
+
| Handshake stages | Presence of `/challenge`, `/rejection`, `/resolution` endpoints | Not supported |
|
|
146
|
+
|
|
147
|
+
### 4.4 Capability Negotiation
|
|
148
|
+
|
|
149
|
+
INK does not use explicit capability negotiation handshakes. Capabilities are discovered by reading the Agent Card before first contact.
|
|
150
|
+
|
|
151
|
+
If a sender requires a capability the receiver does not advertise (e.g. encryption for `schedule_meeting`), the sender MUST NOT send the message. It MAY inform the user that the recipient does not support the required capability.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 5. Wire Format Stability
|
|
156
|
+
|
|
157
|
+
### 5.1 Signature Base
|
|
158
|
+
|
|
159
|
+
The signature base format is the most stability-critical element of INK:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
ink/<version>\n<METHOD>\n<PATH>\n<recipientDid>\n<JCS(body)>\n<timestamp>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Any change to this format, field order, separator, domain prefix, canonicalization algorithm, is a breaking change.
|
|
166
|
+
|
|
167
|
+
### 5.2 Auth Header
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
INK-Ed25519 <base64url(signature)> [keyId=<keyId>]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The `keyId` parameter is optional and was added in a backward-compatible way. The regex `/^INK-Ed25519\s+(\S+)(?:\s+keyId=(\S+))?$/` accepts both forms.
|
|
174
|
+
|
|
175
|
+
Future parameters MUST use the same `key=value` syntax after the signature, space-separated.
|
|
176
|
+
|
|
177
|
+
### 5.3 Encoding Conventions
|
|
178
|
+
|
|
179
|
+
These conventions are fixed for the lifetime of INK v1:
|
|
180
|
+
|
|
181
|
+
| Data | Encoding |
|
|
182
|
+
|------|----------|
|
|
183
|
+
| Ed25519/X25519 signatures | base64url (no padding, RFC 4648 §5) |
|
|
184
|
+
| Public keys in Agent Card | multibase (base58btc with multicodec prefix) |
|
|
185
|
+
| Hashes (SHA-256) | lowercase hex |
|
|
186
|
+
| Timestamps | ISO 8601 (e.g. `2026-03-25T12:00:00Z`) |
|
|
187
|
+
| Nonces | UUID without hyphens (hex) or base64url |
|
|
188
|
+
| AES-GCM nonces | base64url |
|
|
189
|
+
| Body canonicalization | JCS (RFC 8785) |
|
|
190
|
+
|
|
191
|
+
### 5.4 Multicodec Prefixes
|
|
192
|
+
|
|
193
|
+
| Algorithm | Prefix bytes |
|
|
194
|
+
|-----------|-------------|
|
|
195
|
+
| Ed25519 | `0xed 0x01` |
|
|
196
|
+
| X25519 | `0xec 0x01` |
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 6. Deprecation Policy
|
|
201
|
+
|
|
202
|
+
### 6.1 Feature Deprecation
|
|
203
|
+
|
|
204
|
+
A feature or field MAY be deprecated by:
|
|
205
|
+
1. Documenting it as deprecated in the spec
|
|
206
|
+
2. Adding a note to the compliance checklist
|
|
207
|
+
3. Continuing to support it for at least one major version
|
|
208
|
+
|
|
209
|
+
### 6.2 Version Sunset
|
|
210
|
+
|
|
211
|
+
A major version MAY be sunset after:
|
|
212
|
+
1. The successor version has been stable for at least 6 months
|
|
213
|
+
2. All known active implementations have been notified
|
|
214
|
+
3. A documented migration path exists
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 7. Extension Points
|
|
219
|
+
|
|
220
|
+
### 7.1 Intent Types
|
|
221
|
+
|
|
222
|
+
New intent types can be added without a version bump. They follow the same envelope format and signing rules.
|
|
223
|
+
|
|
224
|
+
Custom intent types SHOULD use reverse-domain naming (e.g. `network.tulpa.custom_intent`) to avoid collisions.
|
|
225
|
+
|
|
226
|
+
### 7.2 Audit Event Types
|
|
227
|
+
|
|
228
|
+
New audit event types can be added without a version bump. They use the same `InkAuditEvent` envelope and chain mechanics.
|
|
229
|
+
|
|
230
|
+
### 7.3 Agent Card Extensions
|
|
231
|
+
|
|
232
|
+
Agent Cards MAY include additional top-level or nested fields. Unknown fields MUST be ignored by consumers.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 8. Implementation Guidance
|
|
237
|
+
|
|
238
|
+
### 8.1 Version Checking
|
|
239
|
+
|
|
240
|
+
Implementations MUST check `protocol` on every inbound message. The check SHOULD compare only the major version for forward compatibility:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
const [major] = protocol.split("/")[1].split(".");
|
|
244
|
+
if (major !== "0") reject("unsupported_protocol_version");
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 8.2 Forward Compatibility
|
|
248
|
+
|
|
249
|
+
Implementations SHOULD be written to tolerate:
|
|
250
|
+
- Unknown optional fields on any message type
|
|
251
|
+
- Unknown intent types (reject gracefully)
|
|
252
|
+
- Unknown audit event types (include in chain, skip processing)
|
|
253
|
+
- Unknown key algorithms (skip during verification)
|
|
254
|
+
- Unknown Agent Card fields (ignore)
|
|
255
|
+
|
|
256
|
+
### 8.3 Strict Validation
|
|
257
|
+
|
|
258
|
+
Implementations MUST strictly validate:
|
|
259
|
+
- Signature base construction (exact format)
|
|
260
|
+
- Timestamp freshness windows
|
|
261
|
+
- Required fields on all message types
|
|
262
|
+
- Key status semantics (active/retired/revoked)
|
|
263
|
+
- Hash chain integrity for audit events
|