Mandate verification
@codespar/sdk/mandate: decode a mandate presentation token and check its Ed25519 signatures offline, with no API call.
@codespar/sdk/mandate is a separate entry point of the same package. It answers one question, and it answers it without the network: is this mandate token signed by the agent and the issuer it claims?
import { } from "@codespar/sdk/mandate";
const = (..!, {
: .., // raw 32-byte Ed25519 key, hex
});
if (.) {
.(.., .., .);
}Why offline matters. The bank, the auditor and the counterparty can check a mandate without asking CodeSpar anything: the public keys come from the agent's did:web document. The org HMAC that also travels in the token is not offline-verifiable, because checking it needs the org secret; the Ed25519 pair is the part a third party can verify. The same check from a terminal is codespar mandate verify.
verifyMandateToken
verifyMandateToken(token: string, opts?: VerifyMandateOptions): MandateVerificationOffline-verifies a V3 presentation token against the public keys you supply.
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | yes | The presentation token: base64url JSON of the signed fields plus the signature envelope. |
opts.agentPublicKey | string | Uint8Array | no | Raw 32-byte Ed25519 agent key, hex or bytes. |
opts.issuerPublicKey | string | Uint8Array | no | Raw 32-byte Ed25519 issuer key, hex or bytes. |
Returns MandateVerification: verified (true only if at least one carried signature verified and none failed), the decoded mandate, the agentDid and kid when present, and one SignatureCheck for the agent and one for the issuer. Each check reports verified, failed, skipped (carried, but you passed no key for it) or absent.
It throws only when the token cannot be decoded, so a malformed token and a well-formed unverified one are different outcomes.
const = (, {
: "7f3c…",
: "a91b…",
});
..; // "verified" | "failed" | "skipped" | "absent"
..;
..; // UNIX secondsdecodeMandateToken
decodeMandateToken(token: string): MandateDecodeResultSplits the envelope from the signed fields and verifies nothing. Returns { ok: true, token } or { ok: false, error }, where error is "invalid_payload" or "mandate_format_unsupported". Use it when you want to read a mandate you are not checking, for example to show scope and ceiling in a UI.
reconstructSigningString
reconstructSigningString(fields: Record<string, unknown>): stringRebuilds the canonical string the signatures cover: V3 is 14 fields and 13 : separators, V2 is the first 12. Absent optionals render empty so the separator count never changes, purposes is sorted lexicographically and comma-joined with escaping (\ first, then ,), and colons inside agent_kid are emitted verbatim because the string is serialized one way and never re-split.
You need this only to sign or to debug a signature; verifyMandateToken calls it for you.
verifyEd25519
verifyEd25519(signingString: string, signatureB64url: string, pub: Buffer): booleanOne raw signature check. Returns false on malformed input instead of throwing.
agentDidFromKid
agentDidFromKid(kid: string): stringStrips the #<fragment> from a key id (did:web:acme.example#1) and gives back the bare agent DID, which is where you fetch the public keys.
Types
MandateFields is the signed field set: format_version, id, agent_id, type (payment, subscription or delegation), amount as a decimal string, currency, purposes, expires_at in UNIX seconds, the optional max_amount, parent_id and denomination, secret_version, and the two V3-only fields principal_kyc_ref and agent_kid.
DecodedMandateToken adds the envelope: the org HMAC signature, and for V3 the agent_sig, issuer_sig and kid.