Skip to content

Reform Assistant — Developer docs ​

Reform Assistant is the family-facing cloud portal for the Hermes "Prison Reform" Securus eMessage service. Families sign up, add their incarcerated loved one (the inmate), and pay for a plan through Stripe. Once payment lands, the portal marks the inmate as waiting to be connected; the Hermes host picks that up, registers the inmate with the Securus skill, and grants a monthly message allowance. The portal then shows usage reported back by Hermes.

Everything lives in one folder of the Hermes repository, cloud/reform-assistant/, which has its own package.json and lockfile (it is not part of the root npm workspaces).

What is in the box ​

PieceTechnologyWhere
API and request routingOne Cloudflare Worker, TypeScript, no framework, no runtime dependenciessrc/
PersistenceCloudflare D1 (SQLite), binding DBmigrations/0001_init.sql, migrations/0002_checkout_sessions.sql
Event archiveCloudflare R2 bucket reform-assistant-files, binding FILESStripe webhook payloads (stripe/events/) and duplicate-subscription records (stripe/duplicates/)
Static sitePlain HTML, CSS, and ES-module JavaScript served by Workers Static Assets, binding ASSETSpublic/
BillingStripe Checkout, Billing Portal, and webhooks over the REST API with fetch (no Stripe SDK)src/billing.ts, src/stripe_webhook.ts
Hermes linkBearer-token JSON endpoints under /api/hermes/ plus a standard-library Python cron scriptsrc/hermes.ts, hermes_bridge/portal_sync.py

Architecture at a glance ​

Family-facing traffic and billing:

text
 Family browser
      |
      v
 +---------------------------------------------+
 | Cloudflare Worker  (src/index.ts)           |
 |   /api/*   -> Worker routes (JSON)          |
 |   others   -> env.ASSETS.fetch (public/)    |
 +-----+---------------+-----------+-----------+
       |               |           |
       v               v           v
   D1 (DB)         R2 (FILES)   Stripe REST API
   users,          stripe/      Checkout sessions,
   sessions,       events/      Billing Portal
   inmates,        <id>.json
   subscriptions,
   connections,
   usage_events,
   stripe_events
       ^
       |  POST /api/stripe/webhook  (Stripe-Signature verified)
     Stripe

Hermes link (the Worker never calls Hermes; Hermes polls the Worker):

text
 Hermes cron (no-agent job, every few minutes)
      |
      v
 hermes_bridge/portal_sync.py
      |   Authorization: Bearer <HERMES_LINK_TOKEN>
      |   GET  /api/hermes/connections?status=pending
      |   POST /api/hermes/connections/<id>/ack
      |   GET  /api/hermes/allowances
      |   POST /api/hermes/usage
      v
 Cloudflare Worker  --->  D1
      |
      v  (local subprocess + local Securus store)
 Securus skill  skills/communication/securus-emessage/scripts/securus_poc.py
   add_contact, stamps show / grant / revoke

Plans ​

The Worker's GET /api/plans is the single source for plan data; the landing page shows the same numbers as static HTML.

idlabelpricebillingsavemessages per month
monthlyMonthly$8.00per monthnone40
quarterly3 months$21.60billed once10%40
annual12 months$81.60billed once15%40

The allowance is per month on every plan. Quarterly and annual subscribers are billed once but get the same 40 messages in each calendar month of their billing period; the portal and the Hermes bridge work with a monthly window inside the Stripe billing period (see Monthly allowance window).

Where to go next ​