@adastracomputing/ink 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +43 -5
- package/CODE_OF_CONDUCT.md +1 -1
- package/README.md +7 -5
- package/SECURITY.md +1 -1
- package/bin/ink.mjs +51 -0
- package/bin/verify-inclusion-impl.mjs +483 -0
- package/docs/maturity.md +3 -3
- package/docs/threat-model.md +1 -1
- package/package.json +7 -3
- package/specs/ink-auditability.md +37 -12
- package/specs/ink-compliance-checklist.md +9 -1
- package/src/audit/inclusion-receipt.ts +604 -0
- package/src/crypto/ink.ts +173 -29
- package/src/index.ts +14 -0
- package/src/models/ink-audit.ts +8 -8
package/docs/maturity.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
implementations should report discrepancies as issues.
|
|
18
18
|
- The protocol is in use by one production integrator (Tulpa). That is
|
|
19
19
|
one data point, not a guarantee of robustness at scale.
|
|
20
|
-
- The
|
|
20
|
+
- The library in `src/` runs on any runtime providing
|
|
21
21
|
standard Web Crypto (`crypto.subtle`) and `fetch`, modern Node, Deno,
|
|
22
22
|
Bun, and edge runtimes. Browser use is feasible but not exercised by
|
|
23
23
|
the maintainers.
|
|
@@ -55,7 +55,7 @@ Pre-1.0 releases follow `0.Y.Z` semantics:
|
|
|
55
55
|
|
|
56
56
|
- `0.Y.0`, Minor version bump indicates a wire-format change. Receivers
|
|
57
57
|
must support at least one prior minor during a transition window.
|
|
58
|
-
- `0.Y.Z` (Z > 0), Patch bumps fix bugs in the
|
|
58
|
+
- `0.Y.Z` (Z > 0), Patch bumps fix bugs in the library
|
|
59
59
|
and update test vectors where needed. They do not change wire format.
|
|
60
60
|
|
|
61
61
|
Breaking changes before v1.0 will be announced in the repository
|
|
@@ -71,7 +71,7 @@ be a real incident:
|
|
|
71
71
|
falls inside the in-scope protections and you accept the out-of-scope
|
|
72
72
|
limits.
|
|
73
73
|
2. Run `../test-vectors/*` against your implementation.
|
|
74
|
-
3. Fuzz your envelope parser. The
|
|
74
|
+
3. Fuzz your envelope parser. The library's tests are
|
|
75
75
|
not a substitute.
|
|
76
76
|
4. Pen-test the rotation and revocation flows specifically. The
|
|
77
77
|
authority rule is the single most security-sensitive piece and the
|
package/docs/threat-model.md
CHANGED
|
@@ -80,7 +80,7 @@ bounds the damage window but does not eliminate it. Key custody is out of
|
|
|
80
80
|
scope.
|
|
81
81
|
|
|
82
82
|
### Malicious marketplace extensions (if you integrate one)
|
|
83
|
-
The
|
|
83
|
+
The library does not include an extension/marketplace
|
|
84
84
|
layer. A product that integrates INK and adds a delegation-token layer
|
|
85
85
|
(for third-party agents to act on behalf of users) must design its own
|
|
86
86
|
trust model for the marketplace, manifest review, and capability
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adastracomputing/ink",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
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.",
|
|
7
7
|
"repository": {
|
|
@@ -26,8 +26,12 @@
|
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=22"
|
|
28
28
|
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"ink": "./bin/ink.mjs"
|
|
31
|
+
},
|
|
29
32
|
"files": [
|
|
30
33
|
"src/",
|
|
34
|
+
"bin/",
|
|
31
35
|
"specs/",
|
|
32
36
|
"docs/",
|
|
33
37
|
"test-vectors/",
|
|
@@ -67,6 +71,6 @@
|
|
|
67
71
|
"ed25519",
|
|
68
72
|
"atproto",
|
|
69
73
|
"agent-to-agent",
|
|
70
|
-
"
|
|
74
|
+
"transparency-log"
|
|
71
75
|
]
|
|
72
76
|
}
|
|
@@ -461,9 +461,9 @@ The audit service:
|
|
|
461
461
|
1. Accepts signed `InkAuditEvent` submissions from agents
|
|
462
462
|
2. Appends them to a **Merkle tree** (not just a hash chain, enables efficient inclusion proofs)
|
|
463
463
|
3. Returns a **signed inclusion receipt** proving the event was recorded at a specific tree position and timestamp
|
|
464
|
-
4. Serves **inclusion proofs** and **
|
|
464
|
+
4. Serves **inclusion proofs** on demand (per-submission via the inclusion receipt and per-query via the signed `audit_query_response` envelope). **Consistency proofs** between two arbitrary checkpoints are not in scope for alpha.3; consistency-proof verification against external `tlog-witness` cosigners (§7.0) is the alpha.3 mitigation against split-view attacks.
|
|
465
465
|
|
|
466
|
-
The service CANNOT forge events
|
|
466
|
+
The service CANNOT forge events that verifiers will accept, because every returned event carries the submitting agent's Ed25519 `agentSignature` and §7.3 verifiers re-check it against the agent's published keys. A witness that commits a fabricated event_json into its Merkle tree can produce a valid inclusion proof, but verifiers will reject the response when the agent signature fails to validate. (Verifiers that walk Merkle proofs without checking `agentSignature` lose this guarantee; see §7.5.) The service CAN prove:
|
|
467
467
|
- That a specific event was submitted at a specific time (inclusion)
|
|
468
468
|
- That the log is append-only and no events have been removed (consistency)
|
|
469
469
|
- That two parties submitted conflicting events for the same message (conflict detection)
|
|
@@ -531,41 +531,66 @@ Authorization: INK-Ed25519 <signature>
|
|
|
531
531
|
}
|
|
532
532
|
```
|
|
533
533
|
|
|
534
|
-
**Response includes Merkle inclusion proofs:**
|
|
534
|
+
**Response includes Merkle inclusion proofs and is signed by the witness:**
|
|
535
535
|
|
|
536
536
|
```json
|
|
537
537
|
{
|
|
538
538
|
"protocol": "ink/0.1",
|
|
539
|
-
"type": "network.tulpa.
|
|
539
|
+
"type": "network.tulpa.audit_query_response",
|
|
540
|
+
"serviceDid": "did:web:witness.example.com",
|
|
540
541
|
"messageId": "msg-123",
|
|
541
|
-
"
|
|
542
|
+
"requester": "did:plc:requester",
|
|
543
|
+
"events": [ /* InkAuditEvent[] visible to the requester */ ],
|
|
542
544
|
"proofs": [
|
|
543
545
|
{
|
|
544
546
|
"eventId": "01JBTEST0001",
|
|
545
547
|
"leafIndex": 48290,
|
|
546
|
-
"
|
|
547
|
-
"treeSize": 48291,
|
|
548
|
-
"rootHash": "<SHA-256 hex>"
|
|
548
|
+
"inclusionProof": ["<hash>", "<hash>", "..."]
|
|
549
549
|
}
|
|
550
550
|
],
|
|
551
|
-
"
|
|
551
|
+
"treeSize": 48291,
|
|
552
|
+
"rootHash": "<SHA-256 hex of Merkle tree root at response time>",
|
|
553
|
+
"timestamp": "2026-03-19T13:00:01Z",
|
|
554
|
+
"serviceSignature": "<Ed25519, see canonical format below>"
|
|
552
555
|
}
|
|
553
556
|
```
|
|
554
557
|
|
|
558
|
+
**Canonical signature format.** `serviceSignature` is an Ed25519 signature over the bytes:
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
"ink/audit-query-response/v1\n" || JCS(response-fields-without-serviceSignature)
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
The signed payload binds the witness's `serviceDid`, the `messageId` requested, the authenticated `requester` whose access-control scope produced the result, every returned event, every inclusion proof, the witness's `treeSize` and `rootHash` at response time and the `timestamp`. The `proofs` array has one entry per event, keyed by `eventId`; verifiers MUST reject if proofs do not match events one-to-one. `treeSize` and `rootHash` apply uniformly to every proof. The `requester` binding prevents cross-requester replay: a witness response generated for Alice cannot be presented to Bob as Bob's authoritative view of the same `messageId`. Verifiers MUST check `requester` equals the locally authenticated requester before treating the response as their own scoped view.
|
|
565
|
+
|
|
566
|
+
Per-event scope: a signed envelope binds `messageId` and `requester` but says nothing about the event objects until verifiers look inside them. To prevent a witness or a tampering intermediary from smuggling out-of-scope events into a signed response, verifiers MUST reject any response where, for any returned event, `event.messageId` differs from the envelope `messageId`, OR the envelope `requester` is neither `event.agentId` nor `event.counterpartyId`. Witnesses SHOULD reject the same conditions at signing time as defense in depth against storage corruption.
|
|
567
|
+
|
|
568
|
+
Empty-log responses: a witness that has not yet committed any leaves reports `treeSize: 0` and `rootHash` equal to SHA-256 of the empty string (`e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`). A signed response with `treeSize: 0` is legitimate but MUST also have empty `events`, empty `proofs` and the empty-tree `rootHash`. Verifiers MUST reject any `treeSize: 0` response that deviates from this shape.
|
|
569
|
+
|
|
570
|
+
Per-event agent signatures: Merkle validity alone does NOT prove a returned event was produced by the agent named in `event.agentId`. A witness could in principle commit a fabricated event_json that is not a real `InkAuditEvent`. Every returned event MUST therefore include its `agentSignature` field. Verifiers MUST resolve the submitting agent's published Ed25519 keys (via Agent Card §2) and verify `agentSignature` on every event in addition to walking the Merkle proof. A response that omits `agentSignature` on any event MUST be rejected as structurally invalid.
|
|
571
|
+
|
|
572
|
+
Truncation: witnesses MUST NOT silently sign a partial result. If the requester's visible event set for a `messageId` exceeds the witness's response cap, the witness MUST return an unsigned error response (HTTP 413). A signed response is, by definition, a complete enumeration of the requester's visible events for that `messageId` at `(treeSize, rootHash)`.
|
|
573
|
+
|
|
574
|
+
Determinism: witnesses MUST emit `events` and matching `proofs` in a stable, deterministic order so verifiers can reproduce the signed bytes from the underlying records.
|
|
575
|
+
|
|
576
|
+
Leaf hash: each event's Merkle leaf hash is `SHA-256(0x00 || JCS(event-without-agentSignature))`. The leading `0x00` byte is the RFC 6962 leaf-domain-separation tag; internal Merkle nodes use `0x01 || left || right`. Verifiers MUST rehash the returned `event` object themselves (stripping `agentSignature`, then JCS, then SHA-256 with the `0x00` prefix) and use that hash as the leaf input to `inclusionProof`. They MUST NOT trust any leaf-hash value supplied by the witness alongside the event. Walking the proof from this computed leaf hash up through `inclusionProof` MUST reach the top-level `rootHash` per the proof construction in §7.2. The INK library exposes this exact computation as `computeAuditMerkleLeafHash`; it is distinct from `computeEventHash`, which is the unprefixed SHA-256 used for `previousEventHash` chain linkage and MUST NOT be used as the Merkle leaf input.
|
|
577
|
+
|
|
555
578
|
#### 7.4 Access Control
|
|
556
579
|
|
|
557
580
|
The audit service operates under **access-controlled transparency** (per SCITT):
|
|
558
581
|
- Events are tagged with the `messageId` and the DIDs of sender/recipient
|
|
559
|
-
- Only the sender
|
|
582
|
+
- Only the sender or recipient (i.e. an event's own `agentId` or `counterpartyId`) can query events for a given `messageId`. The witness MUST refuse to serve a row to any other requester
|
|
560
583
|
- The service verifies the requester's identity via INK auth (§3.3) before serving events
|
|
561
|
-
- The Merkle tree structure is public
|
|
584
|
+
- The Merkle tree structure is public: anyone can verify inclusion proofs against signed checkpoints and, where consistency-proof endpoints are deployed, cross-check that checkpoints are append-only. Event contents remain access-controlled
|
|
585
|
+
|
|
586
|
+
Delegated queries (where a third-party agent queries events on behalf of a principal via an INK Authorization Chain) are not in scope for alpha.3. A future revision will define the additional envelope fields the witness signs to bind the effective principal alongside the immediate requester, and verifiers will be updated accordingly. Until then, conformant witnesses MUST treat the per-event scope rule in §7.3 as authoritative: a returned event's `agentId` or `counterpartyId` MUST equal the response `requester`.
|
|
562
587
|
|
|
563
588
|
This follows SCITT's model: the transparency guarantee (append-only, no suppression) is public, but the data itself is private.
|
|
564
589
|
|
|
565
590
|
#### 7.5 Trust Model
|
|
566
591
|
|
|
567
592
|
The audit service is a **semi-trusted witness**, not an arbiter:
|
|
568
|
-
- It CANNOT forge events
|
|
593
|
+
- It CANNOT forge events that verifiers will accept, because verifiers re-check `event.agentSignature` against the agent's published Ed25519 keys (§7.3). A witness that commits a fabricated event_json into its Merkle tree can produce a valid inclusion proof, but the per-event agent-signature check fails and the response is rejected. Verifiers that walk the proof without checking `agentSignature` lose this guarantee.
|
|
569
594
|
- It CANNOT modify events without breaking Merkle proofs
|
|
570
595
|
- It CAN suppress events by refusing to include them (detectable via consistency proofs between submissions)
|
|
571
596
|
- It CAN be unavailable (agents fall back to bilateral exchange)
|
|
@@ -16,7 +16,7 @@ This checklist lets an independent implementer verify INK conformance without re
|
|
|
16
16
|
- **SHOULD**, recommended; deviations require justification
|
|
17
17
|
- **MAY**, truly optional; advertised via capability
|
|
18
18
|
|
|
19
|
-
**Status column** applies to the Tulpa
|
|
19
|
+
**Status column** applies to the Tulpa implementation:
|
|
20
20
|
- **Required**, part of the v1 wire contract
|
|
21
21
|
- **Optional**, capability-gated, not assumed
|
|
22
22
|
- **Extension**, defined but not required for base interop
|
|
@@ -145,6 +145,14 @@ This checklist lets an independent implementer verify INK conformance without re
|
|
|
145
145
|
| W6 | Checkpoint: C2SP tlog-checkpoint format at `GET /ink/v1/checkpoint` | SHOULD | Optional | Auditability §7 |, | `witness/witness/test/endpoints.test.ts (witness repo)` |
|
|
146
146
|
| W7 | Transport auth on submit: dual signature (transport + event) | MUST | Optional | Auditability §7 | `witness.json` | `witness/witness/test/endpoints.test.ts (witness repo)` |
|
|
147
147
|
| W8 | Submit includes `signingKeyId` in transport auth | SHOULD | Required | Key Rotation Phase 3 |, | `test/ink-key-rotation.test.ts` |
|
|
148
|
+
| W9 | Query response is the signed `network.tulpa.audit_query_response` envelope binding `serviceDid`, `messageId`, `requester`, `events`, `proofs`, `treeSize`, `rootHash`, `timestamp` | MUST | Optional | Auditability §7.3 | `witness.json` | `test/audit-query-response.test.ts`, `test/verify-audit-query-response.test.ts` |
|
|
149
|
+
| W10 | Per-event Merkle proof rule: leaf = `SHA-256(0x00 \|\| JCS(event-without-agentSignature))` (RFC 6962) | MUST | Optional | Auditability §7.3 | `witness.json` | `test/merkle-leaf-hash.test.ts` |
|
|
150
|
+
| W11 | Per-event scope: `event.messageId == envelope.messageId` AND `envelope.requester ∈ {event.agentId, event.counterpartyId}` | MUST | Optional | Auditability §7.3, §7.4 |, | `test/verify-audit-query-response.test.ts` |
|
|
151
|
+
| W12 | Deterministic result-set ordering so signed bytes are reproducible | MUST | Optional | Auditability §7.3 |, | `witness/witness/test/security-round12.test.ts (witness repo)` |
|
|
152
|
+
| W13 | Fail-closed on truncation: refuse to sign a partial result; return unsigned 413 | MUST | Optional | Auditability §7.3 |, | `witness/witness/test/security-round12.test.ts (witness repo)` |
|
|
153
|
+
| W14 | Fail-closed on storage integrity (event_hash mismatch, missing Merkle node, column-vs-event_json drift): HTTP 500, no signed response | MUST | Optional | Auditability §7.3 |, | `witness/witness/test/security-round12.test.ts (witness repo)` |
|
|
154
|
+
| W15 | Empty-log response: `treeSize == 0` MUST have empty `events`, empty `proofs` and canonical empty-tree `rootHash` | MUST | Optional | Auditability §7.3 |, | `test/verify-audit-query-response.test.ts` |
|
|
155
|
+
| W16 | Every returned event MUST include `agentSignature`; verifiers MUST verify it against the agent's published keys (witness Merkle validity does not prove agent provenance) | MUST | Optional | Auditability §7.3, §7.5 |, | `test/verify-audit-query-response.test.ts` |
|
|
148
156
|
|
|
149
157
|
---
|
|
150
158
|
|