Commit Graph

4 Commits

Author SHA1 Message Date
anthonyrawlins
2fd9a96950 Add Sequential Thinking compatibility server and JWKS support 2025-10-13 17:04:00 +11:00
anthonyrawlins
9190c75440 Implement Beat 3: Policy Gate (JWT Authentication)
This commit completes Beat 3 of the SequentialThinkingForCHORUS implementation,
adding KACHING JWT policy enforcement with scope checking.

## Deliverables

### 1. JWT Validation Package (pkg/seqthink/policy/)

**jwt.go** (313 lines): Complete JWT validation system
- `Validator`: JWT token validation with JWKS fetching
- `Claims`: JWT claims structure with scope support
- JWKS fetching and caching (1-hour TTL)
- RSA public key parsing from JWK format
- Space-separated and array scope formats
- Automatic JWKS refresh on cache expiration

**Features**:
- RS256 signature verification
- Expiration and NotBefore validation
- Required scope checking
- JWKS caching to reduce API calls
- Thread-safe key cache with mutex
- Base64 URL encoding/decoding utilities

**jwt_test.go** (296 lines): Comprehensive test suite
- Valid token validation
- Expired token rejection
- Missing scope detection
- Space-separated scopes parsing
- Not-yet-valid token rejection
- JWKS caching behavior verification
- Invalid JWKS server handling
- 5 test scenarios, all passing

### 2. Authorization Middleware

**middleware.go** (75 lines): HTTP authorization middleware
- Bearer token extraction from Authorization header
- Token validation via Validator
- Policy denial metrics tracking
- Optional enforcement (disabled if no JWKS URL)
- Request logging with subject and scopes
- Clean error responses (401 Unauthorized)

**Integration**:
- Wraps `/mcp/tool` endpoint (both encrypted and plaintext)
- Wraps `/mcp/sse` endpoint (both encrypted and plaintext)
- Health and metrics endpoints remain open (no auth)
- Automatic mode detection based on configuration

### 3. Proxy Server Integration

**Updated server.go**:
- Policy middleware initialization in `NewServer()`
- Pre-fetches JWKS on startup
- Auth wrapper for protected endpoints
- Configuration-based enforcement
- Graceful fallback if JWKS unavailable

**Configuration**:
```go
ServerConfig{
    KachingJWKSURL: "https://auth.kaching.services/jwks",
    RequiredScope:  "sequentialthinking.run",
}
```

If both fields are set → policy enforcement enabled
If either is empty → policy enforcement disabled (dev mode)

## Testing Results

### Unit Tests
```
PASS: TestValidateToken (5 scenarios)
  - valid_token with required scope
  - expired_token rejection
  - missing_scope rejection
  - space_separated_scopes parsing
  - not_yet_valid rejection

PASS: TestJWKSCaching
  - Verifies JWKS fetched only once within cache window
  - Verifies JWKS re-fetched after cache expiration

PASS: TestParseScopes (5 scenarios)
  - Single scope parsing
  - Multiple scopes parsing
  - Extra spaces handling
  - Empty string handling
  - Spaces-only handling

PASS: TestInvalidJWKS
  - Handles JWKS server errors gracefully

PASS: TestGetCachedKeyCount
  - Tracks cached key count correctly
```

**All 5 test groups passed (16 total test cases)**

### Integration Verification

**Without Policy** (development):
```bash
export KACHING_JWKS_URL=""
./build/seqthink-wrapper
# → "Policy enforcement disabled"
# → All requests allowed
```

**With Policy** (production):
```bash
export KACHING_JWKS_URL="https://auth.kaching.services/jwks"
export REQUIRED_SCOPE="sequentialthinking.run"
./build/seqthink-wrapper
# → "Policy enforcement enabled"
# → JWKS pre-fetched
# → Authorization: Bearer <token> required
```

## Security Properties

 **Authentication**: RS256 JWT signature verification
 **Authorization**: Scope-based access control
 **Token Validation**: Expiration and not-before checking
 **JWKS Security**: Automatic key rotation support
 **Metrics**: Policy denial tracking for monitoring
 **Graceful Degradation**: Works without JWKS in dev mode
 **Thread Safety**: Concurrent JWKS cache access safe

