Great Energy is a small spiritual-education company: two founders, six websites, about seven thousand students and subscribers, and something to publish nearly every day. GLIV is the place they open each morning. It drafts the content in their own voice, watches every number, remembers every person from first hello to graduate, and quietly does the overnight busywork, so the founders spend their hours teaching, not wrangling tools. Nothing goes out and no money moves without one of them saying yes.
Running a modern education business means being in a dozen places at once: email lists, five payment systems, Facebook groups, LinkedIn, search rankings, a calendar full of classes, and a steady stream of new people who each need a warm welcome and, sometimes, a reply today. Done by hand it eats the whole week and still leaks. Someone always slips through a crack.
GLIV is the answer to that. It is a single place that watches all of it and does the repetitive parts on its own overnight. It drafts each day's post in the founder's real voice, keeps every subscriber's whole story in one view, flags the person who is waiting on a reply, and refreshes the numbers before breakfast. The founders stay in charge of everything that matters. The rule that never bends: the machine can read and prepare anything, but it never sends a message, posts in public, or moves money without a person saying yes.
Real screenshots of the running app. To keep real people private, they are captured from a built-in demo mode, so every name, email, and number here is made up. The software is exactly what the founders use.
Everything below is the engineering: the architecture, the data design, the integrations, the automation fleet, the testing, and the security model. All hand-built.
Every request hits the same Cloudflare Worker and is sorted into one of three lanes before anything else runs: a public lane (no auth), an ingest lane (shared-secret, for machine writers), and an authed lane (cookie or IP, for the founders' dashboard). State is Workers KV. Compute is stateless and global.
No origin to keep alive, no cold starts, global by default, and a security model where one if(!authed) line provably gates every private read. The whole control plane is one deployable artifact shipped with wrangler deploy.
KV is fast and global but eventually consistent with no atomic read-modify-write. That single fact drove two core design choices: one key per event, and event payloads stored in key metadata so lists never do a read per key.
Anything needing Gmail, a browser, or long-running research runs as a scheduled Claude agent on a Mac, then POSTs a compact result to the ingest lane. The Worker stays fast and stateless; the messy world stays outside it.
Workers KV has no locks and no atomic read-modify-write. Rather than fight it, the schema is shaped so concurrent writers never collide and reads never fan out. Two techniques carry most of the weight.
Engagement events arrive by webhook and can burst. A shared eng:<email> record would lose writes under KV's last-write-wins. Instead every event is its own key, so two events for one person never clobber each other. Warmth is computed at read time with a 14-day half-life.
To browse 6,996 people, a naive read lists keys then GETs each — thousands of subrequests, past the Worker cap, silently truncating. Storing the row in key metadata means one paginated list() returns the data inline. Reads are O(pages), not O(rows).
| Key pattern | Holds | Consistency / TTL | Read by |
|---|---|---|---|
| cust:<email> | full Drip record + stage; compact row in metadata | 30d TTL, daily refresh | people, person, enrich |
| eng:<email>:<ts> | one engagement event in metadata | 90d TTL, per-event | engagement, person |
| people:cache | lightweight roster for the browser | 1h TTL, invalidated on pull | /__data/people |
| drip:snapshot | active count + launch-tag count | 30m TTL | Overview |
| tagcounts:latest / :prev | per-tag counts with 24h delta | 14d, day-over-day | Products, Rules |
| learn:<id> | before/after voice lesson queue | 60d TTL | voice write-back agent |
| img:<id> | self-hosted image bytes + content-type | served at /img/<id> | public |
| needs: / brief: / shipped: | Morning Brief + attention queues | 48h TTL | Brief, Overview |
The platform is organized into four operating tracks. Each is independently scheduled and independently observable, and all read and write the same KV spine through the same lane discipline.
A real-time engagement webhook plus a nightly full-subscriber pull feed one shared journey-stage function (subscriber → lead → live → lowcost → main → graduate). Powers an every-person browser with server-side search, stage/spend filters, a warmth meter, and a click-through profile that joins Drip tags, orders, lifetime value, and engagement history.
A daily agent researches a fresh topic, drafts in a specific human voice via voice skills, and renders a branded quote graphic with PIL using per-region contrast selection. A four-section composer (Words / Image / Destinations / Review) lets the founder edit any section without losing the others, then approve.
Where an API exists (Facebook Pages, LinkedIn), posts publish with a per-target confirm and a durable one-shot guard. Where it does not (private groups), a browser agent stages the post and stops for the human's final click. Groups can never repost; a rare public double-post is tolerated, a group double-post is structurally impossible.
Search Console, GA4, and Bing feed a traffic view with finance-style comparison windows. Payments from five Stripe accounts, Square, and Squarespace are de-duplicated into one order stream. Structured data (JSON-LD) and an entity strategy target AI answer engines, not just blue links.
The email service (Drip) exposes identity, tags, orders, and lifetime value over its REST API, but deliberately does not serve opens and clicks as pull data. Engagement is only available as outbound webhooks. So the integration is split: a scheduled pull for the slow-moving facts, and a live webhook for the fast-moving behavior.
Each webhook event carries a weight; the score decays with a 14-day half-life so a person cools when they go quiet. Computed at read time over the stored events, clamped 0–100. No lead-scoring feature required on the account.
Promise.all and pages in small windows, so a 6,996-row refresh never trips the subrequest limit.Each founder has a distinct writing voice, encoded as a skill with craft rules, phrase banks, and an "AI tells to strip" filter. But a voice guide drifts unless it learns from real edits. The problem: naive learning bloats the file until it is too long to load before every draft.
The fix is a two-tier learning loop. When the founder edits a draft and picks a voice to teach, the before/after is queued. A nightly agent captures the full pair in an append-only raw log (the evidence trail) and then merges a distilled lesson into a short curated file, deduplicating against existing rules and marking recurrence rather than piling on duplicates. The curated file, capped near two dozen rules, is what actually shapes the next draft.
Work that needs a real inbox, a browser, or open-ended research runs as a scheduled Claude agent on the Claude Agent SDK. Each is a self-contained prompt with a cron, strict read-only rules on calendars, email, and tasks, and a heartbeat back to the Worker so the app can show it ran. Side-effectful steps stop at a human clutch.
| Agent | Cadence | Reads | Writes / guard |
|---|---|---|---|
| gliv-drip-pull | daily 05:07 | Drip REST (token) | KV cust:*; invalidates people:cache |
| gliv-new-students | 2×/day | Drip + Gmail | read-only; reply-needed flag |
| gliv-smart-tasks | 3×/day | Calendar + Gmail + Todoist | read-only; pushes Morning Brief |
| gliv-daily-content | daily 04:08 | web research + vault | drafts to review queue only |
| gliv-voice-learn | 3×/day | /__learn/queue | appends to voice files; never posts |
| gliv-group-post | hourly window | approved queue | stages post, stops for human click |
| gliv-publish-run | hourly window | approved queue | API post + per-target confirm |
| gliv-selftest | nightly 02:03 | /__selftest | raises failures to Needs Attention |
| gliv-sched-watch | 4×/day | scheduler state | flags runs missed while asleep |
| gliv-fb-status | 3×/day | page tokens | flags expired auth |
/__ingest/tasklog); a 401 or expiry raises itself into the app's Needs Attention queue via /__ingest/taskissue.Calendar, email, and task connectors are used read-only. The prompts forbid send, reply, label, delete, complete, or accept. The only writes are to GLIV's own store and, at a clutch, an approved post.
Scheduled work only fires when the machine is awake. A watcher compares expected vs actual run times and surfaces anything that slipped, so a silent gap never masquerades as "all clear."
Posting agents claim an item before acting, so a re-run can never re-stage the same post. Durable flags (postedToPages, groupStagedOnce) make double-posting structurally impossible in the sensitive lane.
A one-operator business cannot babysit its automation, so the platform is built to catch its own regressions. Three layers do it.
A /__selftest endpoint asserts the critical invariants with no side effects: the one-shot posting guard holds, tag categorization is stable, seeding is correct. The publishing self-test calls the same eligibility predicates the live path uses, so the test can't drift from reality. 200 if all pass, 500 on any failure.
Every substantial change runs a rubric self-check, then an independent reviewer agent against the same rubric: security, correctness, honest UI, house style. Both must clear 95 with zero outstanding violations before it ships. This overview was built under that gate.
A black-box contract harness hits every public route with a real browser user-agent. A nightly self-test raises failures into the app. A scheduler watcher flags missed runs. Nothing fails quietly; everything failing arrives in one Needs Attention queue.
if(!authed) line. Public and ingest routes are deliberately, visibly above it.env, never logged, never written to KV. Rotation is a one-command wrangler secret put.| Actor | Auth | Can |
|---|---|---|
| Public visitor | none | landing, opt-in, images |
| Machine writer | X-Gliv-Ingest | push to ingest lane only |
| Drip webhook | ?s=secret | append engagement, write-only |
| Founder | cookie / IP | read all; stage actions |
| Deploy | scoped CF token | ship the Worker |
The deploy token, the Drip API token, the ingest secret, and the webhook secret are four separate credentials with four separate blast radii. Compromise of any one does not imply the others.
| KV | Key-Value store (Cloudflare's eventually-consistent edge database) |
| SPA | Single-Page Application |
| MCP | Model Context Protocol — the connector standard the agents use |
| SDK | Software Development Kit (here, the Claude Agent SDK) |
| ESP | Email Service Provider (Drip) |
| TTL | Time To Live — auto-expiry on a stored key |
| OAuth | Open Authorization — delegated, tokenized access |
| TLS | Transport Layer Security |
| GSC | Google Search Console |
| GA4 | Google Analytics 4 |
| WMT | (Bing) Webmaster Tools |
| SEO | Search Engine Optimization |
| GEO | Generative Engine Optimization — being cited by AI answers |
| AEO | Answer Engine Optimization — featured snippets, voice |
| JSON-LD | JSON Linked Data — structured markup for search |
| POP | Point of Presence — a Cloudflare edge location |