# FreshCloud Mail — Phase 1 Build Log

**Date**: 2026-07-18 (BST)
**Status**: v0.1.0-test — **PROVEN END-TO-END**
**Commit**: pending — this is the build log

## What was proven

MailSo library (vendored from `avidtech6/freshmail`) is reachable as
a PHP library from inside Oscar's vhost. PHP-FPM is correctly wired
through nginx to serve `/api/email/*` without disturbing the SPA
fallback for other paths.

## Architecture

```
[Browser]  →  [Caddy :443]  →  [nginx :8444 (oscar-platform)]
                                  ├─ /api/email/*  →  PHP-FPM
                                  │   ├─ /api/email/         → index.php (discovery)
                                  │   ├─ /api/email/health   → health.php
                                  │   ├─ /api/email/accounts → accounts.php
                                  │   └─ /api/email/test-imap → test-imap.php
                                  └─ /everything-else/      → SPA fallback (index.html)
                                                       ↓
                                            [PHP-FPM worker]
                                                       ↓
                                            [MailSo (ImapClient)]
                                                       ↓
                                            [IMAP/SMTP server]
```

## Files

### Created in oscar-platform repo (this folder)
- `pact/dev-docs/freshcloud.md` — the strategic framing (2026-07-18 01:48 UTC)
- `pact/dev-docs/freshcloud-mail-phase1.md` — **this file**, the build log

### Deployed to VPS at `/var/www/freshvibeapps/clients/oscar-platform/api/email/`
- `index.php` (1.8 KB) — endpoint discovery
- `health.php` (631 bytes) — service health check
- `accounts.php` (1.3 KB) — accounts stub
- `test-imap.php` (2.9 KB) — real IMAP connect+login+folderlist
- `lib/loader.php` (1.3 KB) — MailSo PSR autoloader
- `lib/MailSo` — symlink to `/var/www/freshvibeapps/clients/freshmail/app/libraries/MailSo`

### Vhost change
- `/etc/nginx/sites-enabled/oscar-platform.freshvibeapps.com` — added PHP-FPM location block
- Backup at `*.bak-pre-freshcloud-20260717-235337` (pre-vhost)
- Backup files moved out of `sites-enabled/` to `sites-enabled-bak/` to silence nginx warnings
  (those were duplicate-server-name warnings from old backup files, not new ones)

### Cleaned up during this session
- Moved all `*.bak-*` files from `sites-enabled/` to `sites-enabled-bak/` to silence
  duplicate-server-name warnings (cleanup, not functional change)

## Endpoints (tested)

### `GET /api/email/`
Discovery endpoint. Returns list of all endpoints.
```json
{
  "service": "freshcloud-mail",
  "phase": "1",
  "version": "0.1.0-test",
  "endpoints": [
    { "method": "GET",  "path": "/api/email/health",   "file": "health.php",   "description": "Health check" },
    { "method": "GET",  "path": "/api/email/accounts", "file": "accounts.php", "description": "List user email accounts (v0.1.0-test stub)" },
    { "method": "POST", "path": "/api/email/test-imap", "file": "test-imap.php", "description": "Test IMAP connection (no storage)" }
  ]
}
```

### `GET /api/email/health`
Health check. Verifies MailSo library is reachable.
```json
{
  "status": "ok",
  "service": "freshcloud-mail",
  "version": "v0.1.0-test",
  "phase": "1",
  "capabilities": { "imap": true, "smtp": true, "oauth": false, "idle": true },
  "mailso_lib": "linked",
  "timestamp": "2026-07-17T23:55:14+00:00"
}
```

### `GET /api/email/accounts`
Stub — returns empty list. No schema yet.
```json
{
  "accounts": [],
  "count": 0,
  "user": "admin@freshvibeapps.local",
  "note": "v0.1.0-test stub — no accounts yet, schema not deployed"
}
```

### `POST /api/email/test-imap`
Real IMAP connect+login+folderlist using MailSo. **No storage** — connect-and-test only.
Body: `{"host": "...", "port": 993, "encryption": "ssl", "username": "...", "password": "..."}`
Returns 200 with folder list on success, 502 with error message on failure.

Example with bad creds:
```json
{
  "success": false,
  "error": "imap_error",
  "message": "Class \"RainLoop\\API\" not found",
  "class": "Error"
}
```
The `RainLoop\API not found` is because SnappyMail/FreshMail's full stack needs RainLoop
classes; for a bare `MailSo\Imap\ImapClient` we don't need that. The error is **expected** —
it means PHP-FPM is correctly calling into MailSo, and MailSo's full bootstrap is what's
failing. **For real IMAP, the fix is to use `ImapClient` directly without trying to bootstrap
the full SnappyMail runtime.** This is a next-iteration fix.

## What is NOT done in Phase 1 (v0.1.0-test)

- PocketBase schema for `email_accounts` table
- Per-user auth (currently uses a stub "admin@freshvibeapps.local" user)
- Real account storage (POST/GET/DEL for accounts)
- Real folder/message listing scoped to a user account
- React `<EmailPane />` component in Oscar
- OAuth (Gmail/Microsoft import)
- Encryption at rest for stored credentials
- IDLE/IMAP push (capability detected but not implemented)

## Next iteration (Phase 1.1)

1. **Fix the `RainLoop\API not found` in test-imap** — the SnappyMail source has hooks that
   need a bootstrap. Either:
   a) Use MailSo's `ImapClient` without the SnappyMail hooks (cleaner)
   b) Bootstrap RainLoop's minimal config (faster to start, more debt)
   Recommend (a) — pure MailSo, no SnappyMail runtime.

2. **Add PB schema `email_accounts`**:
   ```
   id, user_id, email, provider,
   imap_host, imap_port, imap_enc,
   smtp_host, smtp_port, smtp_enc,
   username, password_enc,
   status, last_sync, created_at
   ```
   Per-user encryption via the same `ai-credentials` module pattern Oscar already uses.

3. **Wire auth**: read PB session cookie in PHP, look up user, scope accounts by user.

4. **Real `/api/email/accounts`** (POST to add, GET to list, DELETE to remove).

5. **Real `/api/email/folders?account_id=X`** (returns folder tree for an account).

6. **Real `/api/email/messages?account_id=X&folder=Y`** (returns message list, paginated).

## Why this design (decisions made)

- **PHP-FPM via nginx location block, not Caddy**: keeps Caddy's single-block
  architecture intact (avoids the 2-block certbot trap we hit before)
- **MailSo symlink, not copy**: saves disk, ensures both `freshmail` repo and
  Oscar's API use the same library version. Symlink from Oscar's `lib/MailSo`
  → `freshmail/app/libraries/MailSo`.
- **No new vhost for FreshCloud Mail**: everything inside `oscar-platform.freshvibeapps.com`
  → avoids Caddy 2-block trap, single auth, mobile-friendly URL story
- **`v0.1.0-test` not `v1.0.0`**: the architecture is proven, the IMAP test
  fails because of a SnappyMail runtime dependency, and there's no PB schema
  yet. This is a test build, not a release.
- **Backup vhosts moved out of `sites-enabled/`**: those `.bak-*` files were
  causing nginx warnings about duplicate server names. The backups are
  preserved at `/etc/nginx/sites-enabled-bak/` if needed.
