Skip to content

API Reference

import receipt "github.com/agent-receipts/ar/sdk/go/receipt"

Core package for creating, signing, verifying, and chaining Agent Receipts.

func Create(input CreateInput) UnsignedAgentReceipt

Build an unsigned receipt. Auto-generates an ID (urn:uuid:...) and sets the issuance timestamp.

func Sign(unsigned UnsignedAgentReceipt, privateKeyPEM string, verificationMethod string) (AgentReceipt, error)

Sign an unsigned receipt with an Ed25519 private key (PEM-encoded). Returns a signed AgentReceipt with an Ed25519Signature2020 proof.

func Verify(r AgentReceipt, publicKeyPEM string) (bool, error)

Verify the Ed25519 signature on a signed receipt.

func GenerateKeyPair() (KeyPair, error)

Generate an Ed25519 key pair in PEM format.

func HashReceipt(r AgentReceipt) (string, error)

Compute the SHA-256 hash of a receipt using canonical JSON serialization. Returns a string in sha256:<hex> format.

func VerifyChain(receipts []AgentReceipt, publicKeyPEM string) ChainVerification

Verify an entire receipt chain: signatures, hash linkage between consecutive receipts, and sequence numbers.

func Canonicalize(v any) (string, error)

RFC 8785 canonical JSON serialization.

func SHA256Hash(data string) string

Compute a SHA-256 hash, returned as hex.

func TruncatePromptPreview(s string, maxLen int) (preview string, truncated bool)

Truncate a prompt preview string to maxLen, returning whether it was truncated.

func Context() []string
func CredentialType() []string

Return the W3C VC @context and type arrays used in receipts.

A signed W3C Verifiable Credential with proof.

type AgentReceipt struct {
Context []string `json:"@context"`
ID string `json:"id"`
Type []string `json:"type"`
Version string `json:"version"`
Issuer Issuer `json:"issuer"`
IssuanceDate string `json:"issuanceDate"`
CredentialSubject CredentialSubject `json:"credentialSubject"`
Proof Proof `json:"proof"`
}

Same as AgentReceipt but without the Proof field.

type CreateInput struct {
Issuer Issuer
Principal Principal
Action Action
Outcome Outcome
Chain Chain
Intent *Intent
Authorization *Authorization
}
type KeyPair struct {
PublicKey string
PrivateKey string
}

PEM-encoded Ed25519 key pair.

