This commit adds the read path (Cortex) to complement the write path (Spine): ## Crates - stemedb-api: HTTP API with axum + utoipa OpenAPI - /v1/assert, /v1/query, /v1/epoch, /v1/skeptic, /v1/trace, /v1/audit - Metered endpoints with quota enforcement - Ed25519 signature verification - stemedb-lens: Truth resolution lenses - RecencyLens, ConsensusLens, ConfidenceLens - VoteAwareConsensusLens (Ballot Box pattern) - TrustAwareAuthorityLens (The Hive pattern) - SkepticLens (conflict analysis) - EpochAwareLens (paradigm-safe queries) - stemedb-query: Query engine with materialized views ## Storage Extensions - VoteStore: Vote aggregation with cached counts - TrustRankStore: Agent reputation with decay - AuditStore: Query audit trail - IndexStore: SP/P/S index structures - SupersessionStore: Epoch supersession chains ## SDKs - sdk/go/steme: Go HTTP client with Ed25519 signing - sdk/go/adk: ADK-Go tools for AI agents ## Documentation - Updated CLAUDE.md, architecture.md, roadmap.md - New ai-lookup entries for all services - Use case docs for consumer health intelligence - Arena roadmap for simulation advancement Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
10 KiB
StemeDB Go SDK
The official Go SDK for StemeDB - A probabilistic knowledge graph database.
Overview
The Go SDK provides a type-safe, ergonomic API for interacting with StemeDB. It handles all the cryptographic complexity (Ed25519 signing, BLAKE3 hashing) and provides fluent builders for constructing assertions and queries.
Installation
go get github.com/orchard9/stemedb-go/steme
Quick Start
package main
import (
"context"
"log"
"github.com/orchard9/stemedb-go/steme"
)
func main() {
// Create a signer (or load from environment)
signer, err := steme.GenerateSigner()
if err != nil {
log.Fatal(err)
}
// Create client
client := steme.NewClient("http://localhost:3000", signer)
// Build and submit an assertion
assertion := steme.NewAssertion("Tesla_Inc", "has_revenue").
WithNumber(96.7).
WithConfidence(0.95).
WithSourceHash("0000000000000000000000000000000000000000000000000000000000000000").
Build()
hash, err := client.Assert(context.Background(), assertion)
if err != nil {
log.Fatal(err)
}
log.Printf("Created assertion: %s", hash)
}
Core Concepts
1. Signers
Every assertion submitted to StemeDB must be cryptographically signed using Ed25519. The SDK abstracts this complexity.
// Generate new keypair
signer, err := steme.GenerateSigner()
// Load from hex-encoded seed
signer, err := steme.NewSignerFromHex("your-64-char-hex-seed")
// Load from environment variable
signer, err := steme.SignerFromEnv("STEME_PRIVATE_KEY")
// Get your agent_id (public key)
agentID := signer.PublicKey() // 64-char hex string
// Save seed for recovery
seed := signer.Seed() // Store this securely!
2. Assertions
Assertions are the atomic units of knowledge in StemeDB. Use the fluent builder API.
assertion := steme.NewAssertion("Subject", "Predicate").
WithNumber(42.5). // Object value
WithConfidence(0.95). // 0.0 to 1.0
WithLifecycle(steme.LifecycleApproved). // Stage
WithSourceClass(steme.SourceClassClinical). // Authority tier
WithSourceHash("..."). // Evidence hash
Build()
Object Values
StemeDB supports four value types:
// Text
assertion.WithText("hello world")
// Number (float64)
assertion.WithNumber(96.7)
// Boolean
assertion.WithBoolean(true)
// Reference (entity ID)
assertion.WithReference("Other_Entity")
Lifecycle Stages
Assertions progress through lifecycle stages:
steme.LifecycleProposed // Initial idea/RFC
steme.LifecycleUnderReview // Active debate
steme.LifecycleApproved // Current truth
steme.LifecycleDeprecated // Was true, now superseded
steme.LifecycleRejected // Explicitly declined
Source Authority Tiers
Sources are tiered from most authoritative (Tier 0) to least (Tier 5):
steme.SourceClassRegulatory // Tier 0: FDA, EMA, WHO
steme.SourceClassClinical // Tier 1: Peer-reviewed trials
steme.SourceClassObservational // Tier 2: Real-world evidence
steme.SourceClassExpert // Tier 3: Guidelines
steme.SourceClassCommunity // Tier 4: Curated forums
steme.SourceClassAnecdotal // Tier 5: Social media
3. Queries
Query assertions with optional filters and lens-based conflict resolution.
params := steme.NewQuery().
WithSubject("Tesla_Inc").
WithPredicate("has_revenue").
WithLifecycle(steme.LifecycleApproved).
WithLens(steme.LensConsensus).
WithLimit(10).
Build()
result, err := client.Query(ctx, params)
Lenses
Lenses resolve conflicts among competing assertions:
steme.LensRecency // Latest timestamp wins
steme.LensConsensus // Most common value
steme.LensAuthority // Weighted by source tier
steme.LensVoteAwareConsensus // Highest vote weight
steme.LensTrustAwareAuthority // Weighted by TrustRank
4. Skeptic Queries ("Trust but Verify")
Unlike standard queries that pick a winner, Skeptic shows all competing claims.
result, err := client.Skeptic(ctx, steme.SkepticQueryParams{
Subject: "Semaglutide",
Predicate: "muscle_effect",
})
// Resolution status
switch result.Status {
case steme.ResolutionUnanimous:
// All sources agree
case steme.ResolutionAgreed:
// >75% weight on one claim
case steme.ResolutionContested:
// Significant disagreement
}
// Conflict score: 0.0 (unanimous) to 1.0 (chaos)
fmt.Printf("Conflict: %.2f\n", result.ConflictScore)
// All competing claims
for _, claim := range result.Claims {
fmt.Printf("%v: %.1f%% support (%d assertions)\n",
claim.Value.Value,
claim.WeightShare*100,
claim.AssertionCount,
)
}
Advanced Features
Context Support
All API calls accept context.Context for cancellation and timeouts.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
result, err := client.Query(ctx, params)
Custom HTTP Client
Customize timeouts, TLS, or add middleware.
httpClient := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, // Don't do this in production!
},
},
}
client := steme.NewClient("https://stemedb.example.com", signer).
WithHTTPClient(httpClient)
Forking Assertions
Create a new assertion that supersedes an existing one.
originalHash := "abc123..." // Hash from previous assertion
updated := steme.NewAssertion("Tesla_Inc", "has_revenue").
WithNumber(102.5). // New value
WithParentHash(originalHash). // Link to original
WithLifecycle(steme.LifecycleApproved).
WithSourceHash("...").
Build()
newHash, err := client.Assert(ctx, updated)
Validation
Assertions are validated client-side before submission.
assertion := steme.NewAssertion("Tesla_Inc", "has_revenue").
WithNumber(96.7).
WithConfidence(1.5). // Invalid! Must be 0.0-1.0
Build()
if err := assertion.Validate(); err != nil {
// Handle validation error
fmt.Printf("Validation failed: %v\n", err)
}
Error Handling
The SDK uses typed errors for common failure modes.
hash, err := client.Assert(ctx, assertion)
if err != nil {
switch e := err.(type) {
case *steme.APIError:
// HTTP error from server
fmt.Printf("API error [%d]: %s (code: %s)\n",
e.StatusCode, e.Message, e.Code)
case *steme.ValidationError:
// Client-side validation failure
fmt.Printf("Invalid %s: %s\n", e.Field, e.Message)
default:
// Other errors (network, etc.)
fmt.Printf("Error: %v\n", err)
}
}
Examples
Basic Usage
See sdk/go/examples/basic/main.go for a complete example showing:
- Keypair generation
- Assertion creation
- Automatic signing
- Querying with lenses
cd sdk/go/examples/basic
go run main.go
Skeptic Lens
See sdk/go/examples/skeptic/main.go for a "Trust but Verify" example showing:
- Seeding conflicting assertions
- Analyzing disagreement
- Interpreting conflict scores
cd sdk/go/examples/skeptic
go run main.go
Testing
Run unit tests:
cd sdk/go/steme
go test -v
Integration tests require a running StemeDB server:
# Start StemeDB server
cd /path/to/stemedb
cargo run --bin stemedb-api
# Run integration tests
export STEMEDB_URL=http://localhost:3000
cd sdk/go/steme
go test -v
Best Practices
Key Management
- Never commit private keys to version control
- Use environment variables or key management services
- Rotate keys periodically
- Use different keys for dev/staging/production
// Good: Load from environment
signer, err := steme.SignerFromEnv("STEME_PRIVATE_KEY")
// Bad: Hardcoded seed
signer, err := steme.NewSignerFromHex("abc123...") // Don't do this!
Source Hashing
- Use BLAKE3 for source document hashing
- Include timestamp + URL for web sources
- Include file hash + timestamp for local files
import "lukechampine.com/blake3"
// Hash a source document
sourceBytes := []byte(documentContent)
sourceHash := blake3.Sum256(sourceBytes)
sourceHashHex := hex.EncodeToString(sourceHash[:])
assertion.WithSourceHash(sourceHashHex)
Confidence Scores
- Start conservative (0.7-0.8 for human-submitted claims)
- Use 0.95+ only for regulatory/peer-reviewed sources
- Use 0.5-0.6 for anecdotal evidence
- Never use 1.0 (implies certainty)
// Good: Conservative confidence
assertion.WithConfidence(0.85)
// Bad: False certainty
assertion.WithConfidence(1.0)
Migration from Manual HTTP
If you're currently using raw HTTP calls, migration is straightforward.
Before (Manual HTTP)
// Build JSON payload
payload := map[string]interface{}{
"subject": "Tesla_Inc",
"predicate": "has_revenue",
"object": map[string]interface{}{"type": "Number", "value": 96.7},
"confidence": 0.95,
// ... compute signature manually ...
}
// POST to API
resp, err := http.Post("http://localhost:3000/v1/assert", "application/json", ...)
After (SDK)
assertion := steme.NewAssertion("Tesla_Inc", "has_revenue").
WithNumber(96.7).
WithConfidence(0.95).
WithSourceHash("...").
Build()
hash, err := client.Assert(ctx, assertion)
Performance Considerations
Batching
For high-volume ingestion, batch assertions:
for i := 0; i < 1000; i++ {
assertion := steme.NewAssertion(fmt.Sprintf("Entity_%d", i), "predicate").
WithNumber(float64(i)).
WithSourceHash("...").
Build()
if _, err := client.Assert(ctx, assertion); err != nil {
log.Printf("Failed to assert %d: %v", i, err)
}
}
Future SDK versions will include a BatchAssert() method for improved performance.
Connection Pooling
The SDK uses http.Client which automatically reuses connections. Reuse the same Client instance.
// Good: Reuse client
client := steme.NewClient(baseURL, signer)
for i := 0; i < 1000; i++ {
client.Assert(ctx, assertions[i])
}
// Bad: Create new client each time
for i := 0; i < 1000; i++ {
client := steme.NewClient(baseURL, signer) // Wasteful!
client.Assert(ctx, assertions[i])
}
Contributing
See the main repository's CONTRIBUTING.md for guidelines.
License
MIT