@onchaindiligence/agent-evidence 0.1.0 → 0.2.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/README.md +97 -5
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts +76 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +83 -0
- package/dist/registry.js.map +1 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -0
- package/dist/schema.js.map +1 -1
- package/dist/tclk.d.ts +139 -0
- package/dist/tclk.d.ts.map +1 -0
- package/dist/tclk.js +190 -0
- package/dist/tclk.js.map +1 -0
- package/dist/technocore.d.ts +41 -0
- package/dist/technocore.d.ts.map +1 -0
- package/dist/technocore.js +157 -0
- package/dist/technocore.js.map +1 -0
- package/examples/tclk-evidence.mjs +130 -0
- package/examples/technocore-evidence.mjs +69 -0
- package/package.json +2 -1
- package/schemas/agent-evidence-key-registry.schema.json +74 -0
- package/schemas/catalog.json +4 -2
package/README.md
CHANGED
|
@@ -5,17 +5,34 @@ OnChainDiligence Agent Evidence v0 protocol. It creates deterministic records,
|
|
|
5
5
|
validates the complete evidence DAG, seals bundles with Ed25519 DSSE, and
|
|
6
6
|
verifies portable bundles offline with caller-supplied trust.
|
|
7
7
|
|
|
8
|
-
The package is
|
|
9
|
-
npm**. Do not advertise a registry install until the owner completes npm trusted
|
|
10
|
-
publishing. From this repository, test the exact public artifact with:
|
|
8
|
+
The package is publicly available on npm:
|
|
11
9
|
|
|
12
10
|
```sh
|
|
13
|
-
npm
|
|
14
|
-
|
|
11
|
+
npm install @onchaindiligence/agent-evidence
|
|
12
|
+
# or pin this release
|
|
13
|
+
npm install @onchaindiligence/agent-evidence@0.2.0
|
|
15
14
|
```
|
|
16
15
|
|
|
16
|
+
This source README reflects the public release. The immutable README embedded
|
|
17
|
+
in the already-published `0.1.0` tarball carried its pre-release wording; this
|
|
18
|
+
release includes the corrected package documentation.
|
|
19
|
+
|
|
17
20
|
Node.js 20.19 or newer and ESM are required.
|
|
18
21
|
|
|
22
|
+
## 0.2.0
|
|
23
|
+
|
|
24
|
+
- Adds strict parsing of generic Agent Evidence signer registries and
|
|
25
|
+
`TrustPolicy` construction from independently supplied registry metadata.
|
|
26
|
+
- Packages the key-registry JSON Schema, interoperability profile support, and
|
|
27
|
+
browser/offline-friendly registry types.
|
|
28
|
+
- Adds Technocore signed-message and tclk/1 coordination-evidence adapters.
|
|
29
|
+
|
|
30
|
+
The verifier remains caller-trust-controlled: embedded bundle keys are never
|
|
31
|
+
trust roots. A Technocore signature proves key possession/authorship of exact
|
|
32
|
+
bytes, not truth; tclk evidence proves coordination, not settlement; and
|
|
33
|
+
PaperRail represents no real value. `VALID`, `INVALID`, and `UNVERIFIABLE`
|
|
34
|
+
semantics are unchanged.
|
|
35
|
+
|
|
19
36
|
## What a signature proves
|
|
20
37
|
|
|
21
38
|
A valid signature proves that the identified key signed the exact canonical
|
|
@@ -90,6 +107,81 @@ where policy withholds an invoice payment because recipient ownership evidence
|
|
|
90
107
|
is missing. The execution is `withheld-not-submitted`; the example does not
|
|
91
108
|
fabricate a transaction or settlement.
|
|
92
109
|
|
|
110
|
+
## Technocore signed-message evidence
|
|
111
|
+
|
|
112
|
+
`technocore.chat` signed-lane messages can be captured as attributable, offline
|
|
113
|
+
verifiable input with no resolver or network call. The adapter mirrors the
|
|
114
|
+
official single-line sweep and verifies the exact UTF-8
|
|
115
|
+
`<room>|<nonce>|<stored-text>` string against the Ed25519 public key embedded in
|
|
116
|
+
the sender's `did:key`.
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
import { createTechnocoreEvidence, verifyTechnocoreMessage } from '@onchaindiligence/agent-evidence'
|
|
120
|
+
|
|
121
|
+
// Treat every field as untrusted data obtained from Technocore's JSON response.
|
|
122
|
+
if (!verifyTechnocoreMessage({ did, room, nonce, text, sig })) throw new Error('invalid assertion')
|
|
123
|
+
const evidence = createTechnocoreEvidence({ did, room, nonce, text, sig }, {
|
|
124
|
+
runRef: run.id,
|
|
125
|
+
observedAt: '2026-09-01T12:00:01.000Z',
|
|
126
|
+
serverMetadata: { seq: String(seq), ts: String(ts), generation: String(generation) },
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The resulting record preserves the DID, room, nonce, exact stored text, its
|
|
131
|
+
SHA-256 digest, signature, signing format, and optional server metadata. It
|
|
132
|
+
always uses `trust_mode: 'agent-assertion'`: a valid signature proves only that
|
|
133
|
+
that `did:key` asserted those bytes. It never proves the message is true,
|
|
134
|
+
authorizes an action, supplies a trusted instruction, or permits a wallet/
|
|
135
|
+
network action. `verifyTechnocoreMessage` and normal bundle verification are
|
|
136
|
+
fully offline and make no HTTP requests.
|
|
137
|
+
|
|
138
|
+
[`examples/technocore-evidence.mjs`](./examples/technocore-evidence.mjs) builds
|
|
139
|
+
Mandate → Technocore Evidence → Policy → Decision → non-execution → DSSE-sealed
|
|
140
|
+
bundle → offline `VALID` verification.
|
|
141
|
+
|
|
142
|
+
## tclk/1 (Technocore Lock Protocol) transcript evidence
|
|
143
|
+
|
|
144
|
+
Signed agent-to-agent deal coordination on [`technocore.chat`](https://github.com/flop-labs/technocore-chat)
|
|
145
|
+
using FLOP Labs' [`@flop-labs/tclk`](https://github.com/flop-labs/tclk) (`offer →
|
|
146
|
+
accept → lock → reveal/refund`) can be captured as Agent Evidence. This adapter
|
|
147
|
+
uses the official `@flop-labs/tclk` package directly — it never reimplements
|
|
148
|
+
frame validation or the state machine, and never uses tclk's unaudited
|
|
149
|
+
PTLC/adaptor-signature path.
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import { verifyTclkTranscript, createTclkEvidence } from '@onchaindiligence/agent-evidence'
|
|
153
|
+
|
|
154
|
+
// Each entry is a Technocore signed message whose text is a tclk/1 frame line,
|
|
155
|
+
// plus the wall-clock time (ms) at which that frame was applied.
|
|
156
|
+
const transcript = verifyTclkTranscript([
|
|
157
|
+
{ message: offerMsg, atMs }, { message: acceptMsg, atMs }, { message: lockMsg, atMs },
|
|
158
|
+
])
|
|
159
|
+
const evidence = createTclkEvidence(transcript, {
|
|
160
|
+
runRef: run.id, observedAt, messageEvidenceRefs: [/* one createTechnocoreEvidence(...).id per message */],
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`verifyTclkTranscript` verifies, independently, per frame: the Technocore
|
|
165
|
+
transport signature (reusing `verifyTechnocoreMessage`, not a second
|
|
166
|
+
implementation), the tclk frame's own validity, that the frame's `from` matches
|
|
167
|
+
the transport-authenticated DID, and the official state-machine transition. It
|
|
168
|
+
fails closed on a bad signature, a malformed frame, or a sender/DID mismatch; a
|
|
169
|
+
frame the *official* machine itself rejects as a designed-in no-op (a replay, a
|
|
170
|
+
duplicate, an out-of-order transition) is recorded in the result rather than
|
|
171
|
+
treated as fatal, per tclk's own spec.
|
|
172
|
+
|
|
173
|
+
**Valid signed coordination is evidence of what the agents agreed/asserted. The
|
|
174
|
+
named settlement rail remains authoritative for actual value movement** — a
|
|
175
|
+
`lock` frame is captured as "payer asserted/announced lock on rail X," never as
|
|
176
|
+
"funds were locked," unless the caller separately supplies an independently
|
|
177
|
+
verified `settlementRail` observation (this package implements no rail).
|
|
178
|
+
|
|
179
|
+
[`examples/tclk-evidence.mjs`](./examples/tclk-evidence.mjs) builds a full
|
|
180
|
+
hash-lock transcript (offer → accept → lock → reveal) between two local test
|
|
181
|
+
identities and turns it into Mandate → tclk Evidence → Policy → Decision
|
|
182
|
+
(`ACCEPT_COORDINATION_EVIDENCE`) → Execution (`NO_REAL_VALUE_SETTLEMENT`, since
|
|
183
|
+
no real rail is wired up) → sealed bundle → offline `VALID` verification.
|
|
184
|
+
|
|
93
185
|
## Schemas and interoperability
|
|
94
186
|
|
|
95
187
|
The npm artifact includes the canonical v0 schemas and public conformance
|
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,15 @@ export { AgentEvidenceError, CanonicalizationError, EvidenceValidationError, Par
|
|
|
6
6
|
export { validateBundlePayload } from './graph.js';
|
|
7
7
|
export { createBundlePayload, createRecord } from './records.js';
|
|
8
8
|
export type { CreateBundlePayloadOptions, CreateRecordOptions } from './records.js';
|
|
9
|
+
export { createTechnocoreEvidence, sweepTechnocoreText, technocoreDidFromPublicKey, technocoreSigningInput, technocoreTextDigest, verifyTechnocoreMessage, } from './technocore.js';
|
|
10
|
+
export type { TechnocoreEvidenceOptions, TechnocoreSignedMessage } from './technocore.js';
|
|
11
|
+
export { createTclkEvidence, verifyTclkTranscript } from './tclk.js';
|
|
12
|
+
export type { SettlementRailObservation, TclkEvidenceOptions, TclkTranscriptMessage, TclkTranscriptResult, TclkTranscriptStep, } from './tclk.js';
|
|
9
13
|
export { validateDocument } from './schema.js';
|
|
10
14
|
export { AttestationKey, createKeyRecord, deriveKeyId, evaluateKeyLifecycle, loadPublicKey, TrustPolicy, } from './trust.js';
|
|
11
15
|
export type { CreateKeyRecordOptions, TrustPolicyOptions } from './trust.js';
|
|
16
|
+
export { parseAgentEvidenceKeyRegistry, trustPolicyFromKeyRegistry } from './registry.js';
|
|
17
|
+
export type { AgentEvidenceKeyRegistry, AgentEvidenceKeyRegistryEntry, ParseAgentEvidenceKeyRegistryOptions, } from './registry.js';
|
|
12
18
|
export { overallState, verifyBundle } from './verifier.js';
|
|
13
19
|
export type { AgentEvidenceRecord, AttestationKeyRecord, BundlePayload, ComponentResult, DsseEnvelope, DsseSignature, Ed25519Signer, JsonObject, JsonPrimitive, JsonValue, KeyInput, PortableBundle, RecordKind, VerificationMaterial, VerificationReport, VerificationState, } from './types.js';
|
|
14
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,eAAe,EACf,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChE,YAAY,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC1D,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,aAAa,EACb,SAAS,EACT,QAAQ,EACR,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,eAAe,EACf,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChE,YAAY,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACnF,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AACzF,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACpE,YAAY,EACV,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAA;AACzF,YAAY,EACV,wBAAwB,EACxB,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAC1D,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,aAAa,EACb,aAAa,EACb,UAAU,EACV,aAAa,EACb,SAAS,EACT,QAAQ,EACR,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,10 @@ export { createEd25519Signer, dssePae, sealBundle } from './dsse.js';
|
|
|
4
4
|
export { AgentEvidenceError, CanonicalizationError, EvidenceValidationError, ParseError, SchemaValidationError, SigningError, TrustPolicyError, } from './errors.js';
|
|
5
5
|
export { validateBundlePayload } from './graph.js';
|
|
6
6
|
export { createBundlePayload, createRecord } from './records.js';
|
|
7
|
+
export { createTechnocoreEvidence, sweepTechnocoreText, technocoreDidFromPublicKey, technocoreSigningInput, technocoreTextDigest, verifyTechnocoreMessage, } from './technocore.js';
|
|
8
|
+
export { createTclkEvidence, verifyTclkTranscript } from './tclk.js';
|
|
7
9
|
export { validateDocument } from './schema.js';
|
|
8
10
|
export { AttestationKey, createKeyRecord, deriveKeyId, evaluateKeyLifecycle, loadPublicKey, TrustPolicy, } from './trust.js';
|
|
11
|
+
export { parseAgentEvidenceKeyRegistry, trustPolicyFromKeyRegistry } from './registry.js';
|
|
9
12
|
export { overallState, verifyBundle } from './verifier.js';
|
|
10
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,eAAe,EACf,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEpE,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,eAAe,EACf,SAAS,EACT,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEpE,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,EACV,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,GACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAQpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAA;AAMzF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { TrustPolicy, type TrustPolicyOptions } from './trust.js';
|
|
2
|
+
import type { JsonObject } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Agent Evidence Interoperability Profile v1 — public signer key registry.
|
|
5
|
+
*
|
|
6
|
+
* See docs/AGENT_EVIDENCE_INTEROP.md for the full profile. This module parses
|
|
7
|
+
* and validates the WIRE format any issuer (ArcFX is the current production
|
|
8
|
+
* reference) can publish at `GET /.well-known/agent-evidence-keys`, and maps
|
|
9
|
+
* validated entries into the existing `AttestationKeyRecord` shape so they
|
|
10
|
+
* flow through the same `TrustPolicy` / `evaluateKeyLifecycle` machinery as
|
|
11
|
+
* every other trusted key in this package — no second lifecycle model.
|
|
12
|
+
*
|
|
13
|
+
* This module does NOT fetch anything over the network. Network fetching is
|
|
14
|
+
* the caller's concern (see the profile doc for the recommended pattern); the
|
|
15
|
+
* core SDK stays fully usable offline. Hosting this endpoint is a discovery
|
|
16
|
+
* convenience only — it does not by itself make an issuer trustworthy, and an
|
|
17
|
+
* embedded bundle key never establishes trust regardless of what any registry
|
|
18
|
+
* says. Trust is always the caller's `TrustPolicy`.
|
|
19
|
+
*/
|
|
20
|
+
export interface AgentEvidenceKeyRegistryEntry extends JsonObject {
|
|
21
|
+
key_id: string;
|
|
22
|
+
algorithm: 'Ed25519';
|
|
23
|
+
public_key_pem: string;
|
|
24
|
+
valid_from: string | null;
|
|
25
|
+
valid_until: string | null;
|
|
26
|
+
revoked_at: string | null;
|
|
27
|
+
status: 'active' | 'retired' | 'revoked' | 'compromised';
|
|
28
|
+
}
|
|
29
|
+
export interface AgentEvidenceKeyRegistry extends JsonObject {
|
|
30
|
+
schema_version: 1;
|
|
31
|
+
issuer: string;
|
|
32
|
+
environment: string;
|
|
33
|
+
keys: AgentEvidenceKeyRegistryEntry[];
|
|
34
|
+
}
|
|
35
|
+
export interface ParseAgentEvidenceKeyRegistryOptions {
|
|
36
|
+
/** Reject the registry unless `issuer` equals exactly this string. */
|
|
37
|
+
expectedIssuer?: string;
|
|
38
|
+
/** Reject the registry unless `environment` equals exactly this string. */
|
|
39
|
+
expectedEnvironment?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates untrusted parsed JSON as an Agent Evidence key registry (schema
|
|
43
|
+
* `agent-evidence-key-registry.schema.json`), optionally pinning the exact
|
|
44
|
+
* `issuer`/`environment` a caller expects.
|
|
45
|
+
*
|
|
46
|
+
* Fails closed: throws `TrustPolicyError` on any structural violation
|
|
47
|
+
* (missing/extra fields, wrong types, a malformed key entry anywhere in the
|
|
48
|
+
* array, an issuer/environment mismatch) rather than returning a partial or
|
|
49
|
+
* best-effort result. A caller integrating this over the network should treat
|
|
50
|
+
* a thrown error here — like a failed fetch — as "no trust available from
|
|
51
|
+
* this source" (typically UNVERIFIABLE for anything that key would have
|
|
52
|
+
* signed), never as INVALID: an unreachable or misconfigured registry is not
|
|
53
|
+
* evidence that a bundle's signature is fraudulent.
|
|
54
|
+
*
|
|
55
|
+
* `key_id` alone is never sufficient to establish trust, and this function
|
|
56
|
+
* does not establish trust either — it only validates shape. Feed the result
|
|
57
|
+
* to `trustPolicyFromKeyRegistry`, or your own mapping, to actually build a
|
|
58
|
+
* `TrustPolicy`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseAgentEvidenceKeyRegistry(payload: unknown, options?: ParseAgentEvidenceKeyRegistryOptions): AgentEvidenceKeyRegistry;
|
|
61
|
+
/**
|
|
62
|
+
* Parses and validates an Agent Evidence key registry, then constructs a
|
|
63
|
+
* `TrustPolicy` from its keys — the recommended one-call path from an
|
|
64
|
+
* untrusted registry payload to a usable trust policy.
|
|
65
|
+
*
|
|
66
|
+
* Equivalent to `TrustPolicy.fromKeyRecords(parseAgentEvidenceKeyRegistry(payload, opts).keys.map(...), trustPolicyOptions)`,
|
|
67
|
+
* so it inherits every existing `TrustPolicy`/`evaluateKeyLifecycle` guarantee:
|
|
68
|
+
* a revoked or compromised key still fails closed to INVALID when it signs
|
|
69
|
+
* something, a key with no `valid_from` is UNVERIFIABLE, and so on — this
|
|
70
|
+
* function adds no new trust semantics, only a convenient on-ramp into the
|
|
71
|
+
* existing ones.
|
|
72
|
+
*/
|
|
73
|
+
export declare function trustPolicyFromKeyRegistry(payload: unknown, options?: ParseAgentEvidenceKeyRegistryOptions & {
|
|
74
|
+
trustPolicy?: TrustPolicyOptions;
|
|
75
|
+
}): TrustPolicy;
|
|
76
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,YAAY,CAAA;AAElE;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,6BAA8B,SAAQ,UAAU;IAC/D,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAA;CACzD;AAED,MAAM,WAAW,wBAAyB,SAAQ,UAAU;IAC1D,cAAc,EAAE,CAAC,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,6BAA6B,EAAE,CAAA;CACtC;AAED,MAAM,WAAW,oCAAoC;IACnD,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,2EAA2E;IAC3E,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,oCAAyC,GACjD,wBAAwB,CAmB1B;AA6BD;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,oCAAoC,GAAG;IAAE,WAAW,CAAC,EAAE,kBAAkB,CAAA;CAAO,GACxF,WAAW,CAGb"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SchemaValidationError, TrustPolicyError } from './errors.js';
|
|
2
|
+
import { validateDocument } from './schema.js';
|
|
3
|
+
import { TrustPolicy } from './trust.js';
|
|
4
|
+
/**
|
|
5
|
+
* Validates untrusted parsed JSON as an Agent Evidence key registry (schema
|
|
6
|
+
* `agent-evidence-key-registry.schema.json`), optionally pinning the exact
|
|
7
|
+
* `issuer`/`environment` a caller expects.
|
|
8
|
+
*
|
|
9
|
+
* Fails closed: throws `TrustPolicyError` on any structural violation
|
|
10
|
+
* (missing/extra fields, wrong types, a malformed key entry anywhere in the
|
|
11
|
+
* array, an issuer/environment mismatch) rather than returning a partial or
|
|
12
|
+
* best-effort result. A caller integrating this over the network should treat
|
|
13
|
+
* a thrown error here — like a failed fetch — as "no trust available from
|
|
14
|
+
* this source" (typically UNVERIFIABLE for anything that key would have
|
|
15
|
+
* signed), never as INVALID: an unreachable or misconfigured registry is not
|
|
16
|
+
* evidence that a bundle's signature is fraudulent.
|
|
17
|
+
*
|
|
18
|
+
* `key_id` alone is never sufficient to establish trust, and this function
|
|
19
|
+
* does not establish trust either — it only validates shape. Feed the result
|
|
20
|
+
* to `trustPolicyFromKeyRegistry`, or your own mapping, to actually build a
|
|
21
|
+
* `TrustPolicy`.
|
|
22
|
+
*/
|
|
23
|
+
export function parseAgentEvidenceKeyRegistry(payload, options = {}) {
|
|
24
|
+
try {
|
|
25
|
+
validateDocument('agent-evidence-key-registry.schema.json', payload);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof SchemaValidationError)
|
|
29
|
+
throw new TrustPolicyError(error.message, { cause: error });
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
const registry = payload;
|
|
33
|
+
if (options.expectedIssuer !== undefined && registry.issuer !== options.expectedIssuer) {
|
|
34
|
+
throw new TrustPolicyError(`registry issuer "${registry.issuer}" does not match the expected issuer "${options.expectedIssuer}"`);
|
|
35
|
+
}
|
|
36
|
+
if (options.expectedEnvironment !== undefined && registry.environment !== options.expectedEnvironment) {
|
|
37
|
+
throw new TrustPolicyError(`registry environment "${registry.environment}" does not match the expected environment "${options.expectedEnvironment}"`);
|
|
38
|
+
}
|
|
39
|
+
return registry;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Maps one validated registry entry onto the package's own trusted-key record
|
|
43
|
+
* shape. The internal `AttestationKeyRecord` model requires an "active" key to
|
|
44
|
+
* carry no `valid_until` (open-ended) -- a bounded validity window is what
|
|
45
|
+
* "retired" means internally. A generic registry has no such constraint (an
|
|
46
|
+
* issuer's own "active" just means "not revoked"), so a registry-reported
|
|
47
|
+
* active key with a bounded `valid_until` maps to the internal "retired"
|
|
48
|
+
* status; the underlying lifecycle window is honored either way via
|
|
49
|
+
* `evaluateKeyLifecycle`, only the label changes.
|
|
50
|
+
*/
|
|
51
|
+
function toAttestationKeyRecord(entry) {
|
|
52
|
+
const revoked = entry.status === 'revoked' || entry.status === 'compromised';
|
|
53
|
+
const status = entry.status === 'active' && entry.valid_until !== null ? 'retired' : entry.status;
|
|
54
|
+
return {
|
|
55
|
+
key_id: entry.key_id,
|
|
56
|
+
algorithm: 'ed25519',
|
|
57
|
+
public_key_pem: entry.public_key_pem,
|
|
58
|
+
status,
|
|
59
|
+
valid_from: entry.valid_from,
|
|
60
|
+
valid_until: entry.valid_until,
|
|
61
|
+
status_changed_at: revoked ? entry.revoked_at : entry.valid_from,
|
|
62
|
+
replacement_key_id: null,
|
|
63
|
+
compromised_at: entry.status === 'compromised' ? entry.revoked_at : null,
|
|
64
|
+
...(revoked ? { status_reason: `registry-reported ${entry.status} at ${entry.revoked_at ?? 'unknown time'}` } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parses and validates an Agent Evidence key registry, then constructs a
|
|
69
|
+
* `TrustPolicy` from its keys — the recommended one-call path from an
|
|
70
|
+
* untrusted registry payload to a usable trust policy.
|
|
71
|
+
*
|
|
72
|
+
* Equivalent to `TrustPolicy.fromKeyRecords(parseAgentEvidenceKeyRegistry(payload, opts).keys.map(...), trustPolicyOptions)`,
|
|
73
|
+
* so it inherits every existing `TrustPolicy`/`evaluateKeyLifecycle` guarantee:
|
|
74
|
+
* a revoked or compromised key still fails closed to INVALID when it signs
|
|
75
|
+
* something, a key with no `valid_from` is UNVERIFIABLE, and so on — this
|
|
76
|
+
* function adds no new trust semantics, only a convenient on-ramp into the
|
|
77
|
+
* existing ones.
|
|
78
|
+
*/
|
|
79
|
+
export function trustPolicyFromKeyRegistry(payload, options = {}) {
|
|
80
|
+
const registry = parseAgentEvidenceKeyRegistry(payload, options);
|
|
81
|
+
return TrustPolicy.fromKeyRecords(registry.keys.map(toAttestationKeyRecord), options.trustPolicy);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,WAAW,EAA2B,MAAM,YAAY,CAAA;AA6CjE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,6BAA6B,CAC3C,OAAgB,EAChB,UAAgD,EAAE;IAElD,IAAI,CAAC;QACH,gBAAgB,CAAC,yCAAyC,EAAE,OAAO,CAAC,CAAA;IACtE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,qBAAqB;YAAE,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QACvG,MAAM,KAAK,CAAA;IACb,CAAC;IACD,MAAM,QAAQ,GAAG,OAAmC,CAAA;IACpD,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC;QACvF,MAAM,IAAI,gBAAgB,CACxB,oBAAoB,QAAQ,CAAC,MAAM,yCAAyC,OAAO,CAAC,cAAc,GAAG,CACtG,CAAA;IACH,CAAC;IACD,IAAI,OAAO,CAAC,mBAAmB,KAAK,SAAS,IAAI,QAAQ,CAAC,WAAW,KAAK,OAAO,CAAC,mBAAmB,EAAE,CAAC;QACtG,MAAM,IAAI,gBAAgB,CACxB,yBAAyB,QAAQ,CAAC,WAAW,8CAA8C,OAAO,CAAC,mBAAmB,GAAG,CAC1H,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,sBAAsB,CAAC,KAAoC;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,aAAa,CAAA;IAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAA;IACjG,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,SAAS,EAAE,SAAS;QACpB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,MAAM;QACN,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU;QAChE,kBAAkB,EAAE,IAAI;QACxB,cAAc,EAAE,KAAK,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;QACxE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,qBAAqB,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,UAAU,IAAI,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpH,CAAA;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAgB,EAChB,UAAuF,EAAE;IAEzF,MAAM,QAAQ,GAAG,6BAA6B,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAChE,OAAO,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;AACnG,CAAC"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const SCHEMA_BASE = "https://onchaindiligence.com/schemas/agent-evidence/v0/";
|
|
2
|
-
export declare const SCHEMA_NAMES: readonly ["common.schema.json", "attestation-key.schema.json", "dsse-envelope.schema.json", "proof.schema.json", "record.schema.json", "bundle-payload.schema.json", "portable-file.schema.json"];
|
|
2
|
+
export declare const SCHEMA_NAMES: readonly ["common.schema.json", "attestation-key.schema.json", "dsse-envelope.schema.json", "proof.schema.json", "record.schema.json", "bundle-payload.schema.json", "portable-file.schema.json", "agent-evidence-key-registry.schema.json"];
|
|
3
3
|
export declare function validateDocument(name: string, value: unknown): void;
|
|
4
4
|
//# sourceMappingURL=schema.d.ts.map
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,WAAW,4DAA4D,CAAA;AACpF,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,WAAW,4DAA4D,CAAA;AACpF,eAAO,MAAM,YAAY,8OASf,CAAA;AA6BV,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAOnE"}
|
package/dist/schema.js
CHANGED
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,aAAa,EAAE,EAA2C,MAAM,kBAAkB,CAAA;AACzF,OAAO,gBAAgB,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,CAAC,MAAM,WAAW,GAAG,yDAAyD,CAAA;AACpF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,oBAAoB;IACpB,6BAA6B;IAC7B,2BAA2B;IAC3B,mBAAmB;IACnB,oBAAoB;IACpB,4BAA4B;IAC5B,2BAA2B;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,aAAa,EAAE,EAA2C,MAAM,kBAAkB,CAAA;AACzF,OAAO,gBAAgB,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnD,MAAM,CAAC,MAAM,WAAW,GAAG,yDAAyD,CAAA;AACpF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,oBAAoB;IACpB,6BAA6B;IAC7B,2BAA2B;IAC3B,mBAAmB;IACnB,oBAAoB;IACpB,4BAA4B;IAC5B,2BAA2B;IAC3B,yCAAyC;CACjC,CAAA;AAOV,MAAM,OAAO,GAAG,aAAgF,CAAA;AAChG,MAAM,UAAU,GAAG,gBAA6D,CAAA;AAChF,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;AAC1D,UAAU,CAAC,GAAG,CAAC,CAAA;AACf,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,GAAG,CAAC,cAAc,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAA;IAClF,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;AAClC,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,CAAE,YAAkC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,UAAU,CAAC,kCAAkC,IAAI,EAAE,CAAC,CAAA;IAChE,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG,WAAW,GAAG,IAAI,EAAE,CAAC,CAAA;IACvD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,IAAI,EAAE,CAAC,CAAA;IACzF,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,KAAkB;IACtC,OAAO,GAAG,KAAK,CAAC,YAAY,SAAS,KAAK,CAAC,UAAU,SAAS,KAAK,CAAC,OAAO,EAAE,CAAA;AAC/E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,KAAc;IAC3D,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAChC,IAAI,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAM;IAC3B,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACxH,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IACvB,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,qBAAqB,CAAC,GAAG,IAAI,6BAA6B,CAAC,CAAA;IACjF,MAAM,IAAI,qBAAqB,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,YAAY,IAAI,GAAG,KAAK,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;AAC3G,CAAC"}
|
package/dist/tclk.d.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type LockKind, type OfferFrame, type TclkFrame, type TclkStatus } from '@flop-labs/tclk';
|
|
2
|
+
import { type TechnocoreSignedMessage } from './technocore.js';
|
|
3
|
+
import type { AgentEvidenceRecord, JsonObject } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Technocore Lock Protocol (`tclk/1`, by FLOP Labs — https://github.com/flop-labs/tclk)
|
|
6
|
+
* adapter: turns a Technocore-carried tclk transcript into Agent Evidence.
|
|
7
|
+
*
|
|
8
|
+
* tclk is a coordination convention, not a settlement service: it proves which
|
|
9
|
+
* `did:key` signed a deal-coordination frame (offer/accept/lock/reveal/refund/
|
|
10
|
+
* cancel/receipt) and whether the transcript is valid under tclk's own
|
|
11
|
+
* fail-closed state machine (`applyFrame`). It does NOT prove the counterparty
|
|
12
|
+
* is trustworthy, that an asserted statement is objectively true, or that money
|
|
13
|
+
* actually moved — a named settlement rail remains authoritative for that. This
|
|
14
|
+
* module never implements a rail, never moves value, and deliberately does not
|
|
15
|
+
* use tclk's unaudited PTLC/adaptor-signature path (see SPEC.md §7).
|
|
16
|
+
*
|
|
17
|
+
* Verification here is layered and independently distinguishable, per frame:
|
|
18
|
+
* 1. transport — `verifyTechnocoreMessage` (the existing Technocore adapter;
|
|
19
|
+
* not reimplemented here)
|
|
20
|
+
* 2. frame — `decodeFrame`/`validateFrame` from the official `@flop-labs/tclk`
|
|
21
|
+
* package (not reimplemented here)
|
|
22
|
+
* 3. attribution — the frame's own `from` must equal the transport-verified DID
|
|
23
|
+
* 4. state — `openContract`/`applyFrame`, the official state machine
|
|
24
|
+
*/
|
|
25
|
+
/** One Technocore message believed to carry a tclk/1 frame as its text. */
|
|
26
|
+
export type TclkTranscriptMessage = TechnocoreSignedMessage;
|
|
27
|
+
/**
|
|
28
|
+
* One transcript entry: a message plus the wall-clock time (unix ms) at which
|
|
29
|
+
* THAT frame was applied. tclk's deadline checks (`expiresMs`, `refundAfterMs`)
|
|
30
|
+
* are transition guards evaluated at the moment of each transition (see
|
|
31
|
+
* `applyFrame`'s `nowMs` parameter) — an `accept` many minutes after `offer` and
|
|
32
|
+
* a `refund` days later are each checked against their own real time, not a
|
|
33
|
+
* single "verification time" for the whole historical transcript. Reusing one
|
|
34
|
+
* frozen instant for every step would make it impossible to correctly replay a
|
|
35
|
+
* transcript that legitimately spans an offer's expiry window through to its
|
|
36
|
+
* refund window.
|
|
37
|
+
*/
|
|
38
|
+
export interface TclkTranscriptEntry {
|
|
39
|
+
message: TclkTranscriptMessage;
|
|
40
|
+
atMs: number;
|
|
41
|
+
}
|
|
42
|
+
/** One step of a verified transcript: either an accepted transition or an official rejection. */
|
|
43
|
+
export interface TclkTranscriptStep {
|
|
44
|
+
message: TclkTranscriptMessage;
|
|
45
|
+
frameType: TclkFrame['type'];
|
|
46
|
+
accepted: boolean;
|
|
47
|
+
/** Present when `accepted` is false — the official machine's own rejection reason. */
|
|
48
|
+
reason?: string;
|
|
49
|
+
}
|
|
50
|
+
/** The result of successfully replaying a tclk transcript through the official state machine. */
|
|
51
|
+
export interface TclkTranscriptResult {
|
|
52
|
+
offer: OfferFrame;
|
|
53
|
+
/** Set once an `accept` frame is itself accepted. */
|
|
54
|
+
contract: string | null;
|
|
55
|
+
status: TclkStatus;
|
|
56
|
+
terminal: boolean;
|
|
57
|
+
outcome: 'claimed' | 'refunded' | 'cancelled' | null;
|
|
58
|
+
payerDid: string | null;
|
|
59
|
+
payeeDid: string | null;
|
|
60
|
+
amount: string;
|
|
61
|
+
asset: string;
|
|
62
|
+
lock: LockKind;
|
|
63
|
+
offeredRails: readonly string[];
|
|
64
|
+
/** Set once a `lock` frame is accepted — the rail the payer announced, not independently verified. */
|
|
65
|
+
rail: string | null;
|
|
66
|
+
railRef: string | null;
|
|
67
|
+
claimByMs: number;
|
|
68
|
+
refundAfterMs: number;
|
|
69
|
+
expiresMs: number;
|
|
70
|
+
/** Every message processed, in order, whether accepted or officially rejected. */
|
|
71
|
+
steps: readonly TclkTranscriptStep[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Verifies a Technocore-carried tclk/1 transcript end to end and replays it
|
|
75
|
+
* through the official `@flop-labs/tclk` state machine.
|
|
76
|
+
*
|
|
77
|
+
* Fails closed (throws `EvidenceValidationError`) when:
|
|
78
|
+
* - a message's Technocore transport signature does not verify
|
|
79
|
+
* - a message's text does not decode/validate as a tclk/1 frame
|
|
80
|
+
* - a frame's own `from` does not match its message's transport-verified DID
|
|
81
|
+
* - the transcript does not open with a valid `offer` frame, or a second
|
|
82
|
+
* `offer` appears mid-transcript (which contract would the rest belong to?)
|
|
83
|
+
*
|
|
84
|
+
* A frame that decodes and is honestly attributed but that the OFFICIAL state
|
|
85
|
+
* machine itself rejects (wrong sender for the transition, a replay, a
|
|
86
|
+
* duplicate, an out-of-order transition, a tampered `contract`/`ref` id that
|
|
87
|
+
* fails the machine's own recomputation) is NOT a fail-closed error here: per
|
|
88
|
+
* tclk's own spec (SPEC.md §2, §4), those are designed-in no-op rejections a
|
|
89
|
+
* reader in a world-writable room must expect, not evidence the transcript
|
|
90
|
+
* itself is corrupt. Every such rejection is still recorded in `steps` with its
|
|
91
|
+
* reason — it is never silently discarded — and does not advance contract state.
|
|
92
|
+
*
|
|
93
|
+
* Each entry supplies its own `atMs` (see `TclkTranscriptEntry`); deadline
|
|
94
|
+
* guards are evaluated per-transition against that entry's own time, not a
|
|
95
|
+
* single instant for the whole replay.
|
|
96
|
+
*/
|
|
97
|
+
export declare function verifyTclkTranscript(entries: readonly TclkTranscriptEntry[]): TclkTranscriptResult;
|
|
98
|
+
/**
|
|
99
|
+
* An OPTIONAL, independently-verified observation from a real settlement rail —
|
|
100
|
+
* NOT implemented, simulated, or connected to by this module. tclk coordination
|
|
101
|
+
* evidence plus a caller-supplied `SettlementRailObservation` is what would let a
|
|
102
|
+
* decision claim actual value movement; without one, a decision built on tclk
|
|
103
|
+
* evidence alone must describe the lock as asserted/announced, never as settled.
|
|
104
|
+
*/
|
|
105
|
+
export interface SettlementRailObservation {
|
|
106
|
+
rail: string;
|
|
107
|
+
ref: string;
|
|
108
|
+
observedAt: string;
|
|
109
|
+
/** Free-form, source-specific detail the caller already independently verified. */
|
|
110
|
+
detail: JsonObject;
|
|
111
|
+
}
|
|
112
|
+
export interface TclkEvidenceOptions {
|
|
113
|
+
runRef: string;
|
|
114
|
+
observedAt: string;
|
|
115
|
+
/**
|
|
116
|
+
* Parent evidence ids for the underlying signed Technocore messages — recommended:
|
|
117
|
+
* one `createTechnocoreEvidence(...)` call per transcript step, so the raw signed
|
|
118
|
+
* room messages remain independently inspectable evidence in their own right.
|
|
119
|
+
*/
|
|
120
|
+
messageEvidenceRefs: readonly string[];
|
|
121
|
+
expiresAt?: string | null;
|
|
122
|
+
toolVersion?: string;
|
|
123
|
+
/** See `SettlementRailObservation`. Never fabricated by this module. */
|
|
124
|
+
settlementRail?: SettlementRailObservation;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Converts a verified tclk transcript into schema-valid Agent Evidence. The
|
|
128
|
+
* transcript must already have passed `verifyTclkTranscript` — this function
|
|
129
|
+
* does not re-verify signatures or replay frames, only records what was found.
|
|
130
|
+
*
|
|
131
|
+
* A `lock` frame is reported as "payer asserted/announced lock on rail X",
|
|
132
|
+
* never as "funds were locked" — this module has no independent way to observe
|
|
133
|
+
* a settlement rail. If the caller supplies a verified `settlementRail`
|
|
134
|
+
* observation, that is captured alongside the coordination evidence and
|
|
135
|
+
* labeled as independently observed, but it is still the caller's evidence,
|
|
136
|
+
* not something this adapter establishes.
|
|
137
|
+
*/
|
|
138
|
+
export declare function createTclkEvidence(transcript: TclkTranscriptResult, options: TclkEvidenceOptions): AgentEvidenceRecord;
|
|
139
|
+
//# sourceMappingURL=tclk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tclk.d.ts","sourceRoot":"","sources":["../src/tclk.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAA;AAIxB,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,2EAA2E;AAC3E,MAAM,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,qBAAqB,CAAA;IAC9B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,iGAAiG;AACjG,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,qBAAqB,CAAA;IAC9B,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5B,QAAQ,EAAE,OAAO,CAAA;IACjB,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,UAAU,CAAA;IACjB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,UAAU,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,CAAA;IACpD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,QAAQ,CAAA;IACd,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,sGAAsG;IACtG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAA;CACrC;AAID;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS,mBAAmB,EAAE,GACtC,oBAAoB,CAgGtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,mFAAmF;IACnF,MAAM,EAAE,UAAU,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,cAAc,CAAC,EAAE,yBAAyB,CAAA;CAC3C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,oBAAoB,EAChC,OAAO,EAAE,mBAAmB,GAC3B,mBAAmB,CAyErB"}
|
package/dist/tclk.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { applyFrame, decodeFrame, openContract, validateFrame, } from '@flop-labs/tclk';
|
|
2
|
+
import { contentId } from './canonical.js';
|
|
3
|
+
import { EvidenceValidationError } from './errors.js';
|
|
4
|
+
import { createRecord } from './records.js';
|
|
5
|
+
import { verifyTechnocoreMessage } from './technocore.js';
|
|
6
|
+
const TERMINAL_STATUSES = new Set(['claimed', 'refunded', 'cancelled']);
|
|
7
|
+
/**
|
|
8
|
+
* Verifies a Technocore-carried tclk/1 transcript end to end and replays it
|
|
9
|
+
* through the official `@flop-labs/tclk` state machine.
|
|
10
|
+
*
|
|
11
|
+
* Fails closed (throws `EvidenceValidationError`) when:
|
|
12
|
+
* - a message's Technocore transport signature does not verify
|
|
13
|
+
* - a message's text does not decode/validate as a tclk/1 frame
|
|
14
|
+
* - a frame's own `from` does not match its message's transport-verified DID
|
|
15
|
+
* - the transcript does not open with a valid `offer` frame, or a second
|
|
16
|
+
* `offer` appears mid-transcript (which contract would the rest belong to?)
|
|
17
|
+
*
|
|
18
|
+
* A frame that decodes and is honestly attributed but that the OFFICIAL state
|
|
19
|
+
* machine itself rejects (wrong sender for the transition, a replay, a
|
|
20
|
+
* duplicate, an out-of-order transition, a tampered `contract`/`ref` id that
|
|
21
|
+
* fails the machine's own recomputation) is NOT a fail-closed error here: per
|
|
22
|
+
* tclk's own spec (SPEC.md §2, §4), those are designed-in no-op rejections a
|
|
23
|
+
* reader in a world-writable room must expect, not evidence the transcript
|
|
24
|
+
* itself is corrupt. Every such rejection is still recorded in `steps` with its
|
|
25
|
+
* reason — it is never silently discarded — and does not advance contract state.
|
|
26
|
+
*
|
|
27
|
+
* Each entry supplies its own `atMs` (see `TclkTranscriptEntry`); deadline
|
|
28
|
+
* guards are evaluated per-transition against that entry's own time, not a
|
|
29
|
+
* single instant for the whole replay.
|
|
30
|
+
*/
|
|
31
|
+
export function verifyTclkTranscript(entries) {
|
|
32
|
+
if (entries.length === 0) {
|
|
33
|
+
throw new EvidenceValidationError('tclk transcript: no messages supplied');
|
|
34
|
+
}
|
|
35
|
+
const steps = [];
|
|
36
|
+
let offer = null;
|
|
37
|
+
let state = null;
|
|
38
|
+
for (const { message, atMs } of entries) {
|
|
39
|
+
if (!verifyTechnocoreMessage(message)) {
|
|
40
|
+
throw new EvidenceValidationError(`tclk transcript: message (room ${message.room}, nonce ${message.nonce}) has an invalid Technocore transport signature`);
|
|
41
|
+
}
|
|
42
|
+
let frame;
|
|
43
|
+
try {
|
|
44
|
+
frame = decodeFrame(message.text);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new EvidenceValidationError(`tclk transcript: message (room ${message.room}, nonce ${message.nonce}) is not a valid tclk/1 frame: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
if (frame.from !== message.did) {
|
|
50
|
+
throw new EvidenceValidationError(`tclk transcript: frame sender ${frame.from} does not match the transport-authenticated DID ${message.did}`);
|
|
51
|
+
}
|
|
52
|
+
if (frame.type === 'offer') {
|
|
53
|
+
if (offer !== null) {
|
|
54
|
+
throw new EvidenceValidationError('tclk transcript: a second offer frame cannot open a new contract mid-transcript');
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
offer = validateFrame(frame);
|
|
58
|
+
state = openContract(offer);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw new EvidenceValidationError(`tclk transcript: invalid opening offer: ${error instanceof Error ? error.message : String(error)}`);
|
|
62
|
+
}
|
|
63
|
+
steps.push({ message, frameType: frame.type, accepted: true });
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (state === null) {
|
|
67
|
+
throw new EvidenceValidationError('tclk transcript: no opening offer frame appears before a subsequent frame');
|
|
68
|
+
}
|
|
69
|
+
const result = applyFrame(state, frame, atMs);
|
|
70
|
+
if (!result.ok) {
|
|
71
|
+
// Official, designed-in rejection (replay, duplicate, wrong sender for
|
|
72
|
+
// this transition, tampered id, out-of-order transition, …) — recorded,
|
|
73
|
+
// never dropped, never treated as fatal to the whole transcript.
|
|
74
|
+
steps.push({ message, frameType: frame.type, accepted: false, reason: result.reason ?? 'rejected' });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
state = result.state;
|
|
78
|
+
steps.push({ message, frameType: frame.type, accepted: true });
|
|
79
|
+
}
|
|
80
|
+
if (offer === null || state === null) {
|
|
81
|
+
throw new EvidenceValidationError('tclk transcript: transcript never opened a contract');
|
|
82
|
+
}
|
|
83
|
+
const outcome = TERMINAL_STATUSES.has(state.status)
|
|
84
|
+
? state.status
|
|
85
|
+
: null;
|
|
86
|
+
return {
|
|
87
|
+
offer,
|
|
88
|
+
contract: state.contract ?? null,
|
|
89
|
+
status: state.status,
|
|
90
|
+
terminal: TERMINAL_STATUSES.has(state.status),
|
|
91
|
+
outcome,
|
|
92
|
+
payerDid: state.payerDid ?? null,
|
|
93
|
+
payeeDid: state.payeeDid ?? null,
|
|
94
|
+
amount: offer.amount,
|
|
95
|
+
asset: offer.asset,
|
|
96
|
+
lock: offer.lock,
|
|
97
|
+
offeredRails: offer.rails,
|
|
98
|
+
rail: state.rail ?? null,
|
|
99
|
+
railRef: state.railRef ?? null,
|
|
100
|
+
claimByMs: offer.claimByMs,
|
|
101
|
+
refundAfterMs: offer.refundAfterMs,
|
|
102
|
+
expiresMs: offer.expiresMs,
|
|
103
|
+
steps,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Converts a verified tclk transcript into schema-valid Agent Evidence. The
|
|
108
|
+
* transcript must already have passed `verifyTclkTranscript` — this function
|
|
109
|
+
* does not re-verify signatures or replay frames, only records what was found.
|
|
110
|
+
*
|
|
111
|
+
* A `lock` frame is reported as "payer asserted/announced lock on rail X",
|
|
112
|
+
* never as "funds were locked" — this module has no independent way to observe
|
|
113
|
+
* a settlement rail. If the caller supplies a verified `settlementRail`
|
|
114
|
+
* observation, that is captured alongside the coordination evidence and
|
|
115
|
+
* labeled as independently observed, but it is still the caller's evidence,
|
|
116
|
+
* not something this adapter establishes.
|
|
117
|
+
*/
|
|
118
|
+
export function createTclkEvidence(transcript, options) {
|
|
119
|
+
const lockNote = transcript.rail === null
|
|
120
|
+
? 'no lock frame observed in this transcript'
|
|
121
|
+
: `payer asserted/announced lock on rail "${transcript.rail}"` +
|
|
122
|
+
(transcript.railRef !== null ? ` (rail ref: ${transcript.railRef})` : '') +
|
|
123
|
+
'; this is the payer\'s claim, not independent proof that funds were escrowed';
|
|
124
|
+
const captured = {
|
|
125
|
+
protocol: 'tclk/1',
|
|
126
|
+
offer_id: transcript.offer.id,
|
|
127
|
+
contract_id: transcript.contract,
|
|
128
|
+
payer_did: transcript.payerDid,
|
|
129
|
+
payee_did: transcript.payeeDid,
|
|
130
|
+
amount: transcript.amount,
|
|
131
|
+
asset: transcript.asset,
|
|
132
|
+
lock_kind: transcript.lock,
|
|
133
|
+
offered_rails: [...transcript.offeredRails],
|
|
134
|
+
asserted_settlement_rail: transcript.rail,
|
|
135
|
+
asserted_settlement_rail_ref: transcript.railRef,
|
|
136
|
+
settlement_note: lockNote,
|
|
137
|
+
claim_by_ms: transcript.claimByMs,
|
|
138
|
+
refund_after_ms: transcript.refundAfterMs,
|
|
139
|
+
expires_ms: transcript.expiresMs,
|
|
140
|
+
transcript_status: transcript.status,
|
|
141
|
+
terminal: transcript.terminal,
|
|
142
|
+
outcome: transcript.outcome,
|
|
143
|
+
frame_sequence: transcript.steps.map((step) => ({
|
|
144
|
+
type: step.frameType,
|
|
145
|
+
from: step.message.did,
|
|
146
|
+
room: step.message.room,
|
|
147
|
+
nonce: step.message.nonce,
|
|
148
|
+
accepted: step.accepted,
|
|
149
|
+
reason: step.reason ?? null,
|
|
150
|
+
})),
|
|
151
|
+
...(options.settlementRail !== undefined
|
|
152
|
+
? {
|
|
153
|
+
independent_settlement_observation: {
|
|
154
|
+
rail: options.settlementRail.rail,
|
|
155
|
+
ref: options.settlementRail.ref,
|
|
156
|
+
observed_at: options.settlementRail.observedAt,
|
|
157
|
+
detail: options.settlementRail.detail,
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
: {}),
|
|
161
|
+
verification: 'technocore-transport-and-tclk-state-machine-verified',
|
|
162
|
+
};
|
|
163
|
+
const request = { offer_id: transcript.offer.id, contract_id: transcript.contract };
|
|
164
|
+
return createRecord('evidence', {
|
|
165
|
+
evidence_type: 'tclk-transcript',
|
|
166
|
+
run_ref: options.runRef,
|
|
167
|
+
trust_mode: 'agent-assertion',
|
|
168
|
+
source: { id: 'https://github.com/flop-labs/tclk', type: 'tclk-technocore-lock-protocol' },
|
|
169
|
+
tool: { name: 'onchaindiligence-tclk-adapter', version: options.toolVersion ?? '1' },
|
|
170
|
+
request: {
|
|
171
|
+
digest: { sha256: contentId(request).slice('sha256:'.length) },
|
|
172
|
+
media_type: 'application/vnd.tclk.transcript-request+json',
|
|
173
|
+
},
|
|
174
|
+
response: {
|
|
175
|
+
mode: 'embedded',
|
|
176
|
+
media_type: 'application/vnd.tclk.transcript+json',
|
|
177
|
+
value: captured,
|
|
178
|
+
digest: { sha256: contentId(captured).slice('sha256:'.length) },
|
|
179
|
+
},
|
|
180
|
+
observed_at: options.observedAt,
|
|
181
|
+
expires_at: options.expiresAt ?? null,
|
|
182
|
+
scope: {
|
|
183
|
+
offer_id: transcript.offer.id,
|
|
184
|
+
contract_id: transcript.contract,
|
|
185
|
+
payer_did: transcript.payerDid,
|
|
186
|
+
payee_did: transcript.payeeDid,
|
|
187
|
+
},
|
|
188
|
+
}, { parents: [options.runRef, ...options.messageEvidenceRefs] });
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=tclk.js.map
|
package/dist/tclk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tclk.js","sourceRoot":"","sources":["../src/tclk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AgentEvidenceRecord, JsonObject, KeyInput } from './types.js';
|
|
2
|
+
/** A stored Technocore signed-lane message. All fields are untrusted input. */
|
|
3
|
+
export interface TechnocoreSignedMessage {
|
|
4
|
+
did: string;
|
|
5
|
+
room: string;
|
|
6
|
+
nonce: string;
|
|
7
|
+
text: string;
|
|
8
|
+
sig: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Explicit server observations accompanying a message. Keep numeric server values
|
|
12
|
+
* as strings when they might exceed JavaScript's safe-integer range.
|
|
13
|
+
*/
|
|
14
|
+
export interface TechnocoreEvidenceOptions {
|
|
15
|
+
runRef: string;
|
|
16
|
+
observedAt: string;
|
|
17
|
+
parents?: readonly string[];
|
|
18
|
+
expiresAt?: string | null;
|
|
19
|
+
serverMetadata?: JsonObject;
|
|
20
|
+
toolVersion?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Mirrors Technocore's documented single-line sweep before it signs or stores text. */
|
|
23
|
+
export declare function sweepTechnocoreText(text: string): string;
|
|
24
|
+
/** The exact UTF-8 string verified by Technocore's signed message lane. */
|
|
25
|
+
export declare function technocoreSigningInput(message: Pick<TechnocoreSignedMessage, 'room' | 'nonce' | 'text'>): Uint8Array;
|
|
26
|
+
/** SHA-256 of the exact stored message text, encoded as unpadded base64url. */
|
|
27
|
+
export declare function technocoreTextDigest(text: string): string;
|
|
28
|
+
/** Derives the Ed25519 did:key identifier used by Technocore from a public key. */
|
|
29
|
+
export declare function technocoreDidFromPublicKey(publicKey: KeyInput): string;
|
|
30
|
+
/**
|
|
31
|
+
* Verifies a Technocore signed message entirely offline. A true result proves
|
|
32
|
+
* only that the embedded did:key signed this stored message, never that its text
|
|
33
|
+
* is true or safe to act on.
|
|
34
|
+
*/
|
|
35
|
+
export declare function verifyTechnocoreMessage(message: TechnocoreSignedMessage): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Converts a verified Technocore assertion into schema-valid Agent Evidence.
|
|
38
|
+
* The embedded message remains untrusted data and uses agent-assertion trust mode.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createTechnocoreEvidence(message: TechnocoreSignedMessage, options: TechnocoreEvidenceOptions): AgentEvidenceRecord;
|
|
41
|
+
//# sourceMappingURL=technocore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"technocore.d.ts","sourceRoot":"","sources":["../src/technocore.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAY3E,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,cAAc,CAAC,EAAE,UAAU,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,GAAG,UAAU,CAEpH;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,QAAQ,GAAG,MAAM,CAatE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAWjF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,yBAAyB,GACjC,mBAAmB,CAuCrB"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createHash, createPublicKey, KeyObject, verify } from 'node:crypto';
|
|
2
|
+
import { contentId } from './canonical.js';
|
|
3
|
+
import { EvidenceValidationError } from './errors.js';
|
|
4
|
+
import { createRecord } from './records.js';
|
|
5
|
+
const DID_PREFIX = 'did:key:';
|
|
6
|
+
const BASE58BTC_PREFIX = 'z';
|
|
7
|
+
const ED25519_MULTICODEC = Buffer.from([0xed, 0x01]);
|
|
8
|
+
const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
|
|
9
|
+
const BASE58BTC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
10
|
+
const ROOM = /^[a-z0-9][a-z0-9_-]{0,47}$/;
|
|
11
|
+
const NONCE = /^[0-9]{1,19}$/;
|
|
12
|
+
const SIGNATURE = /^[A-Za-z0-9_-]{86}$/;
|
|
13
|
+
const INVISIBLE = /[\p{Cc}\p{Cf}\p{Cs}\p{Co}\p{Zl}\p{Zp}]/gu;
|
|
14
|
+
/** Mirrors Technocore's documented single-line sweep before it signs or stores text. */
|
|
15
|
+
export function sweepTechnocoreText(text) {
|
|
16
|
+
return text.replace(INVISIBLE, ' ').trim();
|
|
17
|
+
}
|
|
18
|
+
/** The exact UTF-8 string verified by Technocore's signed message lane. */
|
|
19
|
+
export function technocoreSigningInput(message) {
|
|
20
|
+
return Buffer.from(`${message.room}|${message.nonce}|${message.text}`, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
/** SHA-256 of the exact stored message text, encoded as unpadded base64url. */
|
|
23
|
+
export function technocoreTextDigest(text) {
|
|
24
|
+
return createHash('sha256').update(text, 'utf8').digest('base64url');
|
|
25
|
+
}
|
|
26
|
+
/** Derives the Ed25519 did:key identifier used by Technocore from a public key. */
|
|
27
|
+
export function technocoreDidFromPublicKey(publicKey) {
|
|
28
|
+
const key = publicKey instanceof KeyObject && publicKey.type === 'public'
|
|
29
|
+
? publicKey
|
|
30
|
+
: createPublicKey(publicKey instanceof Uint8Array ? Buffer.from(publicKey) : publicKey);
|
|
31
|
+
const spki = key.export({ format: 'der', type: 'spki' });
|
|
32
|
+
if (!Buffer.isBuffer(spki) || spki.length !== ED25519_SPKI_PREFIX.length + 32 ||
|
|
33
|
+
!spki.subarray(0, ED25519_SPKI_PREFIX.length).equals(ED25519_SPKI_PREFIX)) {
|
|
34
|
+
throw new EvidenceValidationError('Technocore requires an Ed25519 public key');
|
|
35
|
+
}
|
|
36
|
+
return `${DID_PREFIX}${BASE58BTC_PREFIX}${base58Encode(Buffer.concat([
|
|
37
|
+
ED25519_MULTICODEC,
|
|
38
|
+
spki.subarray(ED25519_SPKI_PREFIX.length),
|
|
39
|
+
]))}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Verifies a Technocore signed message entirely offline. A true result proves
|
|
43
|
+
* only that the embedded did:key signed this stored message, never that its text
|
|
44
|
+
* is true or safe to act on.
|
|
45
|
+
*/
|
|
46
|
+
export function verifyTechnocoreMessage(message) {
|
|
47
|
+
try {
|
|
48
|
+
if (!ROOM.test(message.room) || !NONCE.test(message.nonce) || !SIGNATURE.test(message.sig))
|
|
49
|
+
return false;
|
|
50
|
+
if (message.text !== sweepTechnocoreText(message.text) || Array.from(message.text).length > 4096)
|
|
51
|
+
return false;
|
|
52
|
+
const signature = Buffer.from(message.sig, 'base64url');
|
|
53
|
+
if (signature.length !== 64 || signature.toString('base64url') !== message.sig)
|
|
54
|
+
return false;
|
|
55
|
+
const publicKey = publicKeyFromDid(message.did);
|
|
56
|
+
return verify(null, technocoreSigningInput(message), publicKey, signature);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Converts a verified Technocore assertion into schema-valid Agent Evidence.
|
|
64
|
+
* The embedded message remains untrusted data and uses agent-assertion trust mode.
|
|
65
|
+
*/
|
|
66
|
+
export function createTechnocoreEvidence(message, options) {
|
|
67
|
+
if (!verifyTechnocoreMessage(message)) {
|
|
68
|
+
throw new EvidenceValidationError('Technocore message has an invalid did:key signature or canonical form');
|
|
69
|
+
}
|
|
70
|
+
const captured = {
|
|
71
|
+
did: message.did,
|
|
72
|
+
room: message.room,
|
|
73
|
+
nonce: message.nonce,
|
|
74
|
+
text: message.text,
|
|
75
|
+
text_digest_sha256: technocoreTextDigest(message.text),
|
|
76
|
+
signature: message.sig,
|
|
77
|
+
signature_algorithm: 'ed25519',
|
|
78
|
+
signing_input: '<room>|<nonce>|<text-after-technocore-sweep>',
|
|
79
|
+
verification: 'valid-did-key-signature',
|
|
80
|
+
};
|
|
81
|
+
if (options.serverMetadata !== undefined)
|
|
82
|
+
captured.server_metadata = options.serverMetadata;
|
|
83
|
+
const request = { did: message.did, room: message.room, nonce: message.nonce };
|
|
84
|
+
return createRecord('evidence', {
|
|
85
|
+
evidence_type: 'technocore-signed-message',
|
|
86
|
+
run_ref: options.runRef,
|
|
87
|
+
trust_mode: 'agent-assertion',
|
|
88
|
+
source: { id: 'https://technocore.chat', type: 'technocore-chat' },
|
|
89
|
+
tool: { name: 'onchaindiligence-technocore-adapter', version: options.toolVersion ?? '1' },
|
|
90
|
+
request: {
|
|
91
|
+
digest: { sha256: contentId(request).slice('sha256:'.length) },
|
|
92
|
+
media_type: 'application/vnd.technocore.signed-message-request+json',
|
|
93
|
+
},
|
|
94
|
+
response: {
|
|
95
|
+
mode: 'embedded',
|
|
96
|
+
media_type: 'application/vnd.technocore.signed-message+json',
|
|
97
|
+
value: captured,
|
|
98
|
+
digest: { sha256: contentId(captured).slice('sha256:'.length) },
|
|
99
|
+
},
|
|
100
|
+
observed_at: options.observedAt,
|
|
101
|
+
expires_at: options.expiresAt ?? null,
|
|
102
|
+
scope: { did: message.did, room: message.room, nonce: message.nonce },
|
|
103
|
+
}, { parents: options.parents ?? [options.runRef] });
|
|
104
|
+
}
|
|
105
|
+
function publicKeyFromDid(did) {
|
|
106
|
+
if (!did.startsWith(`${DID_PREFIX}${BASE58BTC_PREFIX}`))
|
|
107
|
+
throw new EvidenceValidationError('not a base58btc did:key');
|
|
108
|
+
const decoded = base58Decode(did.slice(`${DID_PREFIX}${BASE58BTC_PREFIX}`.length));
|
|
109
|
+
if (decoded.length !== 34 || !decoded.subarray(0, 2).equals(ED25519_MULTICODEC)) {
|
|
110
|
+
throw new EvidenceValidationError('Technocore did:key is not an Ed25519 multicodec key');
|
|
111
|
+
}
|
|
112
|
+
return createPublicKey({
|
|
113
|
+
key: Buffer.concat([ED25519_SPKI_PREFIX, decoded.subarray(2)]),
|
|
114
|
+
format: 'der',
|
|
115
|
+
type: 'spki',
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function base58Encode(bytes) {
|
|
119
|
+
let value = 0n;
|
|
120
|
+
for (const byte of bytes)
|
|
121
|
+
value = (value << 8n) | BigInt(byte);
|
|
122
|
+
let encoded = '';
|
|
123
|
+
while (value > 0n) {
|
|
124
|
+
const remainder = Number(value % 58n);
|
|
125
|
+
encoded = BASE58BTC[remainder] + encoded;
|
|
126
|
+
value /= 58n;
|
|
127
|
+
}
|
|
128
|
+
for (const byte of bytes) {
|
|
129
|
+
if (byte !== 0)
|
|
130
|
+
break;
|
|
131
|
+
encoded = `1${encoded}`;
|
|
132
|
+
}
|
|
133
|
+
return encoded || '1';
|
|
134
|
+
}
|
|
135
|
+
function base58Decode(value) {
|
|
136
|
+
if (value.length === 0)
|
|
137
|
+
throw new EvidenceValidationError('empty base58btc identifier');
|
|
138
|
+
let decoded = 0n;
|
|
139
|
+
for (const character of value) {
|
|
140
|
+
const index = BASE58BTC.indexOf(character);
|
|
141
|
+
if (index < 0)
|
|
142
|
+
throw new EvidenceValidationError('invalid base58btc identifier');
|
|
143
|
+
decoded = decoded * 58n + BigInt(index);
|
|
144
|
+
}
|
|
145
|
+
let hex = decoded.toString(16);
|
|
146
|
+
if (hex.length % 2 !== 0)
|
|
147
|
+
hex = `0${hex}`;
|
|
148
|
+
const bytes = decoded === 0n ? Buffer.alloc(0) : Buffer.from(hex, 'hex');
|
|
149
|
+
let leadingZeros = 0;
|
|
150
|
+
for (const character of value) {
|
|
151
|
+
if (character !== '1')
|
|
152
|
+
break;
|
|
153
|
+
leadingZeros += 1;
|
|
154
|
+
}
|
|
155
|
+
return Buffer.concat([Buffer.alloc(leadingZeros), bytes]);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=technocore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"technocore.js","sourceRoot":"","sources":["../src/technocore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { generateKeyPairSync, sign } from 'node:crypto'
|
|
2
|
+
import { encodeFrame, generateHashLock, makeAccept, makeOffer } from '@flop-labs/tclk'
|
|
3
|
+
import {
|
|
4
|
+
createBundlePayload,
|
|
5
|
+
createEd25519Signer,
|
|
6
|
+
createKeyRecord,
|
|
7
|
+
createRecord,
|
|
8
|
+
createTclkEvidence,
|
|
9
|
+
createTechnocoreEvidence,
|
|
10
|
+
contentId,
|
|
11
|
+
sealBundle,
|
|
12
|
+
sweepTechnocoreText,
|
|
13
|
+
technocoreDidFromPublicKey,
|
|
14
|
+
technocoreSigningInput,
|
|
15
|
+
TrustPolicy,
|
|
16
|
+
verifyBundle,
|
|
17
|
+
verifyTclkTranscript,
|
|
18
|
+
} from '@onchaindiligence/agent-evidence'
|
|
19
|
+
|
|
20
|
+
// This is an offline example: no real money, no real Technocore room, no
|
|
21
|
+
// PTLC/adaptor-signature material (SAFE hash-lock path only). It shows the
|
|
22
|
+
// generic shape a real integration follows: two agents coordinate a deal as
|
|
23
|
+
// signed tclk/1 frames, OnChainDiligence captures the verified transcript as
|
|
24
|
+
// Agent Evidence, and — because no real settlement rail is wired up here — the
|
|
25
|
+
// resulting execution record says so honestly rather than claiming a payment
|
|
26
|
+
// happened.
|
|
27
|
+
const ROOM = 'tclk-offers'
|
|
28
|
+
function frameMessage(privateKey, did, nonce, frame) {
|
|
29
|
+
const text = sweepTechnocoreText(encodeFrame(frame))
|
|
30
|
+
const sig = sign(null, technocoreSigningInput({ room: ROOM, nonce, text }), privateKey).toString('base64url')
|
|
31
|
+
return { did, room: ROOM, nonce, text, sig }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const payerKey = generateKeyPairSync('ed25519')
|
|
35
|
+
const payeeKey = generateKeyPairSync('ed25519')
|
|
36
|
+
const payerDid = technocoreDidFromPublicKey(payerKey.publicKey)
|
|
37
|
+
const payeeDid = technocoreDidFromPublicKey(payeeKey.publicKey)
|
|
38
|
+
|
|
39
|
+
const nowMs = Date.parse('2026-09-02T12:00:00.000Z')
|
|
40
|
+
const claimByMs = nowMs + 60 * 60 * 1000
|
|
41
|
+
const refundAfterMs = claimByMs + 60 * 60 * 1000
|
|
42
|
+
const expiresMs = nowMs + 30 * 60 * 1000
|
|
43
|
+
|
|
44
|
+
// offer -> accept -> lock announcement -> reveal (hash-lock path only).
|
|
45
|
+
const offer = makeOffer({
|
|
46
|
+
from: payerDid, role: 'payer', amount: '1000000', asset: 'FLOP', lock: 'hash',
|
|
47
|
+
rails: ['flop-htlc'], claimByMs, refundAfterMs, expiresMs,
|
|
48
|
+
})
|
|
49
|
+
const hashLock = generateHashLock()
|
|
50
|
+
const accept = makeAccept(offer, { from: payeeDid, statement: hashLock.hash })
|
|
51
|
+
const lock = { type: 'lock', from: payerDid, contract: accept.contract, rail: 'flop-htlc', ref: 'paper-escrow-1' }
|
|
52
|
+
const reveal = { type: 'reveal', from: payeeDid, contract: accept.contract, secret: hashLock.preimage }
|
|
53
|
+
|
|
54
|
+
const offerMsg = frameMessage(payerKey.privateKey, payerDid, '1756814400001', offer)
|
|
55
|
+
const acceptMsg = frameMessage(payeeKey.privateKey, payeeDid, '1756814400002', accept)
|
|
56
|
+
const lockMsg = frameMessage(payerKey.privateKey, payerDid, '1756814400003', lock)
|
|
57
|
+
const revealMsg = frameMessage(payeeKey.privateKey, payeeDid, '1756814400004', reveal)
|
|
58
|
+
|
|
59
|
+
// Verify: transport signature + tclk frame validity + sender attribution +
|
|
60
|
+
// official state-machine replay, each independently.
|
|
61
|
+
const transcript = verifyTclkTranscript([
|
|
62
|
+
{ message: offerMsg, atMs: nowMs },
|
|
63
|
+
{ message: acceptMsg, atMs: nowMs },
|
|
64
|
+
{ message: lockMsg, atMs: nowMs },
|
|
65
|
+
{ message: revealMsg, atMs: claimByMs - 1000 },
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
const bundleKey = generateKeyPairSync('ed25519')
|
|
69
|
+
const key = createKeyRecord(bundleKey.publicKey, { validFrom: '2026-09-01T00:00:00.000Z' })
|
|
70
|
+
const principal = createRecord('principal', { principal_id: 'urn:example:ocd', principal_type: 'organization' })
|
|
71
|
+
const agent = createRecord('agent', {
|
|
72
|
+
agent_id: 'urn:example:ocd-tclk-agent', agent_version: '1', operator_ref: principal.id,
|
|
73
|
+
}, { parents: [principal.id] })
|
|
74
|
+
const mandate = createRecord('mandate', {
|
|
75
|
+
mandate_id: 'tclk-coordination-capture', principal_ref: principal.id, scope: { action: 'record-only' },
|
|
76
|
+
valid_from: '2026-09-01T00:00:00.000Z', valid_until: '2026-09-03T00:00:00.000Z',
|
|
77
|
+
}, { parents: [principal.id] })
|
|
78
|
+
const run = createRecord('run', {
|
|
79
|
+
run_external_id: 'tclk-example', agent_ref: agent.id, mandate_ref: mandate.id,
|
|
80
|
+
started_at: '2026-09-02T12:00:00.000Z',
|
|
81
|
+
}, { parents: [agent.id, mandate.id] })
|
|
82
|
+
|
|
83
|
+
// One Technocore evidence record per underlying signed message (reusing the
|
|
84
|
+
// existing Technocore adapter, not a second implementation of it) ...
|
|
85
|
+
const messageEvidence = [offerMsg, acceptMsg, lockMsg, revealMsg].map((message, i) =>
|
|
86
|
+
createTechnocoreEvidence(message, { runRef: run.id, observedAt: `2026-09-02T12:0${i}:00.000Z` }))
|
|
87
|
+
// ... plus one tclk-transcript evidence record summarizing the verified replay.
|
|
88
|
+
const tclkEvidence = createTclkEvidence(transcript, {
|
|
89
|
+
runRef: run.id, observedAt: '2026-09-02T12:05:00.000Z',
|
|
90
|
+
messageEvidenceRefs: messageEvidence.map((e) => e.id),
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const policyDocument = { action: 'accept-coordination-evidence-only', require_real_rail_for_settlement_claim: true }
|
|
94
|
+
const policy = createRecord('policy', {
|
|
95
|
+
policy_id: 'tclk-coordination-capture', version: '1', source: 'https://example.invalid/policy/tclk',
|
|
96
|
+
digest: { sha256: contentId(policyDocument).slice('sha256:'.length) },
|
|
97
|
+
effective_from: '2026-09-01T00:00:00.000Z', policy: policyDocument,
|
|
98
|
+
}, { parents: [run.id] })
|
|
99
|
+
|
|
100
|
+
const evidenceRefs = [...messageEvidence.map((e) => e.id), tclkEvidence.id]
|
|
101
|
+
const decision = createRecord('decision', {
|
|
102
|
+
decision_id: 'accept-tclk-coordination-evidence', run_ref: run.id, agent_ref: agent.id,
|
|
103
|
+
decision_type: 'tclk-transcript-review',
|
|
104
|
+
outcome: {
|
|
105
|
+
disposition: 'ACCEPT_COORDINATION_EVIDENCE',
|
|
106
|
+
authorized_to_execute: false,
|
|
107
|
+
reason: 'valid signed coordination is evidence of what the agents agreed/asserted, not proof of settlement',
|
|
108
|
+
},
|
|
109
|
+
evidence_refs: evidenceRefs,
|
|
110
|
+
policy_ref: policy.id, policy_digest: policy.statement.digest,
|
|
111
|
+
decided_at: '2026-09-02T12:06:00.000Z',
|
|
112
|
+
}, { parents: [run.id, policy.id, ...evidenceRefs] })
|
|
113
|
+
|
|
114
|
+
// PaperRail-equivalent here: no real rail is wired up, so execution must say so.
|
|
115
|
+
const execution = createRecord('execution', {
|
|
116
|
+
execution_id: 'no-real-value-settlement', decision_ref: decision.id, execution_type: 'no-external-action',
|
|
117
|
+
status: 'NO_REAL_VALUE_SETTLEMENT', submitted_at: '2026-09-02T12:06:01.000Z',
|
|
118
|
+
}, { parents: [decision.id] })
|
|
119
|
+
|
|
120
|
+
const payload = createBundlePayload(
|
|
121
|
+
[principal, agent, mandate, run, ...messageEvidence, tclkEvidence, policy, decision, execution],
|
|
122
|
+
{ createdAt: '2026-09-02T12:07:00.000Z' },
|
|
123
|
+
)
|
|
124
|
+
const bundle = await sealBundle(payload, createEd25519Signer(bundleKey.privateKey), { keys: [key] })
|
|
125
|
+
const report = verifyBundle(bundle, TrustPolicy.fromKeyRecords([key], { now: new Date('2026-09-02T12:08:00.000Z') }))
|
|
126
|
+
|
|
127
|
+
console.log(report.state, payload.bundle_id)
|
|
128
|
+
console.log('decision:', decision.statement.outcome.disposition)
|
|
129
|
+
console.log('execution:', execution.statement.status)
|
|
130
|
+
console.log('tclk transcript status:', transcript.status, '(terminal:', transcript.terminal + ')')
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { generateKeyPairSync, sign } from 'node:crypto'
|
|
2
|
+
import {
|
|
3
|
+
createBundlePayload,
|
|
4
|
+
createEd25519Signer,
|
|
5
|
+
createKeyRecord,
|
|
6
|
+
createRecord,
|
|
7
|
+
createTechnocoreEvidence,
|
|
8
|
+
contentId,
|
|
9
|
+
sealBundle,
|
|
10
|
+
sweepTechnocoreText,
|
|
11
|
+
technocoreDidFromPublicKey,
|
|
12
|
+
technocoreSigningInput,
|
|
13
|
+
TrustPolicy,
|
|
14
|
+
verifyBundle,
|
|
15
|
+
} from '@onchaindiligence/agent-evidence'
|
|
16
|
+
|
|
17
|
+
// This is an offline example. It never reads a room, follows message content,
|
|
18
|
+
// or sends a transaction. A real adapter caller passes the exact stored message
|
|
19
|
+
// returned by Technocore's JSON API and may retain server metadata alongside it.
|
|
20
|
+
const technocoreKey = generateKeyPairSync('ed25519')
|
|
21
|
+
const text = sweepTechnocoreText('A did:key signature establishes an assertion, not truth.')
|
|
22
|
+
const message = {
|
|
23
|
+
did: technocoreDidFromPublicKey(technocoreKey.publicKey),
|
|
24
|
+
room: 'ocd-evidence',
|
|
25
|
+
nonce: '1740000000001',
|
|
26
|
+
text,
|
|
27
|
+
sig: sign(null, technocoreSigningInput({ room: 'ocd-evidence', nonce: '1740000000001', text }), technocoreKey.privateKey)
|
|
28
|
+
.toString('base64url'),
|
|
29
|
+
}
|
|
30
|
+
const bundleKey = generateKeyPairSync('ed25519')
|
|
31
|
+
const key = createKeyRecord(bundleKey.publicKey, { validFrom: '2026-09-01T00:00:00.000Z' })
|
|
32
|
+
const principal = createRecord('principal', { principal_id: 'urn:example:ocd', principal_type: 'organization' })
|
|
33
|
+
const agent = createRecord('agent', {
|
|
34
|
+
agent_id: 'urn:example:ocd-technocore-agent', agent_version: '1', operator_ref: principal.id,
|
|
35
|
+
}, { parents: [principal.id] })
|
|
36
|
+
const mandate = createRecord('mandate', {
|
|
37
|
+
mandate_id: 'technocore-capture', principal_ref: principal.id, scope: { action: 'record-only' },
|
|
38
|
+
valid_from: '2026-09-01T00:00:00.000Z', valid_until: '2026-09-02T00:00:00.000Z',
|
|
39
|
+
}, { parents: [principal.id] })
|
|
40
|
+
const run = createRecord('run', {
|
|
41
|
+
run_external_id: 'technocore-example', agent_ref: agent.id, mandate_ref: mandate.id,
|
|
42
|
+
started_at: '2026-09-01T12:00:00.000Z',
|
|
43
|
+
}, { parents: [agent.id, mandate.id] })
|
|
44
|
+
const evidence = createTechnocoreEvidence(message, {
|
|
45
|
+
runRef: run.id, observedAt: '2026-09-01T12:00:01.000Z',
|
|
46
|
+
serverMetadata: { seq: '1', ts: '2026-09-01T12:00:01.123456Z' },
|
|
47
|
+
})
|
|
48
|
+
const policyDocument = { action: 'never-execute-from-technocore-content', require_signature: true }
|
|
49
|
+
const policy = createRecord('policy', {
|
|
50
|
+
policy_id: 'technocore-ingestion', version: '1', source: 'https://example.invalid/policy/technocore',
|
|
51
|
+
digest: { sha256: contentId(policyDocument).slice('sha256:'.length) },
|
|
52
|
+
effective_from: '2026-09-01T00:00:00.000Z', policy: policyDocument,
|
|
53
|
+
}, { parents: [run.id] })
|
|
54
|
+
const decision = createRecord('decision', {
|
|
55
|
+
decision_id: 'record-no-action', run_ref: run.id, agent_ref: agent.id, decision_type: 'technocore-message-review',
|
|
56
|
+
outcome: { execute: false, reason: 'signed assertion is not authorization or truth' }, evidence_refs: [evidence.id],
|
|
57
|
+
policy_ref: policy.id, policy_digest: policy.statement.digest, decided_at: '2026-09-01T12:00:02.000Z',
|
|
58
|
+
}, { parents: [run.id, evidence.id, policy.id] })
|
|
59
|
+
const execution = createRecord('execution', {
|
|
60
|
+
execution_id: 'non-execution', decision_ref: decision.id, execution_type: 'no-external-action',
|
|
61
|
+
status: 'withheld-not-submitted', submitted_at: '2026-09-01T12:00:03.000Z',
|
|
62
|
+
}, { parents: [decision.id] })
|
|
63
|
+
const payload = createBundlePayload(
|
|
64
|
+
[principal, agent, mandate, run, evidence, policy, decision, execution],
|
|
65
|
+
{ createdAt: '2026-09-01T12:00:04.000Z' },
|
|
66
|
+
)
|
|
67
|
+
const bundle = await sealBundle(payload, createEd25519Signer(bundleKey.privateKey), { keys: [key] })
|
|
68
|
+
const report = verifyBundle(bundle, TrustPolicy.fromKeyRecords([key], { now: new Date('2026-09-01T12:01:00.000Z') }))
|
|
69
|
+
console.log(report.state, payload.bundle_id, execution.statement.status)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onchaindiligence/agent-evidence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Deterministic Agent Evidence v0 construction, DSSE sealing, and offline tri-state verification for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"node": ">=20.19"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
+
"@flop-labs/tclk": "^0.1.0",
|
|
54
55
|
"ajv": "^8.20.0",
|
|
55
56
|
"ajv-formats": "^3.0.1"
|
|
56
57
|
},
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://onchaindiligence.com/schemas/agent-evidence/v0/agent-evidence-key-registry.schema.json",
|
|
4
|
+
"title": "Agent Evidence Interoperability Profile v1 — public signer key registry",
|
|
5
|
+
"description": "The recommended shape for GET /.well-known/agent-evidence-keys, as defined by docs/AGENT_EVIDENCE_INTEROP.md. This registry is public metadata: an issuer's own signer key_id, public key, and lifecycle -- never a private key. Hosting this endpoint is a discovery convenience only; it does not by itself make an issuer trustworthy, and a verifier's caller-supplied TrustPolicy remains the sole source of trust.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "issuer", "environment", "keys"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": { "const": 1 },
|
|
10
|
+
"issuer": { "$ref": "common.schema.json#/$defs/nonEmptyString" },
|
|
11
|
+
"environment": { "$ref": "common.schema.json#/$defs/nonEmptyString" },
|
|
12
|
+
"keys": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": { "$ref": "#/$defs/keyEntry" },
|
|
15
|
+
"maxItems": 256
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"$defs": {
|
|
20
|
+
"keyEntry": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"title": "One published signer key. key_id alone never establishes trust, and this record is a hint until a caller's TrustPolicy independently trusts it.",
|
|
23
|
+
"required": ["key_id", "algorithm", "public_key_pem", "valid_from", "valid_until", "revoked_at", "status"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"key_id": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^ed25519-[A-Za-z0-9_-]{16}$",
|
|
28
|
+
"description": "Self-certifying: derived as ed25519-<first 16 base64url chars of sha256(SPKI DER)>, the same convention Agent Evidence's own AttestationKey uses. A verifier SHOULD re-derive and reject a mismatch."
|
|
29
|
+
},
|
|
30
|
+
"algorithm": { "const": "Ed25519" },
|
|
31
|
+
"public_key_pem": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"pattern": "^-----BEGIN PUBLIC KEY-----\\n[A-Za-z0-9+/=\\n]+-----END PUBLIC KEY-----\\n?$",
|
|
34
|
+
"maxLength": 4096
|
|
35
|
+
},
|
|
36
|
+
"valid_from": {
|
|
37
|
+
"anyOf": [
|
|
38
|
+
{ "$ref": "common.schema.json#/$defs/timestamp" },
|
|
39
|
+
{ "type": "null" }
|
|
40
|
+
],
|
|
41
|
+
"description": "null means no defensible activation boundary is published; a verifier must then treat the key as UNVERIFIABLE, never VALID."
|
|
42
|
+
},
|
|
43
|
+
"valid_until": {
|
|
44
|
+
"anyOf": [
|
|
45
|
+
{ "$ref": "common.schema.json#/$defs/timestamp" },
|
|
46
|
+
{ "type": "null" }
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"revoked_at": {
|
|
50
|
+
"anyOf": [
|
|
51
|
+
{ "$ref": "common.schema.json#/$defs/timestamp" },
|
|
52
|
+
{ "type": "null" }
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"status": { "enum": ["active", "retired", "revoked", "compromised"] }
|
|
56
|
+
},
|
|
57
|
+
"additionalProperties": false,
|
|
58
|
+
"allOf": [
|
|
59
|
+
{
|
|
60
|
+
"if": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": { "status": { "enum": ["revoked", "compromised"] } },
|
|
63
|
+
"required": ["status"]
|
|
64
|
+
},
|
|
65
|
+
"then": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"required": ["revoked_at"],
|
|
68
|
+
"properties": { "revoked_at": { "$ref": "common.schema.json#/$defs/timestamp" } }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
package/schemas/catalog.json
CHANGED
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
"proof.schema.json",
|
|
9
9
|
"record.schema.json",
|
|
10
10
|
"bundle-payload.schema.json",
|
|
11
|
-
"portable-file.schema.json"
|
|
11
|
+
"portable-file.schema.json",
|
|
12
|
+
"agent-evidence-key-registry.schema.json"
|
|
12
13
|
],
|
|
13
14
|
"entry_points": {
|
|
14
15
|
"portable_file": "portable-file.schema.json",
|
|
15
16
|
"signed_payload": "bundle-payload.schema.json",
|
|
16
17
|
"record": "record.schema.json",
|
|
17
18
|
"proof": "proof.schema.json",
|
|
18
|
-
"trusted_key_record": "attestation-key.schema.json"
|
|
19
|
+
"trusted_key_record": "attestation-key.schema.json",
|
|
20
|
+
"agent_evidence_key_registry": "agent-evidence-key-registry.schema.json"
|
|
19
21
|
}
|
|
20
22
|
}
|