Management panel.
The management panel at /manage/* is a platform operator dashboard for managing tenants, billing, and reviewing audit logs. It is protected by Cloudflare Access with full JWT verification.
Authentication
Management routes verify the Cf-Access-Jwt-Assertion header:
- Cloudflare Access: Handles the login flow and issues a signed JWT
- JWT verification: Concierge fetches the team's JWKS, verifies the RS256 signature, checks the
audclaim againstCF_ACCESS_AUD, and extracts the email
Anyone with a valid Access JWT (matching your Access policy) is authorized. Scope access via your Cloudflare Access policy.
Dashboard
The main dashboard (/manage) shows:
- The authenticated operator's email
- Total tenant count
- Links to tenant management, billing, and audit log
Tenant Management
At /manage/tenants, operators can:
- List all tenants with their email, plan, and creation date
- View individual tenant details including connected accounts
- See a tenant's billing state (credits remaining, used, granted)
- Grant free reply credits to a tenant
Billing Controls
At /manage/billing, operators can:
- View credit packs: See all defined packs with their reply counts, prices, and status
- Create packs: Add new credit packs with name, reply count, INR price, USD price, and sort order
- Update packs: Edit existing pack details
- Activate/Deactivate packs: Control which packs are visible to tenants
- Delete packs: Remove packs entirely
- Grant credits: Give free reply credits to a specific tenant (at
/manage/billing/grant/{tenant_id})
Persona Catalog
At /manage/personas, operators curate the platform‑wide catalog of persona archetypes that the public demo persona picker and the tenant onboarding wizard read from. Each row is a slug + label + description + a serialized PersonaSource (the editable middle, either builder with archetype + sample business fields or custom raw text) + a greeting + a safety verdict.
- List: every row, including drafts and rejects. Tenants and the public demo only see
approvedrows. - Create / edit: the same builder fields a tenant fills in (voice archetype, business name + type, city, goal, catch‑phrases, off‑topics, handoff conditions). Every save resets
safety_statustodraftand enqueues aSafetyJobonSAFETY_QUEUEkeyed by slug. The classifier's verdict (approvedorrejectedwith a vague user‑facing reason) lands back on the row. - System rows: rows with
is_system=1are undeletable. The seed migration ships one —concierge— which powers the homepage live demo. - Audit:
create_persona,update_persona,delete_personaall write to the audit log with the actor email and the slug.
When a tenant picks an archetype during onboarding, the catalog row is copied into their PersonaConfig rather than referenced by slug. Subsequent management edits to the catalog don't reach back into existing tenants — each tenant owns their own snapshot and mutates it independently from /dashboard/persona.
Audit Log
At /manage/audit, operators can review a chronological log of all management actions. Each entry records:
| Field | Description |
|---|---|
actor_email | The operator who performed the action |
action | What was done (e.g., grant_replies, create_pack, update_pack, delete_pack) |
resource_type | The type of resource affected (e.g., billing, credit_pack) |
resource_id | The specific resource identifier (optional) |
details | JSON payload with action-specific details |
created_at | Timestamp |
The audit log shows the most recent 100 entries.
Setup
- In the Cloudflare dashboard, go to Zero Trust > Access > Applications
- Create a Self-hosted application with the path
your-domain/manage*(no slash before the asterisk) - Copy the Application Audience (AUD) Tag from the application overview
- Set
CF_ACCESS_TEAM(your team name) andCF_ACCESS_AUDas plain‑text variables in Workers › concierge › Settings › Variables and Secrets - Add a policy allowing the emails that should have management access
- Deploy with
wrangler deploy
/manage*, not /manage/* or /manage.
The wildcard placement matters. /manage alone matches the dashboard but lets /manage/audit, /manage/tenants/…, and /manage/billing/… through unauthenticated. /manage/* protects the subroutes but leaves the bare /manage dashboard exposed. /manage* covers both.
If /manage 403’s with “Forbidden: Cloudflare Access required” after a successful Access login, run wrangler tail while reloading the page. The worker logs the specific reason (missing token, missing CF_ACCESS_AUD/CF_ACCESS_TEAM, JWT verification error) instead of a bare 403.