Overview

Project Overview

A private, self-maintaining knowledge system. A locked-down GCP box runs the Firney MCP broker so claude.ai can reach Notion; a git-markdown vault is the single source of truth for a hybrid wiki that auto-publishes to both a polished, login-gated website and a Notion mirror. No public inbound ports anywhere; every surface is authenticated.

type overviewstatus activeoverview · architecture · security · gcp

The system at a glance

Three planes — clients, the broker (on GCP), and the knowledge base (vault → site + Notion). Everything reaches the box over outbound channels, so the firewall stays shut.

flowchart LR
  subgraph Clients
    CA["claude.ai (cloud)"]
    CC["Claude Code (your Mac)"]
  end
  EDGE["Cloudflare edge"]
  subgraph GCP["GCP e2-small · London"]
    FW{{"deny-all-ingress firewall"}}
    CFD["cloudflared"] --> BRK["Firney broker :8002 (Docker)"]
    TS["tailscaled"]
  end
  NOTION[("Notion workspace")]
  MAC["Your Mac (admin)"]
  CA -- "HTTPS → /proxy/notion_api" --> EDGE
  EDGE -- "outbound tunnel" --> CFD
  CC -. "header-auth" .-> EDGE
  BRK -- "OAuth (stored token)" --> NOTION
  MAC -- "Tailscale mesh (SSH/Ansible)" --> TS

Components

Piece What it is Where it runs
Firney MCP broker OAuth token broker + reverse proxy for remote MCP; exposes Notion tools GCP box, Docker, port 8002
Tailscale WireGuard mesh for all admin/SSH/Ansible — no public SSH GCP box + your Mac
Cloudflare-Tunnel Outbound tunnel publishing the broker at bobsmcp.uk — no open ports GCP box (cloudflared)
Notion connector The broker's stored Notion OAuth token; proxies MCP calls to Notion broker → Notion API
The wiki vault Git-markdown knowledge base — the source of truth your Mac / BobMck/Obsidian-Docs
The static site build_site.py renders the vault → styled HTML Cloudflare edge, docs.bobsmcp.uk
Cloudflare Access Login gate (email one-time-PIN) in front of the docs site Cloudflare edge

Security model

The guiding rule: the GCP box has zero public inbound ports. Reachability is always outbound-initiated.

  • Firewall — a Terraform-managed deny-all-ingress rule at priority 100 overrides GCP's default permissive rules. The public IP is kept for egress only.
  • Admin — only over the Tailscale mesh (ssh ubuntu@gcp-mcp-broker). Tailscale SSH was disabled in favour of plain sshd + key so automation works.
  • Public broker traffic — arrives via the Cloudflare Tunnel (outbound), never an open port. Gated by the broker's inbound OAuth 2.1 with an exact-match redirect-URI allow-list.
  • Docs site — static files on Cloudflare's edge (no server), gated by Cloudflare Access; the public *.workers.dev URL is disabled so the Access-gated domain is the only way in.
  • Secrets — never committed: terraform.tfvars (Tailscale key), broker .env, and the broker key (${NOTION_BROKER_KEY} env var) are all git-ignored.

Access & authentication

Every surface is authenticated, each in the way that fits it:

Surface Address Auth Who gets in
Broker (from claude.ai) bobsmcp.uk/proxy/notion_api OAuth 2.1 (dynamic client registration) allow-listed redirect URIs
Broker (from Claude Code) same header X-Broker-Key + X-App-Id holder of the br_ key
Admin / SSH / Ansible gcp-mcp-broker (100.x) Tailscale (WireGuard) your tailnet
Docs website docs.bobsmcp.uk Cloudflare Access (email OTP) allow-listed email
Notion mirror your Notion workspace Notion sign-in workspace members

