HoneyBun is a multi-vertical, multi-city SEO website platform. Operators get fully deployed WordPress sites with AI-generated content, schema markup, and a DCC engine that personalizes every page from a single JSON config.
An operator signs up, their site is provisioned, AI generates content, and the render worker delivers optimized HTML to every visitor — all from a single JSON config.
Diagnostic call → Simplex checkout → webhook fires provisioning. Operator picks their vertical (plumber, gym, med spa, etc.) and market.
hb-provision worker clones the golden WordPress instance on Cloudways, assigns a subdomain, and stores client data in KV.
hb-content worker runs two passes: Sonnet researches the market, Opus writes high-converting local SEO copy for every page and city.
The WordPress theme fetches from hb-render on every request. HTML fragments, CSS vars, and JSON-LD schema are injected into the page — no plugin required.
Schema.org markup is injected per page type — LocalBusiness, Service, FAQPage, AggregateRating, BreadcrumbList — all wired to DCC data.
Monthly retainer covers ongoing content updates, SEO monitoring via hb-agent, and dashboard access at app.gethoneybun.com.
Every page view travels through three systems: WordPress theme → Cloudflare Worker → KV Store. The mu-plugin bridges them invisibly.
Four core workers carry the request path. Fifty-six specialist workers handle orchestration, monitoring, audit, SEO, funnel, OG/image rendering, messaging-template CRUD (hb-messages), and platform plumbing. All deployed via Wrangler from workers/configs/*.toml.
{ html, css, schema } payload to the WordPress mu-plugin on every page load. Fastest path to dynamic, personalized pages.DCC JSON → KV (HB_CLIENTS) → hb-heartbeat.phpwp_option cache → BBS_Vertical_Loaderbbs_get() / bbs_get_section() → Templates
Three layers of vertical readiness: 13 golden template apps deployed and ready to clone (from deploy-manifest.json) · 15 verticals with full implementation under workers/verticals/ (DCC schema, slug patterns, schema.org type, voice preset, nav items, trust signals) · 24 verticals with marketing landing pages on gethoneybun.com. The extra 9 are sales-ready but await full build-out. 5 are running in production right now as live operator sites.
Every operator site is configured by a single DCC JSON stored in KV. The BBS_Vertical_Loader reads it and exposes it to all templates via bbs_get().
Every DCC write runs through one canonicalization + validation pipeline (lib/dcc-schema.js), so the PWA, intake, and workers can't drift the shape. Source of truth is Supabase clients.dcc_data; KV is the hot read path for the render worker.
dccCanonicalizeKeys — legacy aliases → canonical field names (camelCase contract).dccCanonicalizeStructure — nested transforms (logo↔logoUrl, media→hero, title→name, subPills→subEvents).dccRepairUrls — auto-repairs URL/media fields: sentinels (n/a) untouched, bare host → https://, junk → cleared. Never blanks a valid value.validatePatch — rejects flat dot-path keys + unknown sections; fatal/error → 422, warn → logged as drift.Writes are dual-persisted (Supabase + KV) and every mutation is logged to dcc_change_history / dcc_mutations. normalizeDCC() bridges legacy → canonical shape on the read side so consumers always see one schema.
Data integrity (Sync Contract). Beyond shape validation, the write path guarantees provenance: a stale write is rejected with 409 (optimistic _version lock); every changed field is stamped with field-level provenance — { source, ts, confidence, version }; and a confidence-precedence guard stops a low-confidence source (scrape / research) from overwriting a field a human confirmed (operator-confirmed always wins). Confirmed business NAP also syncs one-way to the linked GHL contact — to dedicated business_email / business_phone_number custom fields, never the contact's login credentials.
Each operator fact is answered once and reused everywhere. Research seeds the canonical record before the operator's first touch; the operator confirms rather than types; the generators render from the record. Five surfaces, one clients.dcc_data.
confidence:low. The operator hasn't typed anything yet.hb-intake (intake.gethoneybun.com) — token-gated, server-injected DCC, operator confirms / corrects, one-shot signed submit.brand-data.php + 4-layer schema stack) and the brand-kit generator render from the record — read-only consumers.The two generators are hosted inside the internal app and pre-seed from the DCC: open ops.gethoneybun.com/brand-kit?client_id=<id> or /edsa-generator?client_id=<id> (one click from the Sites screen or a CRM linked-site row). They ride the internal Supabase session — no token to paste, no data to re-key.
seo.php hooks into wp_head at priority 5. Every page type outputs its specific schema.org graph — all driven by DCC data, zero hardcoding.
Every automated message — to prospects, operators-in-onboarding, and live operators — is governed, channel-aware, and fail-safe. Emails send via Resend; SMS via Simplex (white-labeled GoHighLevel); push via the PWA. The full visual map lives at /frontend/tools/cadence-map.
message_templates store + hb-messages worker. Cadences read override-or-hardcoded-default (KV-cached). Edit at /frontend/tools/message-editor; toggle off → instant revert.A contact-form lead hits /site-lead-event → operator alert fans out over SMS + email + push honoring notification_preferences → optional DCC-configured routing to a webhook or second email. The alert copy is vertical-aware: photo-booth gets event-shaped fields (date/guests/venue/booth); every other vertical gets the universal "Looking for / Location / Details" copy. Safe default: unknown vertical → universal.
One operator owns one city cluster of ~550,000 population per vertical. Clusters are built by ZIP/ZCTA adjacency from an anchor ZIP up to the population cap — cities are emergent from the ZIP set, not entered by hand. The engine (GET /territory/preview, algorithm polygon-boundary-v1, ACS census data) returns the ZIP list + exact population; growth appends ZIPs to territory_slots.zip_list (extend-cluster), never reshaping shared geography.
Tables: market_clusters, territory_slots. Binary ownership (owned/open) per city/ZIP. Founding-Partner pricing is decoupled from territory size; final price scales with page count (cities × services).
Four storage systems, each purpose-built for its domain.
47 PHP page templates. 24 vertical landing pages. 17 resource guides. 5 comparison pages.
/vertical-seo/ with industry-specific copy, pricing, demo site link, and calendar embed./local-seo-vertical/. Builds organic authority and attracts operators researching SEO solutions.vertical-template.php./book/. Vertical slug passed as ?v=plumber for personalized booking experience.honeybun_get_vertical_data() and honeybun_get_resource_data() power all pages.All production HoneyBun platform code lives under honeybun/operations/. Each subfolder owns its own deploy target.
wrangler deploy --config configs/wrangler-*.toml.app.gethoneybun.com. Operator tools, intake, analytics, board.hb-operator-app (operator-facing) and hb-internal-app (internal ops).photo-booth (base / golden), realtor, plus verticals/ per-industry themes. Synced to Cloudways.hb-heartbeat.php (render bridge), hb-hero-preview.php. Mirrored to Cloudways wp-content/mu-plugins/.hb-deployer.php, hb-theme-sync.php. Bridge between Cloudflare Workers and Cloudways WordPress.go.gethoneybun.com via node generate-catalog.js.The operator-facing control center. Analytics, integrations, site management — all in one place.
Operator-facing admin pages under /frontend/tools/ on the dashboard, also linked from the internal PWA drawer (Tools section).
/frontend/tools/cadence-map — every automated message with trigger, channel, sender, vertical scope, schedule, and verbatim copy./frontend/tools/message-editor — edit live message copy (scoped editor key). Saves to message_templates; live within ~5 min via override-or-default.The runner is the execution layer between the task board and the git repos. It runs on the Mac Mini, receives tasks from the dispatcher, and routes each one through the appropriate pipeline based on risk tier. All eight phases of the autopilot migration are live.
DONE_WHEN, zero files_touched, no write verbs in description. Single verifier pass in source repo. No worktree. Closes in ~30s. Examples: [AUDIT] confirm commit X landed on main.critical, infra, recovery, handoff, deploy. Or files_touched contains sensitive paths (wrangler*.toml, .env*, migrations/, auth/, billing/, CI workflows). Requires a human Claude Code session.RESULT: PASS/FAIL. Runner PATCHes task directly: status=completed + verified_at + verification_output + files_touched=['(verified-no-files-changed)']. No Researcher, no Fixer, no worktree.FILES_TO_CHANGE + FIX_SPEC + DONE_WHEN. Pass 2 (Builder): implements in isolated worktree on branch autopilot/<taskId>. Commits only — runner handles push after diff inspection.inspectDiff checks committed-but-not-pushed diff. >200 lines or >5 files or sensitive path hit → escalateDiff (stash diff, reset, PATCH blocked, fire alert). Pass → git merge --ff-only from sourceRepo + push. Worktree torn down./tmp/hb-build/<jobId>.pass3-verifier) runs DONE_WHEN against the worktree. PASS → PATCH status=testing + verified_at + verification_output + files_touched + proposed_diff (≤20KB). FAIL → PATCH failed, teardown. The agent that built never verifies its own work./task-board review lane with full diff + verifier output. Approve: dashboard → POST /tasks/:id/approve-merge → hb-tasks → hb-task-dispatcher → runner POST /merge → git merge --ff-only + push + PATCH completed + teardown. Reject: same chain → runner POST /reject → PATCH pending + teardown.fetch(). Complex shell pipelines are skipped — safer than a false-positive revert.postmerge:fail:<taskId> + postmerge:fail-at:<taskId> with 30-min TTL. 2 consecutive failures within a 5-min window → triggers auto-revert. Window resets on success or TTL expiry.POST /revert: captures HEAD SHA, runs git revert <SHA> --no-edit, pushes to origin main, PATCHes task to blocked with revert details, fires alert. Alert body distinguishes "revert requested" vs "runner unreachable — manual rollback required."hb-tasks.js: POST /tasks (create), PATCH status→pending, PATCH unclaim→pending. Each fires ctx.waitUntil(maybeFireDispatch) — response never blocked. Kill switch checked at event source (defense in depth; dispatcher checks again).POST /dispatch-task on hb-task-dispatcher. dispatchSingleTask() runs full eligibility: kill switch, autopilot-ok tag, DONE_WHEN present, capacity gate, KV dedup. Delegates to dispatchOne(). Cron (*/15 min) stays as safety net for misses.Every task must carry a machine-verifiable success criterion before it can enter the dispatch queue. Enforced at the API layer — POST /tasks returns 422 if either gate fails. No exceptions.
description422 description required. Gives the runner enough context to do the work without reading the board session history.DONE_WHENDONE_WHEN: clause — a shell command that exits 0 on success, or the literal string human-review-required. Missing clause → 422 DONE_WHEN clause required. The runner and the auto-review pipeline both depend on this field to verify completion objectively.[ALERT], [AUTO-ESCALATE], [AUTO-REMEDIATE], [REVIEW:*], [FOLLOW-UP]) are exempt — they're fired programmatically by cron workers with controlled shapes. Tasks created by a human action (e.g. pressing → Claude on an alert card) are never exempt: a button press is a work decision, not a notification.When any task transitions to status=completed, the pipeline fires two hooks inside ctx.waitUntil() — response is never blocked. Kill switch: KV key auto-review:enabled must equal "1".
maybeQueueReview: if the completed task touched code files (non-empty files_touched), classify the review level — review, review+security, or audit (6-pass P0–P5) based on file count and path patterns. Queue a [REVIEW:level] task tagged autopilot-ok with a structured DONE_WHEN. Daily cap: 20 auto-generated tasks. Depth cap: 3 review cycles.maybeQueueFollowUp: when a [REVIEW] task closes, parse its board_review_summary for structured findings JSON. If CRITICAL or HIGH findings exist, queue a [FOLLOW-UP] coding task listing each finding and the files to fix. Includes repeat-finding detection: if the same finding hash recurs after a fix attempt, the chain escalates to [HUMAN-REVIEW-REQUIRED] instead of looping.queueHumanEscalation creates a [HUMAN-REVIEW-REQUIRED] board task and simultaneously calls HB_PROVISION /internal/send-alert (SMS + email + push) as a fallback — escalation failure is never silent even if Supabase is down.Documented for technical due diligence. Every layer below is verified from source — not inferred from configuration intent.
frame-ancestors 'self' · base-uri 'self' · script-src locked to self + GTM + Turnstile · connect-src explicit allowlist of all HoneyBun endpoints + GA4. unsafe-inline required for Breeze full-page cache compatibility; nonce-based CSP evaluated and documented as blocked until Breeze is replaced with edge-level caching.Strict-Transport-Security: max-age=31536000; includeSubDomains — HTTPS enforced at browser level across the entire domain tree. Referrer-Policy: strict-origin-when-cross-origin — limits referrer leakage on cross-origin navigation.X-Content-Type-Options: nosniff — blocks MIME-type sniffing.X-Frame-Options: SAMEORIGIN — clickjacking prevention.Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=() — all sensitive browser features explicitly disabled.lib/permissions.js — can(), isPrivilegedAdmin(), ROLE_AGENCY_ADMIN and role constants. Not ad-hoc if-checks. Privileged operations (cross-tenant reads, admin routes, force-release) require isPrivilegedAdmin() explicitly.X-HB-Key is scoped to their own clientId. One operator's key cannot reach another operator's data. Master key and operator keys are separate credential classes; operator keys cannot self-elevate.AbortSignal.timeout() on every external call — 3s for fast paths, 5–8s for Supabase operations. No hanging requests that cascade into worker CPU exhaustion. Timeout errors are classified and logged the same as network errors.sha256_live (freshly read) and sha256_assumed_base (what it thinks is live). If they don't match, the write returns 409 — no silent overwrites from concurrent sessions. Removes the race condition from parallel deploys.unsafe-inline on CSP script-src (Breeze cache constraint — documented above). These are tracked, not unknown.