Skip to content

05 — Appendix

ResourceURLRelevance
Cloudflare Workers docshttps://developers.cloudflare.com/workers/Primary runtime for admin-api
Cloudflare Pages docshttps://developers.cloudflare.com/pages/Hosts admin-ui SPA
Cloudflare Access docshttps://developers.cloudflare.com/cloudflare-one/policies/access/Operator authentication at edge
Cloudflare Hyperdrive docshttps://developers.cloudflare.com/hyperdrive/MySQL connection pooling from Workers
Cloudflare R2 docshttps://developers.cloudflare.com/r2/Override file storage (replaces S3)
Workers Analytics Enginehttps://developers.cloudflare.com/analytics/analytics-engine/Structured observability from Workers
Wrangler CLI docshttps://developers.cloudflare.com/workers/wrangler/Deploy, dev, secret management
Workers Cron Triggershttps://developers.cloudflare.com/workers/configuration/cron-triggers/Scheduled report delivery replacement
CF Access service tokenshttps://developers.cloudflare.com/cloudflare-one/identity/service-tokens/Playwright E2E auth
PackageURLPurpose
Honohttps://hono.devTypeScript HTTP framework for Workers
@hono/zod-openapihttps://hono.dev/snippets/zod-openapiOpenAPI spec generation from Zod schemas
Zodhttps://zod.devRuntime schema validation
TanStack Routerhttps://tanstack.com/routerType-safe SPA routing
TanStack Queryhttps://tanstack.com/queryServer-state management, caching
shadcn/uihttps://ui.shadcn.comUI component primitives
Radix UIhttps://www.radix-ui.comAccessible headless component primitives
Tailwind CSShttps://tailwindcss.comUtility-first CSS
Vitesthttps://vitest.devUnit + integration testing
Playwrighthttps://playwright.devE2E testing
Stripe Node SDKhttps://github.com/stripe/stripe-nodeStripe API (Workers-compatible)
openapi-typescripthttps://openapi-ts.devUI type generation from OpenAPI spec
ProjectPathRelationship
Public API Cloudflare Migration../Public API Cloudflare Migration/Predecessor project — auth model, deployment pipeline, observability approach mirror this project
Stripe Billing Transition../Stripe Billing Transition/Sibling project — billing surface area of this admin is co-designed with that project’s decisions
Adventive Brand Guidehttps://brandguide.adventive.com/dashboardVisual and interaction language reference for admin-ui

Files analyzed during this planning engagement:

FileDescriptionSourceDate
application/controllers/Auth.phpJumpCloud LDAP auth + Duo 2FA controlleradventive-admin repo2026-04-23
application/controllers/Dashboard.phpDashboard aggregation controlleradventive-admin repo2026-04-23
application/controllers/Account.phpAccount CRUD, user management, service levelsadventive-admin repo2026-04-23
application/controllers/Billing.phpInvoice generation, payment, managed servicesadventive-admin repo2026-04-23
application/controllers/Campaign.phpCampaign and ad managementadventive-admin repo2026-04-23
application/controllers/Report.phpOn-demand reporting (22 methods)adventive-admin repo2026-04-23
application/controllers/Reporting.phpScheduled report delivery (25 cron methods)adventive-admin repo2026-04-23
application/controllers/Tools.phpAdmin tooling: lookup, Cognito sync, deployadventive-admin repo2026-04-23
application/controllers/Override.phpOverride file versioning (S3)adventive-admin repo2026-04-23
application/controllers/Chart.phpChart JSON endpointsadventive-admin repo2026-04-23
application/controllers/ManagedServiceJob.phpManaged service job CRUDadventive-admin repo2026-04-23
application/controllers/Utility.phpCLI utilitiesadventive-admin repo2026-04-23
application/models/Account_model.phpAccount queries (1,321 lines)adventive-admin repo2026-04-23
application/models/Billing_model.phpBilling queries (933 lines)adventive-admin repo2026-04-23
application/models/Campaigns_model.phpCampaign queries (665 lines)adventive-admin repo2026-04-23
application/models/Report_model.phpPartner report queries (5,333 lines, 24 functions)adventive-admin repo2026-04-23
application/models/ManagedService_model.phpManaged service job queries (505 lines)adventive-admin repo2026-04-23
application/models/Stats_model.phpStats queries (1,286 lines)adventive-admin repo2026-04-23
application/config/adventive.phpApplication config including hardcoded secretsadventive-admin repo2026-04-23
application/config/config.phpCI framework config (CSRF disabled at line 320)adventive-admin repo2026-04-23
composer.jsonPHP dependency manifestadventive-admin repo2026-04-23
application/views/56 view files across all controller domainsadventive-admin repo2026-04-23

The following items require a human decision before or during implementation. Items marked BLOCKING must be resolved before the indicated phase begins.