Hosting

  • GCPe2-small, Ubuntu 22.04, europe-west2 (London). Runs the broker, cloudflared, tailscaled.
  • Cloudflare edge — terminates the tunnel (bobsmcp.uk) and serves the static site (docs.bobsmcp.uk) from Workers static assets. TLS + CDN handled for free.
  • Notion — managed SaaS; the read-mirror.
  • Your Mac — Claude Code compiles the wiki; Obsidian reads it.
  • Domainsbobsmcp.uk is a dedicated domain on Cloudflare Registrar; robmck.uk stays untouched in AWS Route 53.

Deployment

Two independent pipelines — the broker (rarely) and the site (on every push).

flowchart TB
  subgraph BrokerPipe["Broker — on changes"]
    TF["terraform apply"] --> VM["GCP instance + deny-ingress firewall"]
    VM --> BOOT["startup script joins Tailscale"]
    BOOT --> ANS["ansible-playbook install.yml"]
    ANS --> RUN["broker + cloudflared running"]
  end
  subgraph SitePipe["Site — every commit"]
    PUSH["git push"] --> CFBUILD["Cloudflare build: build_site.py"]
    CFBUILD --> DEP["deploy → docs.bobsmcp.uk (Access)"]
  end

Git strategy

  • ~/Terraform is a plain folder, not a repo. Each project subfolder is its own git repo.
  • The vault repo BobMck/Obsidian-Docs is the single source of truth. Markdown is canonical; the HTML site and the Notion mirror are derived outputs.
  • Generated artifacts are never committedpublic/ (built site) and .venv are git-ignored; the site is rebuilt fresh in CI on every push.
  • Secrets are never committedterraform.tfvars, .env, vault .env, and the broker key live outside git (env vars / gitignored files).
  • Push is the triggergit push → Cloudflare builds and deploys the site automatically.

The knowledge base

A hybrid LLM-Wiki (Model-Context-Protocol-adjacent in spirit): compile once at ingest, store the artifact, never re-derive.

flowchart LR
  RAW["sources/ (raw, human)"] --> ING["/wiki-ingest"]
  ING --> WIKI["wiki/*.md (compiled)"]
  WIKI --> GIT["git: BobMck/Obsidian-Docs"]
  GIT --> SITE["docs.bobsmcp.uk"]
  WIKI -- "/wiki-publish" --> NOTION[("Notion mirror")]
  OBS["Obsidian (read)"] --- WIKI
  • Layerssources/ (raw inputs you own), wiki/ (model-generated), CLAUDE.md (the schema: templates, conventions, lint rules).
  • Taxonomystudy/ (cloud · hashicorp · ai-ml · ansible · linux · claude-course), concepts/ (cross-cutting reference), projects/ (per-project README/HLD/LLD/ADR/runbook).
  • Skills/wiki-ingest (source → notes), /wiki-query (answer from the wiki with citations), /wiki-lint (health-check), /wiki-publish (push to Notion).
  • Two derived views — this website (rich, rendered Mermaid, login-gated) and the Notion mirror (read anywhere).

Day-to-day

  1. Drop a source in sources/ or run /wiki-ingest <source> → it files notes into the right study/<area> (or a project), updates the index + log.
  2. git push → the site rebuilds and deploys to docs.bobsmcp.uk automatically.
  3. /wiki-publish to refresh the Notion mirror; /wiki-lint for a health report; /wiki-query to answer from your own notes.

Cost

A deliberately light box (the heavy compile runs on your Mac). London on-demand pricing:

Line item Detail Per month
Compute e2-small, 2 vCPU, 2 GB ~$13.50
Boot disk 20 GB standard PD ~$0.80
External IP egress only, no inbound ~$3.00
Egress light API proxying <$1.00
Domain bobsmcp.uk (~£1/yr) ~$0.10
Tailscale · Cloudflare Tunnel · Pages · Access personal / free tiers Free
Total all-in ≈ $17/mo

New GCP accounts get $300 of credit (~18 months at this rate). It is not the Always-Free tier — that needs an e2-micro in a US region; this runs e2-small in London by choice (latency + RAM headroom).

Compiled from wiki/Project-Overview.md · git is the source of truth