ProDG Mainframe — Consolidated Infrastructure Plan

Version: 2.0
Date: 2026-05-08
Owner: Mitch Ngugi, CEO ProDG Studio
Domain: prodg.studio


1. Executive Summary

This document consolidates all infrastructure planning from the original Option D migration through Phase 9 hardening, incorporating new decisions around:

  • Tavily as the web search API for agent research workloads
  • Infisical (self-hosted) as the single source of truth for all secrets
  • Vaultwarden (self-hosted) as the team password manager
  • Backblaze B2 as the offsite backup target
  • Tailscale as the secure networking layer for API routing
  • Modal for burst/untrusted inference workloads
  • SSH key-based access (Mainframe ed25519 key for Mitch)

Trust Model

Workload TypeLocationContainer RuntimeNetwork
Trusted agents (research, infra)On-box (mainframe)Docker containersTailscale + Caddy
Untrusted code / burst inferenceRemote Modal nodesModal sandboxTailscale egress

2. Architecture Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                          ProDG Mainframe                                 │
│  (Bare metal — Ubuntu 22.04, Docker, Tailscale)                       │
├─────────────────────────────────────────────────────────────────────────┤
│  Public Edge                                                            │
│  ┌─────────┐  ┌──────────┐  ┌────────────────┐  ┌─────────────────┐  │
│  │ Caddy   │  │Headscale │  │  Infisical     │  │  Vaultwarden    │  │
│  │:80,:443 │  │:8080     │  │  :8082         │  │  :8083          │  │
│  └────┬────┘  └──────────┘  └────────────────┘  └─────────────────┘  │
│       │                                                                 │
│  Internal Services (prodg-internal network)                            │
│  ┌──────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ ┌─────────────┐  │
│  │ Postgres │ │ Redis   │ │ MinIO   │ │ Grafana  │ │ Prometheus  │  │
│  │ :5432    │ │ :6379   │ │ :9000   │ │ :3000    │ │ :9090       │  │
│  └──────────┘ └─────────┘ └─────────┘ └──────────┘ └─────────────┘  │
│  ┌──────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐                   │
│  │ Loki     │ │Promtail │ │Node Exp │ │ cAdvisor  │                   │
│  │ :3100    │ │         │ │         │ │           │                   │
│  └──────────┘ └─────────┘ └─────────┘ └──────────┘                   │
│  ┌──────────┐                                                          │
│  │ Hermes   │ ← Tavily API (research agents)                          │
│  │ API      │ ← Backblaze B2 (offsite backups)                        │
│  │ :8000    │ ← Tailscale API routing                                 │
│  └──────────┘                                                          │
├─────────────────────────────────────────────────────────────────────────┤
│  Remote / Burst                                                         │
│  ┌────────────────────┐                                                │
│  │ Modal (Serverless) │ ← Untrusted code, burst inference              │
│  │  GPU/CPU workloads │                                                │
│  └────────────────────┘                                                │
└─────────────────────────────────────────────────────────────────────────┘

3. Service Inventory

3.1 Public-Facing Services

ServicePortDomainPurposeAuth
Caddy80, 443*.prodg.studioReverse proxy, TLS terminationLet’s Encrypt
Headscale8080Tailscale control serverPre-auth keys
Infisical8082secrets.mainframe.prodg.studioSecret managementService tokens
Vaultwarden8083vault.mainframe.prodg.studioPassword vaultMaster password

3.2 Internal Services

ServicePortImageUser (UID)Data Dir
Postgres5432postgres:16-alpine70:70/opt/prodg/data/postgres
Redis6379redis:7-alpine1000:1000/opt/prodg/data/redis
MinIO9000minio/minio:RELEASE.2026...1000:1000/opt/prodg/data/minio
Grafana3000grafana/grafana:11.6.0/opt/prodg/data/grafana
Prometheus9090prom/prometheus:v3.3.1/opt/prodg/data/prometheus
Loki3100grafana/loki:3.5.0/opt/prodg/data/loki
Promtailgrafana/promtail:3.5.0
Node Exporter9100prom/node-exporter:v1.9.1
cAdvisor8085gcr.io/cadvisor:v0.52.1

3.3 Application Services