## API Flow with Policy

### Successful Request:
```
1. Client → POST /mcp/tool
   Authorization: Bearer eyJhbGci...
   Content-Type: application/age
   Body: <encrypted request>

2. Middleware extracts Bearer token
3. Middleware validates JWT signature (JWKS)
4. Middleware checks required scope
5. Request forwarded to handler
6. Handler decrypts request
7. Handler calls MCP server
8. Handler encrypts response
9. Response sent to client
```

### Unauthorized Request:
```
1. Client → POST /mcp/tool
   (missing Authorization header)

2. Middleware checks for header → NOT FOUND
3. Policy denial metric incremented
4. 401 Unauthorized response
5. Request rejected
```

## Configuration Modes

**Full Security** (Beat 2 + Beat 3):
```bash
export AGE_IDENT_PATH=/etc/seqthink/age.key
export AGE_RECIPS_PATH=/etc/seqthink/age.pub
export KACHING_JWKS_URL=https://auth.kaching.services/jwks
export REQUIRED_SCOPE=sequentialthinking.run
```
→ Encryption + Authentication + Authorization

**Development Mode**:
```bash
# No AGE_* or KACHING_* variables set
```
→ Plaintext, no authentication

## Next Steps (Beat 4)

Beat 4 will add deployment infrastructure:
- Docker Swarm service definition
- Network overlay configuration
- Secret management for age keys
- KACHING integration documentation
- End-to-end testing in swarm

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 08:48:42 +11:00
anthonyrawlins
a658a7364d Implement Beat 2: Age Encryption Envelope
This commit completes Beat 2 of the SequentialThinkingForCHORUS implementation,
adding end-to-end age encryption for all MCP communications.

## Deliverables

### 1. Age Encryption/Decryption Package (pkg/seqthink/ageio/)
- `crypto.go`: Core encryption/decryption with age
- `testkeys.go`: Test key generation and convenience functions
- `crypto_test.go`: Comprehensive unit tests (11 tests, all passing)
- `golden_test.go`: Golden tests with real MCP payloads (12 tests, all passing)

**Features:**
- File-based identity and recipient key loading
- Streaming encryption/decryption support
- Proper error handling for all failure modes
- Performance benchmarks showing 400+ MB/s throughput

**Test Coverage:**
- Round-trip encryption/decryption for various payload sizes
- Unicode and emoji support
- Large payload handling (100KB+)
- Invalid ciphertext rejection
- Wrong key detection
- Truncated/modified ciphertext detection

### 2. Encrypted Proxy Handlers (pkg/seqthink/proxy/)
- `server_encrypted.go`: Encrypted tool call handler
- Updated `server.go`: Automatic routing based on encryption config
- Content-Type enforcement: `application/age` required when encryption enabled
- Metrics tracking for encryption/decryption failures

**Flow:**
1. Client sends encrypted request with `Content-Type: application/age`
2. Wrapper decrypts using age identity
3. Wrapper calls MCP server (plaintext on loopback)
4. Wrapper encrypts response
5. Client receives encrypted response with `Content-Type: application/age`

### 3. SSE Streaming with Encryption (pkg/seqthink/proxy/sse.go)
- `handleSSEEncrypted()`: Encrypted Server-Sent Events streaming
- `handleSSEPlaintext()`: Plaintext SSE for testing
- Base64-encoded encrypted frames for SSE transport
- `DecryptSSEFrame()`: Client-side frame decryption helper
- `ReadSSEStream()`: SSE stream parsing utility

**SSE Frame Format (Encrypted):**
```
event: thought
data: <base64-encoded age-encrypted JSON>
id: 1
```

### 4. Configuration-Based Mode Switching
The wrapper now operates in two modes based on environment variables:

**Encrypted Mode** (AGE_IDENT_PATH and AGE_RECIPS_PATH set):
- All requests/responses encrypted with age
- Content-Type: application/age enforced
- SSE frames base64-encoded and encrypted

**Plaintext Mode** (no encryption paths set):
- Direct plaintext proxying for development/testing
- Standard JSON Content-Type
- Plaintext SSE frames

## Testing Results

