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>
458 lines
10 KiB
Markdown
458 lines
10 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
go get github.com/orchard9/stemedb-go/steme
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
// 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.
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```go
|
|
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):
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
cd sdk/go/examples/skeptic
|
|
go run main.go
|
|
```
|
|
|
|
## Testing
|
|
|
|
Run unit tests:
|
|
|
|
```bash
|
|
cd sdk/go/steme
|
|
go test -v
|
|
```
|
|
|
|
Integration tests require a running StemeDB server:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. Never commit private keys to version control
|
|
2. Use environment variables or key management services
|
|
3. Rotate keys periodically
|
|
4. Use different keys for dev/staging/production
|
|
|
|
```go
|
|
// 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
|
|
|
|
1. Use BLAKE3 for source document hashing
|
|
2. Include timestamp + URL for web sources
|
|
3. Include file hash + timestamp for local files
|
|
|
|
```go
|
|
import "lukechampine.com/blake3"
|
|
|
|
// Hash a source document
|
|
sourceBytes := []byte(documentContent)
|
|
sourceHash := blake3.Sum256(sourceBytes)
|
|
sourceHashHex := hex.EncodeToString(sourceHash[:])
|
|
|
|
assertion.WithSourceHash(sourceHashHex)
|
|
```
|
|
|
|
### Confidence Scores
|
|
|
|
1. Start conservative (0.7-0.8 for human-submitted claims)
|
|
2. Use 0.95+ only for regulatory/peer-reviewed sources
|
|
3. Use 0.5-0.6 for anecdotal evidence
|
|
4. Never use 1.0 (implies certainty)
|
|
|
|
```go
|
|
// 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)
|
|
|
|
```go
|
|
// 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)
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
// 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
|