Portable receipt export (*.attesto.json): export_receipt_file /
verify_receipt_file in Python, exportReceiptFile / verifyReceiptFile in
TypeScript, ExportReceiptFile / VerifyReceiptExport in Go, plus
`attesto verify file` in the CLI. New normative corpus
golden-vectors/sdk-parity/receipt-export.json (valid, tampered-inner,
linkage-mismatch, wrong-format, embedded-hint-only) passes identically in
all three SDKs; a Python-made export verifies through the Go CLI
end-to-end. Embedded witness keys are explicit second-class hints
(kind=receipt-export-selfcontained).
attestedFetch (TS) attests AI calls at the transport exactly like the
gateway: OpenAI-compatible paths -> attesto.model_decision with
commitments only (SSE reassembled after byte-for-byte pass-through),
anything else -> http_call; fail-open by default with onError, strict
rejects; attest() wraps any function with a commitment event +
lastReceipt. 5 emulator tests prove raw prompt/completion text never
appears in any stored object.
Edge runtimes: new guard test fails the build if any node: builtin enters
the dist/index.js module graph (FileHeadStore stays out by design), and
the receipt+export corpora now run on Bun in CI (10 cases green locally).
render_receipt_pdf ships behind the attesto[receipt-pdf] extra (fpdf2 +
qrcode, pure Python; core stays light) — one-page rendering with a QR of
{receipt_hash, event_hash} and a disclaimer that the JSON, not the PDF,
is the evidence; clean ImportError naming the extra when absent.
Also fixed a stale CI assertion: the npm package-install smoke pinned
SDK_VERSION 0.1.1; it now reads the version from package.json.
Suites: Python 106 passed, TypeScript 67+5 passed, Go green, package
policy contract green. Connectorkit already exists in all three languages
(no port needed).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package attesto
|
|
|
|
// [P3.4] Receipt-export parity corpus — Go verifier.
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestReceiptExportParity(t *testing.T) {
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "golden-vectors", "sdk-parity", "receipt-export.json"))
|
|
if err != nil {
|
|
t.Fatalf("read corpus: %v", err)
|
|
}
|
|
var corpus struct {
|
|
Cases []struct {
|
|
ID string `json:"id"`
|
|
ExpectOK bool `json:"expect_ok"`
|
|
PublicKeyHex *string `json:"public_key_hex"`
|
|
Export json.RawMessage `json:"export"`
|
|
} `json:"cases"`
|
|
}
|
|
if err := json.Unmarshal(raw, &corpus); err != nil {
|
|
t.Fatalf("parse corpus: %v", err)
|
|
}
|
|
if len(corpus.Cases) < 5 {
|
|
t.Fatalf("expected >=5 cases, got %d", len(corpus.Cases))
|
|
}
|
|
for _, testCase := range corpus.Cases {
|
|
t.Run(testCase.ID, func(t *testing.T) {
|
|
key := ""
|
|
if testCase.PublicKeyHex != nil {
|
|
key = *testCase.PublicKeyHex
|
|
}
|
|
report := VerifyReceiptExport(testCase.Export, key)
|
|
if report.OK != testCase.ExpectOK {
|
|
t.Fatalf("ok=%v want %v (problems: %v)", report.OK, testCase.ExpectOK, report.Problems)
|
|
}
|
|
if testCase.PublicKeyHex == nil && testCase.ExpectOK && report.Kind != "receipt-export-selfcontained" {
|
|
t.Fatalf("kind=%q, want receipt-export-selfcontained", report.Kind)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExportReceiptFileRoundTrip(t *testing.T) {
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "golden-vectors", "sdk-parity", "receipt-export.json"))
|
|
if err != nil {
|
|
t.Fatalf("read corpus: %v", err)
|
|
}
|
|
var corpus struct {
|
|
Cases []struct {
|
|
PublicKeyHex *string `json:"public_key_hex"`
|
|
Export struct {
|
|
Receipt json.RawMessage `json:"receipt"`
|
|
} `json:"export"`
|
|
} `json:"cases"`
|
|
}
|
|
if err := json.Unmarshal(raw, &corpus); err != nil {
|
|
t.Fatalf("parse corpus: %v", err)
|
|
}
|
|
valid := corpus.Cases[0]
|
|
path := filepath.Join(t.TempDir(), "receipt.attesto.json")
|
|
if _, err := ExportReceiptFile(valid.Export.Receipt, path, *valid.PublicKeyHex); err != nil {
|
|
t.Fatalf("export: %v", err)
|
|
}
|
|
exported, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("read export: %v", err)
|
|
}
|
|
report := VerifyReceiptExport(exported, *valid.PublicKeyHex)
|
|
if !report.OK {
|
|
t.Fatalf("round-trip verify failed: %v", report.Problems)
|
|
}
|
|
}
|