verify-agent-credential-smoke.mjs
119 lines 3.8 KB
Raw
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6 docs: record AIP-b SD-21 land (KN #308) Human 9 days ago
1 #!/usr/bin/env node
2 /**
3 * Live Phase C smoke: opaque kt_agent_ credential → access JWT → vault:read.
4 *
5 * Does not print secrets. Exit 0 only on full PASS.
6 *
7 * Usage:
8 * KNOWTATION_HUB_AGENT_CREDENTIAL='kt_agent_…' \
9 * KNOWTATION_HUB_VAULT_ID=default \
10 * node scripts/verify-agent-credential-smoke.mjs
11 *
12 * Or:
13 * KNOWTATION_HUB_AGENT_CREDENTIAL_FILE=~/.config/knowtation/agent_cred \
14 * node scripts/verify-agent-credential-smoke.mjs
15 */
16
17 import fs from 'node:fs';
18 import path from 'node:path';
19
20 const apiBase = (process.env.KNOWTATION_HUB_URL || 'https://api.knowtation.store').replace(/\/$/, '');
21 const vaultId = process.env.KNOWTATION_HUB_VAULT_ID || 'default';
22
23 function resolveCredential() {
24 let c = (process.env.KNOWTATION_HUB_AGENT_CREDENTIAL || '').trim();
25 const fp = (process.env.KNOWTATION_HUB_AGENT_CREDENTIAL_FILE || '').trim();
26 if (!c && fp) {
27 const expanded = fp.startsWith('~')
28 ? path.join(process.env.HOME || '', fp.slice(1))
29 : fp;
30 c = fs.readFileSync(expanded, 'utf8').trim();
31 }
32 return c;
33 }
34
35 function redactJwtClaims(jwt) {
36 try {
37 const payload = JSON.parse(Buffer.from(jwt.split('.')[1], 'base64url').toString('utf8'));
38 return {
39 type: payload.type,
40 typ: payload.typ,
41 aud: payload.aud,
42 scopes: payload.scopes,
43 vault_ids: payload.vault_ids,
44 exp_in_s: typeof payload.exp === 'number' ? payload.exp - Math.floor(Date.now() / 1000) : null,
45 };
46 } catch {
47 return { parse: 'failed' };
48 }
49 }
50
51 function fail(step, detail) {
52 console.log(`FAIL ${step}: ${detail}`);
53 process.exit(1);
54 }
55
56 const credential = resolveCredential();
57 if (!credential) {
58 fail('setup', 'set KNOWTATION_HUB_AGENT_CREDENTIAL or KNOWTATION_HUB_AGENT_CREDENTIAL_FILE');
59 }
60 if (!credential.startsWith('kt_agent_')) {
61 fail('setup', 'credential must start with kt_agent_');
62 }
63
64 console.log(`api=${apiBase}`);
65 console.log(`vault=${vaultId}`);
66 console.log(`credential_prefix=${credential.slice(0, 12)}… len=${credential.length}`);
67
68 const health = await fetch(`${apiBase}/health`);
69 const healthBody = await health.text();
70 console.log(`health status=${health.status} body=${healthBody.slice(0, 120)}`);
71 if (!health.ok) fail('health', `HTTP ${health.status}`);
72
73 const exch = await fetch(`${apiBase}/api/v1/auth/agent/token`, {
74 method: 'POST',
75 headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
76 body: JSON.stringify({ credential }),
77 });
78 const exchText = await exch.text();
79 let exchJson;
80 try {
81 exchJson = JSON.parse(exchText);
82 } catch {
83 fail('exchange', `HTTP ${exch.status} non-JSON: ${exchText.slice(0, 160)}`);
84 }
85 if (!exch.ok) {
86 fail('exchange', `HTTP ${exch.status} code=${exchJson.code || '?'} error=${exchJson.error || exchText.slice(0, 120)}`);
87 }
88 const access = exchJson.access_token;
89 if (!access || typeof access !== 'string') fail('exchange', 'missing access_token');
90 console.log('exchange OK', {
91 token_type: exchJson.token_type,
92 expires_in: exchJson.expires_in,
93 scopes: exchJson.scopes,
94 vault_ids: exchJson.vault_ids,
95 claims: redactJwtClaims(access),
96 });
97
98 const headers = {
99 Accept: 'application/json',
100 Authorization: `Bearer ${access}`,
101 'X-Vault-Id': vaultId,
102 };
103
104 const vaults = await fetch(`${apiBase}/api/v1/vaults`, { headers });
105 const vaultsText = await vaults.text();
106 console.log(`vaults status=${vaults.status} bytes=${vaultsText.length}`);
107 if (!vaults.ok) {
108 fail('vaults', `HTTP ${vaults.status} ${vaultsText.slice(0, 200)}`);
109 }
110
111 const notes = await fetch(`${apiBase}/api/v1/notes?limit=3`, { headers });
112 const notesText = await notes.text();
113 console.log(`notes status=${notes.status} bytes=${notesText.length}`);
114 if (!notes.ok) {
115 fail('notes', `HTTP ${notes.status} ${notesText.slice(0, 200)}`);
116 }
117
118 console.log('PASS agent-credential smoke (exchange + vaults + notes read)');
119 process.exit(0);
File History 1 commit
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6 docs: record AIP-b SD-21 land (KN #308) Human 9 days ago