Adds payload_commitment / metadata_commitment / verify_payload_commitment and assert_commitment_safe_numbers to the Python, TypeScript, and Go SDKs, each building on the frozen canonical_json/domain_hash primitives (no change to their byte output). The number preflight is a byte-for-byte port of the backend assert_commitment_safe_numbers (floats rejected, |int| > 2^53-1 rejected, bool exempt) and is wired into the v2 log_event / log_events send path, raising a typed AttestoUnsafeNumberError with the JSON path so the rule fails at dev time rather than as a production 422; preflight=False / SkipPreflight defers to the server. New shared corpus golden-vectors/sdk-parity/canonical-numbers.json (15 accept + 8 reject), accept-hashes generated from the backend _commitment. Proven: Python = TypeScript = Go = backend produce byte-identical commitment hashes for every accept vector and identical reject paths (the Go float64-vs-Python- int serialization parity holds). READMEs updated per SDK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Attesto Go SDK
Official Go SDK for Attesto 2.0 Proofstream. The default API base URL is
https://verify.attesto.eu. Use it from server-side, infrastructure, security
tooling, CI, evidence exporters, and operator automation. Do not embed Attesto API keys in browser bundles, mobile apps, or public artifacts.
Install
go get git.rotz.ai/rotzmediagroup/attesto-v1/sdk/go
The first release is VCS-resolved from the Attesto repository. It intentionally uses only the Go standard library.
Quickstart
package main
import (
"context"
"fmt"
"log"
"os"
"time"
attesto "git.rotz.ai/rotzmediagroup/attesto-v1/sdk/go"
)
func main() {
ctx := context.Background()
client, err := attesto.NewClient(os.Getenv("ATTESTO_API_KEY"))
if err != nil {
log.Fatal(err)
}
stream, err := client.CreateStream(ctx, attesto.StreamCreateInput{
UseCase: "ai-governance",
PolicyID: "policy-main",
})
if err != nil {
log.Fatal(err)
}
receipt, err := client.LogEvent(ctx, stream.StreamID, attesto.EventInput{
SourceRef: "decision-42",
OccurredAt: time.Now().UTC().Format(time.RFC3339Nano),
Payload: attesto.M{
"model": "risk-classifier",
"score": 0.92,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(receipt.StreamEventID, receipt.EventHash)
}
Attesto stores source-system time separately from backend ingest time.
OccurredAt must be RFC3339 with a timezone offset. The Go SDK fills it with
time.Now().UTC() when omitted, but production integrations should pass the
real upstream event timestamp whenever the source system provides one.
Committed payload number rule
When events are committed to a Proofstream, payload and metadata numbers must
serialize identically across Python, Go, and JavaScript. Non-integer numbers
and integers beyond ±(2^53−1) are rejected at ingestion (HTTP 422); encode
decimals and large integers as strings (e.g. {"score": "0.87"}). This keeps
cross-language commitment recomputation byte-exact (CanonicalJSON).
The SDK enforces the same rule locally before sending, so you see it at dev
time rather than as a production 422. LogEvent / LogEvents return an
*UnsafeNumberError (with .Path, the JSON path to the offending value). Set
RequestOptions{SkipPreflight: true} to defer to the server.
// Commitment a Proofstream stores for a payload, byte-identical to the server
// (and to the Python / TypeScript SDKs):
commitment, _ := attesto.PayloadCommitment(map[string]any{"decision": "approve", "score_bp": 8700})
// commitment["canonical_payload_hash"] == server's stored hash
ok, _ := attesto.VerifyPayloadCommitment(myPayload, event) // recompute and compare
Verification
Remote verification uses Attesto's public /v2/verify API. Offline receipt
verification uses ATTESTO-PROOFSTREAM-001 canonical JSON, domain-separated
hashes, and Ed25519 signature verification locally.
report := attesto.VerifyReceiptOffline(receipt.Receipt, publicKeyHex)
if !report.OK {
log.Fatalf("receipt failed verification: %v", report.Problems)
}
Operator and Admin Endpoints
System-key clients are created with attesto.NewClient. Tenant/operator
endpoints, including connector installation and Local Vault installation
management, use attesto.NewBearerClient with a tenant bearer token obtained
from the dashboard session flow.
Secrets returned once by connector creation are present only in the returned struct and are never logged by the SDK.