Part of Just Every · MIT licensed
Watch the full demo · 56 seconds

Your agent's fix, your reply and the customer's thanks, in one conversation.

Move in from Zendesk: imports keep source details and never message your customers.
HappyToHelp is an open-source support workspace with a customer chat widget, shared inbox, AI-assisted replies, help centers, customer context and optional work delegated to your own Codex or Claude. It works with human replies without an AI provider. The editable React application runs on Cloudflare Workers with Hono, D1, R2, Queues and Durable Objects.
Deploy version 0.4.28, tagged v0.4.28 once published. v0.4.26 is the upgrade baseline; the release upgrade checks cover v0.4.26, v0.4.25 and v0.4.24. The pinned installer is rer 0.2.117.
This release includes the content of the tagged but unreleased v0.4.27. Credential-shaped notification test fixtures blocked source scanning for that tag; they now use short example markers. The local size documentation also reflects the qualified rer 0.2.117 results. v0.4.26 remains the previous public release.
- Reports: summary and team reports read indexed, message-bearing date cohorts, with separate timing distributions and SLA breakdowns. Workload reads the selected cohort and older active backlog while a maintained inventory preserves historical empty buckets. Overlapping handle sessions still include older tickets worked in the interval. Report evidence, labels and permissions remain live reads; these projections are not cached report totals.
- Conversations and knowledge: transcript assembly reuses article references within a response and skips article-free source arrays. Socket replay reads bounded pages of raw message and ordered attachment projections, then renders current knowledge and rechecks authority before sending. Message corrections, attachment changes and erasure update the replay data while retaining event identities. An attachment index narrows message reads, and a solved-time index narrows the scheduler’s due cohort without changing rule eligibility or cursor order.
- Backup and restore: derived report projections are excluded from portable archives and rebuilt after source rows and current write triggers are restored. Historical archive handling skips these derived backfills when the original inventory lacks their source subsystems. Keep normal backups and the installation’s existing custody and recovery requirements.
- Installer and mail settings: the managed integration recipe now carries the complete native migration inventory and current binding declarations. Public mail validation covers native SendGrid and Mailgun as well as Resend, Cloudflare and the managed relay. Explicit off, none or disabled selections normalize to mail off and remove stale public provider settings from emitted plans; private bindings remain separately selected and checked. This does not activate mail or establish provider readiness.
- Local size tooling: the pinned rer installer advances from 0.2.112 to 0.2.117. A seed-owned declaration exercises the built application with disposable synthetic local installations, exports SQL statement artifacts and supports a deliberate unbounded-read rejection exercise. Selected source and migration changes and daily scheduled verification use the shared build with queue consumers disabled for size measurement. This local tooling does not qualify live-provider limits or real-customer scale; synthetic text and attachment distributions and scheduled-consumer completion remain limitations.
- Fresh hosted installs: after the full migration inventory and this Worker are installed, normal writes maintain the new report and replay projections and existing reads use the new indexes. On an empty database there is no historical cohort or replay data to backfill. Managed recovery still requires compatible platform custody and a verified assertion; SQL alone enables no recovery authority. Legacy import and adoption remain inert until a package is imported through the approved flow. Updating starts no import and activates no mail provider; live hosted delivery and provider qualification remain separate.
- Migrations: four since v0.4.26, bringing the inventory from 196 to 200 files.
333_report_cohorts.sqlis additive: it creates report tables, a view, indexes and maintenance triggers and immediately backfills derived membership, workload and bucket-presence rows without rewriting business rows.334_media_message_reads.sqladds only the media-message read index.335_socket_replay_projection.sqlis additive to the outbox schema: it adds nullable raw-message and media columns, immediately fills existing outbox rows from retained native data, and installs synchronization triggers. It therefore writes derived data on upgrade.336_solved_time_rule_reads.sqladds only the solved-time expression index. Apply missing migrations once in filename order before the new Worker serves HTTP or queue traffic. Migration 326 remains absent.
Update from v0.4.26 without resetting; v0.4.25 and v0.4.24 are also upgrade baselines. Back up D1, R2, Durable Object state, private settings and installation encryption custody, then stop the old Worker. Use Node 22.20.0, pnpm 10.12.4 and pinned rer 0.2.117. Retain native owners, permissions, support history, files, erasure fences and every applied migration identity. On v0.4.26 apply 333_report_cohorts.sql, 334_media_message_reads.sql, 335_socket_replay_projection.sql and 336_solved_time_rule_reads.sql once in that order. On v0.4.25 or v0.4.24 first apply the missing 330_identity_recovery.sql once even though 331 and 332 are already recorded, then apply 333 through 336 in filename order. Retain earlier published buyer steps, including 332’s conditional cleanup; older installations first apply every other missing published migration in filename order. Finish all missing migrations BEFORE the new Worker serves HTTP or queue traffic. Report and replay backfills run during migration; report projections are rebuilt after portable restore. There is no migration 326. Keep the durable recovery revocation watermark on rollback, and require fresh Managed custody binding after portable restore. Run rer deploy --dry-run against the retained installation before any separately authorized deployment. Legacy v1 remains cleanup-only and incomplete cleanup remains pending. No reset, mail activation, recovery adoption, production import, cutover or production deployment is authorized by this preparation.
See RELEASE.json, Updates and recovery and the changelog.
Version 0.4.0 adds Move in for reviewed support-history imports, resolution
feedback with workspace-calendar Reports, Connections with scoped API tokens,
signed webhook events and Slack delivery, and the rer local
path. Native passwords, owners and data
are kept. Before the updated Worker serves traffic, back up and apply the ten
additive migrations 0110_move_in.sql to 119_local_portability.sql in order;
an existing 0.3 installation follows Upgrading from 0.3.x.
The changelog lists every change. Known limits:
- Accessibility is on a confirmed owner HOLD; no WCAG conformance is claimed.
rer exportarchives cover D1, R2 and Durable Object storage (with a digest); full Base-data equivalence is not yet established.- Hosted operating costs are unmeasured; timed activation and paid-provider execution are not qualified.
- Imports and connections are qualified with synthetic fixtures only, without real vendor exports or live Slack.
- No hosted installation, compliance or FedRAMP qualification is claimed.
- The hosted service at app.happytohelp.com is separate legacy production and is not part of this release.
The install and run guide covers installation, local sign-in and stopping the app, step by step. The Run HappyToHelp page has the quick-start command.
Start it with one line. You need Node.js 22 or later (with npm) and Git; check
with node --version.
npx -y @runeditrun/rer dev just-every/run-happytohelpThat clones HappyToHelp into run-happytohelp, provisions the checkout's own pinned
Node and pnpm, installs dependencies, sets up a local owner and starts the app. To
set it up without starting it, use npx -y @runeditrun/rer setup just-every/run-happytohelp.
Without Node, curl -fsSL https://runeditrun.com/install.sh | sh -s -- dev just-every/run-happytohelp
brings its own. To start it again later, run cd run-happytohelp && rer dev.
The install also downloads Chromium (about 280 MB, into your user cache) the
first time: rer verify and the browser checks run in it. Dev uses its local
address for the chat widget and generates a private owner password. Wait for RER_READY, then open the RER_OPEN address to enter as the
real local owner and reach Setup. No private answers, email service or AI
account are needed. Use rer dev --stop from the checkout to stop the managed app.
Already running 0.3? Keep your checkout and data, and follow Upgrading from 0.3.x instead. See Install for private setup answers and Local command reference for ports, preview and idle shutdown.
To try the customer flow, install the widget on a local page, send a visitor message and reply from Inbox. Set that page's exact origin in Settings → Widget → Settings; the app's own origin remains allowed. A visitor's entered email can receive operator replies even when their identity is labelled Unverified. Local visitor and teammate mail is suppressed without storage or external sending. With a hosted sender, Email sent to provider · delivery not confirmed on a reply means acceptance, not confirmed inbox delivery.
Local dev is a loopback trial on your computer. An online installation in your own Cloudflare account lets the widget on your website reach the workspace. Both use the same rer commands. An independently managed project may select its own operating contract; this seed's CLI is not a platform admission requirement.
For a new installation, acquire v0.4.28 once published,
after installing rer as shown above:
git clone --branch v0.4.28 https://github.com/just-every/run-happytohelp
cd run-happytohelpStart with a local build and validation:
rer deploy --dry-runThis needs no provider credentials, makes no Cloudflare requests, sends no mail and changes nothing remotely. It validates the deployment declaration and builds the application locally. The completion report lists any inputs needed for a real deploy; remote resources, credential validity, target secrets and remote readiness remain unchecked.
For a real deployment, read deployment prerequisites. When rer says See the deploy-kit owner-account instructions, it means What you need first: how to make the Cloudflare API token and find the account ID. Configure the real declaration and private account credentials, then:
rer deploy --environment productionThe contract also requires a separately started read-only provider preflight with explicit owner consent and the owner's scoped Cloudflare token. For an existing Worker, that preflight permits only the account's Workers subdomain and the Worker's settings and secret names (three GETs), with no mail or mutations. That separate preflight is not yet implemented or live-qualified; the local dry run does not perform it or prove remote readiness.
For a new installation with outbound email, also supply real EMAIL_PROVIDER,
EMAIL_FROM, EMAIL_API_KEY and OWNER_EMAIL inputs in an owned 0600 --env-file;
the owner email must receive sign-in mail. Existing installations retain held
secrets. The seven optional mailbox settings can stay unset. Remote provisioning
remains separately unqualified.
rer deploy --environment production --env-file ~/happytohelp-production.envDeployment
provisions declared resources, applies migrations, publishes and checks health
through Base deploy-kit. After a healthy deploy, rer deploy claims the owner named
by OWNER_EMAIL (a real setup owner, or the value you supply privately) with a one-time
token it then removes. Open your Worker's address and sign in by email as that
owner to reach Setup. The owner's name is derived from their email. A Workers
hostname does not name your business: a workers.dev website defaults to
My workspace regardless of owner email. An explicitly chosen native workspace
name takes precedence; you can change it in Project settings.
Deploy refuses the local placeholder [email protected]: it has no mailbox.
For a real deployment, supply OWNER_EMAIL privately in your environment or an
owned 0600 --env-file. The local dry run can list missing production inputs
without requiring them.
rer signin --environment production directs you to this installation's /login
page. HappyToHelp uses its native email/password sign-in; the command cannot mint
an owner link. If mail fails, follow the working mail recovery in
operations.
rer deploy --environment NAME --remove --yes
removes that environment's created Worker, D1, R2 and queues in one pass,
including uploaded attachments in its R2 bucket, and reads back that each is gone.
Export anything you need before removal. Hosted installation and real provider
execution are not qualified by this release.
HappyToHelp is MIT licensed with no seats or subscription fees. Your Cloudflare, email and optional AI providers bill your own accounts. Enable R2 and the account capabilities required by the declaration before deployment. An email sender must be verified. Local development needs no external mail or AI credential.
Configure optional model keys, the Apify crawler (for website sources larger than the built-in reader's 40 pages; see Website knowledge), Qdrant search, GitHub and email only when you need them. See Optional features and their settings and Measure local usage and costs. The optional local agent runs on the computer doing the support work and has its own runtime prerequisites.
Your workspace is already set up when you first sign in. It has a starter
assistant for visitors, a reply-by-email mailbox when the installation holds an
INBOUND_EMAIL_TOKEN, and mail that leaves from the installation's own sender
until your sending domain is verified. The inbound token authenticates incoming
mail; configure your email provider's domain routing and webhook before replies
can arrive. Creating the workspace may send you one welcome email. Use Setup,
the first entry in Settings (its row there counts what still needs you; an empty inbox links to it): Needs you lists what cannot be done for you (the widget on your website, an AI key and, with a hosted
email provider, your sending domain's DNS records), and What we've set lists
the rest. Anything under Needs you can be skipped for now and picked up later
from Skipped. The system runs its own checks while Setup is open, and what it
confirms is counted ("N of M confirmed", without what you skipped): mail to you,
sender DNS, inbound mail, model access, spend ceilings, widget origin and an
authenticated widget visit. Saved settings alone do not complete them. Add the widget through Settings →
Widget, set its exact website origin,
publish useful articles and invite teammates through Settings → Agents. By default the widget does not ask
for an email. For visitors to receive replies by email, enable Settings → Widget
→ Settings → Required contact details → Require email. The visitor enters their
address before sending a message; operator replies can then use it on a hosted
installation with email sending configured.
Your starter assistant answers visitors by itself, from the help articles you publish and from what we read on your website, and within your daily limits, as soon as the installation has a key from an AI provider. Until then, new conversations stay with your team and the AI page and Setup say a key is missing.
- Get an API key from OpenAI, Anthropic, Google, DeepSeek, xAI or OpenRouter. The provider bills you for what you use.
- Add the key to your installation under the name for that provider:
OPENAI_API_KEY,ANTHROPIC_API_KEY,GOOGLE_API_KEY(orGEMINI_API_KEY),DEEPSEEK_API_KEY,XAI_API_KEYorOPENROUTER_API_KEY. The setup answers can store one at install time (see Install).- On Cloudflare: in the Cloudflare dashboard, open your Worker, go to
Settings → Variables and Secrets and add the key as a secret. You can
also set it in your terminal before you run
rer deploy --environment production(see Optional features and their settings). - On your own computer: add a line such as
OPENAI_API_KEY=your-keyto a file named.dev.varsin therun-happytohelpfolder, then restart the app.
- On Cloudflare: in the Cloudflare dashboard, open your Worker, go to
Settings → Variables and Secrets and add the key as a secret. You can
also set it in your terminal before you run
- Setup notices the key. While the starter assistant is untouched it uses a model of whichever provider's key is installed, and Setup confirms the key with the provider. To use a different assistant, open Settings → AI, add one (or choose Customize on an existing one), enter the Model it uses and keep it Enabled. Then, under Responder defaults, choose which assistant answers visitors, and with AI replies whether its replies are sent automatically, kept as drafts for your team to send, or turned off. In draft, a reply your local computer agent prepares waits in its report for you to use and send, and a visitor who writes while your team is away gets your away message, as if no assistant were assigned. Such a visitor's conversation also appears in Open and Unassigned.
Keep key values out of chat messages and the browser. To cap what visitors can spend, see Limits and AI ceilings.
When your workspace has no conversations yet, the inbox greets you with one next action and a quiet Try a demo inbox link under it. The link creates a separate, clearly labelled workspace called Demo inbox · Northstar Outfitters, with:
- fictional customer conversations
- a drafting assistant you can edit
- three published FAQ articles
Creating the demo makes no AI, email or other provider calls. To see AI drafting,
open a demo conversation and press Generate. HappyToHelp then drafts
suggested replies for you to review, using your AI provider. The demo's
assistant uses OpenAI's gpt-5.6-luna model, so this needs OPENAI_API_KEY
unless you change the assistant's model (see
Set up AI replies). Generate never sends a reply to a
customer.
When you are done, delete the demo workspace from its settings.
Owner/admin Move in reviews five kinds: conversations, contacts, tags, saved replies and articles. Inspect per-kind plans, rejections, omissions, conflicts and publication changes; approve, resume a stopped batch, follow report links, repeat or undo through the same current role checks. Original source IDs, timestamps and private notes remain attributed; published articles stay published and drafts stay drafts. Apply and undo are inert: no mail, widget messages, model work, assignment or connection notifications. The moving-in guide explains formats, source custody and conflict-safe undo. Validation is documented-format, synthetic-only; realExport: not-supplied. The 9 October owner ruling requires exact business-state equality on repeat and undo. Platform counters/sequences, audit and import journals are excluded; business notes, source links, identities and native contact content remain checked.
A resolved widget conversation asks Did this answer your question? with thumbs up/down and an optional comment up to 1,000 characters. Only its owning visitor session can rate its current closed cycle; widget action limits apply and changes retain history. Native records store AI, assisted, human or unknown provenance. A visitor message reopens the conversation; another resolution gets its own record. Reports uses the workspace timezone and 7/30/90-day or custom calendar ranges for volume, median/p90 first response and resolution times, AI resolution and satisfaction counts/rates, daily series and latest comments with conversation links. Conversation and summary CSV exports guard formula-leading cells. Imports are excluded and Reports calls no model. The Reports guide and workspace workflows explain the denominators.
Settings offers seven explicit token scopes, optional expiry, one-time secret display, hashed storage, revoke, last use and audit. The API guide contains thirteen Bearer-only operations. Webhooks support four signed events, explicit test sends and safe attempt logs; public HTTPS destinations are checked against SSRF. Slack has incoming-webhook and bot-plus-channel modes. Both have recorded-response checks only; live Slack: not-supplied. Deliveries use ordinary platform HTTPS fetch with manual redirects. Every 3xx fails without following its Location. Requests are limited to 1 MiB, responses (including streams) to 64 KiB, and the entire request/response to 10 seconds. Public HTTPS destination syntax and literal IP validation remain enforced; this edition does not claim DNS pinning. Base HTTP 500 remains uncertain without automatic replay; 429 retains four bounded attempts with the same logical identity and exact body.
Installed owners land on the Setup page, where the system has already done what it can and lists only what needs them; new workspaces lead with the widget snippet, with secrets and verification controls under Advanced. Empty inboxes offer widget installation, a link to Setup and a demo. Workspace headers follow the selected workspace; visitors intentionally see the operator's display name. Local development suppresses email without storing messages.
In Reports, choose Export workspace conversations (JSON). The transcripts.json
download includes every workspace conversation across all dates and imports, with
its messages, private notes and attachment names and metadata. Attachment bytes
remain in the workspace; use the portable backup for those. Exports ask for recent
confirmation: accounts without a second factor can choose Email me a sign-in link,
press Sign in from their mailbox in the same browser, then return and press
Continue. This also works for older email-claimed owners whose generated
password was never known. After mailbox confirmation, Profile → Security lets
you set a new password without the old one. Enrolled second factors remain required.
Reports and its analytics CSVs exclude empty widget conversations.
For work on the source, select Node 22.20.0 and pnpm 10.12.4:
pnpm install --frozen-lockfile
pnpm exec rer describe --json
pnpm exec rer check --profile first-partyrer verifyThe complete target checks the contract, real build, typecheck, native runtime,
command journey, current workflows and selected Base copies. For a change, run
its touched tests plus pnpm run typecheck; CI is the full wall. Browser suites
and full verification run on the designated remote runner. Provider fixtures
prove local protocol behavior; hosted acceptance and paid-provider execution
require separate evidence. Preserve the current holds.
After pnpm run build, run pnpm exec rer size --write-statements .local-evidence/size/statements.
The seed-owned declaration uses disposable local D1 installs of
the built application, native owner and widget sessions, and the declared minute
schedule. It checks inbox/list/search, conversation and transcript reads, message
admission, sockets, reports, knowledge, outbound tickets and the AI draft route.
pnpm exec rer check --profile first-party validates the declaration.
The large fixture carries 162,000 contacts, 161,893 conversations, 27,000 messages and outbox rows, 2,835 media rows and 3,034 pending workflow events. The small fixture carries roughly one hundredth; both retain 644 published documents and 373 reply blocks with the historical source-occurrence skew. These are synthetic counts from the 11 November K24/K26/M2 observations, refreshed against migrations through 332. Document text lengths and attachment byte-size distributions remain missing; generated text and 2,048-byte media metadata are placeholders. The fixture moves the bootstrapped owner's native session to its synthetic workspace.
The default 90 FROM-item/expression-depth margin and 10% + 200 rows growth check remain. Reports have initial engineering ceilings of 1M/1.5M rows and explicitly allow cohort growth because they aggregate a selected 90-day window. Those are budgets, not measured provider limits. Existing flows retain the historical working ceilings; added routes have initial engineering budgets. No credentials or real conversations are used. The published 0.2.112 command let consumer work enter the meter, and report statements exceeded the 90-item margin. With the pinned, qualified rer 0.2.117, all 17 unchanged control flows pass (inbox reads 2,359 to 2,403 rows); the copied-build full-table-query fault is refused solely for ceiling and growth. Queues are held. Scheduled knowledge refresh and legacy importer fixtures target admission/wakes; consumer completion is not qualified. The two legacy run rows are discovery fixtures without R2 package custody. The no-key draft flow expects 409 and does not qualify generation.
Verify runs size once nightly and on query/migration changes, using its shared
build. External statement-only handoff: download
size-statements-<full-commit>-rer-<installed-version>-<run-id>-<attempt>;
identity.txt identifies the exact checkout and package, and
statements/{small,large}.json are the stable inputs for separately credentialed
real-D1 statement checks. Do not transfer application data.
To prove rejection, run node tests/size-check/proof.mjs after building. It requires
all original flows to pass, then adds an unbounded conversation read only to a
disposable built Worker copy and requires the same public rer size command to
fail the inbox growth and ceiling checks. Application source stays clean.
Development lives in just-every/run-happytohelp; the private repository is retained for research and evidence only.
- Install: private answers and the rer journey.
- First-run setup: one-screen first run and the Setup page.
- Operations: configuration, data movement, deployment and recovery.
- Updates and recovery: owner customizations and backups.
- Product contract and acceptance: requirements and proof.
- Authoring: source selection and exact Base copies.
MIT. Third-party asset notices remain under docs/licenses.

