provenance-protocol 0.1.3 → 0.2.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/README.md +103 -22
- package/SPEC.md +483 -0
- package/package.json +23 -4
- package/schema/provenance-0.1.json +229 -0
- package/src/cli.js +196 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +54 -6
- package/src/verify.d.ts +85 -0
- package/src/verify.js +277 -0
- package/test-vectors/README.md +52 -0
- package/test-vectors/signatures-0.1.json +80 -0
package/README.md
CHANGED
|
@@ -1,9 +1,59 @@
|
|
|
1
1
|
# provenance-protocol
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Provenance Protocol: an open standard for declaring and verifying AI agent
|
|
4
|
+
identity — and the reference SDK that implements it.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
## The standard
|
|
7
|
+
|
|
8
|
+
| | |
|
|
9
|
+
|---|---|
|
|
10
|
+
| [**SPEC.md**](./SPEC.md) | The specification. `PROVENANCE.yml`, the field reference, the capability vocabulary, signing and verification, conformance. |
|
|
11
|
+
| [**schema/provenance-0.1.json**](./schema/provenance-0.1.json) | JSON Schema for validating a declaration. |
|
|
12
|
+
| [**test-vectors/**](./test-vectors/) | Normative signature vectors. Pass these and you interoperate. |
|
|
13
|
+
|
|
14
|
+
The specification is MIT-licensed and free to implement in any language,
|
|
15
|
+
without permission or notification. A declaration can be read, validated and
|
|
16
|
+
cryptographically verified **entirely offline** — no account, no API key, and
|
|
17
|
+
no call to any service, this one included. Indexes, monitors and attesters are
|
|
18
|
+
applications built on the standard, not part of it.
|
|
19
|
+
|
|
20
|
+
## Offline verification
|
|
21
|
+
|
|
22
|
+
No account, no API key, no call to any service — including this one.
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { verifyDeclaration } from 'provenance-protocol/verify';
|
|
26
|
+
import YAML from 'yaml';
|
|
27
|
+
|
|
28
|
+
const result = await verifyDeclaration(YAML.parse(fileContents), {
|
|
29
|
+
retrievedFrom: 'https://github.com/alice/research-assistant',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
result.valid // the signature verifies against the key in the file
|
|
33
|
+
result.location // 'match' | 'mismatch' | 'unchecked'
|
|
34
|
+
result.trustworthy // valid AND served from the location it claims
|
|
35
|
+
result.fingerprint // SHA-256 of the key — store it to detect rotation
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
A valid signature proves the declaration came from the holder of that private
|
|
39
|
+
key and has not been altered. It does **not** prove who that holder is, that
|
|
40
|
+
the declared capabilities are accurate, or that the declaration is current.
|
|
41
|
+
That is why `trustworthy` also requires the location check: a signature is only
|
|
42
|
+
the project owner's if the file was served from the project it names.
|
|
43
|
+
|
|
44
|
+
Also exported: `verifyChallenge()` for live proof of key control against a key
|
|
45
|
+
you already hold, `verifyRevocation()`, `checkLocation()` and `keyFingerprint()`.
|
|
46
|
+
|
|
47
|
+
Declarations are YAML — parse them with whatever library you already use and
|
|
48
|
+
pass the object. This module has no dependencies.
|
|
49
|
+
|
|
50
|
+
## The SDK
|
|
51
|
+
|
|
52
|
+
This package is one implementation. It adds what cannot be done offline:
|
|
53
|
+
looking up an agent's current standing — open incidents, revocation, drift —
|
|
54
|
+
before you let it in.
|
|
55
|
+
|
|
56
|
+
Drop it into any receiving system — marketplace, API, agent orchestrator.
|
|
7
57
|
|
|
8
58
|
```bash
|
|
9
59
|
npm install provenance-protocol
|
|
@@ -21,9 +71,10 @@ const trust = await provenance.check('provenance:github:alice/research-assistant
|
|
|
21
71
|
console.log(trust);
|
|
22
72
|
// {
|
|
23
73
|
// found: true,
|
|
74
|
+
// identity: 'verified', // 'inferred' | 'declared' | 'verified'
|
|
75
|
+
// identity_verified: true,
|
|
24
76
|
// declared: true,
|
|
25
77
|
// age_days: 142,
|
|
26
|
-
// confidence: 0.9,
|
|
27
78
|
// capabilities: ['read:web', 'write:summaries'],
|
|
28
79
|
// constraints: ['no:financial:transact', 'no:pii'],
|
|
29
80
|
// incidents: 0,
|
|
@@ -39,11 +90,11 @@ The most useful method for receiving systems.
|
|
|
39
90
|
|
|
40
91
|
```js
|
|
41
92
|
const result = await provenance.gate('provenance:github:alice/agent', {
|
|
42
|
-
requireDeclared: true,
|
|
93
|
+
requireDeclared: true, // must have PROVENANCE.yml
|
|
94
|
+
requireVerified: true, // must have identity_verified: true
|
|
43
95
|
requireConstraints: ['no:financial:transact', 'no:pii'], // must have committed to these
|
|
44
|
-
requireClean: true,
|
|
45
|
-
requireMinAge: 30,
|
|
46
|
-
requireMinConfidence: 0.7, // classification confidence
|
|
96
|
+
requireClean: true, // no open incidents
|
|
97
|
+
requireMinAge: 30, // must be at least 30 days old
|
|
47
98
|
});
|
|
48
99
|
|
|
49
100
|
if (!result.allowed) {
|
|
@@ -119,35 +170,42 @@ if (result.fallback) {
|
|
|
119
170
|
| Field | Type | Description |
|
|
120
171
|
|---|---|---|
|
|
121
172
|
| `found` | boolean | Agent exists in Provenance index |
|
|
122
|
-
| `
|
|
173
|
+
| `identity` | string | `'inferred'` \| `'declared'` \| `'verified'` — see below |
|
|
174
|
+
| `identity_verified` | boolean | Cryptographic key ownership confirmed against a public URL |
|
|
175
|
+
| `declared` | boolean | Agent has a PROVENANCE.yml (or registered via API with full fields) |
|
|
123
176
|
| `age_days` | number | Days since first indexed |
|
|
124
|
-
| `confidence` | number | 0–1 classification confidence |
|
|
125
177
|
| `capabilities` | string[] | What the agent declares it can do |
|
|
126
178
|
| `constraints` | string[] | What the agent has publicly committed never to do |
|
|
127
179
|
| `incidents` | number | Number of open incidents |
|
|
128
|
-
| `status` | string | active / suspended / removed |
|
|
180
|
+
| `status` | string | `active` / `suspended` / `removed` |
|
|
129
181
|
| `model` | object | `{ provider, model_id }` if declared |
|
|
130
182
|
| `public_key` | string\|null | Base64 Ed25519 public key, if registered |
|
|
131
|
-
| `identity_verified` | boolean | Cryptographic proof of key ownership confirmed |
|
|
132
183
|
| `ajp_endpoint` | string\|null | AJP job endpoint URL, if the agent accepts delegated jobs |
|
|
133
184
|
| `first_seen` | string | ISO date of first public appearance |
|
|
134
185
|
|
|
186
|
+
### Identity states
|
|
187
|
+
|
|
188
|
+
| State | Meaning |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `inferred` | Indexed by crawler from a public repo. No PROVENANCE.yml, no self-registration. |
|
|
191
|
+
| `declared` | Agent registered itself (or has PROVENANCE.yml) but without cryptographic key verification. |
|
|
192
|
+
| `verified` | Agent registered with a keypair and the public key was confirmed against a publicly fetchable PROVENANCE.yml. **Independently auditable** — anyone can re-verify without trusting the Provenance registry. |
|
|
193
|
+
|
|
135
194
|
---
|
|
136
195
|
|
|
137
196
|
## Registering your own agent
|
|
138
197
|
|
|
139
|
-
|
|
140
|
-
import { Provenance } from 'provenance-protocol';
|
|
141
|
-
import { generateProvenanceKeyPair, signChallenge } from 'provenance-protocol/keygen';
|
|
198
|
+
### Public repo (GitHub / HuggingFace / npm)
|
|
142
199
|
|
|
143
|
-
|
|
200
|
+
Push a `PROVENANCE.yml` containing your public key to the repo, then register. The server fetches the file and confirms the key — independently verifiable by anyone.
|
|
144
201
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Store privateKey as PROVENANCE_PRIVATE_KEY env var — never commit it
|
|
202
|
+
```js
|
|
203
|
+
import { generateProvenanceKeyPair, signForProvenance, signChallenge } from 'provenance-protocol/keygen';
|
|
148
204
|
|
|
149
|
-
|
|
205
|
+
const { publicKey, privateKey } = generateProvenanceKeyPair();
|
|
150
206
|
const provenanceId = 'provenance:github:your-org/your-agent';
|
|
207
|
+
|
|
208
|
+
// Add to PROVENANCE.yml → commit → push, then:
|
|
151
209
|
const signed_challenge = signChallenge(privateKey, provenanceId, 'REGISTER');
|
|
152
210
|
|
|
153
211
|
await provenance.register({
|
|
@@ -158,11 +216,34 @@ await provenance.register({
|
|
|
158
216
|
capabilities: ['read:web', 'write:summaries'],
|
|
159
217
|
constraints: ['no:pii'],
|
|
160
218
|
public_key: publicKey,
|
|
161
|
-
signed_challenge,
|
|
219
|
+
signed_challenge,
|
|
162
220
|
});
|
|
163
|
-
// → { created: true,
|
|
221
|
+
// → { created: true, agent: { identity: 'verified', identity_verified: true } }
|
|
164
222
|
```
|
|
165
223
|
|
|
224
|
+
### Private agent (no public repo)
|
|
225
|
+
|
|
226
|
+
Use `provenance:custom:` platform. Host your `PROVENANCE.yml` at any public URL you control and pass it as `url` — this makes the identity independently verifiable. Without a `url`, verification is registry-dependent (key control only).
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
const provenanceId = 'provenance:custom:your-org/your-agent';
|
|
230
|
+
const signed_challenge = signChallenge(privateKey, provenanceId, 'REGISTER');
|
|
231
|
+
|
|
232
|
+
await provenance.register({
|
|
233
|
+
id: provenanceId,
|
|
234
|
+
url: 'https://yourdomain.com/.well-known/provenance.yml', // optional but recommended
|
|
235
|
+
name: 'Your Agent',
|
|
236
|
+
description: 'What it does',
|
|
237
|
+
capabilities: ['read:web'],
|
|
238
|
+
constraints: ['no:pii'],
|
|
239
|
+
public_key: publicKey,
|
|
240
|
+
signed_challenge,
|
|
241
|
+
});
|
|
242
|
+
// → { created: true, agent: { identity: 'verified', identity_verified: true } }
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
See [getprovenance.dev/docs#ai-quickstart](https://getprovenance.dev/docs#ai-quickstart) for full automated scripts.
|
|
246
|
+
|
|
166
247
|
## Revoking a compromised key
|
|
167
248
|
|
|
168
249
|
```js
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# Provenance Protocol Specification
|
|
2
|
+
**Version 0.1**
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## What is PROVENANCE.yml?
|
|
7
|
+
|
|
8
|
+
`PROVENANCE.yml` is a single file that lives in the root of any AI agent's
|
|
9
|
+
repository. It tells the world — in a structured, machine-readable,
|
|
10
|
+
human-readable way — what this agent is, what it can do, what it will
|
|
11
|
+
never do, and who is responsible for it.
|
|
12
|
+
|
|
13
|
+
Think of it as the agent's public identity card.
|
|
14
|
+
Voluntary. Simple. Five minutes to add. Zero friction.
|
|
15
|
+
|
|
16
|
+
The filename is unambiguous by design. No existing project will already
|
|
17
|
+
have a file called `PROVENANCE.yml` for any other purpose. When you see
|
|
18
|
+
this file in a repository you know exactly what it is and why it exists.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why does this exist?
|
|
23
|
+
|
|
24
|
+
The agent internet is growing faster than the infrastructure to understand it.
|
|
25
|
+
Thousands of agents exist publicly — on GitHub, HuggingFace, npm, PyPI,
|
|
26
|
+
ClawMarket — but there is no standard way to answer basic questions:
|
|
27
|
+
|
|
28
|
+
- What does this agent actually do?
|
|
29
|
+
- What can it access? What will it never touch?
|
|
30
|
+
- Who built it and how do I reach them if something goes wrong?
|
|
31
|
+
- What model powers it, and has that changed recently?
|
|
32
|
+
- Has this agent's behavior been consistent over time?
|
|
33
|
+
|
|
34
|
+
`PROVENANCE.yml` makes these questions answerable — automatically, at scale,
|
|
35
|
+
without anyone needing to manually curate anything.
|
|
36
|
+
|
|
37
|
+
It is the `robots.txt` of the agent internet. A simple convention that
|
|
38
|
+
benefits everyone: developers get discoverability, receiving systems get
|
|
39
|
+
trust signals, the ecosystem gets a shared foundation.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## The File
|
|
44
|
+
|
|
45
|
+
Place a file named exactly `PROVENANCE.yml` in the root of your repository.
|
|
46
|
+
|
|
47
|
+
### Minimal valid PROVENANCE.yml
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
provenance: "0.1"
|
|
51
|
+
name: "Research Assistant"
|
|
52
|
+
description: "Searches the web and summarizes academic papers on a given topic."
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Three lines. That is the minimum. Everything else is optional but valuable.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### Complete PROVENANCE.yml
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
# PROVENANCE.yml
|
|
63
|
+
# Provenance Protocol v0.1
|
|
64
|
+
# https://getprovenance.dev/spec
|
|
65
|
+
|
|
66
|
+
provenance: "0.1"
|
|
67
|
+
|
|
68
|
+
# ── Identity ──────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
name: "Research Assistant"
|
|
71
|
+
version: "1.4.2"
|
|
72
|
+
description: >
|
|
73
|
+
Searches the web, retrieves academic papers, and produces structured
|
|
74
|
+
summaries with citations. Designed for researchers and analysts.
|
|
75
|
+
|
|
76
|
+
# ── Intelligence ──────────────────────────────────────────────────────────────
|
|
77
|
+
# When model changes, Provenance detects behavioral drift automatically
|
|
78
|
+
|
|
79
|
+
model:
|
|
80
|
+
provider: "anthropic"
|
|
81
|
+
model_id: "claude-sonnet-4-5"
|
|
82
|
+
|
|
83
|
+
# ── Capabilities — what this agent CAN do ─────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
capabilities:
|
|
86
|
+
- read:web
|
|
87
|
+
- read:pdf
|
|
88
|
+
- read:arxiv
|
|
89
|
+
- write:summaries
|
|
90
|
+
|
|
91
|
+
# ── Constraints — what this agent will NEVER do ───────────────────────────────
|
|
92
|
+
# These are public commitments. Recorded permanently. Violations are detectable.
|
|
93
|
+
|
|
94
|
+
constraints:
|
|
95
|
+
- no:write:external
|
|
96
|
+
- no:financial:transact
|
|
97
|
+
- no:pii
|
|
98
|
+
- no:delegate:agents
|
|
99
|
+
|
|
100
|
+
# ── Runtime ───────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
runtime:
|
|
103
|
+
type: "task" # task | persistent | scheduled
|
|
104
|
+
trigger: "api" # api | webhook | schedule | event
|
|
105
|
+
|
|
106
|
+
# ── Contact ───────────────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
contact:
|
|
109
|
+
name: "Alice Chen"
|
|
110
|
+
url: "https://alice.dev"
|
|
111
|
+
email: "agent-issues@alice.dev"
|
|
112
|
+
|
|
113
|
+
# ── Skills ────────────────────────────────────────────────────────────────────
|
|
114
|
+
# External skills this agent uses — Provenance tracks these as dependencies
|
|
115
|
+
|
|
116
|
+
skills:
|
|
117
|
+
- id: "web-search-001"
|
|
118
|
+
source: "skillsmp"
|
|
119
|
+
url: "https://skillsmp.com/skills/web-search-001"
|
|
120
|
+
|
|
121
|
+
# ── Provenance ────────────────────────────────────────────────────────────────
|
|
122
|
+
# Links this file to your entry in the Provenance index
|
|
123
|
+
|
|
124
|
+
provenance_id: "provenance:github:alice/research-assistant"
|
|
125
|
+
|
|
126
|
+
# ── Identity — optional, makes this file tamper-evident ───────────────────────
|
|
127
|
+
# Signature covers "<provenance_id>:<public_key>". See Signing and Verification.
|
|
128
|
+
|
|
129
|
+
identity:
|
|
130
|
+
public_key: "MCowBQYDK2VwAyEA..."
|
|
131
|
+
signature: "3n8Kd0vQ..."
|
|
132
|
+
algorithm: "ed25519"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Field Reference
|
|
138
|
+
|
|
139
|
+
### Required
|
|
140
|
+
|
|
141
|
+
| Field | Type | Description |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `provenance` | string | Spec version. Currently `"0.1"` |
|
|
144
|
+
| `name` | string | Human-readable name for this agent |
|
|
145
|
+
| `description` | string | What this agent does, in plain language |
|
|
146
|
+
|
|
147
|
+
### Recommended
|
|
148
|
+
|
|
149
|
+
| Field | Type | Description |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `version` | string | Semantic version of the agent |
|
|
152
|
+
| `model.provider` | string | `anthropic` `openai` `google` `mistral` `meta` `local` `other` |
|
|
153
|
+
| `model.model_id` | string | Specific model e.g. `claude-sonnet-4-5` |
|
|
154
|
+
| `capabilities` | list | What this agent can do |
|
|
155
|
+
| `constraints` | list | What this agent will never do — public commitment |
|
|
156
|
+
| `contact.name` | string | Who is responsible for this agent |
|
|
157
|
+
| `contact.url` | string | Where to learn more |
|
|
158
|
+
| `runtime.type` | string | `task` `persistent` `scheduled` |
|
|
159
|
+
|
|
160
|
+
### Optional
|
|
161
|
+
|
|
162
|
+
| Field | Type | Description |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| `contact.email` | string | Contact for issues or abuse reports |
|
|
165
|
+
| `skills` | list | SkillsMP or other skills this agent depends on |
|
|
166
|
+
| `delegates` | list | Sub-agents this orchestrator spawns |
|
|
167
|
+
| `provenance_id` | string | Links to your Provenance index entry |
|
|
168
|
+
| `runtime.trigger` | string | `api` `webhook` `schedule` `event` |
|
|
169
|
+
| `identity.public_key` | string | Base64 SPKI DER Ed25519 public key |
|
|
170
|
+
| `identity.signature` | string | Base64 Ed25519 signature over `<provenance_id>:<public_key>`. Optional — see Signing and Verification |
|
|
171
|
+
| `identity.algorithm` | string | Always `ed25519` in v0.1 |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Capability Vocabulary
|
|
176
|
+
|
|
177
|
+
Standard capability strings. Provenance indexes and filters by them.
|
|
178
|
+
Custom capabilities allowed — prefix with your domain: `acme:custom-capability`
|
|
179
|
+
|
|
180
|
+
### Read
|
|
181
|
+
```
|
|
182
|
+
read:web fetch public web content
|
|
183
|
+
read:files read local files
|
|
184
|
+
read:database query databases
|
|
185
|
+
read:email read email (requires auth)
|
|
186
|
+
read:calendar read calendar (requires auth)
|
|
187
|
+
read:code read code repositories
|
|
188
|
+
read:pdf parse PDF documents
|
|
189
|
+
read:images process images
|
|
190
|
+
read:audio process audio
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Write
|
|
194
|
+
```
|
|
195
|
+
write:files write local files
|
|
196
|
+
write:database write to databases
|
|
197
|
+
write:email send email
|
|
198
|
+
write:code modify code
|
|
199
|
+
write:summaries generate written content
|
|
200
|
+
write:external any external system write
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Execute
|
|
204
|
+
```
|
|
205
|
+
execute:code run code in a sandbox
|
|
206
|
+
execute:terminal run terminal commands
|
|
207
|
+
execute:browser control a browser
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Financial
|
|
211
|
+
```
|
|
212
|
+
financial:read read financial data
|
|
213
|
+
financial:transact initiate financial transactions
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Delegation
|
|
217
|
+
```
|
|
218
|
+
delegate:agents can spawn or hire sub-agents
|
|
219
|
+
delegate:humans can request human approval
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Constraints (no: prefix)
|
|
223
|
+
|
|
224
|
+
Any capability prefixed with `no:` is a public constraint.
|
|
225
|
+
A commitment that this agent will never exercise that capability.
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
constraints:
|
|
229
|
+
- no:financial:transact # will never initiate transactions
|
|
230
|
+
- no:write:external # will never write to external systems
|
|
231
|
+
- no:delegate:agents # will never spawn sub-agents
|
|
232
|
+
- no:pii # will never collect personal data
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Constraints are the most powerful field in PROVENANCE.yml.**
|
|
236
|
+
A receiving system that requires `no:financial:transact` can filter for it.
|
|
237
|
+
An agent that publicly commits to a constraint and violates it gets a
|
|
238
|
+
permanent incident on its Provenance record. The commitment is real.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Provenance IDs
|
|
243
|
+
|
|
244
|
+
Every agent in the Provenance index has a stable identifier derived from
|
|
245
|
+
where it lives publicly. No registration required — the ID is computed
|
|
246
|
+
deterministically from the public URL.
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
provenance:github:owner/repo
|
|
250
|
+
provenance:npm:@scope/package-name
|
|
251
|
+
provenance:pypi:package-name
|
|
252
|
+
provenance:huggingface:owner/space-name
|
|
253
|
+
provenance:clawmarket:listing-id
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Add `provenance_id` to your PROVENANCE.yml to link your file to your
|
|
257
|
+
index entry and claim your agent profile.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## How Provenance uses PROVENANCE.yml
|
|
262
|
+
|
|
263
|
+
When Provenance discovers or crawls an agent repository it:
|
|
264
|
+
|
|
265
|
+
1. Reads `PROVENANCE.yml` if present
|
|
266
|
+
2. Hashes the file content — future changes are detectable
|
|
267
|
+
3. Records the discovery in the tamper-evident public log
|
|
268
|
+
4. Indexes capabilities and constraints for search
|
|
269
|
+
5. Monitors for changes — what changed, when, from what to what
|
|
270
|
+
6. Computes the Provenance ID from the repository URL
|
|
271
|
+
7. Surfaces the agent in search results for capability queries
|
|
272
|
+
|
|
273
|
+
Without PROVENANCE.yml, Provenance still indexes the agent from code
|
|
274
|
+
signals — framework imports, package keywords, repository topics — but
|
|
275
|
+
with lower confidence. Adding PROVENANCE.yml upgrades the profile from
|
|
276
|
+
inferred to declared.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Signing and Verification
|
|
281
|
+
|
|
282
|
+
A `PROVENANCE.yml` file is readable by anyone, which also means it is
|
|
283
|
+
editable by anyone who re-hosts it. The optional `identity` block makes a
|
|
284
|
+
declaration **tamper-evident**: it proves the file was produced by whoever
|
|
285
|
+
holds a particular private key, and that nobody has altered it since.
|
|
286
|
+
|
|
287
|
+
Verification requires no network access and no account. Any implementation
|
|
288
|
+
can perform it offline.
|
|
289
|
+
|
|
290
|
+
### The identity block
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
provenance_id: "provenance:github:alice/research-assistant"
|
|
294
|
+
|
|
295
|
+
identity:
|
|
296
|
+
public_key: "MCowBQYDK2VwAyEA..." # base64 of SPKI DER Ed25519 public key
|
|
297
|
+
signature: "3n8Kd0vQ..." # base64 of raw 64-byte Ed25519 signature
|
|
298
|
+
algorithm: "ed25519" # always ed25519 in v0.1
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`public_key` is required when `identity` is present. `algorithm` is optional
|
|
302
|
+
and defaults to `ed25519`; no other value is valid in v0.1.
|
|
303
|
+
|
|
304
|
+
`signature` is optional, and its presence changes what the block means:
|
|
305
|
+
|
|
306
|
+
- **`public_key` alone** advertises the key this agent will use to prove
|
|
307
|
+
control live, through the challenge-response below. The declaration itself
|
|
308
|
+
is not tamper-evident. This is the path for an agent with no public
|
|
309
|
+
repository, where there is no stable location to sign against.
|
|
310
|
+
- **`public_key` with `signature`** additionally makes the declaration
|
|
311
|
+
tamper-evident. This requires `provenance_id`, since the identity is part
|
|
312
|
+
of what gets signed.
|
|
313
|
+
|
|
314
|
+
Prefer the signed form wherever the agent has a public location. A verifier
|
|
315
|
+
that finds a key with no signature has learned which key to challenge, and
|
|
316
|
+
nothing about whether the file has been altered.
|
|
317
|
+
|
|
318
|
+
### What gets signed
|
|
319
|
+
|
|
320
|
+
The signed message is the UTF-8 encoding of:
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
<provenance_id>:<public_key>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
where `<public_key>` is the same base64 string that appears in
|
|
327
|
+
`identity.public_key`. Concatenating the two binds the key to the identity,
|
|
328
|
+
so a key lifted from one declaration cannot be replayed under a different
|
|
329
|
+
`provenance_id`.
|
|
330
|
+
|
|
331
|
+
Signing a declaration therefore requires `provenance_id` to be present.
|
|
332
|
+
|
|
333
|
+
### How to verify
|
|
334
|
+
|
|
335
|
+
1. Read `provenance_id` and the `identity` block.
|
|
336
|
+
2. Reconstruct the message as `<provenance_id>:<public_key>`.
|
|
337
|
+
3. Import `public_key` as an Ed25519 SPKI DER key.
|
|
338
|
+
4. Verify `signature` over the message bytes.
|
|
339
|
+
|
|
340
|
+
Ed25519 is deterministic (RFC 8032), so a correct implementation produces
|
|
341
|
+
byte-identical signatures for the same key and message. The test vectors in
|
|
342
|
+
`test-vectors/signatures-0.1.json` let you confirm this without contacting
|
|
343
|
+
anyone.
|
|
344
|
+
|
|
345
|
+
### What verification proves — and what it does not
|
|
346
|
+
|
|
347
|
+
A valid signature proves:
|
|
348
|
+
|
|
349
|
+
- the declaration was produced by the holder of that private key, and
|
|
350
|
+
- not one byte of `provenance_id` or `public_key` has changed since.
|
|
351
|
+
|
|
352
|
+
A valid signature does **not** prove:
|
|
353
|
+
|
|
354
|
+
- that the key belongs to any particular person or organisation, or
|
|
355
|
+
- that the declared capabilities or constraints are accurate, or
|
|
356
|
+
- that the declaration is still current.
|
|
357
|
+
|
|
358
|
+
Binding a key to a real-world identity is a separate step. Two mechanisms
|
|
359
|
+
are available, and they compose:
|
|
360
|
+
|
|
361
|
+
**Location.** A declaration fetched from the repository named by its own
|
|
362
|
+
`provenance_id` was placed there by someone with write access to it. A
|
|
363
|
+
declaration whose `provenance_id` does not match where it was found should
|
|
364
|
+
be treated as unverified regardless of its signature — this is the
|
|
365
|
+
re-hosting case the substituted-key test vector covers.
|
|
366
|
+
|
|
367
|
+
**Continuity.** Once a key has been seen for a `provenance_id`, a later
|
|
368
|
+
declaration signed by a different key is a key rotation and must be treated
|
|
369
|
+
as a material change, not a silent update.
|
|
370
|
+
|
|
371
|
+
This is the same division of labour as a machine-readable passport: the
|
|
372
|
+
document proves its own integrity offline, while identity binding and
|
|
373
|
+
current standing are looked up.
|
|
374
|
+
|
|
375
|
+
### Live proof of key control
|
|
376
|
+
|
|
377
|
+
A signature on a file proves the file's origin. It does not prove that the
|
|
378
|
+
agent running right now controls that key. For that, a receiving system
|
|
379
|
+
issues a nonce and the agent returns a signature over:
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
<provenance_id>:<nonce>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Nonces must be single-use and unpredictable. The receiving system verifies
|
|
386
|
+
the signature against the public key it already holds for that
|
|
387
|
+
`provenance_id`.
|
|
388
|
+
|
|
389
|
+
### Revocation
|
|
390
|
+
|
|
391
|
+
A key holder revokes a `provenance_id` by signing:
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
<provenance_id>:REVOKE
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Revocation is the one operation that cannot be verified offline — a verifier
|
|
398
|
+
has no way to know a revocation has been issued without asking. Implementations
|
|
399
|
+
that cache public keys should re-check revocation status on the same cadence
|
|
400
|
+
they would re-check any other freshness signal.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Conformance
|
|
405
|
+
|
|
406
|
+
An implementation of this specification is conformant if it:
|
|
407
|
+
|
|
408
|
+
1. Accepts every file that validates against `schema/provenance-0.1.json`.
|
|
409
|
+
2. Treats `provenance`, `name` and `description` as required and everything
|
|
410
|
+
else as optional.
|
|
411
|
+
3. Ignores unrecognised top-level fields rather than rejecting the file.
|
|
412
|
+
Future spec versions add fields; a `0.1` reader must not break on them.
|
|
413
|
+
4. Treats `identity.signature` as optional, and an unsigned `identity`
|
|
414
|
+
block as advertising a key rather than attesting the file.
|
|
415
|
+
5. Reproduces every signature in `test-vectors/signatures-0.1.json` marked
|
|
416
|
+
`valid`, and refuses every one marked `invalid`.
|
|
417
|
+
6. Treats a declaration whose `provenance_id` does not match its retrieval
|
|
418
|
+
location as unverified.
|
|
419
|
+
|
|
420
|
+
Points 5 and 6 are what make independent implementations agree. An
|
|
421
|
+
implementation that passes the test vectors interoperates with every other
|
|
422
|
+
one that does, with no reference to any particular service.
|
|
423
|
+
|
|
424
|
+
A reference implementation of offline verification ships in this repository as
|
|
425
|
+
`provenance-protocol/verify`. It is one implementation, not the definition —
|
|
426
|
+
the vectors are the definition.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Implementing this specification
|
|
431
|
+
|
|
432
|
+
This specification, its JSON Schema and its test vectors are published under
|
|
433
|
+
the MIT Licence. You may implement them in any language, for any purpose,
|
|
434
|
+
commercial or otherwise, without permission, notification or fee.
|
|
435
|
+
|
|
436
|
+
Nothing in this specification requires contacting any particular service. A
|
|
437
|
+
conformant implementation can read, validate and cryptographically verify a
|
|
438
|
+
declaration entirely offline. Services built on this protocol — indexes,
|
|
439
|
+
monitors, attesters — are applications of the standard, not part of it.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Versioning
|
|
444
|
+
|
|
445
|
+
The `provenance` field records which spec version you are using.
|
|
446
|
+
We commit to backwards compatibility — a `0.1` file will always be
|
|
447
|
+
readable regardless of future spec versions.
|
|
448
|
+
New versions add fields. Existing fields are never removed.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## FAQ
|
|
453
|
+
|
|
454
|
+
**Do I have to register anywhere?**
|
|
455
|
+
No. Provenance discovers agents automatically. Adding PROVENANCE.yml
|
|
456
|
+
improves your profile but requires no account, no API key, no permission.
|
|
457
|
+
|
|
458
|
+
**What if I already have a file in my repo I want to keep private?**
|
|
459
|
+
PROVENANCE.yml is only read from public repositories. Private repos
|
|
460
|
+
are not crawled.
|
|
461
|
+
|
|
462
|
+
**What if my constraints are inaccurate?**
|
|
463
|
+
Constraints are public commitments. Be honest. A constraint you declare
|
|
464
|
+
but violate becomes a permanent incident on your record.
|
|
465
|
+
|
|
466
|
+
**Can I use custom capability strings?**
|
|
467
|
+
Yes. Prefix with your domain: `acme:internal-tool`. Standard vocabulary
|
|
468
|
+
recommended for interoperability.
|
|
469
|
+
|
|
470
|
+
**What happens when I change my model?**
|
|
471
|
+
Provenance detects it from the repository commit and from behavioral
|
|
472
|
+
fingerprinting. The change is recorded — not penalized. Transparency
|
|
473
|
+
is the point.
|
|
474
|
+
|
|
475
|
+
**What if my agent is on HuggingFace, not GitHub?**
|
|
476
|
+
Same process. Add PROVENANCE.yml to your Space or model repository root.
|
|
477
|
+
Provenance crawls HuggingFace the same way it crawls GitHub.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
*Provenance Protocol v0.1 — MIT License*
|
|
482
|
+
*https://getprovenance.dev*
|
|
483
|
+
*https://github.com/ilucky21c/provenance-protocol*
|