# 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:18180", 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:18180
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:18180/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
