# 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 1. **No state outside Executor**: API handlers only call `SubmitTransition()` or `GetStateSnapshot()` 2. **No background goroutines**: All concurrency managed by Executor's single-threaded loop 3. **No invented transitions**: Only catalogued transitions in `reducer.go` are valid 4. **Deterministic mapping required**: External requests must map to exactly one transition 5. **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:** ```json { "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):** ```json { "success": true, "state_hash": "new-hash-xyz...", "quarantined": false } ``` **Response (400 Bad Request):** ```json { "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):** ```json { "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):** ```json { "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_LOADED` - `COUNCIL_QUORUM_CERT` - `COUNCIL_ELECT_COMPLETE` This handler will: 1. Parse WHOOSH council opportunity payload 2. Map to appropriate SWOOSH transition 3. Build `TransitionProposal` 4. Submit via `executor.SubmitTransition()` **Current Response (501 Not Implemented):** ```json { "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.ActiveWindowID` - `Execution.BeatIndex` - Evidence arrays in transition proposals **Current Response (501 Not Implemented):** ```json { "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`:** ```yaml 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 ```bash cd /home/tony/chorus/SWOOSH go build -o build/swoosh-server ./cmd/swoosh-server ``` ### Run ```bash export SWOOSH_LISTEN_ADDR=:8080 export SWOOSH_WAL_DIR=/path/to/wal export SWOOSH_SNAPSHOT_DIR=/path/to/snapshots ./build/swoosh-server ``` ### Test ```bash # 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 ```bash 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: ```go 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`: ```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: ```go 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 `Executor` mutates 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/http` and `encoding/json` - ✅ **Compiles successfully**: `go build ./...` passes - ✅ **Test coverage**: All handlers tested --- ## Summary The HTTP API layer (`api.go`) is a **thin, stateless adapter** that: 1. Accepts HTTP requests 2. Maps to `TransitionProposal` structures 3. Submits to `executor.SubmitTransition()` 4. Blocks on result channel 5. 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.