ajp-protocol 0.1.0 → 0.2.2

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.
@@ -8,13 +8,72 @@ jobs:
8
8
  publish:
9
9
  runs-on: ubuntu-latest
10
10
  permissions:
11
+ contents: read
12
+ # Mints the short-lived OIDC token npm trades for publish rights.
13
+ # Without it npm looks for a token instead and fails with a 404 on
14
+ # write, which reads like "package not found" rather than "not
15
+ # authorised". Do not remove.
11
16
  id-token: write
12
17
  steps:
13
18
  - uses: actions/checkout@v4
19
+
14
20
  - uses: actions/setup-node@v4
15
21
  with:
16
- node-version: '20'
22
+ # Trusted publishing needs Node >= 22.14 and npm >= 11.5.1.
23
+ node-version: '24'
17
24
  registry-url: 'https://registry.npmjs.org'
18
- - run: npm publish --access public --provenance
19
- env:
20
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
25
+
26
+ # Upgrade npm explicitly rather than trusting whatever version the
27
+ # runner's Node bundles, and print both. A too-old npm ignores OIDC
28
+ # and fails with that same misleading 404, so keep the versions in
29
+ # the log where they can be read.
30
+ - run: npm install -g npm@latest
31
+ - run: node -v && npm -v
32
+
33
+ # Install before testing. Not every package here is dependency-free —
34
+ # ajp-protocol depends on provenance-protocol and its tests import it, and
35
+ # omitting this step made the test fail to resolve the module rather than
36
+ # fail a real assertion. `npm install` rather than `npm ci` because these
37
+ # packages carry no lockfile.
38
+ - run: npm install --no-audit --no-fund
39
+
40
+ # A release that fails its own tests must not reach the registry.
41
+ - run: npm test --if-present
42
+
43
+ # No NODE_AUTH_TOKEN: npm exchanges this workflow's OIDC identity for
44
+ # publish rights, configured as a trusted publisher on npmjs.com against
45
+ # this repository and this workflow filename. Provenance attestations are
46
+ # generated automatically, so --provenance is not passed.
47
+ # Skip rather than fail when this version is already on npm. A tag may
48
+ # exist to release a sibling package in this repo, and a red run that
49
+ # actually means "nothing to publish here" trains people to ignore red
50
+ # runs. The log says which happened.
51
+ - name: Publish the protocol package if its version is new
52
+ run: |
53
+ NAME=$(node -p "require('./package.json').name")
54
+ VERSION=$(node -p "require('./package.json').version")
55
+ if npm view "$NAME@$VERSION" version >/dev/null 2>&1; then
56
+ echo "::notice::$NAME@$VERSION is already published — skipping. Bump package.json to release it."
57
+ else
58
+ echo "Publishing $NAME@$VERSION"
59
+ npm publish --access public
60
+ fi
61
+
62
+ # The CLI is a second package in this repo with its own version. Publish it
63
+ # from the same tag, but only when its version is actually new — otherwise
64
+ # every release after an unchanged CLI would fail on "cannot publish over
65
+ # the previously published version".
66
+ #
67
+ # A skip and a publish must not look alike in the log: one means "nothing
68
+ # to do", the other means "shipped".
69
+ - name: Publish the CLI if its version is new
70
+ working-directory: cli
71
+ run: |
72
+ NAME=$(node -p "require('./package.json').name")
73
+ VERSION=$(node -p "require('./package.json').version")
74
+ if npm view "$NAME@$VERSION" version >/dev/null 2>&1; then
75
+ echo "::notice::$NAME@$VERSION is already published — skipping. Bump cli/package.json to release it."
76
+ else
77
+ echo "Publishing $NAME@$VERSION"
78
+ npm publish --access public
79
+ fi
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ajp-protocol
2
2
 
3
3
  The Agent Job Protocol — standard interaction layer for the agent internet.
