Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion audit-trail.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function guardedCall({ manifest, name, action, leaseId, host, lock, polic
// so the demo and the assertions exercise the EXACT same run. Sets up an isolated
// strongroom vault + a clean (pinned) and a poisoned (never-pinned) tool, then drives
// the four beats through the audited gate. Returns the trail + per-beat results.
export function runTrilogy({ home = path.join(os.tmpdir(), 'oys-audit-' + process.pid) } = {}) {
export function runTrilogy({ home = fs.mkdtempSync(path.join(os.tmpdir(), 'oys-audit-')) } = {}) {
process.env.KEEPER_HOME = home;
fs.mkdirSync(home, { recursive: true });
const tmp = (n) => path.join(home, n);
Expand Down
3 changes: 1 addition & 2 deletions demo.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { check } from '@askalf/redstamp';
import { scan, pin, diff } from '@askalf/truecopy';
import { addSecret, grant, redeem } from '@askalf/strongroom';

const HOME = path.join(os.tmpdir(), 'ass-demo-' + process.pid);
const HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'ass-demo-'));
process.env.KEEPER_HOME = HOME;
fs.mkdirSync(HOME, { recursive: true });
const tmp = (n) => path.join(HOME, n);
const lock = tmp('truecopy.lock');
const policy = { egressAllow: ['api.example.com'] };
Expand Down
3 changes: 1 addition & 2 deletions demo/mcp-demo.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import fs from 'node:fs';
import path from 'node:path';

// strongroom reads KEEPER_HOME lazily, so seeding a temp vault here is enough.
const KH = path.join(os.tmpdir(), 'oys-mcp-demo-' + process.pid);
const KH = fs.mkdtempSync(path.join(os.tmpdir(), 'oys-mcp-demo-'));
process.env.KEEPER_HOME = KH;
fs.mkdirSync(KH, { recursive: true });

const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
const { InMemoryTransport } = await import('@modelcontextprotocol/sdk/inMemory.js');
Expand Down
12 changes: 3 additions & 9 deletions mcp.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,16 @@ export function createOysServer(opts = {}) {
}, async ({ manifest }) => {
let parsed;
try { parsed = JSON.parse(manifest); } catch (e) { return err(`manifest is not valid JSON: ${e.message}`); }
const tmp = path.join(os.tmpdir(), `oys-truecopy-${process.pid}-${Math.abs(hashStr(manifest))}.json`);
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'oys-truecopy-'));
const tmp = path.join(dir, 'manifest.json');
try {
fs.writeFileSync(tmp, JSON.stringify(parsed));
const r = truecopyScan(tmp);
return json({ verdict: r.verdict, findings: r.findings ?? r.flags ?? [], skill: r.skill?.name ?? null });
} catch (e) {
return err(`truecopy scan failed: ${e.message}`);
} finally {
try { fs.unlinkSync(tmp); } catch { /* noop */ }
fs.rmSync(dir, { recursive: true, force: true });
}
});

Expand Down Expand Up @@ -103,10 +104,3 @@ export function createOysServer(opts = {}) {

return { server };
}

/** Tiny stable string hash for temp filenames (not security-sensitive). */
function hashStr(s) {
let h = 0;
for (let i = 0; i < s.length; i++) { h = (h << 5) - h + s.charCodeAt(i); h |= 0; }
return h;
}
8 changes: 4 additions & 4 deletions test/audit.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import path from 'node:path';
import { runTrilogy, verifyAuditFile, forgeEntry, findEntry, AuditLog } from '../audit-trail.mjs';

test('the gate records every layer decision into one chain, in order', () => {
const { audit, results } = runTrilogy({ home: path.join(os.tmpdir(), 'oys-audit-order-' + process.pid) });
const { audit, results } = runTrilogy({ home: fs.mkdtempSync(path.join(os.tmpdir(), 'oys-audit-order-')) });
// the four beats produced the expected verdicts (same as stack.test.mjs)
assert.ok(results.proceed.ok, 'beat 0: clean call proceeds');
assert.equal(results.truecopy.by, 'truecopy', 'beat 1: truecopy stops the poisoned tool');
Expand All @@ -32,7 +32,7 @@ test('the gate records every layer decision into one chain, in order', () => {
});

test('the untouched chain verifies, on disk', () => {
const { audit, trailPath } = runTrilogy({ home: path.join(os.tmpdir(), 'oys-audit-intact-' + process.pid) });
const { audit, trailPath } = runTrilogy({ home: fs.mkdtempSync(path.join(os.tmpdir(), 'oys-audit-intact-')) });
// in-memory verify
assert.equal(audit.verify(), true);
// durable verify (the daemon-grade path)
Expand All @@ -43,7 +43,7 @@ test('the untouched chain verifies, on disk', () => {
});

test('editing a past verdict breaks verification and pinpoints the entry', () => {
const { audit, trailPath } = runTrilogy({ home: path.join(os.tmpdir(), 'oys-audit-edit-' + process.pid) });
const { audit, trailPath } = runTrilogy({ home: fs.mkdtempSync(path.join(os.tmpdir(), 'oys-audit-edit-')) });
audit.flush(trailPath);
assert.equal(verifyAuditFile(trailPath).ok, true);

Expand All @@ -58,7 +58,7 @@ test('editing a past verdict breaks verification and pinpoints the entry', () =>
});

test('deleting an entry (truncating the chain mid-log) is also caught', () => {
const { audit, trailPath } = runTrilogy({ home: path.join(os.tmpdir(), 'oys-audit-del-' + process.pid) });
const { audit, trailPath } = runTrilogy({ home: fs.mkdtempSync(path.join(os.tmpdir(), 'oys-audit-del-')) });
audit.flush(trailPath);
const lines = fs.readFileSync(trailPath, 'utf8').split('\n').filter((l) => l.trim());
// drop a middle entry: the NEXT entry's `prev` no longer matches the new predecessor's hash
Expand Down
3 changes: 1 addition & 2 deletions test/mcp.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import path from 'node:path';

// strongroom reads KEEPER_HOME lazily (at call time), so setting it here — after the
// hoisted imports — is fine; seed a secret to lease.
const KH = path.join(os.tmpdir(), 'oys-mcp-test-' + process.pid);
const KH = fs.mkdtempSync(path.join(os.tmpdir(), 'oys-mcp-test-'));
process.env.KEEPER_HOME = KH;
fs.mkdirSync(KH, { recursive: true });

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
Expand Down
3 changes: 1 addition & 2 deletions test/stack.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import { check } from '@askalf/redstamp';
import { scan, pin, diff } from '@askalf/truecopy';
import { addSecret, grant, redeem } from '@askalf/strongroom';

const HOME = path.join(os.tmpdir(), 'trifecta-' + process.pid);
const HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'trifecta-'));
process.env.KEEPER_HOME = HOME;
fs.mkdirSync(HOME, { recursive: true });
const tmp = (n) => path.join(HOME, n);
const lock = tmp('truecopy.lock');
const policy = { egressAllow: ['api.example.com'] };
Expand Down