smoke-hosted-multi-vault.mjs
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd
feat(calendar): enforce agent context tiers in retrieval AP…
Human
minor
⚠ breaking
2 days ago
| 1 | #!/usr/bin/env node |
| 2 | /** |
| 3 | * Optional multi-vault smoke against a deployed canister and/or gateway. |
| 4 | * |
| 5 | * Usage: |
| 6 | * CANISTER_URL=https://<id>.icp0.io X_TEST_USER=smoke:user npm run smoke:hosted-multi-vault |
| 7 | * |
| 8 | * Or JWT via gateway (no X-Test-User on production): |
| 9 | * HUB_GATEWAY_URL=https://your-gateway.example HUB_SMOKE_TOKEN='<jwt>' npm run smoke:hosted-multi-vault |
| 10 | * |
| 11 | * Exits 0 on success, 1 on failure or missing env. |
| 12 | */ |
| 13 | const CANISTER_URL = (process.env.CANISTER_URL || '').replace(/\/$/, ''); |
| 14 | const X_TEST_USER = process.env.X_TEST_USER || ''; |
| 15 | const GATEWAY_URL = (process.env.HUB_GATEWAY_URL || '').replace(/\/$/, ''); |
| 16 | const TOKEN = process.env.HUB_SMOKE_TOKEN || ''; |
| 17 | |
| 18 | function fail(msg) { |
| 19 | console.error(msg); |
| 20 | process.exit(1); |
| 21 | } |
| 22 | |
| 23 | async function main() { |
| 24 | if (!CANISTER_URL && !GATEWAY_URL) { |
| 25 | fail('Set CANISTER_URL and X_TEST_USER, or HUB_GATEWAY_URL and HUB_SMOKE_TOKEN.'); |
| 26 | } |
| 27 | |
| 28 | const headersBase = { Accept: 'application/json' }; |
| 29 | let base; |
| 30 | let headers = { ...headersBase }; |
| 31 | |
| 32 | if (GATEWAY_URL && TOKEN) { |
| 33 | base = GATEWAY_URL; |
| 34 | headers.Authorization = `Bearer ${TOKEN}`; |
| 35 | } else if (CANISTER_URL && X_TEST_USER) { |
| 36 | base = CANISTER_URL; |
| 37 | headers['X-Test-User'] = X_TEST_USER; |
| 38 | } else { |
| 39 | fail('Need (CANISTER_URL + X_TEST_USER) or (HUB_GATEWAY_URL + HUB_SMOKE_TOKEN).'); |
| 40 | } |
| 41 | |
| 42 | const healthUrl = `${base}/health`; |
| 43 | const h = await fetch(healthUrl, { headers: { Accept: 'application/json' } }); |
| 44 | if (!h.ok) fail(`GET /health failed: ${h.status} ${healthUrl}`); |
| 45 | |
| 46 | const vUrl = `${base}/api/v1/vaults`; |
| 47 | const v = await fetch(vUrl, { headers }); |
| 48 | if (!v.ok) fail(`GET /api/v1/vaults failed: ${v.status}`); |
| 49 | const vJson = await v.json(); |
| 50 | const vaults = Array.isArray(vJson.vaults) ? vJson.vaults : []; |
| 51 | if (vaults.length < 1) fail('Expected at least one vault in JSON { vaults: [...] }'); |
| 52 | console.log('OK vaults:', vaults.map((x) => x.id).join(', ')); |
| 53 | |
| 54 | const vid = process.env.SMOKE_SECOND_VAULT_ID || 'smoke_second'; |
| 55 | const postUrl = `${base}/api/v1/notes`; |
| 56 | const postHeaders = { |
| 57 | ...headers, |
| 58 | 'Content-Type': 'application/json', |
| 59 | 'X-Vault-Id': vid, |
| 60 | }; |
| 61 | const path = `inbox/.smoke-multi-vault-${Date.now()}.md`; |
| 62 | const body = JSON.stringify({ |
| 63 | path, |
| 64 | body: `smoke ${vid} ${new Date().toISOString()}`, |
| 65 | frontmatter: '---\ntitle: smoke\n---\n', |
| 66 | }); |
| 67 | const p = await fetch(postUrl, { method: 'POST', headers: postHeaders, body }); |
| 68 | if (!p.ok) { |
| 69 | const t = await p.text(); |
| 70 | fail(`POST note failed: ${p.status} ${t.slice(0, 200)}`); |
| 71 | } |
| 72 | |
| 73 | const v2 = await fetch(vUrl, { headers }); |
| 74 | const v2Json = await v2.json(); |
| 75 | const ids = (Array.isArray(v2Json.vaults) ? v2Json.vaults : []).map((x) => x.id); |
| 76 | if (!ids.includes(vid)) { |
| 77 | fail(`Second vault ${vid} not listed after POST; got: ${ids.join(', ')}`); |
| 78 | } |
| 79 | |
| 80 | const exUrl = `${base}/api/v1/export`; |
| 81 | const exDef = await fetch(exUrl, { headers: { ...headers, 'X-Vault-Id': 'default' } }); |
| 82 | const exVid = await fetch(exUrl, { headers: { ...headers, 'X-Vault-Id': vid } }); |
| 83 | if (!exDef.ok || !exVid.ok) { |
| 84 | fail(`export failed default=${exDef.status} ${vid}=${exVid.status}`); |
| 85 | } |
| 86 | const jDef = await exDef.json(); |
| 87 | const jVid = await exVid.json(); |
| 88 | const notesDef = Array.isArray(jDef.notes) ? jDef.notes : []; |
| 89 | const notesVid = Array.isArray(jVid.notes) ? jVid.notes : []; |
| 90 | const inDefault = notesDef.some((n) => n.path === path); |
| 91 | const inSecond = notesVid.some((n) => n.path === path); |
| 92 | if (inDefault) fail('Export leak: new note path appears in default vault export'); |
| 93 | if (!inSecond) fail('Export miss: new note path missing from second vault export'); |
| 94 | |
| 95 | console.log('OK multi-vault smoke: list, post, export isolation'); |
| 96 | } |
| 97 | |
| 98 | main().catch((e) => fail(e?.message || String(e))); |
File History
2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd
feat(calendar): enforce agent context tiers in retrieval AP…
Human
minor
⚠
2 days ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6
docs: accept Calendar Events v0 spec with Phase 0 security …
Human
2 days ago