# Integration Considerations

## Integrating Iron Vault with Aperion Legendary AI

This document outlines considerations for integrating the standalone FSAL
service back into the Aperion ecosystem.

---

## 1. API Compatibility

### Current Endpoints (Maintained)
The Iron Vault maintains 1:1 API compatibility with the original FSAL:

| Endpoint | Method | Payload | Notes |
|----------|--------|---------|-------|
| `/v1/fs/list` | POST | `ListIn` | Returns `list[Entry]` |
| `/v1/fs/read` | POST | `ReadIn` | Returns base64 or text |
| `/v1/fs/write` | POST | `WriteIn` | Accepts base64 |
| `/v1/fs/move` | POST | `MoveCopyIn` | Atomic move |
| `/v1/fs/copy` | POST | `MoveCopyIn` | Recursive copy |
| `/v1/fs/mkdir` | POST | `MkdirIn` | With parents |
| `/v1/fs/delete` | POST | `DeleteIn` | Optional recursive |
| `/v1/fs/hash` | POST | `HashIn` | SHA256 default |
| `/healthz` | GET | None | Health check |

### Breaking Changes: None
All request/response schemas are identical to the original implementation.

---

## 2. Deployment Topology Options

### Option A: Sidecar Pattern (Recommended)
```
┌─────────────────────────────────────────────┐
│  Aperion Agent Container                    │
│  ┌───────────────────┐ ┌──────────────────┐ │
│  │ Agent Process     │ │ Iron Vault (FSAL)│ │
│  │ (Python/Node)     │ │ localhost:4848   │ │
│  │                   ├─►│                  │ │
│  └───────────────────┘ └──────────────────┘ │
│                              │              │
│                        ┌─────▼─────┐        │
│                        │ /data     │        │
│                        │ (volume)  │        │
│                        └───────────┘        │
└─────────────────────────────────────────────┘
```

**Pros:**
- Simple networking (localhost)
- Shared volume access
- Single deployment unit

**Cons:**
- Coupled lifecycle
- Resource contention

### Option B: Separate Service
```
┌──────────────────┐     ┌──────────────────┐
│ Aperion Agents   │────►│ Iron Vault       │
│ (multiple)       │     │ (single instance)│
└──────────────────┘     └────────┬─────────┘
                                  │
                           ┌──────▼──────┐
                           │ Shared NFS  │
                           │ /data       │
                           └─────────────┘
```

**Pros:**
- Independent scaling
- Centralized file access
- Easier security auditing

**Cons:**
- Network latency
- Requires service discovery

---

## 3. Authentication Integration

### Option A: Shared Token
Set `FSAL_TOKEN` to a token known by all Aperion services:
```bash
# In docker-compose or k8s secrets
FSAL_TOKEN=${APERION_INTERNAL_TOKEN}
```

### Option B: JWT Validation
Modify `auth.py` to validate Aperion-issued JWTs:
```python
from jose import jwt

def require_auth(authorization: str | None = Header(None)) -> dict:
    # Validate JWT from Aperion auth service
    token = authorization.split(" ")[1]
    payload = jwt.decode(token, APERION_PUBLIC_KEY, algorithms=["RS256"])
    return payload  # Contains user/agent identity
```

### Option C: mTLS
For zero-trust environments, use mutual TLS:
```yaml
# NGINX config for mTLS
ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client on;
```

---

## 4. Event Bus Integration

The Iron Vault should emit events to the Aperion EventBus for audit/observability.

### Recommended Events

| Event Type | Payload | When |
|------------|---------|------|
| `fsal.file.read` | `{path, size, client}` | After successful read |
| `fsal.file.written` | `{path, size, client}` | After successful write |
| `fsal.file.deleted` | `{path, recursive, client}` | After deletion |
| `fsal.sandbox.violation` | `{path, attempt, client}` | On traversal attempt |
| `fsal.auth.failed` | `{client, reason}` | On auth failure |

