Major enhancements: - Added production-grade durability guarantees with fsync operations - Implemented BadgerDB WAL for crash recovery and persistence - Added comprehensive HTTP API (GET/POST /state, POST /command) - Exported ComputeStateHash for external use in genesis initialization - Enhanced snapshot system with atomic write-fsync-rename sequence - Added API integration documentation and durability guarantees docs New files: - api.go: HTTP server implementation with state and command endpoints - api_test.go: Comprehensive API test suite - badger_wal.go: BadgerDB-based write-ahead log - cmd/swoosh/main.go: CLI entry point with API server - API_INTEGRATION.md: API usage and integration guide - DURABILITY.md: Durability guarantees and recovery procedures - CHANGELOG.md: Version history and changes - RELEASE_NOTES.md: Release notes for v1.0.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
SWOOSH API Integration Guide
Date: 2025-10-25
CHORUS Commit: 17673c3 (v0.5.5)
Integration Method: HTTP REST API
Overview
This document describes the HTTP API layer for SWOOSH, designed to integrate with CHORUS agents at commit 17673c3 using the existing WHOOSH HTTP integration pattern.
Key Principle: The Executor is the single source of truth. The API layer is a thin adapter that submits transitions and retrieves snapshots. No state is maintained outside the executor.
Architecture
CHORUS Agents (TCP/libp2p mesh)
↓
HTTP REST API
↓
SWOOSH API Layer (api.go)
↓
Executor (executor.go)
↓
Reducer (reducer.go) ← Single source of truth
↓
WAL + Snapshot
Strict Constraints
- No state outside Executor: API handlers only call
SubmitTransition()orGetStateSnapshot() - No background goroutines: All concurrency managed by Executor's single-threaded loop
- No invented transitions: Only catalogued transitions in
reducer.goare valid - Deterministic mapping required: External requests must map to exactly one transition
- 501 for unmappable requests: If input cannot be deterministically mapped, return HTTP 501
API Endpoints
Core SWOOSH Endpoints
POST /transition
Submit a state transition proposal.
Request:
{
"current_state_hash": "abc123...",
"transition": "LICENSE_GRANTED",
"inputs_hash": "def456...",
"signer": "node-001",
"idem_key": "unique-operation-id",
"hlc": "1-0-0000000000000001",
"window_id": "window-1",
"evidence": ["ucxl://proof/123"]
}
Response (200 OK):
{
"success": true,
"state_hash": "new-hash-xyz...",
"quarantined": false
}
Response (400 Bad Request):
{
"success": false,
"error": "guard rejected transition",
"state_hash": "unchanged-hash",
"quarantined": false
}
Implementation:
- Calls
executor.SubmitTransition(proposal) - Blocks on result channel
- Returns transition outcome
GET /state
Retrieve current orchestrator state snapshot.
Query Parameters:
projection(reserved for future use)
Response (200 OK):
{
"state_hash": "abc123...",
"hlc_last": "5-0-0000000000000005",
"projection": {
"Meta": {...},
"Boot": {...},
"Ingestion": {...},
"Council": {...},
"Environment": {...},
"Execution": {...},
"Control": {...},
"Policy": {...}
}
}
Implementation:
- Calls
executor.GetStateSnapshot()(deep copy) - Returns full state structure
GET /health
Health check endpoint for monitoring.
Response (200 OK):
{
"licensed": true,
"quarantined": false,
"degraded": false,
"recovering": false,
"hlc_last": "10-0-0000000000000010",
"state_hash": "current-hash"
}
Implementation:
- Calls
executor.GetStateSnapshot() - Extracts health-relevant fields
WHOOSH-Compatible Adapter Endpoints
POST /api/v1/opportunities/council
Status: ⚠️ 501 Not Implemented
Reason: Cannot deterministically map WHOOSH council lifecycle to SWOOSH transitions without defining specific COUNCIL_* transitions in reducer.go.
Future Implementation:
Once reducer.go defines transitions like:
COUNCIL_PROFILES_LOADEDCOUNCIL_QUORUM_CERTCOUNCIL_ELECT_COMPLETE
This handler will:
- Parse WHOOSH council opportunity payload
- Map to appropriate SWOOSH transition
- Build
TransitionProposal - Submit via
executor.SubmitTransition()
Current Response (501 Not Implemented):
{
"error": "council opportunity mapping not yet implemented",
"reason": "cannot deterministically map WHOOSH council lifecycle to SWOOSH transitions",
"contact": "define COUNCIL_* transitions in reducer.go first"
}
GET /api/v1/tasks
Status: ⚠️ 501 Not Implemented
Reason: OrchestratorState does not contain a task queue. SWOOSH uses deterministic state-machine phases, not task lists.
Architecture Note: SWOOSH's state machine tracks execution phases (PLAN, WORK, REVIEW, REVERB) but not individual tasks. Tasks are managed externally (e.g., GITEA issues) and referenced via:
Execution.ActiveWindowIDExecution.BeatIndex- Evidence arrays in transition proposals
Current Response (501 Not Implemented):
{
"error": "task listing not yet implemented",
"reason": "OrchestratorState does not contain task queue",
"note": "SWOOSH uses deterministic state-machine, not task queues"
}
Integration with CHORUS
Docker Compose Configuration
In /home/tony/chorus/project-queues/active/CHORUS/docker/docker-compose.yml:
services:
chorus:
image: anthonyrawlins/chorus:0.5.48
environment:
- WHOOSH_API_BASE_URL=${SWOOSH_API_BASE_URL:-http://swoosh:8080}
- WHOOSH_API_ENABLED=true
# ... other CHORUS env vars
swoosh:
image: your-registry/swoosh:latest
ports:
- target: 8080
published: 8800
protocol: tcp
mode: ingress
environment:
- SWOOSH_LISTEN_ADDR=:8080
- SWOOSH_WAL_DIR=/app/data/wal
- SWOOSH_SNAPSHOT_DIR=/app/data/snapshots
- SWOOSH_DATABASE_DB_HOST=postgres
- SWOOSH_DATABASE_DB_PORT=5432
- SWOOSH_DATABASE_DB_NAME=swoosh
# ... other SWOOSH config
volumes:
- swoosh_data:/app/data
networks:
- tengig
- chorus_ipvlan
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Transition Mapping
When CHORUS communicates with SWOOSH, external API calls must map to internal transitions:
| CHORUS Request | SWOOSH Transition | Status |
|---|---|---|
POST /api/v1/opportunities/council |
COUNCIL_PROFILES_LOADED (proposed) |
501 - Not Implemented |
POST /api/v1/councils/{id}/claims |
COUNCIL_ROLE_CLAIMED (proposed) |
501 - Not Implemented |
GET /api/v1/tasks |
N/A (state query, not transition) | 501 - Not Implemented |
POST /api/v1/tasks/{id}/claim |
EXECUTION_TASK_CLAIMED (proposed) |
501 - Not Implemented |
POST /api/v1/tasks/{id}/complete |
EXECUTION_TASK_COMPLETE (proposed) |
501 - Not Implemented |
Action Required: Define these transitions in reducer.go to enable WHOOSH-compatible endpoints.
Running SWOOSH Server
Build
cd /home/tony/chorus/SWOOSH
go build -o build/swoosh-server ./cmd/swoosh-server
Run
export SWOOSH_LISTEN_ADDR=:8080
export SWOOSH_WAL_DIR=/path/to/wal
export SWOOSH_SNAPSHOT_DIR=/path/to/snapshots
./build/swoosh-server
Test
# Health check
curl http://localhost:8080/health
# Get state
curl http://localhost:8080/state
# Submit transition
curl -X POST http://localhost:8080/transition \
-H "Content-Type: application/json" \
-d '{
"current_state_hash": "genesis",
"transition": "LICENSE_GRANTED",
"inputs_hash": "test",
"signer": "test-node",
"idem_key": "unique-1",
"hlc": "1-0-0000000000000001",
"window_id": "window-1",
"evidence": []
}'
Testing
cd /home/tony/chorus/SWOOSH
go test -v ./...
Test Coverage:
- ✅
POST /transition- Success and failure cases - ✅
GET /state- Snapshot retrieval - ✅
GET /health- Health status - ✅
POST /api/v1/opportunities/council- Returns 501 - ✅
GET /api/v1/tasks- Returns 501
Next Steps for Full CHORUS Integration
1. Define CHORUS-Compatible Transitions
In reducer.go, add:
case "COUNCIL_PROFILES_LOADED":
if state.Council.Phase != "PLAN_ROLES" {
return false, ErrInvalidPhase
}
// Parse PlannedRoles from proposal.Evidence
// Update state.Council.PlannedRoles
state.Council.Phase = "ELECT"
state.Council.Epoch++
return true, nil
case "COUNCIL_ROLE_CLAIMED":
if state.Council.Phase != "ELECT" {
return false, ErrInvalidPhase
}
// Parse CouncilMember from proposal.Evidence
// Append to state.Council.Members
// Check if quorum reached
if quorumReached(state.Council.Members) {
state.Council.Phase = "TOOLING_SYNC"
}
state.Council.Epoch++
return true, nil
// ... additional transitions
2. Update API Handlers
Once transitions are defined, update api.go:
func handleCouncilOpportunity(executor *Executor) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Parse request
var req CouncilOpportunityRequest
json.NewDecoder(r.Body).Decode(&req)
// Build transition proposal
proposal := TransitionProposal{
TransitionName: "COUNCIL_PROFILES_LOADED",
Evidence: []string{
encodeCouncilRoles(req.Roles),
},
HLC: req.HLC,
WindowID: req.WindowID,
// ... other fields
}
// Submit to executor
resultCh, _ := executor.SubmitTransition(proposal)
result := <-resultCh
// Return response
writeJSON(w, http.StatusOK, result)
}
}
3. Implement GuardProvider
Create production guard implementations:
type ProductionGuardProvider struct {
kachingClient *KachingClient
backbeatClient *BackbeatClient
hmmmClient *HMMMClient
shhhClient *SHHHClient
mcpHealthClient *MCPHealthClient
}
func (p *ProductionGuardProvider) Evaluate(t TransitionProposal, s OrchestratorState) (GuardOutcome, error) {
outcome := GuardOutcome{}
// Check KACHING license
outcome.LicenseOK = p.kachingClient.ValidateLicense(s.Boot.NodeID)
// Check BACKBEAT heartbeat
outcome.BackbeatOK = p.backbeatClient.CheckHeartbeat()
// Check HMMM quorum
outcome.QuorumOK = p.hmmmClient.CheckQuorum()
// Check SHHH policy
outcome.PolicyOK = p.shhhClient.CheckPolicy(t)
// Check MCP health
outcome.MCPHealthy = p.mcpHealthClient.CheckHealth()
return outcome, nil
}
4. Add Persistent WAL/Snapshot Stores
Replace in-memory implementations with BadgerDB WAL and atomic file snapshots (already defined in wal.go and snapshot.go).
Design Compliance Checklist
- ✅ Single source of truth: Only
Executormutates state - ✅ No external state: API handlers have no local caches or maps
- ✅ Deterministic transitions only: All mutations via
reducer.go - ✅ HTTP 501 for unmappable requests: WHOOSH endpoints return 501 until transitions defined
- ✅ Blocking on executor: All API calls wait for executor result channel
- ✅ Deep copies for reads:
GetStateSnapshot()returns isolated state - ✅ No background goroutines: Single-threaded executor model preserved
- ✅ Standard library only: Uses
net/httpandencoding/json - ✅ Compiles successfully:
go build ./...passes - ✅ Test coverage: All handlers tested
Summary
The HTTP API layer (api.go) is a thin, stateless adapter that:
- Accepts HTTP requests
- Maps to
TransitionProposalstructures - Submits to
executor.SubmitTransition() - Blocks on result channel
- Returns HTTP response
No orchestration logic lives in the API layer. All state transitions, validation, and persistence are handled by the Executor and Reducer.
WHOOSH-compatible endpoints return HTTP 501 Not Implemented until corresponding transitions are defined in reducer.go, ensuring we never mutate state without deterministic transitions.
For CHORUS integration at commit 17673c3, simply point WHOOSH_API_BASE_URL to SWOOSH's HTTP endpoint and define the required transitions.