Plain-language tour for DNicole. Technical depth for Robert and any auditor your enterprise clients send. Every answer to "where does the data live, who can see it, what is the cost, what happens when it breaks" lives below.
iexdg-nexus-vm, costs roughly $25 a month, inherits FedRAMP and CMMC compliance, and is now mid-migration from bearer-token sign-in to native Google sign-in.Every tool Claude uses on your behalf, every Notion query, every GHL contact lookup, every Culture Pulse SOP generation, runs through this VM. It is the heart of the IEXDG operating system and the single artifact your clients audit when they ask "where does our data go." This document is the canonical answer.
Google Cloud hosts a single virtual machine called iexdg-nexus-vm. That VM runs four daemons that together expose two public surfaces: a static dashboard at the root of brain.iexdg.com, and the MCP endpoint that Claude Desktop talks to. Everything else (Notion, GoHighLevel, RAG, Culture Pulse data) lives behind the VM and is reached over outbound HTTPS to those upstream APIs.
| Service | Port | Purpose | Status |
|---|---|---|---|
| caddy | 80, 443 | Reverse proxy, TLS termination, static dashboard, route gating | Active |
| iexdg-mcp | 127.0.0.1:8765 | MCP server with bearer auth (legacy, used by cron + scripts) | Active |
| iexdg-mcp-oauth | 127.0.0.1:8766 | MCP server with Google OAuth (Apr 27 build, Claude Desktop UI path) | Pre-staged |
| iexdg-api | 127.0.0.1:8000 | Dashboard JSON API, GHL/Notion bridge, capture endpoints | Active |
| https://brain.iexdg.com/ | Static dashboard, the v4.1 cream interface DNicole uses daily |
| https://brain.iexdg.com/mcp | OAuth-protected MCP endpoint, Claude Desktop talks here after Apr 27 |
| https://brain.iexdg.com/mcp-bearer | Legacy bearer-protected MCP, used by cron jobs during the 7-day migration |
| https://brain.iexdg.com/api/health | Public health probe, no auth, returns service liveness JSON |
| https://brain.iexdg.com/api/dashboard | Public dashboard data (read-only), returns Notion DB counts and pipeline state |
Google Cloud Platform is FedRAMP authorized at the High baseline and your VM inherits that compliance posture by default. CMMC Level 1 is automatic. CMMC Level 2 is achievable with the controls you already have. When a state agency or insurance broker sends an enterprise security questionnaire, the answer "we run on FedRAMP authorized infrastructure" is one line and it is true.
The brain runs on roughly $25 a month. Everything is metered, line-itemed, and budget-capped. No surprise bills. No platform tax. Compare to HighLevel agency tier at $297 to $997 a month for a less portable, single-tenant product. The savings compound across your client portfolio.
You own every line of Python, every Caddy directive, every systemd unit. If Google ever changes their pricing or their terms, the brain moves to AWS or DigitalOcean in a few hours of work. With HighLevel or Zapier, you do not own the codebase, the data flow, or the integrations · you rent them.
The Premium Advisory Tools, the Culture Pulse generator, the SOP synthesizer, the voice drift checker · these all run on infrastructure you control. Future ML models for content scoring, lead quality, churn prediction, voice drift trend live in this same VM. None of it leaves your project.
| Instance name | iexdg-nexus-vm |
| Machine type | e2-small (2 vCPU bursting, 2 GB RAM) |
| Disk | 10 GB pd-balanced (29 GB total root, 12 GB used) |
| OS image | Ubuntu 24.04.4 LTS · kernel 6.17.0-1012-gcp |
| Region · Zone | us-east4 · us-east4-b |
| External IP | 35.212.85.205 · static · STANDARD tier |
| DNS | brain.iexdg.com → 35.212.85.205 · A record at Squarespace DNS |
| TLS | Let's Encrypt via Caddy · auto-renew · 89-day cycle |
| Firewall | VPC default-allow-http and default-allow-https · :22 SSH via gcloud IAP only |
| Path | Owner | Mode | Purpose |
|---|---|---|---|
| /opt/iexdg-mcp/ | iexdg:iexdg | 755 | Application root · Python wrappers, RAG db, venv |
| /opt/iexdg-mcp/venv/ | iexdg:iexdg | 755 | Python 3.12 virtualenv · isolated deps |
| /opt/iexdg-mcp/iexdg_content_mcp_v3_2_apr22.py | iexdg:iexdg | 644 | The 33-tool source · canonical legacy |
| /opt/iexdg-mcp/iexdg_mcp_oauth_wrapper.py | iexdg:iexdg | 644 | OAuth wrapper · Apr 27 build · pre-staged |
| /etc/default/iexdg-mcp | root:iexdg | 640 | Env file · API keys, OAuth client secret · NOT world-readable |
| /etc/systemd/system/iexdg-mcp*.service | root:root | 644 | systemd unit files for the 4 daemons |
| /etc/caddy/Caddyfile | root:root | 644 | Caddy config · routing + auth |
| /var/lib/iexdg-brain/ | iexdg:iexdg | 700 | OAuth token store · iexdg-only readable |
| /var/log/iexdg-mcp/ | iexdg:iexdg | 755 | Service logs · 30-day rotation via logrotate |
| /var/log/caddy/ | caddy:caddy | 755 | Caddy access + error logs · 30-day rotation |
| /srv/brain/public/ | caddy:caddy | 755 | Static dashboard files · index.html and assets |
systemd
├── caddy.service (root → caddy user)
├── iexdg-api.service (iexdg user, FastAPI :8000)
├── iexdg-mcp.service (iexdg user, FastMCP bearer :8765)
├── iexdg-mcp-oauth.service (iexdg user, FastMCP OAuth :8766)
├── iexdg-health-check.timer → iexdg-health-check.service (every 5 min)
├── iexdg-ghl-observer.timer → iexdg-ghl-observer.service (every 15 min)
└── iexdg-content-drop.timer → daily_content_drop.py (2 AM CST)
All four primary services run as the unprivileged iexdg user. None can escalate to root, none can access each other's home directory, none can read /etc/default/iexdg-mcp outside of the systemd-injected env. Service hardening details are documented inside each .service file.
iexdg_mcp_oauth_wrapper.py uploaded to /opt/iexdg-mcp/iexdg-mcp-oauth.service registered with systemd, currently disabled (will not start until Google credentials are present)/var/lib/iexdg-brain/ created, mode 700, owned by iexdg user/etc/caddy/Caddyfile.oauth.staged · not yet active/opt/iexdg-mcp/venv alongside authlib, httpx, itsdangerousdrdnicole-youtube-manager project (Phase 1 of the OAuth runbook). Re-using the existing project saves 20 minutes and keeps audit logs in one place. Authorized redirect URI: https://brain.iexdg.com/auth/callback. Grab the Client ID and Client Secret from the credentials modal./etc/default/iexdg-mcp, swap Caddyfile, start iexdg-mcp-oauth.service, reload Caddy, run smoke tests against the seven OAuth metadata endpointshttps://brain.iexdg.com/mcp, complete Google sign-in, verify 33 tools appeardrdnicole@iexdg.com/mcp-bearer route stays for cron during the migration week, then we remove it (or keep it gated for cron-only)/mcp-bearer endpoint never notice the upgrade. We sunset cleanly after 7 stable days.
| API / Service | Purpose | Status | Cost |
|---|---|---|---|
| Compute Engine | The VM itself | In use | ~$14/mo (e2-small + disk) |
| VPC Networking | Static external IP, firewall rules | In use | $7/mo (static IP) |
| Cloud Logging | Aggregated logs for caddy + systemd journal | In use | ~$0-3/mo (under free tier most months) |
| Cloud Scheduler | 3 cron timers via gcloud_setup_schedulers.sh | In use | $0 (3 jobs free tier) |
| Secret Manager | Migrate API keys out of /etc/default/iexdg-mcp file | Gate 7 open | ~$1/mo when migrated |
| IAM | Service accounts for VM, future ML training, audits | In use | $0 |
| Cloud DNS | Not used · DNS hosted at Squarespace | Not enabled | $0 |
| Cloud Storage | Future · VM snapshots, backups, RAG corpora | Planned | ~$0-2/mo when enabled |
| YouTube Data API v3 | Daily content drop reads DNicole channel | In use | $0 (quota tier) |
| Gmail API | Send + read emails for content drop, replies | In use | $0 (quota tier) |
| Client ID prefix | Type | Created | Used for | Redirect URIs |
|---|---|---|---|---|
| 918058969668-aqj... | installed (desktop) | 2026 · pre-IEXDG-VM | YouTube + Gmail authentication for daily content drop | http://localhost |
| (NEW) 918058969668-??? | web | Apr 27, 2026 (pending) | brain.iexdg.com OAuth for Claude Desktop · this build | https://brain.iexdg.com/auth/callback |
Re-using the same GCP project for both clients keeps the audit trail in one place. Each client has its own Client ID and Secret, isolated.
| Line item | Unit cost | Monthly | Notes |
|---|---|---|---|
| e2-small VM | $0.0203/hr | $14.62 | 730 hours/month, 24/7 |
| 10 GB pd-balanced disk | $0.10/GB/mo | $1.00 | Boot + application disk |
| Static external IP | $0.01/hr | $7.30 | STANDARD network tier (us-east4) |
| Cloud Logging ingest | $0.50/GB | $0-2 | ~50 MB/day for VM logs, free tier covers most |
| Network egress (NA) | $0.085/GB | $0-2 | Mostly inbound (Notion/GHL pulls) |
| Cloud Scheduler (3 jobs) | free | $0 | First 3 jobs free in any project |
| Secret Manager (after Gate 7) | $0.06/secret/mo | $0.50 | ~8 secrets when migrated |
| Storage egress (browser dashboard) | $0.085/GB | $0-1 | HTML + JS, light traffic |
| Total expected | $23 to $28 | Per month, all-in |
/etc/default/iexdg-mcp mode 640 root:iexdg, only the iexdg user reads them/etc/caddy/brain_bearer.txt (root:caddy, mode 640)/var/lib/iexdg-brain/oauth.db (iexdg only, mode 700)/etc/default/iexdg-mcp are readable. Migrating to Secret Manager closes this · the file becomes a placeholder, secrets resolve at process start via Application Default Credentials. Not blocking the OAuth ship tonight, but worth scheduling for the May sprint.
| Source code | Git on Robert's Mac · pushed to GitHub on demand · canonical local files in /Documents/Belay/IEXDG/ |
| RAG knowledge db | SQLite at /opt/iexdg-mcp/rag/iexdg_knowledge.db · rebuildable from Notion + content corpus |
| Notion data | Lives in Notion's own infrastructure · they handle backup and restore |
| Brand corrections log | Notion · same as above |
| Caddyfile | Local in /Documents/Belay/IEXDG/vm_deploy/Caddyfile + timestamped backups on the VM |
| Secrets | /etc/default/iexdg-mcp · NOT auto-backed-up · Robert has them in 1Password |
| VM disk snapshot | NOT yet automated · planned for May sprint |
gcloud compute resource-policies create snapshot-schedule iexdg-weekly \
--description "Weekly snapshot of iexdg-nexus-vm" \
--max-retention-days=30 \
--start-time=05:00 \
--weekly-schedule=Sunday \
--on-source-disk-delete=keep-auto-snapshots \
--region=us-east4
gcloud compute disks add-resource-policies iexdg-nexus-vm \
--resource-policies=iexdg-weekly \
--zone=us-east4-b
Cost: ~$0.026/GB/month for snapshot storage. 10 GB disk × 4 weeks retention = $1/mo. Negligible.
gcloud compute snapshots list --filter='sourceDisk:iexdg-nexus-vm'gcloud compute disks create iexdg-nexus-vm-restored --source-snapshot=<snapshot-name> --zone=us-east4-bThe iexdg-health-check.timer fires every 5 minutes and runs workers/health_check.py with 7 checks:
Any failure triggers systemctl restart on the affected service via a sudoers drop-in. If restart fails twice in 15 minutes, the worker writes a notice to /var/log/iexdg-mcp/health.alerts.
Anyone can hit https://brain.iexdg.com/api/health with no auth and get a JSON response showing all four services' liveness. This is what the dashboard reads, what the health worker reads, and what an external uptime monitor (Robert's UptimeRobot, future) reads.
{"ok":true,"service":"iexdg-api","ts":"2026-04-27T07:29:18Z","notion_configured":true,"version":"0.1.0"}
When the May sprint adds Cloud Monitoring, the health-alerts log will be tail-piped to Cloud Pub/Sub which fans out to a Slack webhook and an email to dovewebconsulting@gmail.com. RPO 5 minutes for any service crash. Cost: $0 within Cloud Monitoring's free tier.
roles/compute.osLogin on the projectgcloud auth login with their Google accountgcloud compute ssh iexdg-nexus-vm --zone=us-east4-biexdg-admins Linux group on the VM (currently only Robert)gcloud projects remove-iam-policy-binding| Mode | Symptom | Detection | Recovery |
|---|---|---|---|
| F1 · VM down | brain.iexdg.com not reachable | Health worker every 5 min · external uptime ping | Restore from latest snapshot · 30 min RTO |
| F2 · TLS cert expired | Browser cert warning | Health worker checks 7-day expiry buffer | Caddy auto-renews · manual fallback caddy reload |
| F3 · Caddyfile syntax error | Caddy fails to reload, Caddyfile.bak-* still loaded | caddy validate before swap · fail-safe rollback in deploy script | Revert to backup · cp Caddyfile.bak-* Caddyfile && systemctl reload caddy |
| F4 · OAuth provider outage | Google sign-in fails for Claude Desktop users | Manual report from DNicole or external monitor | Bearer route on /mcp-bearer still serves cron, manual users fall back to Path 2 installer |
| F5 · Disk full | Service writes fail, /var/log fills | Health worker disk-usage gate at 85% | Logrotate already configured · manual cleanup of /var/log/iexdg-mcp/*.log.* if persistent |
| F6 · GCP us-east4 outage | Whole region offline | External uptime ping | Cross-region snapshot (planned May) restores in us-central1 · manual DNS update at Squarespace |
| F7 · API key compromise | Anomalous activity in GHL or Gmail logs | Manual review · future Cloud Anomaly Detection | Rotate the affected key in /etc/default/iexdg-mcp · restart affected service |
| F8 · Cron quota exhausted (e.g. YouTube) | Daily content drop fails | Health alerts log · daily content drop email failure | Quota request to Google · or back off polling frequency |
| F9 · ubuntu-2404-lts image renamed | VM rebuild fails because image family does not exist | Build error during snapshot restore | Use ubuntu-2204-lts for stability · or check gcloud compute images list --family first (per BB F9 lesson) |