Reference
View as Markdown
Signatures
Every answer is signed, so you, or whoever you answer to, can prove later exactly what we said, about which address, at which block. Checking it is optional.
What is signed
- AlgorithmEd25519. The signature is base64url, in
signature.sig. - InputThe answer as JSON, with its
signaturefield removed, in canonical form (RFC 8785, JSON Canonicalization Scheme: keys sorted, no spaces). - Which key
signature.key_idnames the key. The public keys are atGET /v1/keys, as a JWKS (ktyOKP,crvEd25519). Fetch them once and keep them. - What it coversEverything in the answer: the verdict, the reasons, the
blockandas_ofit was true at, and itsrequest_id, so an answer can't be passed off as another request's.
Checking one
In JavaScript, with the browser's or Node's own crypto and the canonicalize package (RFC 8785):
import canonicalize from "canonicalize";
const { keys } = await (await fetch("https://api.lorglabs.com/v1/keys")).json();
async function isOurs(answer) {
const { signature, ...signed } = answer;
const jwk = keys.find((k) => k.kid === signature.key_id);
if (!jwk) return false;
const key = await crypto.subtle.importKey("jwk", { kty: "OKP", crv: "Ed25519", x: jwk.x }, { name: "Ed25519" }, false, ["verify"]);
const sig = Uint8Array.from(atob(signature.sig.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
return crypto.subtle.verify({ name: "Ed25519" }, key, sig, new TextEncoder().encode(canonicalize(signed)));
}
Each service signs with its own key: api.lorglabs.com's answers verify against its /v1/keys, the sandbox's against its own.
When it matters
For a platform, a signed answer is a record: if a user asks why a trade was refused, or allowed, the answer you got is the proof, with the block it was true at. For an agent acting for someone, it shows the owner what the agent was told before it moved their money.