Files
SWOOSH/API_INTEGRATION.md
Codex Agent 6f90ad77a4 Release v1.0.0: Production-ready SWOOSH with durability guarantees
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>
2025-10-25 12:23:33 +11:00

453 lines
12 KiB
Markdown

# 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.