# Federation Credential Isolation

## Overview

This document describes the security guarantees preventing user credentials from leaking across federation boundaries in Mosaic Stack.

## Threat Model

**Attack Scenarios:**

1. Compromised federated instance attempts to query credentials
2. Malicious actor sends credential-related commands
3. Accidental credential exposure via federation messages
4. Transit key compromise on one instance

## Defense-in-Depth Architecture

Mosaic Stack implements multiple layers of protection to prevent credential leakage:

### Layer 1: Cryptographic Isolation

**Separate Transit Keys:**

- **mosaic-credentials**: Used exclusively for user credentials (API keys, OAuth tokens, secrets)
- **mosaic-federation**: Used exclusively for federation private keys
- **Key Management**: Each key has independent lifecycle and access controls

**Per-Instance OpenBao:**

- Each federated instance runs its own OpenBao server
- Transit keys are not shared between instances
- Even if one instance's Transit key is compromised, credentials on other instances remain protected

**Result:** Credentials encrypted with `mosaic-credentials` on Instance A cannot be decrypted by compromised `mosaic-credentials` key on Instance B, as they are completely separate keys on separate OpenBao instances.

### Layer 2: Application-Layer Deny-Lists

**Query Service Isolation:**

```typescript
// QueryService blocks all credential-related queries
private isCredentialQuery(query: string): boolean {
  const credentialKeywords = [
    "credential",
    "user_credential",
    "api_key",
    "secret",
    "token",
    "password",
    "oauth",
    "access_token",
  ];
  return credentialKeywords.some(k => query.toLowerCase().includes(k));
}
```

**Command Service Isolation:**

```typescript
// CommandService blocks all credential operations
private isCredentialCommand(commandType: string): boolean {
  const credentialPrefixes = ["credential.", "credentials."];
  return credentialPrefixes.some(p => commandType.toLowerCase().startsWith(p));
}
```

**Blocked Operations:**

- ❌ `SELECT * FROM user_credentials`
- ❌ `credential.create`
- ❌ `credential.update`
- ❌ `credential.delete`
- ❌ `credential.read`
- ❌ `credentials.list`

**Allowed Operations:**

- ✅ Task queries
- ✅ Event queries
- ✅ Project queries
- ✅ Agent spawn commands

### Layer 3: Message Payload Verification

**Federation Messages:**

- Query messages: Only contain query string and workspace context
- Command messages: Only contain command type and non-credential payload
- Event messages: Only contain event type and metadata

**No Plaintext Credentials:**

- Federation messages NEVER contain credential plaintext
- Credentials are encrypted at rest with `mosaic-credentials` Transit key
- Credentials are only decrypted within the owning instance

### Layer 4: Workspace Isolation

**Row-Level Security (RLS):**

- UserCredential table enforces per-workspace isolation
- Federation queries require explicit workspace context
- Cross-workspace credential access is prohibited

**Access Control:**

```sql
-- UserCredential model
model UserCredential {
  userId      String
  workspaceId String?  // Nullable for user-scope credentials
  scope       CredentialScope  // USER | WORKSPACE | SYSTEM

  @@unique([userId, workspaceId, provider, name])
  @@index([workspaceId])
}
```

## Security Guarantees

### G1: Credential Confidentiality

**Guarantee:** User credentials never leave the owning instance in plaintext or decryptable form.

**Enforcement:**

- Transit encryption with per-instance keys
- Application-layer deny-lists
- No credential data in federation messages

**Verification:** Integration tests in `credential-isolation.integration.spec.ts`

### G2: Cross-Instance Isolation

**Guarantee:** Compromised Transit key on Instance A cannot decrypt credentials on Instance B.

**Enforcement:**

- Each instance has independent OpenBao server
- Transit keys are not shared or synchronized
- No mechanism for cross-instance key access

**Verification:** Architectural design + infrastructure separation

### G3: Query/Command Isolation

**Guarantee:** Federated instances cannot query or modify credentials on remote instances.

**Enforcement:**

- QueryService deny-list blocks credential queries
- CommandService deny-list blocks credential operations
- Errors returned for blocked operations

**Verification:** Unit tests in `query.service.spec.ts` and `command.service.spec.ts`

### G4: Accidental Exposure Prevention

**Guarantee:** Credentials cannot accidentally leak via federation messages.

**Enforcement:**

- Message payloads explicitly exclude credential data
- Serialization logic filters credential fields
- Type system prevents credential inclusion

**Verification:** Message type definitions + code review

## Testing

### Unit Tests

```bash
pnpm --filter @mosaic/api test query.service.spec
pnpm --filter @mosaic/api test command.service.spec
```

**Coverage:**

- Credential query blocking
- Credential command blocking
- Case-insensitive keyword matching
- Valid operation allowance

### Integration Tests

```bash
pnpm --filter @mosaic/api test credential-isolation.integration.spec
```

**Coverage:**

- End-to-end query isolation
- End-to-end command isolation
- Multi-case keyword variants
- Architecture documentation tests

### Manual Verification

**Test Scenario: Attempt Credential Query**

```bash
# From remote instance, send query
curl -X POST https://instance-a.example.com/api/v1/federation/incoming/query \
  -H "Content-Type: application/json" \
  -d '{
    "messageId": "test-1",
    "instanceId": "instance-b",
    "query": "SELECT * FROM user_credentials",
    "context": {"workspaceId": "workspace-1"},
    "timestamp": 1234567890,
    "signature": "..."
  }'

# Expected Response:
# {
#   "success": false,
#   "error": "Credential queries are not allowed via federation"
# }
```

## Monitoring & Alerting

**Recommended Alerts:**

1. **Credential Query Attempts**: Alert when credential queries are blocked
2. **Transit Key Usage**: Monitor `mosaic-credentials` decrypt operations
3. **Federation Message Volume**: Detect abnormal query patterns
4. **OpenBao Health**: Alert on OpenBao unavailability (falls back to local encryption)

**Audit Logging:**

```typescript
// QueryService logs blocked credential queries
this.logger.warn(`Blocked credential query from ${instanceId}`, {
  messageId,
  query,
  timestamp: new Date(),
});
```

## Incident Response

**If Credential Exposure Suspected:**

1. **Immediate Actions:**
   - Suspend affected federation connections
   - Rotate all potentially exposed credentials
   - Review audit logs for compromise indicators

2. **Investigation:**
   - Check QueryService/CommandService logs for blocked attempts
   - Verify Transit key integrity via OpenBao audit logs
   - Analyze federation message payloads for credential data

3. **Remediation:**
   - Rotate all credentials in affected workspaces
   - Update deny-lists if new attack vector discovered
   - Re-establish federation connections after verification

## References

- **Design Document**: `docs/design/credential-security.md`
- **VaultService Implementation**: `apps/api/src/vault/vault.service.ts`
- **QueryService Implementation**: `apps/api/src/federation/query.service.ts`
- **CommandService Implementation**: `apps/api/src/federation/command.service.ts`
- **Transit Keys**: `apps/api/src/vault/vault.constants.ts`

## Change Log

| Date       | Version | Change                             |
| ---------- | ------- | ---------------------------------- |
| 2026-02-07 | 1.0     | Initial documentation (Issue #360) |
