# net-socket — Formation Recipe (FvW v8 §38)

**Module ID:** `net-socket`  
**Module name:** Net Socket Gateway  
**Lifecycle phase:** formation  
**Status:** INTENT ONLY. No code yet.  
**Date:** 2026-07-21  

---

## 1. Intent (the "why")
FreshCloud needs a robust, observable, and secure abstraction layer for low-level TCP/SSL connections supporting IMAP, SMTP, and other protocols. This module solves the pain points of manual socket management by:
- Providing uniform connection handling with security hardening
- Enabling per-user state isolation for multi-tenant mail systems
- Delivering observable metrics and audit trails for compliance
- Simplifying protocol implementations (IMAP/SMTP) with pre-vetted connection primitives
- Eliminating AGPLv3 lineage while preserving MailSo's architectural wisdom

## 2. User-facing behaviour
| Verb | Payload | Response | Notes |
|------|---------|----------|-------|
| `connect` | `{host, port, security, timeout}` | `{connection_id, status}` | Initiates connection with async readiness |
| `disconnect` | `connection_id` | `{success, reason?}` | Graceful closure with pending write flushing |
| `write` | `{connection_id, data, timeout?}` | `{bytes_written, timestamp}` | Buffered writes with backpressure handling |
| `read` | `{connection_id, bytes, timeout?}` | `{data, bytes_read, timestamp}` | Non-blocking reads with timeout |
| `securityUpgrade` | `connection_id` | `{success, cipher_info?}` | For opportunistic TLS upgrades |
| `getMetadata` | `connection_id` | `{host, port, security, user_id, account_id}` | Current connection state |

## 3. Public contract (data shapes + signatures)
```typescript
// FreshCloud\Mail\NetSocket\*
interface ConnectSettings {
  host: string;
  port: int;
  security: 'none' | 'ssl' | 'starttls';
  timeout_ms: int;
}

interface ConnectResponse {
  connection_id: string;
  status: 'connecting' | 'connected';
}

interface ConnectionId {
  id: string;
  user_id: string;
  account_id: string;
}

interface WriteRequest {
  connection_id: string;
  data: string;
  timeout_ms?: int;
}
```

## 4. Persistence (what gets stored where)
- **PocketBase tables**:
  - `email_audit` → Connection events (connect/disconnect with durations)
  - `email_accounts` → Connection settings (host/port/security)
- **Local filesystem**: None (offline caching deferred to future module)
- **In-memory state**:
  - Per-(user_id, account_id) connection pool
  - Socket resources with security metadata

## 5. Dependencies (what this module uses, what uses this)
| Dependency Type | Names | Purpose |
|----------------|-------|---------|
| Engine modules | `log`, `auth`, `metrics` | Logging, auth context, observability |
| App modules | `imap`, `smtp` | Higher-level protocol implementations |
| External libs | `pocketbase` | Account settings lookup |
| Runtime | `php:8.0+` | Core socket support |

## 6. Behaviour inventory (the N public methods)
| Method | Signature | Purpose | Side Effects | Errors |
|--------|----------|---------|--------------|--------|
| `connect` | `(ConnectSettings) -> Promise<ConnectResponse>` | Establishes new connection | Audit log write | `HostUnreachable`, `Timeout` |
| `disconnect` | `(ConnectionId) -> Promise<Result>` | Closes connection | Audit log write, resource cleanup | `ConnectionNotFound` |
| `write` | `(WriteRequest) -> Promise<int>` | Sends data to socket | Metrics increment | `WriteTimeout`, `BrokenPipe` |
| `read` | `(ConnectionId, int, int?) -> Promise<string>` | Reads data from socket | Metrics increment | `ReadTimeout`, `Eof` |
| `securityUpgrade` | `(ConnectionId) -> Promise<SecurityInfo>` | Forcibly upgrades to TLS | Audit log write | `UpgradeFailed` |

## 7. State machine
```
[Disconnected] 
  -> connect() -> [Connecting] 
    -> success -> [Connected/Secure] 
      -> securityUpgrade() -> [Connected/Secure (TLS)]
      -> read/write() -> [Connected/Secure]
      -> disconnect() -> [Disconnected]
    -> failure -> [Disconnected]
```

## 8. Side effects
| Side Effect | Target | When Triggered |
|-------------|--------|----------------|
| Audit log write | `email_audit` | On connect/disconnect (>5s flagged as slow) |
| Metrics increment | Prometheus | On read/write operations |
| Event emission | PubSub | On connection state changes |
| Resource cleanup | PHP memory | On disconnection or errors |

## 9. Input validation
- **Host**: Must be valid hostname/IP (RFC 3986)
- **Port**: 1-65535, validated against IANA service ports
- **Security**: Must be one of `'none'|'ssl'|'starttls'`
- **Timeout**: 100-30000ms range enforced
- **Data size**: Max 64KB write chunk size

## 10. Failure modes
| Failure | Handling | Caller Sees |
|---------|----------|-------------|
| Connection timeout | Auto-retry (3×) with backoff | `TimeoutException` |
| TLS handshake fail | Close connection, log failure | `TlsHandshakeException` |
| Socket read EOF | Return empty data, close connection | `EndOfFileException` |
| Write to closed pipe | Abort write, log error | `BrokenPipeException` |
| Invalid credentials | Close connection, auth trigger | `AuthRequiredException` |