ServicePortImageUser (UID)Secrets Source
Hermes API8000prodg/hermes-api:v0.7.11000:1000 (prodg)Infisical → .env
Infisical8082infisical/infisical:v0.159.271000:1000 (node)Self-managed

4. Security Model

4.1 Secrets Architecture

Single Source of Truth: Infisical (self-hosted)

  • Project: eb7f744a-bcd3-4ef6-b5e4-8e50a906bb92
  • Environment: prod
  • Domain: https://secrets.mainframe.prodg.studio
  • Access: Service token (st.ba814a0f...) with read,write scope on / path

Secrets in Infisical (18 total):

SecretTypeConsumers
POSTGRES_PASSWORDConnectionPostgres, Hermes API, Vaultwarden
POSTGRES_USERConnectionPostgres
POSTGRES_DBConnectionPostgres
REDIS_PASSWORDConnectionRedis, Hermes API
MINIO_ROOT_USERCredentialMinIO
MINIO_ROOT_PASSWORDCredentialMinIO, Hermes API
INFISICAL_AUTH_SECRETAuthInfisical
INFISICAL_JWT_SECRETAuthInfisical
INFISICAL_ENCRYPTION_KEYEncryptionInfisical
VAULTWARDEN_ADMIN_TOKENAdminVaultwarden
GRAFANA_ADMIN_PASSWORDAdminGrafana
HERMES_API_TOKENAuthHermes API
TELEGRAM_BOT_TOKENIntegrationBackup scripts, alerts
TELEGRAM_CHAT_IDIntegrationBackup scripts, alerts
B2_KEY_IDCloudBackup scripts
B2_KEY_SECRETCloudBackup scripts
B2_BUCKETCloudBackup scripts
TAVILY_API_KEYAPIHermes API (research agents)

4.2 Access Control

ComponentMethodIdentity
Host SSHed25519 keyMitch (Mainframe key)
TailscaleHeadscale + pre-authNode key registration
InfisicalService tokenMachine identity
VaultwardenMaster passwordMitch (admin)
GrafanaBasic authadmin + GRAFANA_ADMIN_PASSWORD
Hermes APIBearer tokenHERMES_API_TOKEN

4.3 Service User Model

UIDUserServicesRationale
0rootCaddy, Grafana, Prometheus, Loki, Promtail, Node Exporter, cAdvisor, Headscale, VaultwardenRequires root or not yet migrated
70postgresPostgresOfficial image hardcoded UID
1000prodgRedis, MinIO, Infisical, Hermes APIShared non-root for data access

Note: Vaultwarden was reverted to root (UID 0) during Phase 9 due to a Diesel auth loop caused by a stale DATABASE_URL. Future hardening can retry user: "1000:1000" after secrets are stable.


5. Networking

5.1 Docker Networks

NetworkDriverServices
prodg-internalbridgeAll services

5.2 Tailscale Integration

Headscale Control Server

  • Port: 8080 (internal), proxied via Caddy
  • Namespace: prodgroup
  • Users: Mitch (admin keys)

Hermes API Routing

  • API exposed via Tailscale for agent workloads
  • Agents on remote nodes connect through Tailscale tunnel
  • Modal workers can egress through Tailscale to reach api.mainframe.prodg.studio

5.3 Caddy Routing

*.mainframe.prodg.studio {
    # Vaultwarden
    vault.mainframe.prodg.studio → vaultwarden:8083
    
    # Infisical
    secrets.mainframe.prodg.studio → infisical:8082
    
    # Hermes API
    api.mainframe.prodg.studio → hermes-api:8000
    
    # Headscale
    ts.mainframe.prodg.studio → headscale:8080
    
    # Docs (Quartz)
    docs.mainframe.prodg.studio → file server /data/docs/public
}

6. Storage & Backup

6.1 On-Box Storage

PathContentsSizeOwner
/opt/prodg/data/postgresPostgreSQL data70:70
/opt/prodg/data/redisRedis persistence1000:1000
/opt/prodg/data/minioS3-compatible object store1000:1000
/opt/prodg/data/vaultwardenVaultwarden SQLite + attachments
/opt/prodg/data/infisicalInfisical encryption keys1000:1000
/opt/prodg/data/grafanaGrafana dashboards & DB
/opt/prodg/data/lokiLoki log storage
/opt/prodg/quartzQuartz knowledge base source
/opt/prodg/docsObsidian vault + generated site

