Client + offline verification for the Sovereign Sign-off Protocol. Sign the upstream lifecycle, open a case for a human to sign, and verify a sealed Sovereign Record — in your language. Pick a language once and every snippet on this page follows.
Client — sign the upstream events (policy.committed,
ai.inference.completed, guardrail.evaluated) with your Ed25519 key and open a case;
emit outcome.executed once it's sealed. Verify — check a record.ssp.json
offline: canonical claim hash and the Ed25519 trust signature (the Python SDK also verifies the WebAuthn
user signature and the Rekor / RFC 3161 anchors).
From the Sovereign GitLab package registry (you supply a token with read_package_registry).
pip install sovereign-saa-sdk \
--index-url "https://__token__:<read-token>@git.xor.ma/api/v4/groups/sovereign%2Fsovereign-sdk/-/packages/pypi/simple"# with GOPRIVATE set and a token in ~/.netrc for git.xor.ma
go get git.xor.ma/sovereign/sovereign-sdk/saa-go-sdk@latest# .npmrc
@sovereign:registry=https://git.xor.ma/api/v4/groups/sovereign%2Fsovereign-sdk/-/packages/npm/
//git.xor.ma/api/v4/groups/sovereign%2Fsovereign-sdk/-/packages/npm/:_authToken=<read-token>
npm install @sovereign/saa-sdk<dependency>
<groupId>ma.xor.sovereign</groupId>
<artifactId>saa-sdk</artifactId>
<version>0.1.0</version>
</dependency>
<!-- plus the sovereign-sdk <repository> + a Deploy-Token header in settings.xml -->Sign the upstream events and open a case via the client API. The response carries the
submission_id and the approver_url where a human signs.
from sovereign.saa import Client, Signer, base_claim
signer = Signer.load("saa-client.key") # generates + persists on first use
client = Client("https://app.sovereign.xor.ma/api", api_key="saa-dev-client-key",
signer=signer, tenant="acme-bank")
events = [signer.sign_event({**base_claim("ai.inference.completed", "acme-bank", "subj-001"),
"result": {"recommendation": "approve", "risk_score": 0.18, "threshold": 0.70}})]
resp = client.create_case(
domain="finance", artifact=b"Pay vendor ACME 12,750 USD", filename="wire.txt",
mime_type="text/plain", client_events=events, subject="subj-001",
context={"transaction_id": "TXN-1", "amount_usd": 12750, "account_id": "ACC-1"})
print(resp["submission_id"], resp["approver_url"])signer, _ := saa.LoadSigner("saa-client.key")
client, _ := saa.NewClient("https://app.sovereign.xor.ma/api", "saa-dev-client-key",
saa.WithSigner(signer), saa.WithTenant("acme-bank"))
ai := saa.BaseClaim("ai.inference.completed", "acme-bank", "subj-001")
ai["result"] = map[string]any{"recommendation": "approve", "risk_score": 0.18, "threshold": 0.70}
ev, _ := signer.SignEvent(ai)
resp, _ := client.CreateCase(saa.CaseInput{
Domain: "finance", Artifact: []byte("Pay vendor ACME 12,750 USD"),
Filename: "wire.txt", MimeType: "text/plain", Subject: "subj-001",
Context: map[string]any{"transaction_id": "TXN-1", "amount_usd": 12750, "account_id": "ACC-1"},
ClientEvents: []map[string]any{ev},
})
fmt.Println(resp["submission_id"], resp["approver_url"])import { Client, Signer, baseClaim } from "@sovereign/saa-sdk";
const signer = Signer.load("saa-client.key"); // generates + persists on first use
const client = new Client("https://app.sovereign.xor.ma/api", "saa-dev-client-key",
{ signer, tenant: "acme-bank" });
const ai = signer.signEvent({ ...baseClaim("ai.inference.completed", "acme-bank", "subj-001"),
result: { recommendation: "approve", risk_score: 0.18, threshold: 0.7 } });
const resp = await client.createCase({
domain: "finance", artifact: new TextEncoder().encode("Pay vendor ACME 12,750 USD"),
filename: "wire.txt", mimeType: "text/plain", subject: "subj-001",
context: { transaction_id: "TXN-1", amount_usd: 12750, account_id: "ACC-1" },
clientEvents: [ai],
});
console.log(resp.submission_id, resp.approver_url);Signer signer = Signer.load("saa-client.key");
SaaClient client = new SaaClient("https://app.sovereign.xor.ma/api",
"saa-dev-client-key", signer, "acme-bank", /*insecureTls*/ false);
Map<String,Object> ai = Signer.baseClaim("ai.inference.completed", "acme-bank", "subj-001");
ai.put("result", Map.of("recommendation", "approve", "risk_score", 0.18, "threshold", 0.70));
SaaClient.CaseInput in = new SaaClient.CaseInput();
in.domain = "finance";
in.artifact = "Pay vendor ACME 12,750 USD".getBytes("UTF-8");
in.filename = "wire.txt"; in.mimeType = "text/plain"; in.subject = "subj-001";
in.context = Map.of("transaction_id", "TXN-1", "amount_usd", 12750, "account_id", "ACC-1");
in.clientEvents = java.util.Arrays.asList(signer.signEvent(ai));
Map<String,Object> resp = client.createCase(in);
System.out.println(resp.get("submission_id") + " " + resp.get("approver_url"));After the decision is sealed, emit a client-signed outcome.executed linked to the ledger entry.
client.record_outcome(ledger_uuid, status="executed")client.RecordOutcome(ledgerUUID, "executed")await client.recordOutcome(ledgerUuid, "executed");client.recordOutcome(ledgerUuid, "executed");Verify a sealed record.ssp.json with no server. The Sovereign trust public key is supplied
out-of-band — never read from the record — so a forged bundle cannot self-certify.
import json
from sovereign.saa import verify_record
record = json.load(open("record.ssp.json"))
trust = open("trust-public.pem").read() # out-of-band
result = verify_record(record, trust, rp_id="app.sovereign.xor.ma")
print(result) # per-check PASS/FAIL + overall
assert result.okrecord, _ := os.ReadFile("record.ssp.json")
trust, _ := os.ReadFile("trust-public.pem") // out-of-band
res, _ := saa.VerifyRecord(record, trust)
fmt.Println(res) // per-check PASS/FAIL
// res.OK() == trueimport { readFileSync } from "node:fs";
import { verifyRecord } from "@sovereign/saa-sdk";
const result = verifyRecord(
readFileSync("record.ssp.json"),
readFileSync("trust-public.pem", "utf8")); // out-of-band
console.log(result.toString());
// result.ok === truebyte[] record = Files.readAllBytes(Paths.get("record.ssp.json"));
byte[] trust = Files.readAllBytes(Paths.get("trust-public.pem")); // out-of-band
VerifyResult res = Verify.verifyRecord(record, trust);
System.out.println(res); // per-check PASS/FAIL
// res.ok() == trueCanonicalization is RFC 8785-style JCS (key-sorted, compact, UTF-8) and is byte-identical across all four SDKs — a record signed via one verifies in any other. Verification depth in this release:
| Check | Python | Go | TypeScript | Java |
|---|---|---|---|---|
| Canonical claim hash | ✓ | ✓ | ✓ | ✓ |
| Ed25519 trust signature | ✓ | ✓ | ✓ | ✓ |
| WebAuthn user signature | ✓ | — | — | — |
| Rekor inclusion · RFC 3161 timestamp | ✓ | — | — | — |