## 11. User simulation
1. **GIVEN** user has IMAP account settings  
   **WHEN** calling `connect({host: imap.gmail.com, port: 993, security: ssl})`  
   **THEN** connection succeeds with `connection_id` returned  

2. **GIVEN** secure connection established  
   **WHEN** writing `data: "CAPABILITY\r\n"` with 2s timeout  
   **THEN** 12 bytes written and ACK received  

3. **GIVEN** slow connection (>5s)  
   **WHEN** connecting to external server  
   **THEN** connection completes but audit log flags as slow  

4. **GIVEN** network interruption  
   **WHEN** during read operation  
   **THEN** `ReadTimeoutException` after 10s  

5. **GIVEN** TLS upgrade request  
   **WHEN** on plaintext connection with STARTTLS support  
   **THEN` security upgrades and metadata updated  

6. **GIVEN** multi-user environment  
   **WHEN** user A connects and user B connects to same server  
   **THEN** separate connection pools maintained  

## 12. Reproducibility test
1. Create `FreshCloud\Mail\NetSocket` namespace  
2. Implement `ConnectSettings` DTO with validation  
3. Build connection pool class with per-user isolation  
4. Create socket wrapper with non-blocking I/O  
5. Implement audit logging integration  
6. Add Prometheus metrics hooks  
7. Build async connect/disconnect handlers  
8. Implement TLS context factory  
9. Write integration tests with Greenmail mock server  

## 13. Substitutability (FvW v8 §37.4)
A drop-in replacement must:
- Provide identical method signatures in `FreshCloud\Mail\NetSocket\*`
- Maintain identical state machine transitions
- Preserve event emission structure
- Support same audit schema (`email_audit` table)
- Implement identical timeout/retry logic
- Use same TLS context configuration

## 14. Invariants (the 5 things that must always hold)
1. Per-(user_id, account_id) connection isolation  
2. TLS certificates always validated against trusted CA store  
3. Audit records written for all connect/disconnect operations  
4. No shared socket resources between users  
5. All I/O operations time out within configured bounds  

## 15. Sweep fields applied (the 14 design intent fields)
### Sweep 1: Multi-user + PocketBase (6 fields)
| Field | Design Intent | PHP Implementation |
|-------|---------------|--------------------|
| user_scoping | Connections tied to authenticated user | Constructor requires `user_id`, `account_id` |
| pb_integration | Pull settings from PB `email_accounts` | `ConnectSettings` hydrated from PB API |
| auth_boundary | Operates below auth layer | No credential handling, assumes auth context |
| state_isolation | Zero cross-user data exposure | Connection pool keyed by (user_id, account_id) |
| logout_behaviour | Clean state on session end | Public `disconnectAllForUser()` method |
| multi_account | One socket per account | Connection pool tracks multiple accounts |

### Sweep 2: World-class Abilities (5 fields)
| Field | Design Intent | PHP Implementation |
|-------|---------------|--------------------|
| connection_pool | Reusable connections with keep-alive | `ConnectionPool` class with SO_KEEPALIVE |
| offline_cache | Deferred to `net-cache` module | Not implemented in this phase |
| sieve_integration | Deferred to `sieve` module | No sieve-specific logic |
| audit_logging | Compliant audit trail | `email_audit` writes with duration/size metrics |
| ux_primitives | Enable responsive UI | Async/promises for all operations |

### Sweep 3: Migration + Observability + Tests (3 fields)
| Field | Design Intent | PHP Implementation |
|-------|---------------|--------------------|
| migration | Zero data migration needed | Transient state only, no persistent schema |
| observability | Full observability integration | Prometheus metrics + structured logs |
| tests | Comprehensive coverage | PHPUnit + Greenmail fixtures for all modes |

## 16. Cross-references
- **§11**: Anti-drift enforcement (rules.md)  
- **§37**: Fragment requirements (fragment.md)  
- **§38**: Formation purpose (formation.md)  
- **§19**: Clean-room rebuild methodology (plan.md)  
- **Dependent modules**: `imap`, `smtp`, `log`, `auth`, `metrics`  

## 17. What we are intentionally NOT doing (deferred to future)
1. Offline caching (deferred to `net-cache` module)  
2. Sieve protocol support (deferred to `sieve` module)  
3. UI primitives (handled by React bridge)  
4. Connection multiplexing (simpler one-socket-per-account)  
5. WebSocket support (HTTP fallbacks only)  
6. Proxy tunneling (direct connections only)  
7. IPv6 preference (uses system defaults)  
8. Custom certificate storage (system CA store)  
9. Bandwidth limiting (deferred to QoS module)  
10. Real-time connection replication (distributed not supported)  

**Simplification from MailSo**: MailSo had 7 PHP files. We reduce to 4:  
`NetClient.php`, `ConnectionPool.php`, `SettingsValidator.php`, `MetricsHook.php`.  
*Rationale*: Eliminates AGPLv3 drivers, reduces cognitive load, maintains core functionality.