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

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

  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:

{
  "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_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):

{
  "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):

{
  "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 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.