4
- Part of the [Provenance Protocol](https://provenance.dev) family.
4
+ Part of the [Provenance Protocol](https://getprovenance.dev) family.
5
5
 
6
6
  ```bash
7
7
  npm install ajp-protocol
@@ -25,7 +25,8 @@ Add three routes to your agent. AJP handles verification, trust checks,
25
25
  and job lifecycle automatically.
26
26
 
27
27
  ```js
28
- import { AJPServer } from 'ajp-protocol';
28
+ import { AJPServer, declarationKeyResolver, indexStandingCheck } from 'ajp-protocol';
29
+ import { Provenance } from 'provenance-protocol';
29
30
  import express from 'express';
30
31
 
31
32
  const app = express();
@@ -33,14 +34,29 @@ app.use(express.json());
33
34
 
34
35
  const server = new AJPServer({
35
36
  provenanceId: 'provenance:github:alice/research-assistant',
37
+ privateKey: process.env.PROVENANCE_PRIVATE_KEY, // signs results with Ed25519
38
+
39
+ // Constraints from your PROVENANCE.yml — asserted in every signed JobResult.
40
+ // Creates a cryptographic receipt: "this agent declared it honored these constraints for this job."
41
+ constraints: ['no:pii', 'no:persist:data'],
42
+
43
+ // Optional: accept human callers (platforms) with a shared HMAC secret
36
44
  secret: process.env.AJP_SECRET,
37
45
 
38
- // Trust requirements for incoming agent senders
39
- trustRequirements: {
40
- requireDeclared: true, // sender must have PROVENANCE.yml
41
- requireClean: true, // no open incidents
42
- requireMinAge: 7, // not a brand-new agent
43
- },
46
+ // Identity: resolved from the sender's own signed declaration, offline.
47
+ // This is the default — no index is consulted to check a signature.
48
+ resolveSenderKey: declarationKeyResolver(),
49
+
50
+ // Standing (revocation, incidents, freshness) cannot be checked offline, so
51
+ // it is opt-in and you choose whom to ask. Omit it entirely to accept any
52
+ // sender whose identity verifies.
53
+ checkStanding: indexStandingCheck(new Provenance(), {
54
+ requireDeclared: true,
55
+ requireCapabilities: ['delegate:agents'],
56
+ requireClean: true,
57
+ requireMinAge: 7,
58
+ }),
59
+ onStandingUnavailable: 'deny', // an unreachable attester is not an accusation
44
60
 
45
61
  // Your agent logic — receives the job, returns the result
46
62
  onJob: async (job) => {
@@ -68,7 +84,8 @@ const client = new AJPClient({
68
84
  type: 'agent', // 'human' | 'agent' | 'orchestrator'
69
85
  provenance_id: 'provenance:github:alice/orchestrator',
70
86
  },
71
- secret: process.env.AJP_SECRET,
87
+ // Agent/orchestrator callers sign with Ed25519 — no shared secret needed
88
+ privateKey: process.env.PROVENANCE_PRIVATE_KEY,
72
89
  });
73
90
 
74
91
  const result = await client.send(
@@ -94,6 +111,7 @@ console.log(result.output);
94
111
 
95
112
  ### Human hiring an agent
96
113
  ```js
114
+ // Human callers use a shared HMAC secret (agreed out of band with the agent)
97
115
  const client = new AJPClient({
98
116
  from: { type: 'human', id: 'user_alice_123' },
99
117
  secret: process.env.AJP_SECRET,
@@ -103,11 +121,12 @@ const result = await client.send(agentId, task, budget);
103
121
 
104
122
  ### Agent hiring an agent
105
123
  ```js
124
+ // Agent callers sign with Ed25519 — no shared secret, no prior setup
125
+ // The receiving agent verifies by fetching your public key from Provenance index
106
126
  const client = new AJPClient({
107
127
  from: { type: 'agent', provenance_id: 'provenance:github:alice/pipeline' },
108
- secret: process.env.AJP_SECRET,
128
+ privateKey: process.env.PROVENANCE_PRIVATE_KEY,
109
129
  });
110
- // Receiving agent automatically verifies sender via Provenance
111
130
  const result = await client.send(agentId, task, budget);
112
131
  ```
113
132
 
@@ -124,21 +143,28 @@ const [resultA, resultB] = await Promise.all([
124
143
 
125
144
  ## How trust works
126
145
 
127
- When an agent or orchestrator sends a job, the receiving `AJPServer`
128
- automatically calls `provenance-protocol` to verify the sender:
146
+ Two separate questions, and only one of them needs a network service.
129
147
 
130
148
  ```
131
149
  AJPServer.receive()
132
- → verify signature
133
- → provenance.gate(offer.from.provenance_id, trustRequirements)
134
- → is sender in Provenance index?
135
- → has PROVENANCE.yml?
136
- → any open incidents?
137
- → old enough?
138
- → run onJob() only if all checks pass
139
- → return 403 with reason if any check fails
150
+ → validate the offer's shape
151
+ → IDENTITY (offline, always)
152
+ fetch the sender's declaration from from.declaration_url
153
+ → does it verify against the key inside it?
154
+ → was it served from the location its provenance id names?
155
+ → is that id the one the offer claims?
156
+ → has this sender's key changed since last time?
157
+ then check the offer's signature with that key
158
+ → STANDING (online, optional, you choose the attester)
159
+ → revoked? open incidents? evidence stale? old enough?
160
+ → unreachable attester → your policy, not a failed trust check
161
+ → run onJob() only if both pass
162
+ → 403 with a reason and a code if either does not
140
163
  ```
141
164
 
165
+ Identity never depends on anyone's uptime. Only standing does, and you decide
166
+ whose — one index, several, your own attester, or none at all.
167
+
142
168
  Human senders (`from.type: 'human'`) skip Provenance verification.
143
169
  Platform-level auth is assumed for humans.
144
170
 
@@ -198,4 +224,28 @@ export async function POST(req) {
198
224
 
199
225
  ---
200
226
 
201
- ## MIT License — provenance.dev/ajp
227
+ ## CLI
228
+
229
+ ```bash
230
+ # Send a job to any indexed agent from the terminal
231
+ npx @ilucky21c/ajp-cli hire provenance:github:alice/summarizer \
232
+ --instruction "Summarize this paper: https://arxiv.org/abs/..." \
233
+ --budget 0.50 --timeout 60
234
+
235
+ # Check job status
236
+ npx @ilucky21c/ajp-cli jobs job_m0abc123 --endpoint https://alice-agent.example.com/api/agent
237
+ ```
238
+
239
+ Requires Provenance identity — set up first with `npx provenance-protocol keygen` and `npx provenance-protocol register`.
240
+
241
+ Full CLI reference: [getprovenance.dev/docs/ajp#cli](https://getprovenance.dev/docs/ajp#cli)
242
+
243
+ ---
244
+
245
+ ## Full documentation
246
+
247
+ [getprovenance.dev/docs/ajp](https://getprovenance.dev/docs/ajp)
248
+
249
+ ---
250
+
251
+ ## MIT License — getprovenance.dev
package/cli/index.js ADDED
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ajp-cli — Agent Job Protocol CLI
4
+ *
5
+ * Requires Provenance identity (PROVENANCE_ID + PROVENANCE_PRIVATE_KEY).
6
+ * For identity setup: npx provenance-protocol keygen / npx provenance-protocol register
7
+ *
8
+ * Usage:
9
+ * ajp hire <provenance_id> --instruction <text> [--budget <usd>] [--timeout <s>]
10
+ * ajp jobs <job_id> --endpoint <url>
11
+ */
12
+
13
+ import { createPrivateKey, sign as nodeSign, randomBytes } from 'crypto';
14
+
15
+ const API = process.env.PROVENANCE_API_URL || 'https://getprovenance.dev';
16
+ const VERSION = '0.1.0';
17
+
18
+ // ── Colours ───────────────────────────────────────────────────────────────────
19
+
20
+ const c = {
21
+ reset: '\x1b[0m', dim: '\x1b[2m', bold: '\x1b[1m',
22
+ green: '\x1b[32m', amber: '\x1b[33m', red: '\x1b[31m', blue: '\x1b[34m', white: '\x1b[97m',
23
+ };
24
+ const ok = s => `${c.green}✓${c.reset} ${s}`;
25
+ const err = s => `${c.red}✗${c.reset} ${s}`;
26
+ const dim = s => `${c.dim}${s}${c.reset}`;
27
+ const hi = s => `${c.white}${c.bold}${s}${c.reset}`;
28
+ const amb = s => `${c.amber}${s}${c.reset}`;
29
+
30
+ // ── Arg parsing ───────────────────────────────────────────────────────────────
31
+
32
+ function parseArgs(argv) {
33
+ const args = { _: [] };
34
+ let i = 0;
35
+ while (i < argv.length) {
36
+ const a = argv[i];
37
+ if (a.startsWith('--')) {
38
+ const key = a.slice(2);
39
+ const next = argv[i + 1];
40
+ if (next && !next.startsWith('--')) { args[key] = next; i += 2; }
41
+ else { args[key] = true; i++; }
42
+ } else { args._.push(a); i++; }
43
+ }
44
+ return args;
45
+ }
46
+
47
+ // ── Signing ───────────────────────────────────────────────────────────────────
48
+
49
+ function generateJobId() {
50
+ return `job_${Date.now().toString(36)}${randomBytes(6).toString('hex')}`;
51
+ }
52
+
53
+ function signOffer(offer, privateKeyBase64) {
54
+ const { signature: _, ...rest } = offer;
55
+ const canonical = JSON.stringify(rest, Object.keys(rest).sort());
56
+ const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });
57
+ return `ed25519:${nodeSign(null, Buffer.from(canonical, 'utf8'), key).toString('base64')}`;
58
+ }
59
+
60
+ // ── Commands ──────────────────────────────────────────────────────────────────
61
+
62
+ async function cmdHire(args) {
63
+ const targetId = args._[1];
64
+ const instruction = args.instruction || args.i;
65
+ const budget = parseFloat(args.budget || args.b || '1.0');
66
+ const timeout = parseInt(args.timeout || args.t || '120');
67
+ const privateKey = args['private-key'] || process.env.PROVENANCE_PRIVATE_KEY;
68
+ const provenanceId = args['from-id'] || process.env.PROVENANCE_ID;
69
+
70
+ if (!targetId) { console.error(err('Usage: ajp hire <provenance_id> --instruction <text>')); process.exit(1); }
71
+ if (!instruction) { console.error(err('--instruction required')); process.exit(1); }
72
+ if (!privateKey) { console.error(err('PROVENANCE_PRIVATE_KEY not set. Run: npx provenance-protocol keygen')); process.exit(1); }
73
+ if (!provenanceId) { console.error(err('PROVENANCE_ID not set. Run: npx provenance-protocol register')); process.exit(1); }
74
+
75
+ console.log(`\n${amb('Hiring')} ${hi(targetId)}...\n`);
76
+
77
+ // Resolve endpoint
78
+ process.stdout.write(dim(' Resolving endpoint...'));
79
+ const agentRes = await fetch(`${API}/api/agent/${targetId.replace('provenance:', '').replace(':', '/')}`);
80
+ const agentData = await agentRes.json();
81
+ if (!agentData?.ajp?.endpoint) {
82
+ console.log('\n' + err('Agent has no AJP endpoint. Ask them to add ajp.endpoint to PROVENANCE.yml.'));
83
+ process.exit(1);
84
+ }
85
+ const endpoint = agentData.ajp.endpoint.replace(/\/$/, '');
86
+ console.log(` ${c.green}${endpoint}${c.reset}`);
87
+
88
+ // Build and sign offer
89
+ const now = new Date();
90
+ const expiresAt = new Date(now.getTime() + timeout * 1000);
91
+ const jobId = generateJobId();
92
+
93
+ const offer = {
94
+ ajp: '0.1', job_id: jobId, parent_job_id: null,
95
+ from: { type: 'orchestrator', id: null, provenance_id: provenanceId },
96
+ to: { provenance_id: targetId },
97
+ task: { type: 'task', instruction, input: {}, output_format: 'json' },
98
+ context: { credentials: {}, memory: [], constraints: [] },
99
+ budget: { max_usd: budget, max_seconds: timeout, max_llm_tokens: 10000 },
100
+ callback: null,
101
+ issued_at: now.toISOString(),
102
+ expires_at: expiresAt.toISOString(),
103
+ signature: '',
104
+ };
105
+ offer.signature = signOffer(offer, privateKey);
106
+
107
+ // Submit
108
+ process.stdout.write(dim(' Submitting job...'));
109
+ const submitRes = await fetch(`${endpoint}/jobs`, {
110
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(offer),
111
+ });
112
+ const submitData = await submitRes.json().catch(() => ({}));
113
+
114
+ if (!submitRes.ok) {
115
+ console.log('\n' + err(submitData.error || submitData.reason || `HTTP ${submitRes.status}`));
116
+ process.exit(1);
117
+ }
118
+ console.log(` ${c.green}${jobId}${c.reset}`);
119
+
120
+ // Poll
121
+ const deadline = Date.now() + timeout * 1000;
122
+ const frames = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
123
+ let fi = 0;
124
+
125
+ while (Date.now() < deadline) {
126
+ await new Promise(r => setTimeout(r, 2000));
127
+ const pollRes = await fetch(`${endpoint}/jobs/${jobId}`);
128
+ const pollData = await pollRes.json().catch(() => ({}));
129
+
130
+ process.stdout.write(`\r ${c.blue}${frames[fi++ % frames.length]}${c.reset} ${dim(pollData.status || 'polling...')} `);
131
+
132
+ if (pollData.status === 'completed') {
133
+ await fetch(`${endpoint}/jobs/${jobId}/ack`, {
134
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
135
+ body: JSON.stringify({ received: true }),
136
+ }).catch(() => {});
137
+
138
+ const dur = pollData.usage?.duration_seconds?.toFixed(1);
139
+ process.stdout.write(`\r${ok(`Completed${dur ? ` in ${dur}s` : ''}`)} \n\n`);
140
+
141
+ const out = pollData.output;
142
+ console.log(typeof out === 'string' ? out : JSON.stringify(out, null, 2));
143
+
144
+ if (pollData.usage) {
145
+ const u = pollData.usage;
146
+ const parts = [];
147
+ if (u.duration_seconds != null) parts.push(`${u.duration_seconds.toFixed(1)}s`);
148
+ if (u.cost_usd > 0) parts.push(`$${u.cost_usd.toFixed(4)}`);
149
+ if (u.llm_tokens > 0) parts.push(`${u.llm_tokens} tokens`);
150
+ if (parts.length) console.log('\n' + dim(parts.join(' · ')));
151
+ }
152
+ console.log();
153
+ return;
154
+ }
155
+
156
+ if (['failed','expired','rejected'].includes(pollData.status)) {
157
+ process.stdout.write(`\r${err(pollData.status + (pollData.message ? ': ' + pollData.message : ''))} \n\n`);
158
+ process.exit(1);
159
+ }
160
+ }
161
+
162
+ console.log('\n' + err(`Timed out after ${timeout}s`));
163
+ process.exit(1);
164
+ }
165
+
166
+ async function cmdJobs(args) {
167
+ const jobId = args._[1];
168
+ const endpoint = args.endpoint;
169
+ if (!jobId) { console.error(err('Usage: ajp jobs <job_id> --endpoint <url>')); process.exit(1); }
170
+ if (!endpoint) { console.error(err('--endpoint required')); process.exit(1); }
171
+
172
+ const res = await fetch(`${endpoint.replace(/\/$/, '')}/jobs/${jobId}`);
173
+ const data = await res.json();
174
+
175
+ const statusColor = { completed: c.green, failed: c.red, expired: c.red, running: c.blue, accepted: c.amber }[data.status] || c.dim;
176
+ console.log(`\n${dim('job_id:')} ${data.job_id}`);
177
+ console.log(`${dim('status:')} ${statusColor}${data.status}${c.reset}`);
178
+ if (data.output) console.log(`\n${JSON.stringify(data.output, null, 2)}`);
179
+ if (data.message) console.log(`${c.red}${data.message}${c.reset}`);
180
+ console.log();
181
+ }
182
+
183
+ function cmdHelp() {
184
+ console.log(`
185
+ ${hi('ajp')} ${dim(`v${VERSION}`)} — Agent Job Protocol CLI
186
+
187
+ ${amb('Commands:')}
188
+ ${hi('hire')} <provenance_id> Send a job to an agent via AJP
189
+ --instruction <text> What you want the agent to do
190
+ [--budget <usd>] Max cost ceiling (default: 1.00)
191
+ [--timeout <seconds>] Max wait time (default: 120)
192
+ [--from-id <id>] Your Provenance ID (default: $PROVENANCE_ID)
193
+ [--private-key <key>] Your private key (default: $PROVENANCE_PRIVATE_KEY)
194
+
195
+ ${hi('jobs')} <job_id> Check status of a job
196
+ --endpoint <url> The agent's AJP endpoint URL
197
+
198
+ ${amb('Environment variables:')}
199
+ PROVENANCE_ID Your Provenance ID (set up with: npx provenance-protocol register)
200
+ PROVENANCE_PRIVATE_KEY Your Ed25519 private key (set up with: npx provenance-protocol keygen)
201
+ PROVENANCE_API_URL Override Provenance API base URL
202
+
203
+ ${amb('Examples:')}
204
+ ajp hire provenance:github:alice/summarizer \\
205
+ --instruction "Summarize https://arxiv.org/abs/2501.00001" \\
206
+ --budget 0.50 --timeout 60
207
+
208
+ ajp jobs job_m0abc123 --endpoint https://alice-agent.example.com/api/agent
209
+
210
+ ${amb('Identity setup (first time):')}
211
+ npx provenance-protocol keygen
212
+ npx provenance-protocol register --id provenance:github:your-org/your-agent --url <url>
213
+ ${dim('Then set PROVENANCE_ID and PROVENANCE_PRIVATE_KEY in your environment.')}
214
+ `);
215
+ }
216
+
217
+ // ── Main ──────────────────────────────────────────────────────────────────────
218
+
219
+ const argv = process.argv.slice(2);
220
+ const args = parseArgs(argv);
221
+ const cmd = args._[0];
222
+
223
+ try {
224
+ if (!cmd || cmd === 'help' || args.help) cmdHelp();
225
+ else if (cmd === 'hire') await cmdHire(args);
226
+ else if (cmd === 'jobs') await cmdJobs(args);
227
+ else { console.error(err(`Unknown command: ${cmd}\nRun \`ajp help\` for usage.`)); process.exit(1); }
228
+ } catch (e) {
229
+ console.error(err(e.message));
230
+ process.exit(1);
231
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@ilucky21c/ajp-cli",
3
+ "version": "0.1.1",
4
+ "description": "CLI for the Agent Job Protocol \u2014 hire agents and check job status from the terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "ajp": "./index.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/ilucky21c/ajp-protocol.git",
15
+ "directory": "cli"
16
+ },
17
+ "keywords": [
18
+ "ajp",
19
+ "agent-job-protocol",
20
+ "provenance",
21
+ "ai",
22
+ "agents",
23
+ "cli"
24
+ ],
25
+ "license": "MIT"
26
+ }
package/package.json CHANGED
@@ -1,20 +1,30 @@
1
1
  {
2
2
  "name": "ajp-protocol",
3
- "version": "0.1.0",
4
- "description": "Agent Job Protocol — standard interaction layer for the agent internet",
3
+ "version": "0.2.2",
4
+ "description": "Agent Job Protocol \u2014 standard interaction layer for the agent internet",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js"
9
9
  },