### Unit Tests
```
PASS: TestEncryptDecryptRoundTrip (all variants)
PASS: TestEncryptEmptyData
PASS: TestDecryptEmptyData
PASS: TestDecryptInvalidCiphertext
PASS: TestDecryptWrongKey
PASS: TestStreamingEncryptDecrypt
PASS: TestConvenienceFunctions
```

### Golden Tests
```
PASS: TestGoldenEncryptionRoundTrip (7 scenarios)
  - sequential_thinking_request (283→483 bytes, 70.7% overhead)
  - sequential_thinking_revision (303→503 bytes, 66.0% overhead)
  - sequential_thinking_branching (315→515 bytes, 63.5% overhead)
  - sequential_thinking_final (320→520 bytes, 62.5% overhead)
  - large_context_payload (3800→4000 bytes, 5.3% overhead)
  - unicode_payload (264→464 bytes, 75.8% overhead)
  - special_characters (140→340 bytes, 142.9% overhead)

PASS: TestGoldenDecryptionFailures (5 scenarios)
```

### Performance Benchmarks
```
Encryption:
  - 1KB:   5.44 MB/s
  - 10KB:  52.57 MB/s
  - 100KB: 398.66 MB/s

Decryption:
  - 1KB:   9.22 MB/s
  - 10KB:  85.41 MB/s
  - 100KB: 504.46 MB/s
```

## Security Properties

 **Confidentiality**: All payloads encrypted with age (X25519+ChaCha20-Poly1305)
 **Authenticity**: age provides AEAD with Poly1305 MAC
 **Forward Secrecy**: Each encryption uses fresh ephemeral keys
 **Key Management**: File-based identity/recipient keys
 **Tampering Detection**: Modified ciphertext rejected
 **No Plaintext Leakage**: MCP server only on 127.0.0.1 loopback

## Next Steps (Beat 3)

Beat 3 will add KACHING JWT policy enforcement:
- JWT token validation (`pkg/seqthink/policy/`)
- Scope checking for `sequentialthinking.run`
- JWKS fetching and caching
- Policy denial metrics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 08:42:28 +11:00
anthonyrawlins
3ce9811826 Implement Beat 1: Sequential Thinking Age-Encrypted Wrapper (Skeleton)
This commit completes Beat 1 of the SequentialThinkingForCHORUS implementation,
providing a functional plaintext skeleton for the age-encrypted wrapper.

## Deliverables

### 1. Main Wrapper Entry Point
- `cmd/seqthink-wrapper/main.go`: HTTP server on :8443
- Configuration loading from environment variables
- Graceful shutdown handling
- MCP server readiness checking with timeout

### 2. MCP Client Package
- `pkg/seqthink/mcpclient/client.go`: HTTP client for MCP server
- Communicates with MCP server on localhost:8000
- Health check endpoint
- Tool call endpoint with 120s timeout

### 3. Proxy Server Package
- `pkg/seqthink/proxy/server.go`: HTTP handlers for wrapper
- Health and readiness endpoints
- Tool call proxy (plaintext for Beat 1)
- SSE endpoint placeholder
- Metrics endpoint integration

### 4. Observability Package
- `pkg/seqthink/observability/logger.go`: Structured logging with zerolog
- `pkg/seqthink/observability/metrics.go`: Prometheus metrics
- Counters for requests, errors, decrypt/encrypt failures, policy denials
- Request duration histogram

### 5. Docker Infrastructure
- `deploy/seqthink/Dockerfile`: Multi-stage build
- `deploy/seqthink/entrypoint.sh`: Startup orchestration
- `deploy/seqthink/mcp_stub.py`: Minimal MCP server for testing

### 6. Build System Integration
- Updated `Makefile` with `build-seqthink` target
- Uses GOWORK=off and -mod=mod for clean builds
- `docker-seqthink` target for container builds

## Testing

Successfully builds with:
```
make build-seqthink
```

Binary successfully starts and waits for MCP server connection.

## Next Steps

Beat 2 will add:
- Age encryption/decryption (pkg/seqthink/ageio)
- Content-Type: application/age enforcement
- SSE streaming with encrypted frames
- Golden tests for crypto round-trips

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 08:35:43 +11:00