**Question from operator**: "Check everything we have on the VPS that is not open to the web. So you have to either have a password or each Mavis has a way to get in."
```
Internet
↓ :80/:443
[Caddy :2019 admin] — TLS terminator, on-demand certs
↓
[nginx :8081 plain / :8444 SSL] — vhost routing
↓
Backend services on :3000 (panel), :3001 (my-portal), :3002 (?), :3003 (vibecoder-bridge), :8090 (PocketBase)
↓
[Local services]: systemd units (operator-panel, my-portal, pocketbase, vibecoder-bridge, etc.)
↓
[Filesystem]: /var/www/freshvibeapps/clients/*, /opt/operator/{backups,data,panel,secrets,vault,deploy-*}
```
| Endpoint | Auth | Notes |
|---|---|---|
| `/` | None | Login page (public) |
| `/api/mavis/me` | Required (401) | ✅ Token required |
| `/api/mavis/threads` | Required (401) | ✅ Token required |
| `/api/mavis/bulletins` | Required (401) | ✅ Token required |
| `/api/mavis/reports` | Required (401) | ✅ Token required |
| **`/api/artifacts`** | **NONE (200)** | ⚠️ **LEAK** — returns all artifact metadata without auth |
| `/api/artifacts/:id` | NONE (200) | ⚠️ Same leak — individual artifact details exposed |
| `/api/deploys` | Required (401) | ✅ Token required |
| **`/api/plans`** | **NONE (200)** | ⚠️ **LEAK** — returns all plan metadata without auth |
| `/api/caddy/allow` | None | The Caddy allow policy endpoint (public by design) |
| `/mavis/timeline` | None (200) | SPA — public, but only renders data behind auth |
**Finding**: artifacts and plans metadata are public. Names, IDs, types, projects, descriptions, dates — all visible. The actual file CONTENT requires knowing the path on `artifacts.freshvibeapps.com` and there it's public by URL design.
1. **`/api/artifacts` returns all artifact metadata** — IDs, names, projects, types, descriptions, dates, content_size, pinned. No auth required. ~860 artifacts visible.
2. **`/api/plans` returns all plan metadata** — titles, descriptions, step counts, status, dates. No auth required. ~1 plan visible (a863).
3. **Caddy-allow endpoint** (intentional) — lets anyone check if a hostname is allowed. Fine but enables recon.
1. **SSH root login with password** — if password leaks, full root. Should be key-only.
2. **CUPS on 0.0.0.0:631** — shouldn't be public, minor risk.
3. **`/api/caddy/allow` returns 200 with hostname info** — gives attackers a way to enumerate allowed subdomains.
1. `/api/mavis/*` (threads, bulletins, reports, me, plans-write) — 401 without token
2. `/api/deploys` — 401 without auth
3. PocketBase admin (`_/`) — has its own auth
4. Caddy admin (`:2019`) — localhost-only
5. Backend services (3000-3003, 8090) — localhost-only
6. SSH key auth works for `operator` user (just not root)
1. **Make `/api/artifacts` require token** (or at least return summary without descriptions/content_size for non-owners)
2. **Make `/api/plans` require token**
3. **Move SSH to key-only** for root (or at least disable password on root)
4. **Block CUPS from public** (`ufw deny 631` or systemctl disable cups)
5. **Make Caddy-allow rate-limited** to prevent recon scanning
6. **Add a `/api/auth-status` endpoint** that returns a summary of which endpoints need auth (so Mavis sessions can find this from a discoverable spot)
| Method | Auth | Used by |
|---|---|---|
| `/api/mavis/register {name, session_id}` → token | None to register, then Bearer | Every Mavis on first turn |
| `Authorization: Bearer <token>` | Token | All Mavis API calls |
| SSH password (root) | Password | Direct VPS work (most Mavis don't have this) |
| SSH key (operator) | Key | Only Mavis with key file |
| POST bulletin/report | Bearer | All Mavis |
| WebSocket `/ws` | None | SPA only |
**Mavis doesn't need a password** — the token model works. The leaks above are about LISTING data without auth, not about Mavis being able to get in. Mavis can always get in via token.
The question is: **should the LISTING endpoints be public?** Today yes (anyone can see what's in HQ). That might be a feature ("public timeline of Mavis work") or a bug ("Mavis work is supposed to be private to the operator"). The operator needs to decide.