System overview

Architectureby Aaron Reimannupdated Oct 4, 2026#architecture#overview#stack#pressable#linux#self-hosted

A high-level map of how the app is built and where data flows. If you’re new to the codebase, read this once and the other architecture pages will slot into place.

What it is

Clockwork Control is a Laravel 13 web app on PHP 8.4. It runs locally on the agency’s private network — a local workstation today, a dedicated Linux server / mini-PC, or a Mac Studio later — against a fleet of ~50 SpinupWP-managed servers (DigitalOcean, Hetzner Cloud, and Azure VMs) that host ~150 WordPress sites, ~90 more sites hosted on Pressable with no server layer at all, and standalone Companion sites on WP Engine, Kinsta, or custom hosts. There is no public URL and no client login. Every account on Clockwork Control is an agency account.

A Mac is NOT required: The entire backend runs on standard POSIX PHP and MySQL. Linux (Ubuntu, Debian, Fedora, Arch) is a first-class production target alongside macOS.

The app sits next to SpinupWP, Pressable, WP Engine, Kinsta, and custom providers, not on top of them. They run the actual hosting; Clockwork Control watches it, reaches into it when it needs to (SSH for SpinupWP, an API + async-command transport for Pressable, and signed REST for Companion-managed sites), and surfaces what needs human attention. See Concepts → Server, Site, Care plan, Hosting tier for how the hosting models differ under one Site row.

The stack

Layer Choice
Platform / OS Linux (Debian, Ubuntu, Fedora, Rocky, Arch) & macOS. A Mac is not required.
Language PHP 8.4 or newer (composer.json requires ^8.4)
Framework Laravel 13 (server-rendered Blade)
Database MySQL 9.6 / 8.0+ or MariaDB 10.11+ (SQLite supported for dev/tests)
Frontend Tailwind 4, Alpine.js 3, ECharts 6, Font Awesome 7
Build Vite 8 + laravel/vite-plugin
SSH library phpseclib/phpseclib v3
S3 client league/flysystem-aws-s3-v3
Auth laravel/socialite (Google) + local password fallback
Queue / cache / sessions Database driver (no Redis required for v1)
Local LLM LM Studio over loopback OpenAI-compatible API

We deliberately stayed traditional MVC. There’s no SPA, no JS framework, no GraphQL. Forms post, controllers render Blade. Alpine handles the bits that need to be reactive in the browser. ECharts handles the data viz.

How data flows

The app pulls everything; nothing pushes into it (the home LAN isn’t reachable from outside).

SpinupWP ─── inventory ─────────┐
Pressable ─── inventory + async commands ┤
DigitalOcean + Hetzner + Azure ─ metrics ┤
DO Spaces ─── backup history ────┤
Cloudflare ─── DNS + WAF rules ──┤
Bill.com ─── customers + invoices ┤
WP sites (Companion) ─── snapshots ┤
WP sites (SSH+SQL fallback) ─────┤      ┌─────────────┐
nginx logs (SSH tail) ───────────┼─────►│ Clockwork Control   │──► Mattermost / Slack alerts
Gatekeeper / Wordfence ──────────┤      │  Laravel    │──► Mailgun (forms)
External blacklists ─────────────┤      │  + MySQL    │──► fail2ban over SSH
GTmetrix / PSI / Sucuri / Web Risk ─┤      └──────┬──────┘   ──► (admin web UI)
                                                        │
                                                        └──► S3 (targets.json, read by
                                                             standalone backup-relay droplet)
                                                        ◄──  S3 (last-report.json, pulled back)

Outbound side effects are: Mattermost/Slack messages, Mailgun emails, fail2ban bans pushed over SSH, Cloudflare WAF/rate-limit rule writes, and — Pressable-specific — arbitrary bash/wp-cli commands run on a client’s site via Pressable’s async command API (see Architecture → Security model’s note on treating that credential like an SSH key, not a scoped read token). That’s the full list — note the S3 leg above is push/pull only, same as everything else; the backup-relay droplet never connects to Clockwork Control directly, since the home LAN isn’t reachable from outside (see Features → Backup relay).