### Integration Code
```python
# In app.py startup
from aperion.foundation.event_bus import EventBus

bus = EventBus(enable_audit=True)

@app.middleware("http")
async def emit_events(request: Request, call_next):
    response = await call_next(request)
    if request.url.path.startswith("/v1/fs/"):
        bus.emit(
            f"fsal.api.{request.url.path.split('/')[-1]}",
            {"status": response.status_code, "client": request.client.host},
        )
    return response
```

---

## 5. CLI Integration

### Current CLI Pattern (ZIP1)
```python
# In stack/aperion/cli/zip1.py
def _handle_fsal_read(context: CommandContext, args) -> CommandResult:
    response = httpx.post(
        f"{FSAL_URL}/v1/fs/read",
        json={"path": args.path, "as_text": True},
        headers={"Authorization": f"Bearer {FSAL_TOKEN}"},
    )
    if response.status_code == 200:
        return CommandResult(ok=True, data=response.json(), message="Read successful")
    return CommandResult(ok=False, data={}, message=response.json().get("detail", "Failed"))
```

### TUI Integration
Ensure SearchResult models work with FSAL responses:
```python
# Entry from FSAL -> SearchResult for TUI
class FSALSearchResult:
    @property
    def name(self) -> str:
        return Path(self.path).name
    
    @property
    def summary(self) -> str:
        return f"{'Directory' if self.is_dir else 'File'} ({self.size} bytes)"
    
    @property
    def tags(self) -> list[str]:
        return ["fsal", "file" if not self.is_dir else "directory"]
```

---

## 6. Configuration Precedence

When deployed within Aperion, config should flow:

```
1. Environment variables (highest priority)
   FSAL_ROOT, FSAL_TOKEN, FSAL_PORT

2. Aperion config file (runtime.config.json)
   {
     "fsal": {
       "root": "/data/sandbox",
       "port": 4848
     }
   }

3. Defaults (lowest priority)
   port=4848, host=127.0.0.1
```

---

## 7. Health Check Integration

### Aperion Health Aggregator
```python
# In aperion health check
async def check_fsal():
    try:
        response = await httpx.get(f"{FSAL_URL}/healthz", timeout=5.0)
        data = response.json()
        return {
            "service": "fsal",
            "status": "healthy" if data.get("ok") else "unhealthy",
            "sandbox": data.get("sandbox", {}).get("root"),
        }
    except Exception as e:
        return {"service": "fsal", "status": "unreachable", "error": str(e)}
```

---

## 8. Migration Checklist

When integrating back into Aperion:

- [ ] Update `stack/aperion/fsal/` imports to point to standalone service
- [ ] Add FSAL service to docker-compose.yml
- [ ] Configure shared volume for sandbox
- [ ] Set FSAL_TOKEN in secrets
- [ ] Update CLI commands to use HTTP client
- [ ] Add FSAL health check to orchestrator
- [ ] Emit events to EventBus
- [ ] Update TUI to display FSAL entries
- [ ] Add FSAL metrics to Prometheus
- [ ] Document new deployment topology

---

## 9. Backward Compatibility

For gradual migration, the original `stack/aperion/fsal/` can be kept as a
thin client that proxies to the standalone service:

```python
# stack/aperion/fsal/__init__.py
"""Thin client for Iron Vault FSAL service."""

import httpx
import os

FSAL_URL = os.environ.get("FSAL_URL", "http://localhost:4848")
FSAL_TOKEN = os.environ.get("FSAL_TOKEN", "")

def read(path: str, **kwargs) -> bytes:
    """Read file via FSAL service."""
    response = httpx.post(
        f"{FSAL_URL}/v1/fs/read",
        json={"path": path, **kwargs},
        headers={"Authorization": f"Bearer {FSAL_TOKEN}"},
    )
    response.raise_for_status()
    data = response.json()
    if "content_b64" in data:
        import base64
        return base64.b64decode(data["content_b64"])
    return data.get("text", "").encode()
```

This allows existing code to continue working during migration.

---

*Last updated: 2026-02-08*