6.2 Backup Strategy

Tool: rclone → Backblaze B2

ScheduleContentsDestination
DailyPostgres dump, MinIO, Vaultwardenb2:MainframeBackup/daily/
WeeklyFull data directoryb2:MainframeBackup/weekly/
On-demandPre-migration snapshots/opt/prodg/backups/ (local)

Credential Source: Infisical → .envbackup-all.sh

  • No hardcoded credentials in scripts
  • B2 keys loaded from environment at runtime

7. Agent Workloads

7.1 Trusted Agents (On-Box)

Runtime: Docker containers on mainframe Network: prodg-internal + Tailscale Examples:

  • Research agents (Hermes API → Tavily)
  • Infrastructure automation (Docker socket access)
  • Log analysis (Loki queries)

Characteristics:

  • Long-running or cron-scheduled
  • Access to internal services (Postgres, Redis, MinIO)
  • Run as prodg UID 1000 where possible

7.2 Untrusted / Burst Agents (Remote)

Runtime: Modal serverless containers Network: Tailscale egress to mainframe Examples:

  • GPU inference workloads
  • Third-party code execution
  • High-compute research tasks

Characteristics:

  • Ephemeral (spin up ↔ spin down)
  • No direct access to internal Docker network
  • Authenticate to Hermes API via HERMES_API_TOKEN over Tailscale
  • Results written to MinIO or returned via API

7.3 Hermes API Integration

# Remote agent (Modal) calling trusted API
import modal
 
app = modal.App("prodg-agent")
 
@app.function(secrets=[modal.Secret.from_name("prodg-api-token")])
def research_task(query: str):
    # Connects via Tailscale to api.mainframe.prodg.studio
    response = requests.post(
        "https://api.mainframe.prodg.studio/v1/research",
        headers={"Authorization": f"Bearer {os.environ['HERMES_API_TOKEN']}"},
        json={"query": query, "search_provider": "tavily"}
    )
    return response.json()

8. Operational Runbooks

8.1 Regenerate .env from Infisical

/opt/prodg/scripts/load-secrets.sh
# Then restart services that need new env:
cd /opt/prodg/compose && docker compose up -d

8.2 Add a New Secret

  1. Add to Infisical:
    docker exec -e INFISICAL_TOKEN=$INFISICAL_TOKEN infisical \
      infisical --domain=https://secrets.mainframe.prodg.studio/api \
      secrets set --token=$INFISICAL_TOKEN --env=prod \
      --projectId=eb7f744a-bcd3-4ef6-b5e4-8e50a906bb92 \
      NEW_SECRET="value"
  2. Regenerate .env:
    /opt/prodg/scripts/load-secrets.sh
  3. Restart affected services

8.3 Rotate a Service Token

  1. Generate new token in Infisical UI
  2. Update /opt/prodg/compose/.env.infisical
  3. Run load-secrets.sh
  4. Revoke old token in Infisical UI

8.4 Vaultwarden Admin Access

https://vault.mainframe.prodg.studio/admin
Token: (from Infisical → VAULTWARDEN_ADMIN_TOKEN)

8.5 SSH Access (Mitch)

ssh -i ~/.ssh/mainframe mitch@mainframe.prodg.studio
# Authenticates via ed25519 key: AAAAC3NzaC1lZDI1NTE5AAAAIKv5Y45VNS1nb2fwrmVsIe5gcPurhPoIHtXJh4gHgVSB

9. Phase Status

PhaseStatusKey Deliverables
Phase 1: Foundation✅ CompleteDocker, networks, base compose
Phase 2: Observability✅ CompleteGrafana, Prometheus, Loki stack
Phase 3: Headscale✅ CompleteTailscale control plane
Phase 4: Storage✅ CompleteMinIO, Postgres, Redis
Phase 5: Secrets/Auth✅ CompleteInfisical, Vaultwarden deployed
Phase 6: Hermes API✅ CompleteAPI server with Tavily integration
Phase 7: Documentation✅ CompleteQuartz site, Obsidian vault
Phase 8: Backups✅ CompleteRclone → B2, automated scripts
Phase 9: Hardening🔄 17/18 doneSee below

