provenance-protocol 0.1.3 → 0.1.4
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 +50 -19
- package/package.json +5 -1
- package/src/cli.js +196 -0
package/README.md
CHANGED
|
@@ -21,9 +21,10 @@ const trust = await provenance.check('provenance:github:alice/research-assistant
|
|
|
21
21
|
console.log(trust);
|
|
22
22
|
// {
|
|
23
23
|
// found: true,
|
|
24
|
+
// identity: 'verified', // 'inferred' | 'declared' | 'verified'
|
|
25
|
+
// identity_verified: true,
|
|
24
26
|
// declared: true,
|
|
25
27
|
// age_days: 142,
|
|
26
|
-
// confidence: 0.9,
|
|
27
28
|
// capabilities: ['read:web', 'write:summaries'],
|
|
28
29
|
// constraints: ['no:financial:transact', 'no:pii'],
|
|
29
30
|
// incidents: 0,
|
|
@@ -39,11 +40,11 @@ The most useful method for receiving systems.
|
|
|
39
40
|
|
|
40
41
|
```js
|
|
41
42
|
const result = await provenance.gate('provenance:github:alice/agent', {
|
|
42
|
-
requireDeclared: true,
|
|
43
|
+
requireDeclared: true, // must have PROVENANCE.yml
|
|
44
|
+
requireVerified: true, // must have identity_verified: true
|
|
43
45
|
requireConstraints: ['no:financial:transact', 'no:pii'], // must have committed to these
|
|
44
|
-
requireClean: true,
|
|
45
|
-
requireMinAge: 30,
|
|
46
|
-
requireMinConfidence: 0.7, // classification confidence
|
|
46
|
+
requireClean: true, // no open incidents
|
|
47
|
+
requireMinAge: 30, // must be at least 30 days old
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
if (!result.allowed) {
|
|
@@ -119,35 +120,42 @@ if (result.fallback) {
|
|
|
119
120
|
| Field | Type | Description |
|
|
120
121
|
|---|---|---|
|
|
121
122
|
| `found` | boolean | Agent exists in Provenance index |
|
|
122
|
-
| `
|
|
123
|
+
| `identity` | string | `'inferred'` \| `'declared'` \| `'verified'` — see below |
|
|
124
|
+
| `identity_verified` | boolean | Cryptographic key ownership confirmed against a public URL |
|
|
125
|
+
| `declared` | boolean | Agent has a PROVENANCE.yml (or registered via API with full fields) |
|
|
123
126
|
| `age_days` | number | Days since first indexed |
|
|
124
|
-
| `confidence` | number | 0–1 classification confidence |
|
|
125
127
|
| `capabilities` | string[] | What the agent declares it can do |
|
|
126
128
|
| `constraints` | string[] | What the agent has publicly committed never to do |
|
|
127
129
|
| `incidents` | number | Number of open incidents |
|
|
128
|
-
| `status` | string | active / suspended / removed |
|
|
130
|
+
| `status` | string | `active` / `suspended` / `removed` |
|
|
129
131
|
| `model` | object | `{ provider, model_id }` if declared |
|
|
130
132
|
| `public_key` | string\|null | Base64 Ed25519 public key, if registered |
|
|
131
|
-
| `identity_verified` | boolean | Cryptographic proof of key ownership confirmed |
|
|
132
133
|
| `ajp_endpoint` | string\|null | AJP job endpoint URL, if the agent accepts delegated jobs |
|
|
133
134
|
| `first_seen` | string | ISO date of first public appearance |
|
|
134
135
|
|
|
136
|
+
### Identity states
|
|
137
|
+
|
|
138
|
+
| State | Meaning |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `inferred` | Indexed by crawler from a public repo. No PROVENANCE.yml, no self-registration. |
|
|
141
|
+
| `declared` | Agent registered itself (or has PROVENANCE.yml) but without cryptographic key verification. |
|
|
142
|
+
| `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. |
|
|
143
|
+
|
|
135
144
|
---
|
|
136
145
|
|
|
137
146
|
## Registering your own agent
|
|
138
147
|
|
|
139
|
-
|
|
140
|
-
import { Provenance } from 'provenance-protocol';
|
|
141
|
-
import { generateProvenanceKeyPair, signChallenge } from 'provenance-protocol/keygen';
|
|
148
|
+
### Public repo (GitHub / HuggingFace / npm)
|
|
142
149
|
|
|
143
|
-
|
|
150
|
+
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
151
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Store privateKey as PROVENANCE_PRIVATE_KEY env var — never commit it
|
|
152
|
+
```js
|
|
153
|
+
import { generateProvenanceKeyPair, signForProvenance, signChallenge } from 'provenance-protocol/keygen';
|
|
148
154
|
|
|
149
|
-
|
|
155
|
+
const { publicKey, privateKey } = generateProvenanceKeyPair();
|
|
150
156
|
const provenanceId = 'provenance:github:your-org/your-agent';
|
|
157
|
+
|
|
158
|
+
// Add to PROVENANCE.yml → commit → push, then:
|
|
151
159
|
const signed_challenge = signChallenge(privateKey, provenanceId, 'REGISTER');
|
|
152
160
|
|
|
153
161
|
await provenance.register({
|
|
@@ -158,11 +166,34 @@ await provenance.register({
|
|
|
158
166
|
capabilities: ['read:web', 'write:summaries'],
|
|
159
167
|
constraints: ['no:pii'],
|
|
160
168
|
public_key: publicKey,
|
|
161
|
-
signed_challenge,
|
|
169
|
+
signed_challenge,
|
|
162
170
|
});
|
|
163
|
-
// → { created: true,
|
|
171
|
+
// → { created: true, agent: { identity: 'verified', identity_verified: true } }
|
|
164
172
|
```
|
|
165
173
|
|
|
174
|
+
### Private agent (no public repo)
|
|
175
|
+
|
|
176
|
+
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).
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const provenanceId = 'provenance:custom:your-org/your-agent';
|
|
180
|
+
const signed_challenge = signChallenge(privateKey, provenanceId, 'REGISTER');
|
|
181
|
+
|
|
182
|
+
await provenance.register({
|
|
183
|
+
id: provenanceId,
|
|
184
|
+
url: 'https://yourdomain.com/.well-known/provenance.yml', // optional but recommended
|
|
185
|
+
name: 'Your Agent',
|
|
186
|
+
description: 'What it does',
|
|
187
|
+
capabilities: ['read:web'],
|
|
188
|
+
constraints: ['no:pii'],
|
|
189
|
+
public_key: publicKey,
|
|
190
|
+
signed_challenge,
|
|
191
|
+
});
|
|
192
|
+
// → { created: true, agent: { identity: 'verified', identity_verified: true } }
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
See [getprovenance.dev/docs#ai-quickstart](https://getprovenance.dev/docs#ai-quickstart) for full automated scripts.
|
|
196
|
+
|
|
166
197
|
## Revoking a compromised key
|
|
167
198
|
|
|
168
199
|
```js
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "provenance-protocol",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "SDK for querying the Provenance agent identity index",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"types": "./src/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"provenance": "./src/cli.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./src/index.d.ts",
|
|
@@ -20,6 +23,7 @@
|
|
|
20
23
|
"src/index.d.ts",
|
|
21
24
|
"src/keygen.js",
|
|
22
25
|
"src/keygen.d.ts",
|
|
26
|
+
"src/cli.js",
|
|
23
27
|
"README.md"
|
|
24
28
|
],
|
|
25
29
|
"keywords": [
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* provenance CLI
|
|
4
|
+
*
|
|
5
|
+
* npx provenance keygen
|
|
6
|
+
* npx provenance register --id provenance:github:org/agent --url https://...
|
|
7
|
+
* npx provenance status provenance:github:org/agent
|
|
8
|
+
* npx provenance revoke --id provenance:github:org/agent
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { execSync } from 'child_process';
|
|
13
|
+
import { generateProvenanceKeyPair, signChallenge, signForProvenance, signRevocation } from './keygen.js';
|
|
14
|
+
|
|
15
|
+
const API = 'https://getprovenance.dev/api/agents';
|
|
16
|
+
const KEY_FILE = '.provenance-key';
|
|
17
|
+
|
|
18
|
+
function readPrivateKey() {
|
|
19
|
+
if (process.env.PROVENANCE_PRIVATE_KEY) return process.env.PROVENANCE_PRIVATE_KEY.trim();
|
|
20
|
+
if (existsSync(KEY_FILE)) return readFileSync(KEY_FILE, 'utf8').trim();
|
|
21
|
+
console.error(`No private key found. Set PROVENANCE_PRIVATE_KEY or run: npx provenance keygen`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseArgs(argv) {
|
|
26
|
+
const args = {};
|
|
27
|
+
for (let i = 0; i < argv.length; i++) {
|
|
28
|
+
if (argv[i].startsWith('--')) {
|
|
29
|
+
args[argv[i].slice(2)] = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true;
|
|
30
|
+
} else {
|
|
31
|
+
args._ = args._ || [];
|
|
32
|
+
args._.push(argv[i]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return args;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const [,, command, ...rest] = process.argv;
|
|
39
|
+
const args = parseArgs(rest);
|
|
40
|
+
|
|
41
|
+
// ── keygen ──────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
if (command === 'keygen') {
|
|
44
|
+
const { publicKey, privateKey } = generateProvenanceKeyPair();
|
|
45
|
+
|
|
46
|
+
writeFileSync(KEY_FILE, privateKey, { mode: 0o600 });
|
|
47
|
+
try { execSync(`grep -qxF "${KEY_FILE}" .gitignore 2>/dev/null || echo "${KEY_FILE}" >> .gitignore`); } catch {}
|
|
48
|
+
|
|
49
|
+
console.log(`\nKeypair generated.\n`);
|
|
50
|
+
console.log(`Private key → ${KEY_FILE} (chmod 600, added to .gitignore)`);
|
|
51
|
+
console.log(`\nPublic key (add to PROVENANCE.yml):\n`);
|
|
52
|
+
console.log(` identity:`);
|
|
53
|
+
console.log(` public_key: "${publicKey}"`);
|
|
54
|
+
console.log(` algorithm: ed25519`);
|
|
55
|
+
console.log(`\nNext: npx provenance register --id provenance:<platform>:<org>/<name>\n`);
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── register ─────────────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
if (command === 'register') {
|
|
62
|
+
const id = args.id || args._?.[0];
|
|
63
|
+
if (!id) { console.error('Usage: npx provenance register --id provenance:<platform>:<org>/<name> [options]'); process.exit(1); }
|
|
64
|
+
|
|
65
|
+
const privateKey = readPrivateKey();
|
|
66
|
+
const { publicKey } = (() => {
|
|
67
|
+
// derive public key from private key for display — we just use the stored one
|
|
68
|
+
// We can't derive public from private easily here, so read from PROVENANCE.yml or require --public-key
|
|
69
|
+
return { publicKey: args['public-key'] || null };
|
|
70
|
+
})();
|
|
71
|
+
|
|
72
|
+
// If no public key arg, re-generate won't work — need the stored public key
|
|
73
|
+
// Best path: require keygen was run, read public key from PROVENANCE.yml if present
|
|
74
|
+
let pubKey = args['public-key'];
|
|
75
|
+
if (!pubKey) {
|
|
76
|
+
if (existsSync('PROVENANCE.yml')) {
|
|
77
|
+
const yml = readFileSync('PROVENANCE.yml', 'utf8');
|
|
78
|
+
const match = yml.match(/public_key:\s*["']?([A-Za-z0-9+/=]+)["']?/);
|
|
79
|
+
if (match) pubKey = match[1];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!pubKey) {
|
|
83
|
+
console.error('Public key required. Pass --public-key or add identity.public_key to PROVENANCE.yml first.');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const signed_challenge = signChallenge(privateKey, id, 'REGISTER');
|
|
88
|
+
|
|
89
|
+
const body = {
|
|
90
|
+
provenance_id: id,
|
|
91
|
+
public_key: pubKey,
|
|
92
|
+
signed_challenge,
|
|
93
|
+
...(args.url ? { url: args.url } : {}),
|
|
94
|
+
...(args.name ? { name: args.name } : {}),
|
|
95
|
+
...(args.description ? { description: args.description } : {}),
|
|
96
|
+
...(args.capabilities ? { capabilities: args.capabilities.split(',').map(s => s.trim()) } : {}),
|
|
97
|
+
...(args.constraints ? { constraints: args.constraints.split(',').map(s => s.trim()) } : {}),
|
|
98
|
+
...(args.model ? { model_provider: args.model } : {}),
|
|
99
|
+
...(args['model-id'] ? { model_id: args['model-id'] } : {}),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const res = await fetch(`${API}/register`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/json' },
|
|
106
|
+
body: JSON.stringify(body),
|
|
107
|
+
});
|
|
108
|
+
const data = await res.json();
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
console.error(`\nRegistration failed: ${data.error}`);
|
|
111
|
+
if (data.hint) console.error(`Hint: ${data.hint}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
console.log(`\n${data.created ? 'Registered' : 'Updated'}: ${id}`);
|
|
115
|
+
console.log(`identity: ${data.agent?.identity}`);
|
|
116
|
+
console.log(`profile: https://getprovenance.dev/agent/${id.replace('provenance:', '').replace(':', '/')}\n`);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error('Network error:', e.message);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── status ───────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
if (command === 'status') {
|
|
127
|
+
const id = args._?.[0] || args.id;
|
|
128
|
+
if (!id) { console.error('Usage: npx provenance status <provenance_id>'); process.exit(1); }
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const res = await fetch(`${API}/register?provenance_id=${encodeURIComponent(id)}`);
|
|
132
|
+
const data = await res.json();
|
|
133
|
+
if (!data.registered) { console.log(`\nNot registered: ${id}\n`); process.exit(0); }
|
|
134
|
+
const a = data.agent;
|
|
135
|
+
console.log(`\n${id}`);
|
|
136
|
+
console.log(`name: ${a.name || '—'}`);
|
|
137
|
+
console.log(`identity: ${a.identity}`);
|
|
138
|
+
console.log(`status: ${a.status}`);
|
|
139
|
+
console.log(`profile: https://getprovenance.dev/agent/${id.replace('provenance:', '').replace(':', '/')}\n`);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error('Network error:', e.message);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── revoke ───────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
if (command === 'revoke') {
|
|
150
|
+
const id = args.id || args._?.[0];
|
|
151
|
+
if (!id) { console.error('Usage: npx provenance revoke --id <provenance_id>'); process.exit(1); }
|
|
152
|
+
|
|
153
|
+
const privateKey = readPrivateKey();
|
|
154
|
+
const signed_challenge = signRevocation(privateKey, id);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const res = await fetch(`${API}/revoke`, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: { 'Content-Type': 'application/json' },
|
|
160
|
+
body: JSON.stringify({ provenance_id: id, signed_challenge }),
|
|
161
|
+
});
|
|
162
|
+
const data = await res.json();
|
|
163
|
+
if (!res.ok) { console.error(`Revocation failed: ${data.error}`); process.exit(1); }
|
|
164
|
+
console.log(`\nKey revoked for ${id}.`);
|
|
165
|
+
console.log(`Run npx provenance keygen && npx provenance register --id ${id} to re-register with a new key.\n`);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.error('Network error:', e.message);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
process.exit(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── help ─────────────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
console.log(`
|
|
176
|
+
provenance <command>
|
|
177
|
+
|
|
178
|
+
keygen Generate an Ed25519 keypair
|
|
179
|
+
register --id <id> [options] Register or update your agent
|
|
180
|
+
status <id> Check registration status
|
|
181
|
+
revoke --id <id> Revoke your registered key
|
|
182
|
+
|
|
183
|
+
register options:
|
|
184
|
+
--id provenance:<platform>:<org>/<name> (required)
|
|
185
|
+
--url URL to your PROVENANCE.yml (for independent verification)
|
|
186
|
+
--name Agent display name
|
|
187
|
+
--description One-sentence description
|
|
188
|
+
--capabilities read:web,write:summaries
|
|
189
|
+
--constraints no:pii,no:financial:transact
|
|
190
|
+
--model anthropic / openai / etc.
|
|
191
|
+
--model-id claude-sonnet-4-6 / gpt-4o / etc.
|
|
192
|
+
--public-key Base64 public key (auto-read from PROVENANCE.yml if present)
|
|
193
|
+
|
|
194
|
+
Full docs: https://getprovenance.dev/docs
|
|
195
|
+
`);
|
|
196
|
+
process.exit(0);
|