10
- "keywords": ["ai-agent", "agent-protocol", "ajp", "provenance", "llm", "agent-to-agent"],
10
+ "keywords": [
11
+ "ai-agent",
12
+ "agent-protocol",
13
+ "ajp",
14
+ "provenance",
15
+ "llm",
16
+ "agent-to-agent"
17
+ ],
11
18
  "license": "MIT",
12
19
  "dependencies": {
13
- "provenance-protocol": "^0.1.0"
20
+ "provenance-protocol": "^0.2.2"
14
21
  },
15
22
  "repository": {
16
23
  "type": "git",
17
24
  "url": "https://github.com/ilucky21c/ajp-protocol"
18
25
  },
19
- "homepage": "https://provenance.dev/ajp"
26
+ "homepage": "https://getprovenance.dev/ajp",
27
+ "scripts": {
28
+ "test": "node test/trust.test.mjs"
29
+ }
20
30
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://provenance.dev/ajp/schema/0.1/job-offer.json",
3
+ "$id": "https://getprovenance.dev/ajp/schema/0.1/job-offer.json",
4
4
  "title": "AJP JobOffer",
5
5
  "description": "Agent Job Protocol v0.1 — JobOffer message",
6
6
  "type": "object",
@@ -86,8 +86,8 @@
86
86
 
87
87
  "signature": {
88
88
  "type": "string",
89
- "description": "HMAC-SHA256 of the message body excluding this field. Format: sha256:hex",
90
- "pattern": "^sha256:[a-f0-9]{64}$"
89
+ "description": "Signature of the message body excluding this field. Human senders: sha256:hex (HMAC-SHA256). Agent/orchestrator senders: ed25519:base64 (Ed25519 with registered Provenance private key).",
90
+ "pattern": "^(sha256:[a-f0-9]{64}|ed25519:[A-Za-z0-9+/=]+)$"
91
91
  }
92
92
  },
93
93