Architecture Overview
Architecture of the Personal Digital Estate. Human-readable Mermaid diagrams are the source for human understanding; formal C4 diagrams stay in the appendix as an agent-facing exception.
Topic index
Section titled “Topic index”| # | Topic | Where documented |
|---|---|---|
| 1 | Interaction channels | How François talks to the estate |
| 2 | CLI workflow | Interaction channels — Work vs Home vs Hermes contexts |
| 3 | Delivery modes | Delivery workflow — branch-review / autonomous / auto-commit |
| 4 | GitHub auth | Quick reference — fine-grained PAT, scoped per-repo |
| 5 | PAT expiry | SOUL.md cron table — weekly check, Slack alert at 7 days |
| 6 | Security model | ADR-0007 — plaintext .env + OS hardening |
| 7 | Drive mounts | SSOT split + CONTEXT.md glossary — 3 rclone mounts |
| 8 | LLM routing | SOUL.md LLM routing — local / free / premium tiers |
| 9 | VPS agnosticism | ADR-0008 — DRP, daily encrypted GitHub backup + weekly offline copy, 60-min rebuild |
| 10 | Dashboard access | ADR-0006 + Network topology — CF Tunnel + Access email OTP |
| 11 | Secrets management | ADR-0007 — threat model, upgrade triggers |
| 12 | Disaster recovery | ADR-0008 — RTO/RPO, encrypted backup/decrypt procedure |
| 13 | Hermes updates | SOUL.md cron table — notify-only, manual upgrade |
| 14 | Monitoring & health | CONTEXT.md glossary + SOUL.md cron table — Uptime Kuma at health.ducatillon.net |
The Big Picture — Who uses what?
Section titled “The Big Picture — Who uses what?”flowchart TB subgraph people ["👥 People"] F["🧑💻 François\nM1Max · Pixel 9A · M3Pro"] D["👩 Djuly\nPhone"] K["👧👦 Kids\nPhones/tablets"] end
subgraph perso ["🏠 Personal Digital Estate"]
subgraph slack_block ["💬 Slack"] TG["Slack Bot"] end
subgraph vps ["🖥️ Agent Host — Hostinger VPS KVM2"] H["🤖 Hermes Agent\n🔀 Unified Project Radar\npersonal day one · family/pro roadmap"] UK["📊 Uptime Kuma\nhealth.ducatillon.net"] end
subgraph perso_google ["☁️ Google (Personal account)"] GD["📁 Personal Google Drive\nPARA: Projects · Areas\nResources · Archive\n(Knowledge Vault — SSOT)"] GP["📷 Google Photos"] end
subgraph cloudflare ["🌐 Cloudflare"] SITES["🌍 6 Static Sites\nnotes · strata · locationyeu\nchateaudebourgon · sommelier-arena · docs"] end
subgraph perso_tools ["🔧 Personal Tools"] BW["🔐 Bitwarden\nCredentials"] GH_PERSO["🐙 GitHub (perso)\nPrivate repos\nhugo-air · strata · arch-wiki"] OR["🧠 OpenRouter\nFree + Premium LLMs"] end
subgraph backup_block ["💾 Backup"] HD["🗄️ External HD\nWeekly"] GH_BACKUP["🐙 Private GitHub repo\nEncrypted Hermes backups\nDaily"] end
end
subgraph pro ["🏢 Professional Boundary — Decathlon"] SSO["🔒 Company SSO"] PRO_SOURCES["📂 Professional sources\nRoadmap only\nno VPS token day one"] GH_PRO["🐙 GitHub (pro)\nDecathlon org repos"] MISSION["🎯 Pro Mission\n(north star — being written)\nHR ladder · career path"] end
%% People → Personal Estate F -- "chat, ask, delegate" --> TG D -- "chat, ask" --> TG TG -- "messages" --> H
F -- "web dashboard\nhermes.ducatillon.net" --> H
%% Hermes → Personal tools H -- "read/write\n.md notes" --> GD H -- "LLM inference" --> OR H -- "read/write code\n(with approval)" --> GH_PERSO H -. "no direct token\non Agent Host" .-> GH_PRO
%% François → Personal tools F -- "edit .md (VSCode)\nedit .gdoc (browser)" --> GD F -- "push code" --> GH_PERSO F -- "manage sites" --> SITES D -- "shared docs" --> GD K -- "photos, storage" --> GP F -- "photos" --> GP
F -- "all credentials\n(all devices)" --> BW H -- "reads API keys\nfrom ~/.hermes/.env\n(copied from Bitwarden)" --> BW
GH_PERSO -- "auto-deploy\non push" --> SITES
H -. "daily encrypted\nstate backup" .-> GH_BACKUP GH_BACKUP -. "weekly offline copy" .-> HD F -- "manual weekly\nnon-Hermes backup" --> HD
%% Pro/Perso bridges (cross the boundary) F -- "daily work\n(via SSO)" --> SSO SSO --> PRO_SOURCES SSO --> GH_PRO PRO_SOURCES -. "roadmap only:\nno VPS token day one" .-> H GH_PRO -. "no VPS token\nday one" .-> H MISSION -. "guides priorities\nin Unified Radar" .-> H F -. "skills flow both ways:\nDDD · C4 · AI context" .-> pro
style perso fill:#e8f5e9,stroke:#2e7d32 style pro fill:#fff3e0,stroke:#e65100How the boundary works: François decides which project lives in which PARA — by instinct and judgment, not by technical rule. A perso PARA project (like this architecture wiki) can deliver career value; a pro PARA skill (like DDD) can flow into personal projects. The boundary is a human routing decision. Day one, Hermes reads François’s personal sources only on the VPS; Djuly and pro source access remain roadmap until there are approved boundary-safe patterns. The target remains a Unified Project Radar — one prioritized view mixing pro and perso, guided by the Pro Mission.
Inside the Agent Host — What Hermes is made of
Section titled “Inside the Agent Host — What Hermes is made of”flowchart LR subgraph inputs ["📥 Inputs"] TG["💬 Slack\nFrançois + Djuly"] WEB["🌐 Web Dashboard\nhermes.ducatillon.net\nport 9119"] end
subgraph hermes ["🤖 Hermes Agent (VPS)"] GW["🚪 Gateway\nhermes-gateway systemd\nRoutes: Slack · Web · CLI"] CORE["🧠 Core Agent\nReasoning · tool calling\ndelegation"] DASH["📊 Dashboard\nport 9119 — Sessions · Logs\nCron · Analytics · Skills · Chat\nNo web secret editing day one"] ROUTER["🔀 9router\nport 3000 (nginx → 80)\nModel router · multi-provider\nusage stats"] MEM["💾 Memory\nSQLite + FTS5\npersistent cross-session"] SK["⚡ Skills\nLearned procedures\nself-improving"] STATE["⚙️ Config\n~/.hermes/\nSOUL.md · config.yaml\ncron jobs · .env"] end
subgraph data ["📦 Data Access"] BRIDGE["🔗 Drive Bridge\nrclone mount\n/mnt/gdrive/"] GHUB["🐙 GitHub MCP\nread/write code\n(with approval)"] end
subgraph external ["☁️ External"] GD_PERSO["📁 Personal Google Drive\nKnowledge Vault (read/write)"] OR["🧠 OpenRouter\nFree model (default)\nPremium model (on-demand)"] BW["🔐 Bitwarden\nAPI keys & secrets"] end
subgraph backup_agent ["💾 Backup"] HD["🗄️ External HD\nWeekly offline copy"] GH_BACKUP["🐙 Private GitHub repo\nDaily encrypted state"] end
TG -- "messages" --> GW WEB -- "messages" --> DASH DASH -- "embedded Chat\n(PTY/WebSocket)" --> GW GW -- "routes to" --> CORE CORE -- "remembers" --> MEM CORE -- "uses & improves" --> SK CORE -- "reads config" --> STATE CORE -- "reads/writes files" --> BRIDGE CORE -- "reads/writes code\n(with approval)" --> GHUB CORE -- "LLM calls" --> ROUTER ROUTER -- "routes to\nproviders" --> OR CORE -- "reads ~/.hermes/.env" --> STATE BRIDGE -- "Personal Drive API\nread/write with caching" --> GD_PERSO MEM -. "backed up daily\nencrypted" .-> GH_BACKUP SK -. "backed up daily\nencrypted" .-> GH_BACKUP STATE -. "backed up daily\nencrypted" .-> GH_BACKUP GH_BACKUP -. "weekly copy" .-> HDNetwork topology — How traffic flows
Section titled “Network topology — How traffic flows”flowchart LR subgraph home ["🏠 Home / Mobile"] BROWSER["🌐 Browser\n(any device)"] TG_APP["💬 Slack App"] SSH_CLIENT["🔑 SSH\n(M1Max only)"] end
subgraph cloudflare ["☁️ Cloudflare Edge (global CDN)"] CF_ACCESS["🔒 Cloudflare Access\nEmail OTP gate"] CF_PAGES["📄 Cloudflare Pages\ndocs.ducatillon.net\n(static Astro site)"] CF_TUNNEL_EP["🔗 Tunnel Endpoint\nhermes.ducatillon.net"] CF_DNS_R["🏷️ DNS\n*.ducatillon.net"] end
subgraph slack_cloud ["💬 Slack Cloud"] TG_API["Slack Bot API\n(forwards messages)"] end
subgraph github_cloud ["🐙 GitHub"] GH_REPO["fducat18/docs.ducatillon.net\n(SSOT — .md files)"] GH_PAGES_HOOK["Webhook\n(on push → deploy)"] end
subgraph hostinger ["🖥️ Hostinger Datacenter (Lithuania/NL)"] subgraph vps ["Agent Host VPS — KVM2"] CLOUDFLARED["cloudflared daemon\n(outbound tunnel)"] HERMES_GW["hermes-gateway\nport 9119 (dashboard)\nSlack webhook"] NGINX["nginx\nport 80 → 9router:3000"] UFW["🧱 UFW Firewall\nonly port 22 inbound"] end end
%% User → Cloudflare BROWSER -- "HTTPS\ndocs.ducatillon.net" --> CF_ACCESS CF_ACCESS -- "✅ authenticated" --> CF_PAGES BROWSER -- "HTTPS\nhermes.ducatillon.net" --> CF_ACCESS CF_ACCESS -- "✅ authenticated" --> CF_TUNNEL_EP
%% Cloudflare → VPS (tunnel is OUTBOUND from VPS) CF_TUNNEL_EP -. "tunnel\n(VPS initiated)" .-> CLOUDFLARED CLOUDFLARED -- "localhost:9119" --> HERMES_GW
%% Slack flow TG_APP["💬 Slack App"] TG_APP -- "message" --> TG_API TG_API -- "webhook POST\n(via Cloudflare Tunnel)" --> HERMES_GW
%% SSH (direct, only inbound port) SSH_CLIENT -- "port 22\n(key-based only)" --> UFW UFW -- "allowed" --> vps
%% GitHub → Cloudflare Pages GH_REPO -- "push event" --> GH_PAGES_HOOK GH_PAGES_HOOK -- "build & deploy" --> CF_PAGES
%% Hermes → GitHub (outbound) HERMES_GW -- "GitHub MCP\n(reads .md)" --> GH_REPO
%% Styling style hostinger fill:#e3f2fd,stroke:#1565c0 style cloudflare fill:#fff3e0,stroke:#e65100 style home fill:#e8f5e9,stroke:#2e7d32Key security properties of this topology:
- Zero inbound ports on VPS (except SSH/22) — Cloudflare Tunnel is outbound-only
- All web traffic authenticated — Cloudflare Access (email OTP) gates both
hermes.ducatillon.netanddocs.ducatillon.net - Slack webhook secured — delivered through the same tunnel, not a public endpoint
- VPS ↔ Internet — the VPS has its own public IP in Hostinger’s datacenter; it doesn’t use your home internet at all
- Docs site independent — if VPS dies, docs.ducatillon.net stays up (Cloudflare Pages is separate infrastructure)
- SSOT is GitHub — both Cloudflare Pages and Hermes (via MCP) read from the same repo
How things connect — The data flow
Section titled “How things connect — The data flow”flowchart TB subgraph perso_boundary ["🏠 Personal Digital Estate"]
subgraph devices ["📱 Devices"] M1["🖥️ MAC PERSO M1Max\nVSCode · Google Drive Desktop\nWeb browser · Git"] PX["📱 Pixel 9A\nMarkor · Slack\nGoogle Drive · Web browser"] end
subgraph google_drive ["📁 Google Drive — Single Source of Truth"] PARA["📂 PARA Structure\nProjects · Areas · Resources · Archive"] MD[".md notes\nEdited in VSCode / Markor"] GDOC[".gdoc .gsheet\nCollaboration Hub"] end
subgraph vps ["🖥️ Agent Host"] H["🤖 Hermes Agent"] RCL["🔗 rclone mount\n/mnt/gdrive/"] end
subgraph github_block ["🐙 GitHub (Private repos)"] GH_SITES["📦 Site repos\nnotes · strata · locationyeu\nchateaudebourgon · sommelier-arena"] GH_ARCH["📦 Architecture wiki repo\n(this repo → docs.ducatillon.net)"] end
subgraph backup_block ["💾 Backup"] HD["🗄️ External HD\nWeekly"] end
end
subgraph pro_boundary ["🏢 Professional Boundary — Decathlon"] M3["🖥️ Decathlon M3Pro\n⚠️ Managed device\nWeb browser · Slack desktop"] PRO_SOURCES["📂 Professional sources\nRoadmap only\nno VPS token day one"] SSO["🔒 Company SSO"] end
subgraph cloudflare ["🌐 Cloudflare"] TUNNEL["🔒 Tunnel\nNo open ports"] ACCESS["🔑 Access\nEmail OTP"] PAGES["🌍 Pages\n6 sites"] CF_DNS["🏷️ DNS + CDN + SSL"] end
subgraph dns ["🏷️ Domain Registrar"] PH["PlanetHoster"] end
%% MAC PERSO connections M1 -- "Google Drive Desktop\nsync" --> google_drive M1 -- "git push" --> github_block M1 -- "web browser:\nhermes dashboard\ndocs · sites" --> cloudflare M1 -- "weekly backup" --> HD
%% Pixel connections PX -- "Google Drive app\n+ Markor" --> google_drive PX -- "Slack" --> H PX -- "web browser:\nsites · docs" --> cloudflare
%% Decathlon M3Pro connections (crosses boundary) M3 -- "Slack desktop" --> H M3 -- "web browser:\nhermes dashboard\ndocs · sites" --> cloudflare M3 -. "read-only\npersonal files" .-> google_drive M3 -- "daily work" --> PRO_SOURCES SSO -- "gates access to" --> PRO_SOURCES
%% Professional reference is roadmap only; no professional token on the Agent Host day one PRO_SOURCES -. "roadmap:\napproved reference only" .-> google_drive
%% Agent Host connections RCL -- "mounts as\nlocal filesystem" --> google_drive H -- "reads/writes via\n/mnt/gdrive/" --> RCL H -- "reads/writes code\n(with approval)" --> github_block
%% Cloudflare connections H -- "dashboard via" --> TUNNEL TUNNEL --> ACCESS ACCESS -- "authenticated\ntraffic" --> H
github_block -- "auto-deploy\non push" --> PAGES
PH -- "nameservers\ndelegated to" --> CF_DNS CF_DNS -- "routes to" --> PAGES CF_DNS -- "routes to" --> TUNNEL
%% Backup connections google_drive -. "weekly snapshot" .-> HD H -. "weekly state backup" .-> HD H -. "daily encrypted\nstate backup" .-> GH_BACKUPQuick reference — What lives where
Section titled “Quick reference — What lives where”| Location | What’s there | Accessed by |
|---|---|---|
| Google Drive | PARA documents: .md notes, .gdocs, .gsheets (NO code projects) | François (all devices), Hermes via day-one Personal Drive Bridge. Djuly/pro mounts are roadmap only. |
| GitHub (perso) | Code projects + architecture wiki (fducat18, SSH) | François (git from M1Max), Hermes via HERMES_GITHUB_CODE_PAT (selected repos only; branch-review), Cloudflare Pages |
| GitHub (pro) | Decathlon org repos (francoiducat, SSH) | François only (git from M3Pro). Hermes has no pro GitHub token on the Agent Host; it can reason over approved Professional Reference Material only. |
| Hostinger VPS | Hermes Agent (hermes-gateway systemd), Dashboard (port 9119), 9router (port 3000/80), Drive Bridge (rclone), Uptime Kuma (health.ducatillon.net — estate-critical monitoring) | François (SSH + dashboard + health page), Djuly (Slack) |
| Cloudflare | 6 websites, DNS, CDN, Tunnel, Access auth | Public (sites), François (dashboard + docs) |
| Bitwarden | All passwords, API keys, SSH keys | François (all devices), Hermes (reads API keys) |
| External HD | Weekly backups: photos, vault, Hermes state, Bitwarden | François (manual) |
| Private GitHub backup repo | Daily encrypted Hermes state archives only; no plaintext secrets | Agent Host (systemd timer with HERMES_BACKUP_GITHUB_PAT scoped only to this repo), François (decrypts with private key from Bitwarden) |
| Google Photos | Family photos (outside Hermes scope) | François, Djuly, Kids |
Interaction channels — How François talks to the estate
Section titled “Interaction channels — How François talks to the estate”| Context | Tool | Where it runs | What it accesses | When |
|---|---|---|---|---|
| Work (Decathlon M3Pro) | Copilot CLI | Locally on M3Pro | Local files, Pro GitHub | Coding during work hours. Hermes not involved. |
| Home — coding | Pi CLI | Locally on M1Max | Local files, Personal GitHub | Personal project coding. Hermes optional. |
| Home — estate work (preferred) | SSH → hermes --tui | On VPS | Full context: SOUL, memory, Drive Bridge, skills, sessions | The primary channel for estate work. Full Hermes brain. |
| Mobile / async | Slack | Phone / desktop app | Hermes on VPS | Quick questions, triage, reminders — even while coding locally in another terminal. |
| Visual / config | Web dashboard | Browser → VPS (via CF Tunnel) | Same as Hermes + visual UI | Analytics, logs, cron review, session history, config. |
Key insight: Slack is the glue channel. When coding locally (Copilot CLI or Pi CLI), François can ask Hermes a quick question via Slack without leaving his terminal workflow. No SSH needed for simple lookups — just open Slack in another window or on his phone.
Which channel for which need?
| Need | Best channel | Why |
|---|---|---|
| Quick question while coding locally | Slack | Zero friction, async, any device |
| Deep estate work session | SSH → hermes --tui | Full TUI, long conversation, complex tasks |
| Review analytics / logs / config | Web dashboard | Visual, charts, bulk config |
| ”Fix this bug on repo X” (autonomous) | Slack | Fire-and-forget — Hermes clones, fixes, pushes branch, CI validates. See autonomous delivery below. |
Where things live — the SSOT split
Section titled “Where things live — the SSOT split”| Content type | SSOT | Why | Hermes access |
|---|---|---|---|
| Documents, notes, .gdoc, .gsheet, small scripts | Google Drive (PARA) | Lightweight, syncs to all devices | Drive Bridge (rclone) |
| Projects with heavy deps (node_modules, venv, dist/) | GitHub | Build artifacts would kill Drive sync | GitHub MCP (read/write with approval) |
| Graduation trigger | First npm install or python -m venv | That’s when thousands of files appear | — |
| Google Drive PARA ↔ Code project link | README in PARA folder | Points to GitHub repo URL | Reads both sides |
How Hermes produces code — the delivery workflow
Section titled “How Hermes produces code — the delivery workflow”flowchart LR F["🧑💻 François\n'Fix the date bug\non locationyeu'"] H["🤖 Hermes Agent"] GH["🐙 GitHub\n(fducat18)"] TG["💬 Slack\nnotification"]
F -- "1. request via\nSlack / CLI / dashboard" --> H H -- "2. clone / branch\n3. fix code\n4. push branch\n5. create PR" --> GH H -- "6. 🔔 'PR ready\nfor review' + link" --> TG TG -- "7. François reviews\nmerges or requests changes" --> GHThe flow:
- François asks Hermes for a script, feature, or fix (Slack, SSH CLI, or dashboard)
- Hermes writes the code on the VPS
- Hermes pushes to a branch on GitHub and creates a PR
- Hermes notifies François via Slack: ”✅ PR ready — [link]. Summary: fixed date format in locationyeu. CI status: passing.”
- François reviews when convenient (GitHub web, or
git pulllocally) - François merges (or asks Hermes to revise via Slack)
What Hermes can NOT do autonomously: push to main, delete branches, force-push, create/delete repos. All destructive actions require explicit approval.
Autonomous delivery (guardrailed)
Section titled “Autonomous delivery (guardrailed)”For repos with sufficient guardrails, François can ask Hermes via Slack to fix a bug in full autonomy:
flowchart LR F["🧑💻 François\nSlack: 'fix the\nbroken date format\non locationyeu'"] H["🤖 Hermes Agent"] GH["🐙 GitHub"] CI["⚙️ CI\nGH Actions"]
F -- "1. fire-and-forget\nrequest" --> H H -- "2. clone / branch\n3. fix code\n4. push branch" --> GH GH -- "5. PR triggers\nCI pipeline" --> CI CI -- "6. E2E + lint +\nhealth checks" --> GH GH -- "7. ✅ all green?\nauto-merge" --> GH H -- "8. reports result\nback to François" --> FPrerequisites for autonomous mode (per-repo):
- ✅ E2E tests covering the affected area
- ✅ GitHub Actions CI pipeline (lint + test + build)
- ✅ Branch protection rules on
main(require CI pass) - ✅ Health checks / smoke tests post-deploy
- ✅ François has explicitly enabled autonomous mode for this repo
Without these guardrails, Hermes falls back to branch-review mode (push branch, wait for human review). The “careful assistant” rule is the default — autonomous is the earned exception.
| Scenario | Where Hermes writes | How François reviews |
|---|---|---|
| Quick script (one-off utility) | New file in existing repo branch | GitHub web or git pull |
| Feature on existing project | Branch on the project’s repo | git pull + local testing |
| New project bootstrap | Creates new repo (with approval) | git clone locally |
| Config/automation (VPS) | Directly on VPS filesystem | Reviews via Slack/dashboard before applying |
Appendix — Formal C4 Model
Section titled “Appendix — Formal C4 Model”Formal C4 diagrams for agent consumption and technical reference. They intentionally duplicate the human-readable Mermaid views in a more structured notation, even when the result is too dense for human reading.
C4 Level 1 — System Context
Section titled “C4 Level 1 — System Context”C4Context title Personal Digital Estate — System Context (C4 Level 1)
Person(francois, "François", "Estate owner. MAC PERSO M1Max, Pixel 9A, Decathlon M3Pro (read-only)") Person(djuly, "Djuly (wife)", "Slack access to Hermes. Collaboration Hub user") Person(kids, "Kids", "Family Identity users. Google Drive / Photos")
Enterprise_Boundary(estate, "Personal Digital Estate") { System(hermes, "Hermes Agent", "Self-hosted AI assistant. Slack + web dashboard. Reads/writes knowledge, tracks projects, drafts content") System(sites, "Static Sites", "6 websites on Cloudflare Pages: notes, strata, locationyeu, chateaudebourgon, sommelier-arena, docs") }
System_Ext(google, "Google", "Drive (Knowledge Vault SSOT), Photos, Docs, Sheets") System_Ext(cloudflare, "Cloudflare", "DNS, CDN, Pages hosting, Tunnel, Access (email OTP)") System_Ext(github, "GitHub", "Private repos: site source code, architecture wiki") System_Ext(openrouter, "OpenRouter", "LLM provider: free models default, premium on-demand") System_Ext(hostinger, "Hostinger", "VPS KVM2: Agent Host") System_Ext(planethoster, "PlanetHoster", "Domain Registrar") System_Ext(bitwarden, "Bitwarden", "Credential Vault: passwords, API keys, SSH keys") System_Ext(decathlon, "Decathlon (Professional Boundary)", "Company SSO, pro Google Drive/GitHub; no Agent Host token day one") System_Ext(slack, "Slack", "Messaging gateway for Hermes")
Rel(francois, hermes, "Chats via Slack & web dashboard") Rel(francois, sites, "Manages & publishes content") Rel(francois, google, "Reads/writes docs, .md notes, photos") Rel(djuly, hermes, "Chats via Slack") Rel(djuly, google, "Collaboration Hub: shared docs") Rel(kids, google, "Family storage & photos")
Rel(hermes, google, "Reads/writes Knowledge Vault via Drive Bridge") Rel(hermes, openrouter, "LLM inference (free + premium models)") Rel(hermes, github, "Reads/writes code (with approval) via MCP") Rel(sites, cloudflare, "Deployed & served via Cloudflare Pages") Rel(sites, github, "Built from private repos (CI/CD)") Rel(hermes, hostinger, "Runs on VPS KVM2") Rel(hermes, slack, "Gateway: receives/sends messages") Rel(cloudflare, planethoster, "DNS delegation from registrar") Rel(hermes, cloudflare, "Dashboard exposed via Tunnel + Access") Rel(francois, decathlon, "Read-only Professional Reference Material") Rel(francois, bitwarden, "All credentials (all devices)") Rel(hermes, bitwarden, "Reads API keys & secrets")C4 Level 2 — Container
Section titled “C4 Level 2 — Container”C4Container title Personal Digital Estate — Container Diagram (C4 Level 2)
Person(francois, "François", "M1Max / Pixel 9A / M3Pro (read-only)") Person(djuly, "Djuly", "Slack")
System_Ext(google_drive, "Google Drive", "Knowledge Vault SSOT") System_Ext(google_photos, "Google Photos", "Family photos (outside agent scope)") System_Ext(openrouter, "OpenRouter", "Free + premium LLMs") System_Ext(slack_api, "Slack API", "Bot gateway") System_Ext(github_api, "GitHub", "Private repos + MCP") System_Ext(decathlon_sources, "Decathlon sources", "Professional Reference Material; no Agent Host token day one")
Container_Boundary(vps, "Agent Host (Hostinger VPS KVM2)") { Container(hermes_core, "Hermes Agent", "Python, NousResearch/hermes-agent", "AI assistant: memory, skills, learning loop, cron scheduler") Container(hermes_gateway, "Hermes Gateway", "Python", "Multi-platform messaging: Slack, web dashboard") Container(drive_bridge, "Drive Bridge", "rclone FUSE mount", "Mounts Google Drive as /mnt/gdrive for local filesystem access") Container(hermes_memory, "Hermes Memory", "SQLite + FTS5", "Persistent memory, session search, user model") Container(hermes_skills, "Hermes Skills", "YAML + Python", "Learned skills from experience, self-improving") ContainerDb(hermes_state, "Hermes State", "Config + data", "~/.hermes/ — config.yaml, .env, SOUL.md, cron jobs") }
Container_Boundary(cloudflare_b, "Cloudflare") { Container(cf_pages, "Cloudflare Pages", "Static hosting", "6 sites: notes, strata, locationyeu, chateaudebourgon, sommelier-arena, docs") Container(cf_tunnel, "Cloudflare Tunnel", "cloudflared", "Zero-trust tunnel to VPS, no open ports") Container(cf_access, "Cloudflare Access", "Email OTP", "Auth for hermes.ducatillon.net + docs.ducatillon.net") Container(cf_dns, "Cloudflare DNS", "DNS + CDN + SSL", "All domains: *.ducatillon.net, locationyeu.com, chateaudebourgon.com") }
Container_Boundary(devices, "Devices") { Container(mac_perso, "MAC PERSO M1Max", "macOS", "VSCode (.md editing), Google Drive Desktop, primary dev machine") Container(pixel, "Pixel 9A", "Android", "Markor (.md editing), Slack (Hermes), Google Drive, Aegis (2FA target)") Container(mac_pro, "Decathlon M3Pro", "macOS (managed)", "Google Drive web + Slack only. No direct vault access") }
Container_Boundary(backup_b, "Backup") { ContainerDb(ext_hd, "External HD", "Weekly offline backup", "Google Photos export, Knowledge Vault snapshot, encrypted Hermes archive, Bitwarden export") ContainerDb(github_backup, "Private GitHub backup repo", "Daily encrypted backup", "Hermes state archives encrypted with age") }
Rel(francois, hermes_gateway, "Slack + web dashboard (hermes.ducatillon.net)") Rel(djuly, hermes_gateway, "Slack") Rel(hermes_gateway, hermes_core, "Routes messages to agent") Rel(hermes_core, drive_bridge, "Reads/writes .md notes via /mnt/gdrive") Rel(drive_bridge, google_drive, "rclone: Google Drive API with local caching") Rel(hermes_core, openrouter, "LLM inference (model routing: free default, premium on-demand)") Rel(hermes_core, hermes_memory, "Persists knowledge across sessions") Rel(hermes_core, hermes_skills, "Creates and improves skills from experience") Rel(hermes_core, github_api, "Reads/writes code (with approval) via MCP") Rel(hermes_gateway, slack_api, "Sends/receives messages") Rel(hermes_gateway, cf_tunnel, "Dashboard exposed via tunnel") Rel(cf_tunnel, cf_access, "Auth gate: email OTP") Rel(cf_pages, github_api, "Auto-deploy on push") Rel(cf_dns, cf_pages, "Routes *.ducatillon.net to Pages")
Rel(mac_perso, google_drive, "Google Drive Desktop sync") Rel(mac_perso, github_api, "Git push (site source, architecture repo)") Rel(pixel, google_drive, "Google Drive app + Markor") Rel(pixel, slack_api, "Hermes via Slack") Rel(mac_pro, google_drive, "Google Drive web (read-only personal files)") Rel(mac_pro, slack_api, "Hermes via Slack desktop") Rel(mac_pro, decathlon_sources, "Professional work sources")
Rel(hermes_core, github_backup, "Daily encrypted state backup via systemd timer") Rel(github_backup, ext_hd, "Weekly offline copy") Rel(francois, ext_hd, "Manual weekly backup: photos, vault, Bitwarden")