#QuestionBlocking phaseOwnerNotes
OQ-1Identity provider for Cloudflare Access: JumpCloud vs. Google Workspace?Phase 1 (before operators can authenticate)JeffreyDeferred by ADR — but a decision must land before Phase 1 completes. Google Workspace is already in use for email; JumpCloud is the current LDAP provider.
OQ-2Secret rotation scope: which AWS keys are actively used, and by which processes?Phase 0 (prerequisite)Jeffrey + PatrickHardcoded keys include AWS S3 and IAM credentials. Need a full inventory before rotation to avoid breaking the legacy admin during the parallel-run period.
OQ-3Cloudflare account ID and API token provisioning: who owns this?Phase 1PatrickThe GitHub Actions workflows need CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID as repository secrets.
OQ-4New GitHub repositories: where do they live?Phase 1JeffreyThe plan assumes adventive/adventive-admin-api and adventive/adventive-admin-ui on GitHub. Confirm organization name and visibility (private).
OQ-5Hyperdrive configuration: which DB host and credentials?Phase 1PatrickHyperdrive needs a MySQL connection string pointing to the production/staging DB. Confirm the DB host is reachable from Cloudflare Workers (may require allowlisting Cloudflare IP ranges).
OQ-6Custom domain provisioning: admin.adventive.com and admin-api.adventive.comPhase 1 (for staging) / Phase 3 (for production)JeffreyBoth domains must be added to Cloudflare Pages / Workers as custom domains. DNS must point to Cloudflare.
OQ-7Partner report replacement strategy: dedicated analytics Worker, Cloudflare Analytics, or third-party?Phase 4Jeffrey + PatrickReport_model.php is 5,333 lines with 24 bespoke SQL query functions. This is the largest single scope item and cannot be ported inline. Requires a separate architectural decision.
OQ-8Stripe Billing Transition timing: when is Phase B stable?Phase 4 (Cohort 2 migration gate)Jeffrey + PatrickCohort 2 (billing operators) should not migrate before Stripe Billing Transition Phase B is stable. Coordinate cutover timing.
OQ-9Invoice renderer: Cloudflare Browser Rendering vs. external container?Phase 4PatrickFrom Stripe Billing Transition: PDF rendering in Workers is constrained. Browser Rendering is the leading candidate but has cost and concurrency implications at invoice volume.
OQ-10Metering signal contract for Stripe Billing Transition: format, transport, idempotency guaranteesPhase 4Jeffrey + PatrickThe admin-api will surface billing data from Stripe, but the metering signal going into Stripe is owned by the Stripe Billing Transition project. The contract must be stable before billing surface is built in admin-api.
OQ-11Cognito sync tooling: retain or retire?Phase 8Jeffreyapplication/libraries/AwsCognito.php and the Cognito sync in Tools.php may be superseded by the Public API migration or another identity flow. Needs evaluation before tooling cohort is migrated.
OQ-12AppDeploy tooling: retain or retire?Phase 8Jeffrey + Patrickapplication/libraries/AppDeploy.php wraps AWS CodeDeploy. If the legacy EC2 deploy pipeline is retired before Phase 8, this tool becomes irrelevant. Track alongside the EC2 decommission timeline.

TermDefinition
Access GroupA Cloudflare Zero Trust construct that defines a set of users (by email, IDP group, or other attribute) who can match a policy rule. The “Adventive Operators” Access Group contains all ~10 operator email addresses.
AUD tag”Application Audience” — a unique identifier string attached to a Cloudflare Access Application. The admin-api Worker validates that incoming JWTs contain the correct AUD tag, preventing tokens issued for other Access Applications from being used against this API.
CF-Access-Jwt-AssertionHTTP request header set by Cloudflare Access on every authenticated request. Contains a signed JWT with the operator’s identity (email, identity provider, expiry). The admin-api reads this header to identify the operator without an application-managed session.
CohortA group of operators migrated together to the new admin in a single wave. Three cohorts are planned: (1) account management, (2) billing, (3) remaining workflows.
Cron Trigger WorkerA Cloudflare Workers feature that executes a Worker on a cron schedule. Used as the replacement for Reporting.php’s 25 scheduled report delivery methods.
HyperdriveCloudflare’s MySQL (and PostgreSQL) connection pooling and caching layer for Workers. Workers cannot hold persistent TCP connections; Hyperdrive maintains a connection pool on Cloudflare’s infrastructure and proxies queries.
RBACRole-Based Access Control. In this project: three tiers — super-admin (2 operators, all access), billing (billing domain read+write, account read), read-only (all surfaces, no mutations). Tier is derived at runtime from the operator’s email address against Wrangler secret lists.
Schema-freeze policyDuring the parallel-run period (Phases 1–9), no schema changes that break backward compatibility with the CodeIgniter admin are permitted. New nullable columns with defaults are allowed; column drops, renames, and type changes are blocked until the legacy admin is decommissioned.
Service tokenA Cloudflare Access credential (client ID + client secret) that allows non-human callers (Playwright E2E tests, CI pipelines) to authenticate to an Access-protected application without a browser-based identity provider flow.
Smart placementA Cloudflare Workers feature that routes Worker invocations to the data center geographically closest to the bound backend (in this case, the MySQL database). Reduces latency for database-heavy requests without manual region configuration.
WranglerCloudflare’s CLI tool for developing, deploying, and managing Workers and Pages projects. Used for deploys, secret management, tail logging, and local development.
WorkerA Cloudflare serverless function. The admin-api Worker handles all API requests; the invoice-renderer Worker (sibling project) handles PDF generation.
Stripe Billing TransitionThe sibling project that migrates Adventive’s billing pipeline from the custom admin-managed rollup to Stripe Billing as the system of record. The admin’s billing surface is co-designed with this project’s decisions.
Parallel runThe period during which both the legacy CodeIgniter admin and the new admin-api/admin-ui are live simultaneously, pointing at the same database. Operators migrate cohort-by-cohort; either admin can be used by any operator until their cohort is fully migrated.
R2Cloudflare’s S3-compatible object storage. Used in this project as the replacement for AWS S3 for override file versioning.
Override fileAn operator-managed configuration file (stored in S3/R2) that overrides platform defaults for specific accounts. Versioned; operators can view history and roll back.
Managed service jobA manually-created billing line item attached to a customer account for managed services (e.g., campaign management fees). Separate from subscription-based charges.

