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
135 changes: 71 additions & 64 deletions runtime/tests/runtime-signing.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -814,71 +814,78 @@ test("full chain clean -> summarize -> classify verifies with schema using commo
}
});

test("POST /trust-verification/sign/v1.0.0 returns signed proof and verifies via runtime-core", async () => {
const keys = makeKeys();
const srv = await startServer({
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64: keys.privatePemB64,
RECEIPT_SIGNING_PUBLIC_KEY_B64: keys.publicRaw32B64,
RECEIPT_SIGNER_ID: "runtime.commandlayer.eth",
});

try {
const payload = { subject: "hello", scope: "trust-verification" };
const resp = await fetch(`${srv.base}/trust-verification/sign/v1.0.0`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ payload }),
const TRUST_VERIFICATION_VERBS = ["sign", "attest", "authorize", "approve", "reject", "permit", "grant", "authenticate", "endorse"];

for (const verb of TRUST_VERIFICATION_VERBS) {
test(`POST /trust-verification/${verb}/v1.0.0 returns signed proof and verifies via runtime-core`, async () => {
const keys = makeKeys();
const srv = await startServer({
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64: keys.privatePemB64,
RECEIPT_SIGNING_PUBLIC_KEY_B64: keys.publicRaw32B64,
RECEIPT_SIGNER_ID: "runtime.commandlayer.eth",
});
assert.equal(resp.status, 200);
const json = await resp.json();
const { receipt } = unwrapReceiptResponse(json);
assert.ok(receipt?.metadata?.proof);
assert.equal(receipt.metadata.proof.signature.alg, "Ed25519");
assert.equal(receipt.metadata.proof.hash.alg, "SHA-256");

const verifiedResp = await fetch(`${srv.base}/verify`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(receipt),
});
assert.equal(verifiedResp.status, 200);
const verified = await verifiedResp.json();
assert.equal(verified.checks?.signature_valid, true);
assert.equal(verified.checks?.hash_matches, true);

const tampered = JSON.parse(JSON.stringify(receipt));
tampered.verb = "tampered-sign";
const tamperedResp = await fetch(`${srv.base}/verify`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(tampered),
});
assert.equal(tamperedResp.status, 200);
const tamperedVerify = await tamperedResp.json();
assert.equal(tamperedVerify.status, "INVALID");
assert.equal(tamperedVerify.checks?.signature_valid, false);
} finally {
await stop(srv.proc);
}
});

test("POST /trust-verification/sign/v1.0.0 rejects missing payload", async () => {
const keys = makeKeys();
const srv = await startServer({
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64: keys.privatePemB64,
RECEIPT_SIGNING_PUBLIC_KEY_B64: keys.publicRaw32B64,
RECEIPT_SIGNER_ID: "runtime.commandlayer.eth",
try {
const payload = { subject: "hello", scope: "trust-verification", verb };
const resp = await fetch(`${srv.base}/trust-verification/${verb}/v1.0.0`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ payload }),
});
assert.equal(resp.status, 200);
const json = await resp.json();
const { receipt } = unwrapReceiptResponse(json);
assert.ok(receipt?.metadata?.proof);
assert.equal(receipt.verb, verb);
assert.equal(receipt.class, "trust-verification");
assert.equal(receipt.version, "1.0.0");
assert.equal(receipt.metadata.proof.signature.alg, "Ed25519");
assert.equal(receipt.metadata.proof.hash.alg, "SHA-256");

const verifiedResp = await fetch(`${srv.base}/verify`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(receipt),
});
assert.equal(verifiedResp.status, 200);
const verified = await verifiedResp.json();
assert.equal(verified.checks?.signature_valid, true);
assert.equal(verified.checks?.hash_matches, true);

const tampered = JSON.parse(JSON.stringify(receipt));
tampered.verb = `tampered-${verb}`;
const tamperedResp = await fetch(`${srv.base}/verify`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(tampered),
});
assert.equal(tamperedResp.status, 200);
const tamperedVerify = await tamperedResp.json();
assert.equal(tamperedVerify.status, "INVALID");
assert.equal(tamperedVerify.checks?.signature_valid, false);
} finally {
await stop(srv.proc);
}
});
try {
const resp = await fetch(`${srv.base}/trust-verification/sign/v1.0.0`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({}),

test(`POST /trust-verification/${verb}/v1.0.0 rejects missing payload`, async () => {
const keys = makeKeys();
const srv = await startServer({
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64: keys.privatePemB64,
RECEIPT_SIGNING_PUBLIC_KEY_B64: keys.publicRaw32B64,
RECEIPT_SIGNER_ID: "runtime.commandlayer.eth",
});
assert.equal(resp.status, 400);
const json = await resp.json();
assert.equal(json.error, "missing_payload");
} finally {
await stop(srv.proc);
}
});
try {
const resp = await fetch(`${srv.base}/trust-verification/${verb}/v1.0.0`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({}),
});
assert.equal(resp.status, 400);
const json = await resp.json();
assert.equal(json.error, "missing_payload");
} finally {
await stop(srv.proc);
}
});
}
65 changes: 35 additions & 30 deletions server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,38 +1742,43 @@ app.post("/verify", async (req, res) => {
}
});

app.post("/trust-verification/sign/v1.0.0", async (req, res) => {
const payload = extractSignPayload(req.body);
if (payload === null) {
return res.status(400).json({
ok: false,
error: "missing_payload",
message: "Request body must include a non-null payload field",
...instancePayload(),
});
}
const TRUST_VERIFICATION_VERBS_V1 = ["sign", "attest", "authorize", "approve", "reject", "permit", "grant", "authenticate", "endorse"];

const traceId = makeTraceId();
const execution = {
entry: `${CANONICAL_BASE}/trust-verification/sign`,
verb: "sign",
version: "1.0.0",
class: "trust-verification",
};
for (const verb of TRUST_VERIFICATION_VERBS_V1) {
app.post(`/trust-verification/${verb}/v1.0.0`, async (req, res) => {
const payload = extractSignPayload(req.body);
if (payload === null) {
return res.status(400).json({
ok: false,
error: "missing_payload",
message: "Request body must include a non-null payload field",
...instancePayload(),
});
}

try {
const receipt = makeReceipt({
execution,
result: { payload },
status: "success",
traceId,
receiptId: makeFlowReceiptId(),
});
return res.status(200).json(wrapReceiptResponse(receipt, { trace: { trace_id: traceId, provider: process.env.RAILWAY_SERVICE_NAME || "runtime" } }));
} catch (signErr) {
return respondSigningError(res, signErr);
}
});
const traceId = makeTraceId();
const execution = {
entry: `${CANONICAL_BASE}/trust-verification/${verb}`,
verb,
version: "1.0.0",
class: "trust-verification",
};

try {
const receipt = makeReceipt({
execution,
result: { payload },
status: "success",
traceId,
receiptId: makeFlowReceiptId(),
});
receipt.verb = verb;
return res.status(200).json(wrapReceiptResponse(receipt, { trace: { trace_id: traceId, provider: process.env.RAILWAY_SERVICE_NAME || "runtime" } }));
} catch (signErr) {
return respondSigningError(res, signErr);
}
});
}

// verb routes
// -----------------------
Expand Down
Loading