type Issuer struct {
ID string `json:"id"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Operator *Operator `json:"operator,omitempty"`
Model string `json:"model,omitempty"`
SessionID string `json:"session_id,omitempty"`
Runtime *Runtime `json:"runtime,omitempty"` // open metadata container (v0.5.0, ADR-0026)
}
// Runtime is the open container for runtime/observability metadata. The typed
// members are documented; unknown keys are preserved via Extra so the field
// stays open across SDKs.
type Runtime struct {
AgentID string // runtime.agent_id — the sub-agent that issued the receipt
AgentType string // runtime.agent_type — e.g. "general-purpose"
Extra map[string]json.RawMessage // any additional runtime keys, preserved verbatim
}
type Principal struct {
ID string `json:"id"`
Type string `json:"type,omitempty"`
}
type Action struct {
ID string `json:"id"`
Type string `json:"type"`
RiskLevel RiskLevel `json:"risk_level"`
Target *ActionTarget `json:"target,omitempty"`
ParametersHash string `json:"parameters_hash,omitempty"`
Timestamp string `json:"timestamp"`
TrustedTimestamp string `json:"trusted_timestamp,omitempty"`
}
type Outcome struct {
Status OutcomeStatus `json:"status"`
Error string `json:"error,omitempty"`
Reversible *bool `json:"reversible,omitempty"`
ReversalMethod string `json:"reversal_method,omitempty"`
ReversalWindowSeconds *int `json:"reversal_window_seconds,omitempty"`
StateChange *StateChange `json:"state_change,omitempty"`
}
type Chain struct {
Sequence int `json:"sequence"`
PreviousReceiptHash *string `json:"previous_receipt_hash"`
ChainID string `json:"chain_id"`
}
type ChainVerification struct {
Valid bool `json:"valid"`
Length int `json:"length"`
Receipts []ReceiptVerification `json:"receipts"`
BrokenAt int `json:"broken_at"`
Error string `json:"error,omitempty"`
}
const Version = "0.3.0"
type RiskLevel string
const (
RiskLow RiskLevel = "low"
RiskMedium RiskLevel = "medium"
RiskHigh RiskLevel = "high"
RiskCritical RiskLevel = "critical"
)
type OutcomeStatus string
const (
StatusSuccess OutcomeStatus = "success"
StatusFailure OutcomeStatus = "failure"
StatusPending OutcomeStatus = "pending"
)
import receipt "github.com/agent-receipts/ar/sdk/go/receipt"

HPKE-based envelope for encrypting tool-call parameters into a receipt’s parameters_disclosure field (ADR-0012, ciphersuite hpke-x25519-hkdf-sha256-aes-256-gcm). The emitter holds only the forensic public key and never sees the plaintext again after encryption; the private key stays offline. For the threat model and operator configuration see the Parameter Disclosure specification. For the envelope JSON shape see the schema reference.

This is the SDK-direct path. When emitting through the daemon the operator provides the public key in daemon config; the daemon calls these helpers automatically (ADR-0012 §“Operator config”).

func GenerateForensicKeyPair() (ForensicKeyPair, error)

Generate an X25519 key pair for forensic disclosure. PublicKey (32 raw bytes) is shared with emitters so they can encrypt disclosures. PrivateKey (32 raw bytes) must be kept offline — separate from the Ed25519 signing key (ADR-0001 / ADR-0012). Returns an error if the system CSPRNG fails.

func ForensicKeyFingerprint(publicKey []byte) (string, error)

Return the canonical fingerprint of an X25519 forensic public key in sha256:<lowercase hex> form (ADR-0015): SHA-256 over the raw 32-byte key. This is the value the daemon writes as the recipient kid in a DisclosureEnvelope, and the value a forensic tool recomputes from a held key to match receipts. publicKey must be the raw 32-byte X25519 key (not SPKI/PEM). Returns an error if len(publicKey) != 32.

Go SDK only. The TS and Py SDKs do not yet export this helper.

func ForensicPublicFromPrivate(privateKey []byte) ([]byte, error)

Derive the X25519 public key (32 raw bytes) from a 32-byte forensic private key. A forensic responder typically holds only the private key; this function lets it compute the matching ForensicKeyFingerprint and locate receipts encrypted to that key without a separate key registry. Returns an error if len(privateKey) != 32 or the key bytes are invalid.

Go SDK only. The TS and Py SDKs do not yet export this helper.

func EncryptDisclosure(params map[string]any, recipientPublicKey []byte, kid string) (*DisclosureEnvelope, error)

Encrypt params as a v1 HPKE disclosure envelope. params is RFC 8785 JCS-canonicalized before encryption so all SDKs produce the same ciphertext for the same parameters object. recipientPublicKey must be 32 raw bytes; kid is the recipient key identifier (a sha256:<hex> fingerprint from ForensicKeyFingerprint, or a did:key DID URL). Returns an error if any argument is invalid or if the system CSPRNG fails.

func DecryptDisclosure(env *DisclosureEnvelope, recipientPrivateKey []byte) (map[string]any, error)

Recover the plaintext parameters from a v1 HPKE disclosure envelope. recipientPrivateKey must be 32 raw bytes. Returns an error if the envelope version or algorithm is unsupported, if the key or ciphertext is invalid, or if AEAD authentication fails.

// Key management (run once offline, store private key securely)
kp, err := receipt.GenerateForensicKeyPair()
fingerprint, err := receipt.ForensicKeyFingerprint(kp.PublicKey)
// fingerprint == "sha256:..." — use as kid and in daemon config
// Emitter side (holds only the public key)
env, err := receipt.EncryptDisclosure(
map[string]any{"path": "/etc/passwd", "mode": "r"},
kp.PublicKey,
fingerprint,
)
// embed env in action.ParametersDisclosure, then sign the receipt
// Forensic / audit side (holds the offline private key)
params, err := receipt.DecryptDisclosure(env, kp.PrivateKey)
// params == map[string]any{"mode": "r", "path": "/etc/passwd"}
// Deriving a public key from only the private key (forensic tooling)
pub, err := receipt.ForensicPublicFromPrivate(kp.PrivateKey)
fp2, err := receipt.ForensicKeyFingerprint(pub)
// fp2 == fingerprint

In practice the envelope comes from a stored receipt and the private key from the file obsigna-daemon --init-forensic-key wrote — a raw 32-byte X25519 key, not PEM, so read it as bytes and pass it straight in. Pull the envelope out of the receipt JSON at credentialSubject.action.parameters_disclosure:

import (
"encoding/json"
"log"
"os"
receipt "github.com/agent-receipts/ar/sdk/go/receipt"
)
// raw 32-byte private key written by --init-forensic-key (kept offline)
priv, err := os.ReadFile("/path/to/forensic.key")
if err != nil {
log.Fatal(err)
}
// from: obsigna receipt show <seq> --json > receipt.json
data, err := os.ReadFile("receipt.json")
if err != nil {
log.Fatal(err)
}
var rec struct {
CredentialSubject struct {
Action struct {
ParametersDisclosure *receipt.DisclosureEnvelope `json:"parameters_disclosure"`
} `json:"action"`
} `json:"credentialSubject"`
}
if err := json.Unmarshal(data, &rec); err != nil {
log.Fatal(err)
}
params, err := receipt.DecryptDisclosure(rec.CredentialSubject.Action.ParametersDisclosure, priv)
if err != nil {
log.Fatal(err)
}
// params holds the original tool-call parameters
type ForensicKeyPair struct {
PublicKey []byte // 32 bytes; share with emitters
PrivateKey []byte // 32 bytes; keep offline
}

Raw X25519 key bytes. Unlike KeyPair (Ed25519, PEM), these are raw bytes because X25519 has no widespread PKCS8 PEM convention and raw bytes compose naturally with HPKE library APIs.

type DisclosureEnvelope struct {
V string `json:"v"`
Alg string `json:"alg"`
Recipients []DisclosureRecipient `json:"recipients"`
CT string `json:"ct"`
}
type DisclosureRecipient struct {
KID string `json:"kid"`
Enc string `json:"enc"` // unpadded base64url, 43 chars for X25519
}

v1 HPKE envelope stored in action.parameters_disclosure. CT is the base64url-encoded AEAD ciphertext. Field names in DisclosureRecipient follow RFC 9180 §4.1 (enc, not encap).


import "github.com/agent-receipts/ar/sdk/go/taxonomy"

Action type registry and tool call classification.

func ClassifyToolCall(toolName string, mappings []TaxonomyMapping) ClassificationResult

Classify a tool call to an action type and risk level using the provided mappings.

func AllActions() []ActionTypeEntry

Return all 18 built-in action types (filesystem, system, and data categories, plus unknown).

func GetActionType(actionType string) *ActionTypeEntry

Look up an action type by name. Returns nil if not found.

func ResolveActionType(actionType string) ActionTypeEntry

Like GetActionType but returns an “unknown” fallback instead of nil.

func LoadTaxonomyConfig(path string) ([]TaxonomyMapping, error)

Load taxonomy mappings from a JSON file. Validates that no duplicate tool names exist.

var FilesystemActions []ActionTypeEntry // 7 filesystem action types
var SystemActions []ActionTypeEntry // 7 system action types
var DataActions []ActionTypeEntry // 3 data/API action types

Pre-populated slices of action type entries by category. Use AllActions() to get all categories combined.

type ActionTypeEntry struct {
Type string `json:"type"`
Description string `json:"description"`
RiskLevel receipt.RiskLevel `json:"risk_level"`
}
type TaxonomyMapping struct {
ToolName string `json:"tool_name"`
ActionType string `json:"action_type"`
}
type ClassificationResult struct {
ActionType string `json:"action_type"`
RiskLevel receipt.RiskLevel `json:"risk_level"`
}

import "github.com/agent-receipts/ar/sdk/go/store"

SQLite-backed receipt persistence, querying, and chain verification. Uses pure Go SQLite (no CGO) with WAL mode.

func Open(dbPath string) (*Store, error)

Open or create a SQLite receipt store. Pass ":memory:" for an in-memory store.

func (s *Store) Insert(r receipt.AgentReceipt, receiptHash string) error
func (s *Store) GetByID(id string) (*receipt.AgentReceipt, error)
func (s *Store) GetChain(chainID string) ([]receipt.AgentReceipt, error)
func (s *Store) QueryReceipts(q Query) ([]receipt.AgentReceipt, error)
func (s *Store) Stats() (Stats, error)

Returns aggregate statistics: total receipts, chain count, and breakdowns by risk level, status, and action type.

func (s *Store) VerifyStoredChain(chainID string, publicKeyPEM string) (receipt.ChainVerification, error)
func (s *Store) Close() error
type ReceiptStore interface {
Insert(r receipt.AgentReceipt, receiptHash string) error
GetByID(id string) (*receipt.AgentReceipt, error)
GetChain(chainID string) ([]receipt.AgentReceipt, error)
QueryReceipts(q Query) ([]receipt.AgentReceipt, error)
Stats() (Stats, error)
VerifyStoredChain(chainID string, publicKeyPEM string) (receipt.ChainVerification, error)
Close() error
}
type Query struct {
ChainID *string
ActionType *string
RiskLevel *receipt.RiskLevel
Status *receipt.OutcomeStatus
After *string
Before *string
// When nil, all matching rows are returned (no default cap).
Limit *int
// When true, returns newest receipts first; ties broken by sequence descending.
NewestFirst bool
}
type Stats struct {
Total int `json:"total"`
Chains int `json:"chains"`
ByRisk []GroupCount `json:"by_risk"`
ByStatus []GroupCount `json:"by_status"`
ByAction []GroupCount `json:"by_action"`
}
type GroupCount struct {
Label string `json:"label"`
Count int `json:"count"`
}