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.
Files changed (3) hide show
  1. package/README.md +50 -19
  2. package/package.json +5 -1
  3. 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, // must have PROVENANCE.yml
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, // no open incidents
45
- requireMinAge: 30, // must be at least 30 days old
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
- | `declared` | boolean | Has a PROVENANCE.yml file |
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
- ```js
140
- import { Provenance } from 'provenance-protocol';
141
- import { generateProvenanceKeyPair, signChallenge } from 'provenance-protocol/keygen';
148
+ ### Public repo (GitHub / HuggingFace / npm)
142
149
 
143
- const provenance = new Provenance();
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
- // One-time: generate a keypair
146
- const { publicKey, privateKey } = generateProvenanceKeyPair();
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
- // Sign proof of key ownership for registration
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, // proves you control the private key
169
+ signed_challenge,
162
170
  });
163
- // → { created: true, identity_verified: true, confidence: 1.0 }
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",
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);