FIX 11 — reject cross-language-divergent numbers in committed payloads. append_stream_event now rejects non-integer numbers and integers beyond ±(2^53−1) in payload/metadata at ingestion (HTTP 422), so a customer recomputing a commitment in a Go/TS verifier can never read it as tampering. Documented in the protocol spec + all three SDK READMEs; 9 tests. FIX 12 — Nova IVC chain-continuity invariant. record_ivc_epoch now takes a per-stream transaction-scoped advisory lock and fails closed (409) if an epoch does not extend the latest verified epoch's next_state_root or would skip an unproven checkpoint — so two concurrent provers cannot fork Attesto's own lane and a failed proof cannot be chained over. The worker stops the pass on a failed proof (break, not continue) and always backfills the oldest unproven checkpoint first so holes heal. Tests cover both 409 paths plus in-order record + replay. FIX 13 — pin the e2e v1 checkpoint shape. Config now requires PROOFSTREAM_WINDOW_MAX_EVENTS=1 (alongside CHECKPOINT_MAX_WINDOWS=4) when Nova is enabled, and the worker refuses to close an aged checkpoint below the 4-window shape (which would be unprovable). Documented as the deliberate fail-closed v1 behavior; config + worker tests. VERIFY-1 — investigation: the standard production ingestion path (SDK → append_stream_event → persist_stream_event_receipt) does NOT write the nova_e2e boundary metadata the prover requires; no writer exists in backend/app, and _e2e_vector_for_checkpoint requires (not derives) it. So Nova proofs currently cover golden-vector/harness inputs, not live customer receipts — the open Route A/B item. Recorded in CRYPTO_REVIEW_CHECKLIST as a coverage-scope note; no Route A/B implementation in this release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
97 lines
2.7 KiB
Markdown
97 lines
2.7 KiB
Markdown
# 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
|
||
|
||
```shell
|
||
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
|
||
|
||
```go
|
||
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`).
|
||
|
||
## 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.
|
||
|
||
```go
|
||
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.
|