Phase 9 Remaining

TaskStatusNotes
.env backup✅ Done/opt/prodg/backups/dotenv-20260429-132041.bak
Data dir permissions✅ DoneAll dirs owned by correct UIDs
Redis → UID 1000✅ DoneHealthy
MinIO → UID 1000✅ DoneHealthy
Infisical → UID 1000✅ DoneHealthy
Hermes API → UID 1000✅ DoneRebuilt v0.7.1
Backup scripts → no hardcoded✅ DoneSources from .env
Service token created✅ Donest.ba814...
Secrets migrated to Infisical✅ DoneAll 18 secrets pushed
load-secrets.sh created✅ DoneUses container CLI, self-hosted domain
.env regenerated from Infisical✅ DoneValidated, compose config passes
Vaultwarden root revert✅ DoneSQLite fallback after DB auth loop
Infisical CLI on hostN/AUsing container CLI instead
Remove legacy .env⏳ PendingAwaiting decision on caching strategy
Quartz cron re-enable⏳ PendingUser requested pause during changes

10. Decisions Log

DateDecisionRationale
2026-04-27Option D selected (self-hosted)Sovereignty over cloud dependencies
2026-04-27Obsidian + Quartz for docsMarkdown-native, git-tracked, static site
2026-04-28Tavily for web searchBetter citation quality than Serper/Brave for agents
2026-04-28Backblaze B2 for offsiteCost-effective S3-compatible cold storage
2026-04-29Infisical for secretsSelf-hosted, Docker-native, service token auth
2026-04-29Vaultwarden for passwordsBitwarden-compatible, lightweight, self-hosted
2026-04-29prodg UID 1000 for servicesShared non-root identity for cross-service data access
2026-05-08Postgres stays UID 70Official image hardcodes UID; changing breaks DB
2026-05-08Vaultwarden reverted to rootDiesel auth loop with stale DATABASE_URL in config.json
2026-05-08Remove DATABASE_URL from composeAllowed Vaultwarden to start with built-in SQLite
2026-05-08Use container CLI for InfisicalHost CLI install failed (404/no npm package); container has v0.41.89
2026-05-08.env as transient cacheDocker Compose requires .env file for variable interpolation; regenerated from Infisical via script

11. Service Account Reference

Mitch (You)

  • Role: CEO, ProDG Studio
  • SSH Key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKv5Y45VNS1nb2fwrmVsIe5gcPurhPoIHtXJh4gHgVSB Mainframe
  • Added to mainframe: Yes
  • Infisical account: mitch@prodg.studio

Service Tokens

TokenScopePermissionsExpires
st.ba814a0f...prodg-mainframe-core prod /read, write2026-05-09

⚠️ Note: Service token expires 2026-05-09. Plan rotation before expiry.


12. Quick Reference

File Paths

FilePurpose
/opt/prodg/compose/docker-compose.ymlStack definition
/opt/prodg/compose/.envTRANSIENT — regenerated from Infisical
/opt/prodg/compose/.env.infisicalGOLD — service token only, mode 600
/opt/prodg/scripts/load-secrets.shRegenerate .env from Infisical
/opt/prodg/backups/scripts/backup-all.shDaily backups to B2
/opt/prodg/docs/index.mdWiki homepage
/opt/prodg/docs/05-phase-9-plan.mdPhase 9 detailed plan

One-Liners

# Stack status
docker compose -f /opt/prodg/compose/docker-compose.yml ps
 
# Regenerate env from Infisical
/opt/prodg/scripts/load-secrets.sh
 
# View Infisical secrets (from container)
docker exec -e INFISICAL_TOKEN=$(cat /opt/prodg/compose/.env.infisical | cut -d= -f2) \
  infisical infisical --domain=https://secrets.mainframe.prodg.studio/api \
  export --token=$INFISICAL_TOKEN --env=prod \
  --projectId=eb7f744a-bcd3-4ef6-b5e4-8e50a906bb92
 
# Manual backup
/opt/prodg/backups/scripts/backup-all.sh
 
# Check Tailscale status
docker exec headscale headscale nodes list

Document generated by Hermes Agent. Last updated: 2026-05-08.
For updates, edit /opt/prodg/docs/plan.md and run the Quartz rebuild job.