Architecture overview
Keferboeck Ltd runs two applications on one shared DigitalOcean droplet, deployed with Kamal and fronted by kamal-proxy (TLS via Let's Encrypt).
flowchart LR
subgraph Visitors
V[Browser]
end
subgraph Droplet["keferboeck-ltd droplet · 161.35.42.199 · lon1"]
P[kamal-proxy 80/443]
subgraph Website["keferboeck.com (Next.js)"]
W[keferboeck-web]
WDB[(keferboeck Postgres)]
end
subgraph Portal["hub.keferboeck.com (Rails 8)"]
H[portal-web]
HDB[(portal Postgres)]
end
end
subgraph External
MJ[Mailjet HTTP API]
ST[Stripe]
AN[Anthropic API]
GH[GitHub]
end
V --> P
P --> W
P --> H
W --> WDB
H --> HDB
H -. read only .-> WDB
W -- workshop + newsletter relay --> H
W --> AN
H --> AN
H --> MJ
GH -. kamal deploy from laptop .-> Droplet
The website — keferboeck.com
- Repo:
keferboeck/keferboeck.com, local~/Development/RoR/keferboeck.com. - Stack: Next.js (App Router), Tailwind, Prisma on Postgres, framer-motion. Bilingual
(
en-gb,de-at). - Notable: articles with seeded content, the AI advisor (
/api/advisor, Anthropic), the free workshop signup, the newsletter banner (relays server side to the portal with a shared secret so the secret never reaches a browser), the demo store showcase pages. - Deploy:
kamal deployfrom the repo. Builder uses the plaindockerdriver (see incident log: buildkit registry pushes failed).
The portal (hub) — hub.keferboeck.com
- Repo:
keferboeck/keferboeck-portal, local~/Development/RoR/georgkeferboeck/keferboeck-portal. - Stack: Rails 8.1, Hotwire (Turbo + Stimulus), Tailwind, Solid Queue/Cache/Cable in a single Postgres, Devise with mandatory TOTP two factor, Active Record encryption.
- Domains: companies and users, documents, workshop requests with indicative pricing, the newsletter (contacts, topics, append-only consent ledger, vouchers), campaigns and AB tests, the growth simulator (scenarios, debts, committed projects, actuals), the charity pledge (charities, donations), audit events on every admin action.
- Deploy:
kamal deploy; the container entrypoint runsdb:prepare, so migrations apply on boot.
Cross-app integration
- The website relays workshop requests and newsletter signups server side to the
portal API, authenticated by the shared
WORKSHOP_SHARED_SECRETheader. - The portal has read only access to the website database (
SITE_DATABASE_URL) for advisor insights and search statistics. - The five Solidus demo shops (documented in their own knowledge base) push leads into the portal's consent ledger with per-store API keys.