The big patterns

A few decisions are load-bearing and worth knowing up front. The dedicated pages cover each one in detail; this is the orientation list.

  • SSH is the substrate — for SpinupWP. Every SpinupWP server is reached as a non-root sudo user (default clockwork-deploy) over SSH. We do not use the cloud-provider APIs for anything but metrics. Pressable has no SSH at all; its substrate is a REST API plus an async command-execution endpoint that Clockwork Control wraps into something synchronous. See Integrations → SSH + fail2ban and Integrations → Pressable.
  • Blocking is fail2ban-mediated — for SpinupWP. A clockwork jail is provisioned once per server. Bans go via sudo fail2ban-client set clockwork banip <ip>. fail2ban handles iptables, persistence, and TTL. On Pressable, blocking is enforced at the application layer via Gatekeeper’s HTTP 429 response card, as Pressable has no host-level jail. See Architecture → Ingest pipeline.
  • Ingest is human-in-the-loop by default (with auto-ban on select servers). Gatekeeper (REST) + Wordfence + nginx logs feed a review_queue; an operator clicks Approve (unless server auto-ban or fleet repeat auto-approval is active). Pressable sites now stream Gatekeeper login lockouts over signed REST into the review queue for full fleet visibility. See Features → Review queue and Features → Gatekeeper.
  • Companion mu-plugin coexists with SSH+SQL (and, for Pressable, the async command transport). Where Companion is installed, we prefer signed REST calls. Where it isn’t, the SSH+SQL fallback works for SpinupWP sites; Pressable sites without Companion have no fallback at all for anything that needs command execution (checksum verification, malware scan). The companion_capabilities column gates which path runs per feature. See Architecture → Companion plugin.
  • Auth = Google OAuth + a database allowlist. The users table IS the allowlist. No auto-provisioning. See Architecture → Security model.
  • The scheduler is load-bearing. A one-line crontab entry (* * * * * ... php artisan schedule:run) drives drainers (bans, updates) and ingest — deliberately not a long-running schedule:work daemon (which can hold a stale cached PHP binary path across package upgrades). If the scheduler stops, the UI keeps rendering but everything drifts. clockwork:scheduler-heartbeat writes a timestamp every minute; the web UI flags it after 5 minutes of silence. See Runbooks → Scheduler stuck for architectural details and Reference → Scheduled jobs for what runs when.

Surfaces

Five top-level surfaces in the UI:

Surface URL Job
Dashboard / Server health, sorted by attention. SpinupWP-only — Pressable sites have no server to show here.
Sites /sites Fleet-wide site list across both hosting providers — the one place Pressable sites show up alongside SpinupWP’s.
Issues /issues Consolidated “what needs human eyes.”
Bans /bans Queue + active + history. SpinupWP-only.
Capacity /capacity Shared-server pressure + over-quota visits. SpinupWP-only (no server tier concept for Pressable).
Monitoring /monitoring Fleet-wide uptime view. Both providers.
Updates /updates Fleet-wide plugin/theme/core/translation updates. Both providers (reads Companion’s snapshot either way).

Plus per-server (/servers/{id}) and per-site (/sites/{id}) detail pages, with sub-tabs for their respective sub-features.

Two things this app is NOT

  1. Not a SaaS product. No public URL. No multi-tenant model. The DB holds ~150 sets of SSH + WP database credentials for the SpinupWP fleet, plus one account-level Pressable API credential that can reach every Pressable site (no per-site scoping exists on Pressable’s side) — encrypted at rest with APP_KEY-derived AES-256-GCM, but still the highest-value target on the host. Keep it on the LAN.
  2. Not a SpinupWP (or Pressable) replacement. They provision and run the actual hosting. Clockwork Control watches it. Modules\SpinupWp\SpinupWpClient is read-only — we never write to SpinupWP; the Pressable command-execution transport is the one place we do actively act on a client’s site (Companion install, checksum verification), by design.
Clockwork Web Dev
Brought to you by Clockwork Web Dev

A real WordPress development agency building battle-tested operational tooling to run client fleets with zero bloat and total control.

Visit Clockwork Web Dev→