@adastracomputing/ink 0.1.0-alpha.3 → 0.1.1
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 +42 -0
- package/README.md +15 -3
- package/dist/audit/inclusion-receipt.d.ts +142 -0
- package/dist/audit/inclusion-receipt.js +496 -0
- package/dist/crypto/ink.d.ts +178 -0
- package/dist/crypto/ink.js +915 -0
- package/dist/crypto/keys.d.ts +42 -0
- package/dist/crypto/keys.js +179 -0
- package/dist/crypto/multi-key-verify.d.ts +29 -0
- package/dist/crypto/multi-key-verify.js +153 -0
- package/dist/crypto/sign.d.ts +17 -0
- package/dist/crypto/sign.js +152 -0
- package/dist/crypto/verify.js +1 -0
- package/dist/discovery/agent-card.d.ts +83 -0
- package/dist/discovery/agent-card.js +545 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +16 -0
- package/dist/ink/checkpoint.d.ts +19 -0
- package/dist/ink/checkpoint.js +69 -0
- package/dist/ink/discovery-gating.d.ts +247 -0
- package/dist/ink/discovery-gating.js +94 -0
- package/dist/ink/handshake-budget.d.ts +90 -0
- package/dist/ink/handshake-budget.js +397 -0
- package/dist/ink/receipts.d.ts +31 -0
- package/dist/ink/receipts.js +89 -0
- package/dist/ink/transport-auth.d.ts +47 -0
- package/dist/ink/transport-auth.js +77 -0
- package/dist/middleware/ink-auth.d.ts +68 -0
- package/dist/middleware/ink-auth.js +214 -0
- package/dist/models/agent-card.d.ts +170 -0
- package/dist/models/agent-card.js +107 -0
- package/dist/models/ink-audit.d.ts +344 -0
- package/dist/models/ink-audit.js +167 -0
- package/dist/models/ink-handshake.d.ts +129 -0
- package/dist/models/ink-handshake.js +89 -0
- package/dist/models/intent.d.ts +437 -0
- package/dist/models/intent.js +172 -0
- package/dist/models/key-entry.d.ts +60 -0
- package/dist/models/key-entry.js +13 -0
- package/dist/models/profile.d.ts +61 -0
- package/dist/models/profile.js +24 -0
- package/package.json +15 -11
- package/specs/ink-auditability.md +2 -2
- package/specs/ink-containment-phase1-implementation-spec.md +1 -1
- package/src/audit/inclusion-receipt.ts +0 -604
- package/src/crypto/ink.ts +0 -1046
- package/src/crypto/keys.ts +0 -210
- package/src/crypto/multi-key-verify.ts +0 -170
- package/src/crypto/sign.ts +0 -155
- package/src/discovery/agent-card.ts +0 -508
- package/src/index.ts +0 -73
- package/src/ink/checkpoint.ts +0 -75
- package/src/ink/discovery-gating.ts +0 -147
- package/src/ink/handshake-budget.ts +0 -413
- package/src/ink/receipts.ts +0 -114
- package/src/ink/transport-auth.ts +0 -96
- package/src/middleware/ink-auth.ts +0 -263
- package/src/models/agent-card.ts +0 -63
- package/src/models/ink-audit.ts +0 -205
- package/src/models/ink-handshake.ts +0 -123
- package/src/models/intent.ts +0 -201
- package/src/models/key-entry.ts +0 -52
- package/src/models/profile.ts +0 -31
- /package/{src/crypto/verify.ts → dist/crypto/verify.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,48 @@ here. Pre-1.0 releases follow `0.Y.Z` semantics, see
|
|
|
8
8
|
|
|
9
9
|
No unreleased changes.
|
|
10
10
|
|
|
11
|
+
## 0.1.1, discovery rename and normative SSRF floor
|
|
12
|
+
|
|
13
|
+
This release is **wire-compatible with v0.1.0**; the wire version stays `ink/0.1`. Every change below is additive and accepts the prior shape for the duration of the v0.1.x line — implementations that emit and consume v0.1.0 cards / service entries continue to interoperate. One observable library behavior changes: the runtime emit value of the redacted Agent Card `type` field flips from `"tulpa.agent.card"` to `"ink.agent.card"` (see below). Consumers that pinned to the literal must accept either string; the TypeScript union has been widened accordingly.
|
|
14
|
+
|
|
15
|
+
**Service entry rename.** The DID Document service entry for INK endpoints is now `type: "INKAgentEndpoint"`. The legacy `"TulpaAgentEndpoint"` is still accepted by consumers during v0.1.x; new publishers SHOULD emit `INKAgentEndpoint`. When both are present, `INKAgentEndpoint` takes precedence. Removed at the next wire-version bump.
|
|
16
|
+
|
|
17
|
+
**Service entry now points at the Agent Card URL.** `serviceEndpoint` is the URL of the Agent Card, not the inbound message endpoint. Inbound URL stays on the Card itself in the `endpoint` field. Per the spec update, `inboxEndpoint` is also accepted as a synonym for `endpoint`.
|
|
18
|
+
|
|
19
|
+
**Normative SSRF floor for discovery fetches.** The Discovery spec now mandates HTTPS-only, refusal of private/link-local/loopback/cloud-metadata hosts, bounded redirects with host re-checking on each hop, response size and time caps, and honoring `Cache-Control`. Detailed hardening stays implementer responsibility but the normative floor lives in the spec.
|
|
20
|
+
|
|
21
|
+
**Normative DID-binding of fetched cards.** Consumers MUST bind the card's `ownerDid` (when present) to the DID under resolution, and bind the card's `agentId` to the agent identifier being sent to. A mismatch on either field is a hard reject. This closes the substitution attack where a host that legitimately publishes one DID claims to publish another.
|
|
22
|
+
|
|
23
|
+
**Cache and refresh rules lifted into normative Discovery.** Refresh on signature/keyId miss, on observed `keySetVersion` increase, never fall back to bootstrap keys after a valid card has been observed.
|
|
24
|
+
|
|
25
|
+
**Redacted Agent Card type renamed.** The `type` field on a redacted Agent Card is now `"ink.agent.card"`. Consumers MUST also accept the legacy `"tulpa.agent.card"` during v0.1.x. The `network.tulpa.*` wire-message types are explicitly **frozen** for v0.x per the compatibility policy; rewriting them would break every deployed router.
|
|
26
|
+
|
|
27
|
+
**Wire version is `ink/0.1`.** All v0.1.x package releases emit `protocol: "ink/0.1"` on the wire. The next wire-version bump is `ink/0.2`.
|
|
28
|
+
|
|
29
|
+
## 0.1.0-alpha.5, ship compiled JS
|
|
30
|
+
|
|
31
|
+
Fixes a publish-time regression in `0.1.0-alpha.3` (and the unreleased
|
|
32
|
+
`alpha.4`) where the package shipped raw TypeScript under `main` and
|
|
33
|
+
`exports`. Node 24 refuses to strip types from anything under
|
|
34
|
+
`node_modules`, so any consumer following the quickstart hit
|
|
35
|
+
`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING` on the first `import`
|
|
36
|
+
and could not use the library at all.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `npm run build` compiles `src/` to `dist/` via `tsconfig.build.json`;
|
|
41
|
+
`prepublishOnly` runs it automatically so the npm tarball always
|
|
42
|
+
contains compiled JS plus declaration maps.
|
|
43
|
+
- `main`, `types` and `exports."."` now point at `./dist/index.js` and
|
|
44
|
+
`./dist/index.d.ts`. The `files` array ships `dist/` instead of
|
|
45
|
+
`src/`, so consumers no longer see raw TS in `node_modules`.
|
|
46
|
+
- Dev shell and `engines.node` move from Node 22 to Node 24 (the
|
|
47
|
+
current Active LTS) to match CI.
|
|
48
|
+
|
|
49
|
+
End-to-end verified against `witness-demo.tulpa.network`: the
|
|
50
|
+
quickstart `submit.mjs` now returns a signed inclusion receipt on
|
|
51
|
+
Node 24 without modification.
|
|
52
|
+
|
|
11
53
|
## 0.1.0-alpha.3, signed audit-query response
|
|
12
54
|
|
|
13
55
|
Closes the last HIGH conformance-audit finding (witness audit-query
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ INK assumes [AT Protocol](https://atproto.com) for identity by default but isn't
|
|
|
29
29
|
npm install @adastracomputing/ink
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The package ships
|
|
32
|
+
The package ships compiled ESM with bundled type definitions (`dist/index.js` + `dist/index.d.ts`). Any project with a standard JS toolchain can import it directly — no TypeScript build step on the consumer side. The build runs automatically via `prepack` before publish.
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
import {
|
|
@@ -69,6 +69,12 @@ For consumers of bilateral audit-exchange responses (`network.tulpa.audit_respon
|
|
|
69
69
|
|
|
70
70
|
For consumers of witness audit-query responses (`network.tulpa.audit_query_response`, Auditability §7.3, added in `0.1.0-alpha.3`), call `verifyAuditQueryResponse({response, witnessPublicKey, expectedRequester, expectedMessageId, verifyEventSignature, expectedServiceDid?, laterCheckpoint?})`. The `verifyEventSignature` callback is REQUIRED: it resolves the submitting agent's Ed25519 keys (typically via Agent Card §2) and validates each event's `agentSignature`. Without it, the verifier refuses to return valid, because Merkle inclusion alone does not prove a real agent produced the event (§7.5). The verifier enforces envelope shape, the `requester` binding (prevents cross-requester replay), events/proofs strict one-to-one alignment, the §7.4 per-event scope rule, walks every Merkle proof via `computeAuditMerkleLeafHash` up to the response's `rootHash`, runs `verifyEventSignature` on every event and supports an optional later-checkpoint cross-check. The lower-level `verifyAuditQueryResponseSignature` is signature-only and is not sufficient to accept a witness response on its own.
|
|
71
71
|
|
|
72
|
+
## Agent-assisted implementation
|
|
73
|
+
|
|
74
|
+
If you are asking an AI coding agent to add INK support to an existing service, the canonical packet for that workflow is the [Agent-assisted implementation](https://ink.tulpa.network/guides/agent-assisted-implementation/) guide. It contains the curated implementer prompt, a mandatory traceability matrix, the conformance checklist, and a human-review checklist. The guide is updated as the protocol evolves; treat it as the live source rather than copying its contents into your repo.
|
|
75
|
+
|
|
76
|
+
Adopters who want the second open implementation to cross-check against can use the [`examples/foreign-sender-receiver/`](examples/foreign-sender-receiver/) TypeScript reference and the [`examples/interop-cli/`](examples/interop-cli/) Python from-scratch sender.
|
|
77
|
+
|
|
72
78
|
## Tests
|
|
73
79
|
|
|
74
80
|
```bash
|
|
@@ -78,7 +84,7 @@ npm run lint # eslint
|
|
|
78
84
|
npm run check:surface # public-surface drift check
|
|
79
85
|
```
|
|
80
86
|
|
|
81
|
-
For Nix users: `nix develop` gives a pinned Node
|
|
87
|
+
For Nix users: `nix develop` gives a pinned Node 24 + git + gitleaks shell. `nix build` produces the publishable npm tarball under `result/`. `nix run github:Ad-Astra-Computing/ink -- verify-inclusion --file receipt.json --witness https://witness.example.com` runs the CLI without installing anything globally.
|
|
82
88
|
|
|
83
89
|
## Layout
|
|
84
90
|
|
|
@@ -95,7 +101,7 @@ test-vectors/ JSON interop vectors
|
|
|
95
101
|
test/ vitest unit + integration tests
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
The library runs on any runtime providing standard Web Crypto and `fetch`: Node
|
|
104
|
+
The library runs on any runtime providing standard Web Crypto and `fetch`: Node 24+, Deno, Bun, Cloudflare Workers, browsers. The timestamp freshness window is enforced inside `verifyInkAuth`; nonce single-use is enforced when a `NonceStore` is passed (otherwise `checkReplay` must be called separately). Nonce backing storage and its TTL policy are the integrator's choice.
|
|
99
105
|
|
|
100
106
|
## What's stable in v0.1
|
|
101
107
|
|
|
@@ -121,6 +127,12 @@ You will see `network.tulpa.*` on the wire (e.g. `network.tulpa.intent`) and `in
|
|
|
121
127
|
|
|
122
128
|
INK is developed by [Ad Astra Computing](https://adastracomputing.com) as the underlying protocol for [Tulpa](https://tulpa.network). The spec and the library in this repo are deliberately free of Tulpa product code so other agent platforms can adopt INK without inheriting Tulpa's surface area. Tulpa's product integration (message orchestration, marketplace, user-facing APIs) lives in a separate, closed-source codebase.
|
|
123
129
|
|
|
130
|
+
## Interoperability
|
|
131
|
+
|
|
132
|
+
INK is a wire protocol. Any compatible service that publishes a DID and exposes an `/ink/v1/...` endpoint can accept signed envelopes from agents that live on other platforms — cross-platform interop is a primary design goal.
|
|
133
|
+
|
|
134
|
+
[`tulpa.network`](https://tulpa.network) is one current example of an accepting endpoint. Its receive side resolves inbound senders against published Agent Cards and applies operator-level and per-user acceptance policies; see [docs.tulpa.network/guide/foreign-agents](https://docs.tulpa.network/guide/foreign-agents/) for how a Tulpa user opts in. The protocol is intended to support other accepting endpoints.
|
|
135
|
+
|
|
124
136
|
## Security
|
|
125
137
|
|
|
126
138
|
See [`SECURITY.md`](SECURITY.md) for the disclosure path. The threat model is in [`docs/threat-model.md`](docs/threat-model.md). **Do not open a public issue for security problems.**
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export interface InclusionReceipt {
|
|
2
|
+
eventId: string;
|
|
3
|
+
leafIndex: number;
|
|
4
|
+
treeSize: number;
|
|
5
|
+
rootHash: string;
|
|
6
|
+
inclusionProof: string[];
|
|
7
|
+
/** ISO 8601 timestamp at which the witness committed the leaf. */
|
|
8
|
+
timestamp: string;
|
|
9
|
+
/** Base64url Ed25519 signature over the canonical bytes. */
|
|
10
|
+
serviceSignature: string;
|
|
11
|
+
}
|
|
12
|
+
export interface VerifyStep {
|
|
13
|
+
name: string;
|
|
14
|
+
pass: boolean;
|
|
15
|
+
detail?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface InclusionReceiptVerifyResult {
|
|
18
|
+
valid: boolean;
|
|
19
|
+
steps: VerifyStep[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Verify an INK inclusion receipt.
|
|
23
|
+
*
|
|
24
|
+
* Always performs:
|
|
25
|
+
* - Structural validation of the receipt object
|
|
26
|
+
* - Service signature verification against `witnessPublicKey`
|
|
27
|
+
*
|
|
28
|
+
* Optionally performs (when the corresponding input is provided):
|
|
29
|
+
* - Leaf-to-root proof walk (`eventHash`)
|
|
30
|
+
* - Cross-check against a later signed checkpoint (`laterCheckpoint`)
|
|
31
|
+
*/
|
|
32
|
+
export declare function verifyInclusionReceipt(opts: {
|
|
33
|
+
receipt: InclusionReceipt;
|
|
34
|
+
/** Raw 32-byte Ed25519 public key of the witness service. */
|
|
35
|
+
witnessPublicKey: Uint8Array;
|
|
36
|
+
/** Optional RFC 6962 leaf hash for the underlying audit event:
|
|
37
|
+
* SHA-256(0x00 || JCS(event-without-agentSignature)), hex-encoded.
|
|
38
|
+
* Use `computeAuditMerkleLeafHash` to derive it. When provided, the
|
|
39
|
+
* inclusion proof is walked from this leaf up to the claimed rootHash. */
|
|
40
|
+
eventHash?: string;
|
|
41
|
+
/** Optional later checkpoint to cross-check the receipt against.
|
|
42
|
+
* Must come from a `/ink/v1/checkpoint` response that the verifier
|
|
43
|
+
* has separately validated as authentic. */
|
|
44
|
+
laterCheckpoint?: {
|
|
45
|
+
treeSize: number;
|
|
46
|
+
rootHash: string;
|
|
47
|
+
};
|
|
48
|
+
}): Promise<InclusionReceiptVerifyResult>;
|
|
49
|
+
export interface AuditQueryResponse {
|
|
50
|
+
protocol: "ink/0.1";
|
|
51
|
+
type: "network.tulpa.audit_query_response";
|
|
52
|
+
serviceDid: string;
|
|
53
|
+
messageId: string;
|
|
54
|
+
requester: string;
|
|
55
|
+
events: Array<Record<string, unknown> & {
|
|
56
|
+
id: string;
|
|
57
|
+
}>;
|
|
58
|
+
proofs: Array<{
|
|
59
|
+
eventId: string;
|
|
60
|
+
leafIndex: number;
|
|
61
|
+
inclusionProof: string[];
|
|
62
|
+
}>;
|
|
63
|
+
treeSize: number;
|
|
64
|
+
rootHash: string;
|
|
65
|
+
timestamp: string;
|
|
66
|
+
serviceSignature: string;
|
|
67
|
+
}
|
|
68
|
+
export interface AuditQueryResponseVerifyResult {
|
|
69
|
+
valid: boolean;
|
|
70
|
+
steps: VerifyStep[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Full §7.3 verification of a witness audit-query response. Use this in
|
|
74
|
+
* preference to `verifyAuditQueryResponseSignature`, which is the
|
|
75
|
+
* underlying primitive and verifies only the Ed25519 signature. This
|
|
76
|
+
* function additionally enforces:
|
|
77
|
+
*
|
|
78
|
+
* - Envelope shape (protocol, type, serviceDid, requester, messageId,
|
|
79
|
+
* timestamp, treeSize, rootHash, events[], proofs[])
|
|
80
|
+
* - Service signature with the right canonical bytes
|
|
81
|
+
* - Optional caller-supplied bindings: expected `messageId`,
|
|
82
|
+
* `requester`, `serviceDid` (each rejected on mismatch)
|
|
83
|
+
* - `events` and `proofs` align one-to-one by `eventId`
|
|
84
|
+
* - Every event includes a non-empty `agentSignature` field
|
|
85
|
+
* - Every proof walks from `computeAuditMerkleLeafHash(event)` up to
|
|
86
|
+
* the response's `rootHash` at `treeSize`
|
|
87
|
+
* - Optional `laterCheckpoint`: tree only grew, no fork at same size
|
|
88
|
+
*
|
|
89
|
+
* **Per-event agent-signature verification (§7.5 trust model).** A
|
|
90
|
+
* Merkle-valid response is necessary but not sufficient: the witness
|
|
91
|
+
* could in principle commit a fabricated "event" not signed by any
|
|
92
|
+
* agent, sign the resulting `(treeSize, rootHash)`, and the Merkle
|
|
93
|
+
* proof walks just fine. To detect this, callers MUST pass a
|
|
94
|
+
* `verifyEventSignature` callback that resolves the agent's published
|
|
95
|
+
* Ed25519 keys (typically via Agent Card §2) and validates
|
|
96
|
+
* `event.agentSignature`. The callback is REQUIRED, not optional: the
|
|
97
|
+
* verifier refuses to return `valid: true` without it, so a caller
|
|
98
|
+
* cannot accidentally accept witness-fabricated events.
|
|
99
|
+
*
|
|
100
|
+
* **Freshness.** A `valid: true` result attests that the response was a
|
|
101
|
+
* complete enumeration of the requester's visible events at the
|
|
102
|
+
* `(treeSize, rootHash)` snapshot the witness signed, NOT that it is
|
|
103
|
+
* the witness's current authoritative view. The signed envelope binds
|
|
104
|
+
* `timestamp`, but verifiers wanting "is this still current?"
|
|
105
|
+
* semantics MUST additionally fetch a fresh witness checkpoint and
|
|
106
|
+
* compare it (e.g. require `laterCheckpoint.treeSize === response.treeSize
|
|
107
|
+
* && laterCheckpoint.rootHash === response.rootHash` for "current", or
|
|
108
|
+
* use `laterCheckpoint` here only to prove the tree never rewound or
|
|
109
|
+
* forked).
|
|
110
|
+
*
|
|
111
|
+
* Returns `{valid, steps}` where each step explains pass/fail with detail.
|
|
112
|
+
* Pure function. Does not perform network I/O.
|
|
113
|
+
*/
|
|
114
|
+
export declare function verifyAuditQueryResponse(opts: {
|
|
115
|
+
response: AuditQueryResponse;
|
|
116
|
+
/** Raw 32-byte Ed25519 public key of the witness service. */
|
|
117
|
+
witnessPublicKey: Uint8Array;
|
|
118
|
+
/** Locally authenticated requester DID. Verifier MUST supply this so
|
|
119
|
+
* a response signed for Alice cannot be replayed to Bob. */
|
|
120
|
+
expectedRequester: string;
|
|
121
|
+
/** The `messageId` the verifier asked about. Bound for paranoia: the
|
|
122
|
+
* signed envelope already commits to messageId, so this catches
|
|
123
|
+
* client-side routing bugs before they become trust bugs. */
|
|
124
|
+
expectedMessageId: string;
|
|
125
|
+
/** Optional: witness DID the verifier expects (pinned out of band). */
|
|
126
|
+
expectedServiceDid?: string;
|
|
127
|
+
/** Optional later checkpoint to cross-check against. Same semantics
|
|
128
|
+
* as `verifyInclusionReceipt`. */
|
|
129
|
+
laterCheckpoint?: {
|
|
130
|
+
treeSize: number;
|
|
131
|
+
rootHash: string;
|
|
132
|
+
};
|
|
133
|
+
/** Per-event agent-signature verifier (REQUIRED by Auditability §7.5).
|
|
134
|
+
* The caller resolves the event's submitting agent's public key set
|
|
135
|
+
* (typically from the Agent Card) and returns true if
|
|
136
|
+
* `event.agentSignature` verifies. The verifier refuses to return
|
|
137
|
+
* `valid: true` without this: Merkle inclusion alone does not prove
|
|
138
|
+
* the agent produced the event. If a caller genuinely wants to
|
|
139
|
+
* bypass per-event signature checks (e.g. during a pure Merkle
|
|
140
|
+
* audit), they MUST explicitly pass a callback that does so. */
|
|
141
|
+
verifyEventSignature: (event: Record<string, unknown>) => Promise<boolean>;
|
|
142
|
+
}): Promise<AuditQueryResponseVerifyResult>;
|