stemedb/cmd/demo-seed/main.go
jordan 157dbbb9eb feat: Complete Aphoria Phase 8-9 + UAT suite (90/90 tests passing)
## Phase 8: Enterprise Extractor Improvements 
- 14 security extractors (TLS, JWT, SQL injection, XSS, etc.)
- 10 framework-specific extractors (Spring, Django, Rails, etc.)
- Config file security detection (YAML, TOML)

## Phase 9: Autonomous Extractor Generation 
- Shadow mode executor with TP/FP tracking
- Graduation pipeline with confidence thresholds
- Auto-rollback on regression detection
- Cross-project pattern syncing

## UAT Suite Complete (14 scripts, 90 tests)
- test-core-detection.sh (6 tests)
- test-declarative-extractors.sh (5 tests)
- test-domain-frameworks.sh (5 tests)
- test-domain-unreal.sh (3 tests)
- test-llm-extraction.sh (6 tests)
- test-eval-harness.sh (5 tests)
- test-cross-language.sh (3 tests)
- test-precommit-performance.sh (4 tests)
- test-output-formats.sh (8 tests)
- test-drift-detection.sh (6 tests)
- test-exit-codes.sh (12 tests)
+ 3 more scripts

## Other Changes
- Updated roadmap to mark Phase 8-9 complete
- Added .gitignore entries for build artifacts
- Updated pre-commit: 800 line limit, exclude tests/data/cmd

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 22:50:55 -07:00

642 lines
21 KiB
Go

// 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))
}