// Package main seeds StemeDB with demo data using realistic agent identities. // // This program creates properly signed assertions for all 5 demo scenarios // using pre-generated keypairs from demo/keys/agents.json. // // Usage: // // go run ./cmd/demo-seed // go run ./cmd/demo-seed --api-url http://localhost:18180 // go run ./cmd/demo-seed --keys-file ./demo/keys/agents.json --verbose // // The demo scenarios are: // 1. Conflicting Claims - FDA vs Reddit on gastroparesis risk // 2. Cascade Invalidation - Source retraction affecting assertions // 3. Audit Trail - Agent query logging // 4. Time Decay - Historical vs current market status // 5. Trust & Safety - Quarantine and circuit breaker triggers package main import ( "bytes" "context" "encoding/json" "flag" "fmt" "io" "log" "net/http" "os" "path/filepath" "time" "github.com/orchard9/stemedb-go/steme" ) const ( defaultBaseURL = "http://localhost:18180" defaultKeysFile = "demo/keys/agents.json" ) // DemoAgent represents a demo agent from agents.json. type DemoAgent struct { Seed string `json:"seed"` PublicKey string `json:"public_key"` Tier int `json:"tier"` Description string `json:"description"` Sources string `json:"sources"` } // AgentRegistry holds all demo agents. type AgentRegistry struct { agents map[string]DemoAgent signers map[string]*steme.Signer } func main() { // Parse flags apiURL := flag.String("api-url", "", "StemeDB API URL (default: $STEMEDB_API_URL or http://localhost:18180)") keysFile := flag.String("keys-file", "", "Path to agents.json (default: demo/keys/agents.json)") verbose := flag.Bool("verbose", false, "Enable verbose output") flag.Parse() // Resolve API URL url := *apiURL if url == "" { url = os.Getenv("STEMEDB_API_URL") } if url == "" { url = defaultBaseURL } // Resolve keys file path keysPath := *keysFile if keysPath == "" { // Try to find keys file relative to working directory keysPath = findKeysFile() } // Load agents registry, err := loadAgents(keysPath) if err != nil { log.Fatalf("Failed to load agents: %v", err) } ctx := context.Background() fmt.Println("=== StemeDB Demo Data Seeder ===") fmt.Println() fmt.Printf("API URL: %s\n", url) fmt.Printf("Keys File: %s\n", keysPath) fmt.Println() // Print agent mapping fmt.Println("Demo Agents:") for name, agent := range registry.agents { fmt.Printf(" %s (T%d) -> %s...\n", name, agent.Tier, agent.PublicKey[:16]) } fmt.Println() // Verify server is running using FDA agent fdaSigner := registry.signers["fda:drug-label-ingestor"] client := steme.NewClient(url, fdaSigner) health, err := client.Health(ctx) if err != nil { log.Fatalf("Server not reachable at %s: %v\nStart the server with: cargo run --package stemedb-api", url, err) } fmt.Printf("Server Status: %s (v%s, %d assertions)\n", health.Status, health.Version, health.AssertionsCount) fmt.Println() // Register all demo sources BEFORE creating assertions fmt.Println("=== Registering Demo Sources ===") registered := 0 for name, src := range Sources { err := registerSource(ctx, url, name, src) if err != nil { log.Printf(" Warning: %s: %v", name, err) } else { registered++ } } fmt.Printf(" Registered %d sources\n\n", registered) // Populate all 5 demo scenarios demo1Hashes := populateDemo1ConflictingClaims(ctx, registry, url, *verbose) sourceHash := populateDemo2SourceRetraction(ctx, registry, url, *verbose) agentID := populateDemo3AuditTrail(ctx, registry, url, *verbose) populateDemo4TimeDecay(ctx, registry, url, *verbose) populateDemo5TrustSafety(ctx, registry, url, *verbose) // P2.2: Populate Drug Conflict Scenarios (150+ assertions across 3 drugs) populateDrugConflicts(ctx, registry, url) // Wait for ingestion fmt.Println("Waiting for async ingestion...") time.Sleep(1 * time.Second) // Print all demo curl commands fmt.Println() fmt.Println("========================================") fmt.Println("=== DEMO CURL COMMANDS ===") fmt.Println("========================================") fmt.Println() printDemo1Commands(demo1Hashes) printDemo2Commands(sourceHash) printDemo3Commands(agentID) printDemo4Commands() printDemo5Commands() printConflictDemoCommands() printHistoricalDemoCommands() printCascadeDemoCommands() fmt.Println("========================================") fmt.Println("=== AGENT ID REFERENCE ===") fmt.Println("========================================") fmt.Println() fmt.Println("When viewing data in the dashboard, use this mapping:") fmt.Println() for name, agent := range registry.agents { fmt.Printf(" %s...\n -> %s (T%d)\n", agent.PublicKey[:24], name, agent.Tier) } fmt.Println() } // findKeysFile locates the agents.json file. func findKeysFile() string { // Try current directory if _, err := os.Stat(defaultKeysFile); err == nil { return defaultKeysFile } // Try one level up (if running from cmd/demo-seed) upPath := filepath.Join("..", "..", defaultKeysFile) if _, err := os.Stat(upPath); err == nil { return upPath } // Default to standard path return defaultKeysFile } // loadAgents loads agent configurations from agents.json. func loadAgents(path string) (*AgentRegistry, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read %s: %w", path, err) } var agents map[string]DemoAgent if err := json.Unmarshal(data, &agents); err != nil { return nil, fmt.Errorf("failed to parse %s: %w", path, err) } signers := make(map[string]*steme.Signer) for name, agent := range agents { signer, err := steme.NewSignerFromHex(agent.Seed) if err != nil { return nil, fmt.Errorf("failed to create signer for %s: %w", name, err) } signers[name] = signer } return &AgentRegistry{ agents: agents, signers: signers, }, nil } // Demo1Hashes holds the assertion hashes for Demo 1. type Demo1Hashes struct { FDA string Anecdotal string } // populateDemo1ConflictingClaims creates conflicting assertions about gastroparesis risk. func populateDemo1ConflictingClaims(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) Demo1Hashes { fmt.Println("=== Demo 1: Populating Conflicting Claims ===") var hashes Demo1Hashes // FDA/Regulatory (Tier 0): Low risk from clinical trials fdaSigner := registry.signers["fda:drug-label-ingestor"] fdaClient := steme.NewClient(baseURL, fdaSigner) fdaAssertion := steme.NewAssertion("semaglutide:gastroparesis_risk", "risk_level"). WithText("Low incidence in clinical trials (0.2%)"). WithConfidence(0.95). WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassRegulatory). WithSourceHash("0da1111111111111111111111111111111111111111111111111111111110da1"). Build() hash, err := fdaClient.Assert(ctx, fdaAssertion) if err != nil { log.Printf("Warning: Failed to create FDA assertion: %v", err) } else { hashes.FDA = hash fmt.Printf(" fda:drug-label-ingestor (T0): %s\n", hash[:16]+"...") } // Anecdotal (Tier 5): High patient-reported incidence redditSigner := registry.signers["reddit:health-discussion-scraper"] redditClient := steme.NewClient(baseURL, redditSigner) anecdotalAssertion := steme.NewAssertion("semaglutide:gastroparesis_risk", "risk_level"). WithText("High patient-reported incidence (see r/Ozempic)"). WithConfidence(0.70). WithLifecycle(steme.LifecycleProposed). WithSourceClass(steme.SourceClassAnecdotal). WithSourceHash("5555555555555555555555555555555555555555555555555555555555555555"). Build() hash, err = redditClient.Assert(ctx, anecdotalAssertion) if err != nil { log.Printf("Warning: Failed to create anecdotal assertion: %v", err) } else { hashes.Anecdotal = hash fmt.Printf(" reddit:health-discussion-scraper (T5): %s\n", hash[:16]+"...") } fmt.Println() return hashes } // populateDemo2SourceRetraction creates assertions for cascade invalidation demo. func populateDemo2SourceRetraction(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) string { fmt.Println("=== Demo 2: Populating Source for Retraction Demo ===") // Use PubMed agent for clinical trial data pubmedSigner := registry.signers["pubmed:abstract-indexer"] client := steme.NewClient(baseURL, pubmedSigner) // Use the actual hash from the registered CARDIOVASC_MEGA_TRIAL source // This ensures "Preview Impact" on the source page will find these assertions sourceHash := Sources["CARDIOVASC_MEGA_TRIAL"].Hash() fmt.Printf(" Source (NEJM Study): %s\n", sourceHash[:16]+"...") // Create assertions citing this source assertion1 := steme.NewAssertion("GLP1_Agonists", "cardiovascular_benefit"). WithBoolean(true). WithConfidence(0.92). WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassClinical). WithSourceHash(sourceHash). Build() hash, err := client.Assert(ctx, assertion1) if err != nil { log.Printf("Warning: Failed to create CV benefit assertion: %v", err) } else { fmt.Printf(" pubmed:abstract-indexer (T1): %s (CV benefit)\n", hash[:16]+"...") } assertion2 := steme.NewAssertion("GLP1_Agonists", "mortality_reduction"). WithText("20% reduction in cardiovascular mortality"). WithConfidence(0.88). WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassClinical). WithSourceHash(sourceHash). Build() hash, err = client.Assert(ctx, assertion2) if err != nil { log.Printf("Warning: Failed to create mortality assertion: %v", err) } else { fmt.Printf(" pubmed:abstract-indexer (T1): %s (mortality reduction)\n", hash[:16]+"...") } fmt.Println() return sourceHash } // populateDemo3AuditTrail creates assertions and queries to generate audit trail data. func populateDemo3AuditTrail(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) string { fmt.Println("=== Demo 3: Populating Audit Trail Data ===") // Use FDA agent for regulatory assertions fdaSigner := registry.signers["fda:drug-label-ingestor"] fdaClient := steme.NewClient(baseURL, fdaSigner) agentID := fdaSigner.PublicKey() // Create assertion about approved indication indication := steme.NewAssertion("semaglutide", "approved_indication"). WithText("Type 2 diabetes mellitus"). WithConfidence(1.0). WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassRegulatory). WithSourceHash("1001111111111111111111111111111111111111111111111111111111111001"). Build() hash, err := fdaClient.Assert(ctx, indication) if err != nil { log.Printf("Warning: Failed to create indication assertion: %v", err) } else { fmt.Printf(" fda:drug-label-ingestor (T0): %s (indication)\n", hash[:16]+"...") } // Make queries to populate audit logs _, err = fdaClient.Query(ctx, steme.QueryParams{ Subject: ptr("semaglutide"), Predicate: ptr("approved_indication"), Lens: lensPtr(steme.LensAuthority), }) if err != nil { log.Printf("Warning: Query failed: %v", err) } else { fmt.Printf(" Query logged for fda:drug-label-ingestor: %s...\n", agentID[:16]) } // Make a skeptic query from the internal reviewer internalSigner := registry.signers["internal:clinical-ops-reviewer"] internalClient := steme.NewClient(baseURL, internalSigner) _, err = internalClient.Skeptic(ctx, steme.SkepticQueryParams{ Subject: "semaglutide:gastroparesis_risk", Predicate: "risk_level", }) if err != nil { log.Printf("Warning: Skeptic query failed: %v", err) } else { internalID := internalSigner.PublicKey() fmt.Printf(" Skeptic query logged for internal:clinical-ops-reviewer: %s...\n", internalID[:16]) } fmt.Println() return agentID } // populateDemo4TimeDecay creates assertions with different timestamps. func populateDemo4TimeDecay(ctx context.Context, registry *AgentRegistry, baseURL string, _ bool) { fmt.Println("=== Demo 4: Populating Time-Based Assertions ===") // Use FDA agent for market status fdaSigner := registry.signers["fda:drug-label-ingestor"] client := steme.NewClient(baseURL, fdaSigner) // Current market status current := steme.NewAssertion("semaglutide", "market_status"). WithText("FDA approved for weight management (2024)"). WithConfidence(1.0). WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassRegulatory). WithSourceHash("11ae111111111111111111111111111111111111111111111111111111ae1111"). Build() hash, err := client.Assert(ctx, current) if err != nil { log.Printf("Warning: Failed to create current market status: %v", err) } else { fmt.Printf(" fda:drug-label-ingestor (T0): %s (current status)\n", hash[:16]+"...") } // Historical status (deprecated) historical := steme.NewAssertion("semaglutide", "market_status"). WithText("FDA approved for diabetes only (2017)"). WithConfidence(0.85). WithLifecycle(steme.LifecycleDeprecated). WithSourceClass(steme.SourceClassRegulatory). WithSourceHash("11ae222222222222222222222222222222222222222222222222222222ae2222"). Build() hash, err = client.Assert(ctx, historical) if err != nil { log.Printf("Warning: Failed to create historical market status: %v", err) } else { fmt.Printf(" fda:drug-label-ingestor (T0): %s (historical status)\n", hash[:16]+"...") } fmt.Println() } // populateDemo5TrustSafety creates data that triggers quarantine and circuit breaker demos. func populateDemo5TrustSafety(ctx context.Context, _ *AgentRegistry, baseURL string, _ bool) { fmt.Println("=== Demo 5: Populating Trust & Safety Triggers ===") // Create multiple untrusted agents for different scenarios fmt.Println(" Creating suspicious assertions for quarantine demo...") // Scenario 1: Untrusted high-confidence agent untrustedSigner, err := steme.GenerateSigner() if err != nil { log.Printf("Warning: Failed to generate untrusted signer: %v", err) return } untrustedClient := steme.NewClient(baseURL, untrustedSigner) // This assertion from a new agent with high confidence should trigger quarantine suspicious := steme.NewAssertion("miracle_drug", "cures_everything"). WithBoolean(true). WithConfidence(0.99). // Very high confidence from untrusted agent WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassAnecdotal). WithSourceHash("5a00111111111111111111111111111111111111111111111111111111005a11"). Build() hash, err := untrustedClient.Assert(ctx, suspicious) if err != nil { fmt.Printf(" [Quarantine] High-confidence untrusted: %v\n", err) } else { fmt.Printf(" [Quarantine] High-confidence untrusted: %s\n", hash[:16]+"...") } // Scenario 2: Low-quality content (short, low entropy) lowQuality := steme.NewAssertion("test", "x"). WithText("a"). // Very short, low quality WithConfidence(0.5). WithLifecycle(steme.LifecycleProposed). WithSourceClass(steme.SourceClassAnecdotal). WithSourceHash("5a00222222222222222222222222222222222222222222222222222222005a22"). Build() hash, err = untrustedClient.Assert(ctx, lowQuality) if err != nil { fmt.Printf(" [Quarantine] Low-quality content: %v\n", err) } else { fmt.Printf(" [Quarantine] Low-quality content: %s\n", hash[:16]+"...") } // Scenario 3: Pattern match (spam-like keywords) spammy := steme.NewAssertion("free_bitcoin", "get_rich_quick"). WithText("Click here for free money! Limited time offer!"). WithConfidence(0.95). WithLifecycle(steme.LifecycleApproved). WithSourceClass(steme.SourceClassAnecdotal). WithSourceHash("5a00333333333333333333333333333333333333333333333333333333005a33"). Build() hash, err = untrustedClient.Assert(ctx, spammy) if err != nil { fmt.Printf(" [Quarantine] Spam pattern: %v\n", err) } else { fmt.Printf(" [Quarantine] Spam pattern: %s\n", hash[:16]+"...") } fmt.Printf(" Untrusted agent ID: %s...\n", untrustedSigner.PublicKey()[:16]) // Trigger circuit breaker by sending malformed requests fmt.Println("\n Triggering circuit breaker with invalid requests...") triggerCircuitBreaker(baseURL) fmt.Println() } // triggerCircuitBreaker sends malformed requests to trip the circuit breaker. func triggerCircuitBreaker(baseURL string) { // Send multiple invalid assertion requests to trigger failures for i := 0; i < 5; i++ { req, err := http.NewRequest("POST", baseURL+"/v1/assert", bytes.NewReader([]byte(`{"invalid": "payload", "missing_required_fields": true}`))) if err != nil { continue } req.Header.Set("Content-Type", "application/json") // Add a fake agent ID header (invalid signature will cause failure) req.Header.Set("X-Agent-Id", fmt.Sprintf("bad_agent_%d", i)) resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Printf(" [Circuit] Request %d failed: %v\n", i+1, err) continue } resp.Body.Close() fmt.Printf(" [Circuit] Request %d: HTTP %d\n", i+1, resp.StatusCode) } } // ptr returns a pointer to a string. func ptr(s string) *string { return &s } // lensPtr returns a pointer to a Lens. func lensPtr(l steme.Lens) *steme.Lens { return &l } // printDemo1Commands prints curl commands for Demo 1. func printDemo1Commands(_ Demo1Hashes) { fmt.Println("--- Demo 1: Contradictions Visible ---") fmt.Println() fmt.Println("# Skeptic: See all competing claims") fmt.Println(`curl -s "http://localhost:18180/v1/skeptic?subject=semaglutide:gastroparesis_risk&predicate=risk_level" | jq`) fmt.Println() fmt.Println("# Layered: See per-tier consensus") fmt.Println(`curl -s "http://localhost:18180/v1/layered?subject=semaglutide:gastroparesis_risk&predicate=risk_level" | jq`) fmt.Println() fmt.Println("# Expected: conflict_score > 0.5") fmt.Println("# fda:drug-label-ingestor (T0) wins overall, but reddit:health-discussion-scraper (T5) disagrees") fmt.Println() } // printDemo2Commands prints curl commands for Demo 2. func printDemo2Commands(sourceHash string) { fmt.Println("--- Demo 2: Cascade Invalidation ---") fmt.Println() fmt.Println("# Query assertions citing the NEJM source") fmt.Println(`curl -s "http://localhost:18180/v1/query?subject=GLP1_Agonists" | jq`) fmt.Println() fmt.Println("# Expected: Two assertions from pubmed:abstract-indexer (cardiovascular_benefit, mortality_reduction)") fmt.Printf("# Both cite source_hash: %s\n", sourceHash) fmt.Println() } // printDemo3Commands prints curl commands for Demo 3. func printDemo3Commands(agentID string) { now := time.Now().Unix() from := now - 3600 // 1 hour ago fmt.Println("--- Demo 3: Full Audit Trail ---") fmt.Println() fmt.Println("# List recent query audits") fmt.Println(`curl -s "http://localhost:18180/v1/audit/queries?limit=10" | jq`) fmt.Println() fmt.Println("# Trace fda:drug-label-ingestor decisions") fmt.Printf("curl -s \"http://localhost:18180/v1/trace?agent_id=%s&from=%d&limit=50\" | jq\n", agentID, from) fmt.Println() fmt.Println("# Filter trace by subject pattern") fmt.Printf("curl -s \"http://localhost:18180/v1/trace?agent_id=%s&from=%d&subject=semaglutide*\" | jq\n", agentID, from) fmt.Println() } // printDemo4Commands prints curl commands for Demo 4. func printDemo4Commands() { now := time.Now().Unix() sixMonthsAgo := now - (180 * 24 * 60 * 60) fmt.Println("--- Demo 4: Time Decay ---") fmt.Println() fmt.Println("# Current state with Recency lens") fmt.Println(`curl -s "http://localhost:18180/v1/query?subject=semaglutide&predicate=market_status&lens=Recency" | jq`) fmt.Println() fmt.Println("# Historical state (point-in-time query)") fmt.Printf("curl -s \"http://localhost:18180/v1/query?subject=semaglutide&predicate=market_status&lens=Recency&as_of=%d\" | jq\n", sixMonthsAgo) fmt.Println() } // printDemo5Commands prints curl commands for Demo 5. func printDemo5Commands() { fmt.Println("--- Demo 5: Trust & Safety ---") fmt.Println() fmt.Println("# Check quarantine queue") fmt.Println(`curl -s "http://localhost:18180/v1/admin/quarantine?limit=10" | jq`) fmt.Println() fmt.Println("# Check tripped circuit breakers") fmt.Println(`curl -s "http://localhost:18180/v1/admin/circuit-breakers/tripped" | jq`) fmt.Println() fmt.Println("# To approve a quarantined assertion:") fmt.Println("# curl -X POST \"http://localhost:18180/v1/admin/quarantine/{hash}/approve\"") fmt.Println() } // sourceClassTier maps SourceClass to tier number. var sourceClassTier = map[steme.SourceClass]int{ steme.SourceClassRegulatory: 0, steme.SourceClassClinical: 1, steme.SourceClassObservational: 2, steme.SourceClassExpert: 3, steme.SourceClassCommunity: 4, steme.SourceClassAnecdotal: 5, } // registerSource registers a source in the source registry. // Uses raw HTTP since steme.Client.doJSON is private. func registerSource(ctx context.Context, baseURL, name string, src Source) error { tier := sourceClassTier[src.Class] payload := map[string]interface{}{ "hash": src.Hash(), "label": src.Description, "tier": tier, "tier_label": string(src.Class), "url": src.URL, "notes": fmt.Sprintf("Demo source: %s (%s)", name, src.ID), } jsonBytes, err := json.Marshal(payload) if err != nil { return fmt.Errorf("failed to marshal: %w", err) } req, err := http.NewRequestWithContext(ctx, "POST", baseURL+"/v1/sources", bytes.NewReader(jsonBytes)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() // 201 Created = success, 409 Conflict = already exists (OK) if resp.StatusCode == 201 || resp.StatusCode == 409 { return nil } body, _ := io.ReadAll(resp.Body) return fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)) }