Skip to content

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.

#TopicWhere documented
1Interaction channelsHow Francois talks to the estate
2CLI workflowInteraction channels β€” Work vs Home vs Hermes contexts
3Delivery modesDelivery workflow β€” branch-review / autonomous / auto-commit
4GitHub authQuick reference β€” fine-grained PAT, scoped per-repo
5PAT expirySOUL.md cron table β€” weekly check, Telegram alert at 7 days
6Security modelADR-0007 β€” plaintext .env + OS hardening
7Drive mountsSSOT split + CONTEXT.md glossary β€” 3 rclone mounts
8LLM routingSOUL.md LLM routing β€” local / free / premium tiers
9VPS agnosticismADR-0008 β€” DRP, daily encrypted GitHub backup + weekly offline copy, 60-min rebuild
10Dashboard accessADR-0006 + Network topology β€” CF Tunnel + Access email OTP
11Secrets managementADR-0007 β€” threat model, upgrade triggers
12Disaster recoveryADR-0008 β€” RTO/RPO, encrypted backup/decrypt procedure
13Hermes updatesSOUL.md cron table β€” notify-only, manual upgrade
14Monitoring & healthCONTEXT.md glossary + SOUL.md cron table β€” Uptime Kuma at health.ducatillon.net
flowchart TB
subgraph people ["πŸ‘₯ People"]
F["πŸ§‘β€πŸ’» Francois\nM1Max Β· Pixel 9A Β· M3Pro"]
D["πŸ‘© Djuly\nPhone"]
K["πŸ‘§πŸ‘¦ Kids\nPhones/tablets"]
end
subgraph perso ["🏠 Personal Digital Estate"]
subgraph telegram_block ["πŸ’¬ Telegram"]
TG["Telegram 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
%% Francois β†’ 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:#e65100

How the boundary works: Francois 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 Francois’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.

flowchart LR
subgraph inputs ["πŸ“₯ Inputs"]
TG["πŸ’¬ Telegram\nFrancois + Djuly"]
WEB["🌐 Web Dashboard\nhermes.ducatillon.net\nport 9119"]
end
subgraph hermes ["πŸ€– Hermes Agent (VPS)"]
GW["πŸšͺ Gateway\nhermes-gateway systemd\nRoutes: Telegram Β· 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" .-> HD
flowchart LR
subgraph home ["🏠 Home / Mobile"]
BROWSER["🌐 Browser\n(any device)"]
TG_APP["πŸ’¬ Telegram 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 telegram_cloud ["πŸ’¬ Telegram Cloud"]
TG_API["Telegram 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)\nTelegram 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
%% Telegram flow
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:#2e7d32

Key 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.net and docs.ducatillon.net
  • Telegram 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
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 Β· Telegram\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 Β· Telegram 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 -- "Telegram" --> H
PX -- "web browser:\nsites Β· docs" --> cloudflare
%% Decathlon M3Pro connections (crosses boundary)
M3 -- "Telegram 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_BACKUP
LocationWhat’s thereAccessed by
Google DrivePARA documents: .md notes, .gdocs, .gsheets (NO code projects)Francois (all devices), Hermes via day-one Personal Drive Bridge. Djuly/pro mounts are roadmap only.
GitHub (perso)Code projects + architecture wiki (fducat18, SSH)Francois (git from M1Max), Hermes via HERMES_GITHUB_CODE_PAT (selected repos only; branch-review), Cloudflare Pages
GitHub (pro)Decathlon org repos (francoiducat, SSH)Francois only (git from M3Pro). Hermes has no pro GitHub token on the Agent Host; it can reason over approved Professional Reference Material only.
Hostinger VPSHermes Agent (hermes-gateway systemd), Dashboard (port 9119), 9router (port 3000/80), Drive Bridge (rclone), Uptime Kuma (health.ducatillon.net β€” estate-critical monitoring)Francois (SSH + dashboard + health page), Djuly (Telegram)
Cloudflare6 websites, DNS, CDN, Tunnel, Access authPublic (sites), Francois (dashboard + docs)
BitwardenAll passwords, API keys, SSH keysFrancois (all devices), Hermes (reads API keys)
External HDWeekly backups: photos, vault, Hermes state, BitwardenFrancois (manual)
Private GitHub backup repoDaily encrypted Hermes state archives only; no plaintext secretsAgent Host (systemd timer with HERMES_BACKUP_GITHUB_PAT scoped only to this repo), Francois (decrypts with private key from Bitwarden)
Google PhotosFamily photos (outside Hermes scope)Francois, Djuly, Kids

Interaction channels β€” How Francois talks to the estate

Section titled β€œInteraction channels β€” How Francois talks to the estate”
ContextToolWhere it runsWhat it accessesWhen
Work (Decathlon M3Pro)Copilot CLILocally on M3ProLocal files, Pro GitHubCoding during work hours. Hermes not involved.
Home β€” codingPi CLILocally on M1MaxLocal files, Personal GitHubPersonal project coding. Hermes optional.
Home β€” estate work (preferred)SSH β†’ hermes --tuiOn VPSFull context: SOUL, memory, Drive Bridge, skills, sessionsThe primary channel for estate work. Full Hermes brain.
Mobile / asyncTelegramPhone / desktop appHermes on VPSQuick questions, triage, reminders β€” even while coding locally in another terminal.
Visual / configWeb dashboardBrowser β†’ VPS (via CF Tunnel)Same as Hermes + visual UIAnalytics, logs, cron review, session history, config.

Key insight: Telegram is the glue channel. When coding locally (Copilot CLI or Pi CLI), Francois can ask Hermes a quick question via Telegram without leaving his terminal workflow. No SSH needed for simple lookups β€” just open Telegram in another window or on his phone.

Which channel for which need?

NeedBest channelWhy
Quick question while coding locallyTelegramZero friction, async, any device
Deep estate work sessionSSH β†’ hermes --tuiFull TUI, long conversation, complex tasks
Review analytics / logs / configWeb dashboardVisual, charts, bulk config
”Fix this bug on repo X” (autonomous)TelegramFire-and-forget β€” Hermes clones, fixes, pushes branch, CI validates. See autonomous delivery below.
Content typeSSOTWhyHermes access
Documents, notes, .gdoc, .gsheet, small scriptsGoogle Drive (PARA)Lightweight, syncs to all devicesDrive Bridge (rclone)
Projects with heavy deps (node_modules, venv, dist/)GitHubBuild artifacts would kill Drive syncGitHub MCP (read/write with approval)
Graduation triggerFirst npm install or python -m venvThat’s when thousands of files appearβ€”
Google Drive PARA ↔ Code project linkREADME in PARA folderPoints to GitHub repo URLReads both sides
flowchart LR
F["πŸ§‘β€πŸ’» Francois\n'Fix the date bug\non locationyeu'"]
H["πŸ€– Hermes Agent"]
GH["πŸ™ GitHub\n(fducat18)"]
TG["πŸ’¬ Telegram\nnotification"]
F -- "1. request via\nTelegram / 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. Francois reviews\nmerges or requests changes" --> GH

The flow:

  1. Francois asks Hermes for a script, feature, or fix (Telegram, SSH CLI, or dashboard)
  2. Hermes writes the code on the VPS
  3. Hermes pushes to a branch on GitHub and creates a PR
  4. Hermes notifies Francois via Telegram: β€βœ… PR ready β€” [link]. Summary: fixed date format in locationyeu. CI status: passing.”
  5. Francois reviews when convenient (GitHub web, or git pull locally)
  6. Francois merges (or asks Hermes to revise via Telegram)

What Hermes can NOT do autonomously: push to main, delete branches, force-push, create/delete repos. All destructive actions require explicit approval.

For repos with sufficient guardrails, Francois can ask Hermes via Telegram to fix a bug in full autonomy:

flowchart LR
F["πŸ§‘β€πŸ’» Francois\nTelegram: '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 Francois" --> F

Prerequisites 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
  • βœ… Francois 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.

ScenarioWhere Hermes writesHow Francois reviews
Quick script (one-off utility)New file in existing repo branchGitHub web or git pull
Feature on existing projectBranch on the project’s repogit pull + local testing
New project bootstrapCreates new repo (with approval)git clone locally
Config/automation (VPS)Directly on VPS filesystemReviews via Telegram/dashboard before applying

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.

C4Context
title Personal Digital Estate β€” System Context (C4 Level 1)
Person(francois, "Francois", "Estate owner. MAC PERSO M1Max, Pixel 9A, Decathlon M3Pro (read-only)")
Person(djuly, "Djuly (wife)", "Telegram 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. Telegram + 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(telegram, "Telegram", "Messaging gateway for Hermes")
Rel(francois, hermes, "Chats via Telegram & web dashboard")
Rel(francois, sites, "Manages & publishes content")
Rel(francois, google, "Reads/writes docs, .md notes, photos")
Rel(djuly, hermes, "Chats via Telegram")
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, telegram, "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")
C4Container
title Personal Digital Estate β€” Container Diagram (C4 Level 2)
Person(francois, "Francois", "M1Max / Pixel 9A / M3Pro (read-only)")
Person(djuly, "Djuly", "Telegram")
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(telegram_api, "Telegram 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: Telegram, 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), Telegram (Hermes), Google Drive, Aegis (2FA target)")
Container(mac_pro, "Decathlon M3Pro", "macOS (managed)", "Google Drive web + Telegram 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, "Telegram + web dashboard (hermes.ducatillon.net)")
Rel(djuly, hermes_gateway, "Telegram")
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, telegram_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, telegram_api, "Hermes via Telegram")
Rel(mac_pro, google_drive, "Google Drive web (read-only personal files)")
Rel(mac_pro, telegram_api, "Hermes via Telegram 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")