Admin UI universal search — Phase 3 shipped + future polish

Section titled “Admin UI universal search — Phase 3 shipped + future polish”

Status: Phase 3 shipped 2026-04-30. All four sub-features below are live in dev. Promoted from personal Claude memory into this appendix on 2026-06-02.

  1. Type-prefixed searchacct:, inv:, and user: (with full-word forms account:, accounts:, invoice:, invoices:, users:) narrow the fan-out before the API call. Implementation: parseQuery() in src/components/admin/search-palette.tsx strips the prefix and sets the types filter on the useSearch hook.
  2. Server-side multi-resource searchGET /api/search?q=<query>&types=<csv> on admin-api. Mounted in src/index.ts; source in src/routes/search.ts. Fans out across console.account (accounts), console.users (users), and billing.billing_invoice (invoices, numeric q only) in parallel via Promise.all. Per-type limit is 5.
  3. Recent items — sessionStorage-backed list (adv-recent-items, cap 10). useRecentItems() hook plus pushRecentItem() imperative push in src/lib/use-recent-items.ts. The account detail page auto-tracks on mount.
  4. Grouped results UI — the palette renders one section per non-empty bucket (Accounts, Users, Invoices, Recent) with section headers. Keyboard nav (↑/↓) traverses the flat ordered list across sections; Enter routes; Esc closes.

Future polish (not blocking; surface when relevant)

Section titled “Future polish (not blocking; surface when relevant)”
  • Invoice substring search. Invoices currently match only by exact numeric inv_id. A substring match across account name → joined invoice list would let, for example, “Sonoma” surface their open invoices in the palette.
  • Per-invoice detail route. The palette routes invoices to /billing/invoices (the list page). When an invoice detail page exists, deep-link directly.
  • Cross-tab recents sync. sessionStorage does not sync across tabs. localStorage would, at the cost of not resetting on browser close. Ad-hoc BroadcastChannel is the cleanest fix if Jeff flags this.
  • Result ranking. The current per-type ORDER BY is name ASC (accounts) and last_login DESC (users). A unified relevance score (text-match position plus recency) could improve ordering when the user types ambiguous terms.

All formal decisions are in decisions/. Summary of locked-in decisions referenced throughout this document:

DecisionSummaryFile
ADR-1Operator auth = Cloudflare Access. Directory provider deferred.decisions/2026-04-XX-cloudflare-access-auth.md
ADR-2UI stack = React + Vite + TypeScript SPA on Cloudflare Pagesdecisions/2026-04-XX-ui-stack.md
ADR-3API stack = TypeScript + Hono on Cloudflare Workers, OpenAPI via @hono/zod-openapidecisions/2026-04-XX-api-stack.md
ADR-4Deployment = parallel run, cohort-by-cohort operator migrationdecisions/2026-04-XX-deployment-strategy.md
ADR-5No CodeIgniter retrofit — legacy app inventoried, not modifieddecisions/2026-04-XX-no-retrofit.md
ADR-6Data store = existing DB, no migration in scopedecisions/2026-04-XX-data-store.md
ADR-7RBAC = super-admin (2), billing, read-only; ~10 operatorsdecisions/2026-04-XX-rbac.md

VersionDateAuthorChange
1.02026-04-23Jeffrey LambertInitial planning deliverable — repo analysis complete; all chapters populated; PDF generated