Skip to content

00 — Context

Adventive’s internal administrative interface is a single legacy application (adventive-admin, BitBucket-hosted, version 3.3.9) that handles two distinct responsibility domains:

  1. Customer service-level management — operator tooling for managing customer accounts, tiers, entitlements, and configuration.
  2. Billing — invoice generation, line-item rollup, Stripe invoice creation via API, custom invoice PDF rendering, and email delivery via Mailgun.

The application mixes API logic with presentation in a single codebase, does not reflect current Adventive brand guidelines, and has accumulated pain points typical of a long-lived internal tool. It is also the coupling point for a custom billing flow that the Stripe Billing Transition project will evaluate for replacement.

Repo analysis (April 2026) surfaced two findings that raise the urgency of migration beyond architectural debt alone:

  • Production AWS and Bitbucket credentials are hardcoded in source (application/config/adventive.php) — a live secret-exposure risk in every clone and CI artifact.
  • CSRF protection is globally disabled and Duo 2FA is currently bypassed — the admin’s only active access control is JumpCloud username/password.
  • Architectural debt: the admin is the last major internal surface not yet aligned with Adventive’s API-first / Cloudflare Workers direction (Public API migration landed 2026-04; Cloudflare Tunnel landed prior).
  • Secret exposure: AWS access keys, S3 secrets, and Bitbucket OAuth credentials are committed to source for all three environments. Remediation is blocked until secrets are moved to a managed store; this migration provides the natural forcing function.
  • Security posture: CSRF disabled (config.php:320), Duo 2FA bypassed (latest auth commit), payment links secured only by MD5 hash (Billing.php:561). Cloudflare Access restores edge-enforced MFA and eliminates application-managed auth entirely.
  • Presentation coupling: mixing presentation and API logic makes it hard to evolve either independently; a future mobile or partner-facing admin surface is effectively blocked.
  • Brand drift: the visual layer predates the current brand system and reads as legacy to internal users.
  • Billing entanglement: the admin owns both the billing UI and meaningful billing business logic. That logic is the scope-of-analysis input for the sibling Stripe Billing Transition project — separating presentation from API is a prerequisite for cleanly migrating billing.
  • Dependency risk: phpoffice/phpexcel 1.7.9 (archived 2019, no security patches) is installed alongside its successor phpspreadsheet. CodeIgniter 3.1.* receives no active maintenance.
  • In scope:

    • Architecture audit of adventive-admin (routes, data model, integrations, auth). ✓ Complete — see §01.
    • Serverless viability assessment for the API layer (Cloudflare Workers fit). ✓ Complete — see §01.
    • Target architecture: separate API Worker + Cloudflare Pages SPA. ✓ Decided.
    • UI refresh aligned with current Adventive brand guidelines.
    • Auth/authz model: Cloudflare Access for operator identity, application RBAC for authorization. ✓ Decided.
    • Migration plan with phased cutover and rollback. ✓ See §02.
    • Documentation: full planning deliverable set → PDF → Claude Code handoff package.
  • Out of scope:

    • Billing business logic redesign — owned by ../Stripe Billing Transition/. This project defines the seam; that project decides what lives on which side of it.
    • Public-facing customer UI.
    • Migration of the underlying customer / account / billing data stores (unless assessment surfaces a forced dependency — it did not).
    • Partner-specific scheduled reports (Reporting.php, 25 methods) — deferred; evaluated for a dedicated analytics service in Phase 4.
  • Deferred:

    • Mobile or offline operator access.
    • External partner/reseller admin surfaces.
    • Feature additions beyond parity with the current admin.
    • Directory provider choice for Cloudflare Access (JumpCloud vs. Google Workspace) — deferred per ADR.
  1. Written and signed architecture plan matching the Adventive Engineering PDF standard. ✓ This document.
  2. Clear API contract definition (OpenAPI via @hono/zod-openapi) that billing, service-level management, and any future consumer can build against.
  3. Presentation layer demonstrably decoupled — can be rebuilt or replaced without touching the API.
  4. Brand-guideline review sign-off on the new UI direction (reference: brandguide.adventive.com/dashboard).
  5. Cutover plan with rollback path and defined success metrics (operator task completion time, error rate parity or better).
  6. Implementation handoff package ready for Claude Code (CLAUDE.md, PLAN.md, kickoff doc — matching Public API pattern).
  7. Production secrets removed from source and moved to AWS Secrets Manager or Cloudflare secrets before implementation begins.
RoleNameInterest
OwnerJeffrey LambertArchitecture, plan quality, delivery
Approver — UIJeffrey LambertBrand alignment, UX sign-off
Approver — APIPatrickAPI contract, backend architecture sign-off
Approver — Billing UIJeffrey + PatrickJoint sign-off on billing surface area of the admin
InformedAdventive operations team (~10 operators)End users of the new admin

~10 operators total, across three permission tiers:

TierCountCapabilities
Super-admin2All functionality across customer, service-level, billing, and tooling domains
BillingTBDBilling-scoped operations (invoices, payments, managed services, reports). Read access to accounts.
Read-onlyTBDRead access across all surfaces, no mutations

The two super-admins are the only tier with authority to destructive / cross-domain operations. Billing-tier operators can read all customer context but only write billing-domain records.

Visual and interaction language target: https://brandguide.adventive.com/dashboard — the Adventive brand-guide dashboard concept. “Similar in design, refined for utility.” Not a literal reskin; the admin’s job is operator efficiency first.

This project runs first, ahead of the Stripe Billing Transition migration phase. Rationale:

  • The presentation/API separation is a prerequisite for cleanly migrating billing — you can’t cleanly replace a subsystem that shares a codebase with its UI.
  • The new admin will surface whatever billing decision gets made downstream (custom, Stripe-native, or hybrid), so it needs to be designed to tolerate that outcome without rework.
  • Admin-first also front-loads the architectural work that the billing project will consume.
  • The secret-exposure and auth-gap findings make this more urgent, not less.

The billing project can kick off in parallel at its viability-assessment phase, but its migration-plan phase should land after the admin API contract stabilizes.


Update — 2026-04-30: Phase 1 prototype shipped

Section titled “Update — 2026-04-30: Phase 1 prototype shipped”

A visually functional UI prototype is now in place at ~/Repositories/GitHub/Adventive/adventive-admin-ui/ and deploys as a Worker (Static Assets) to genesis-admin.adventive.dev for team review. The prototype establishes the brand language, navigation IA, and operator workflows the team will validate against; it runs entirely against in-memory mocks (no backend wiring) and is not yet behind Cloudflare Access.

What it covers (15 routes):

  • Dashboard — operator landing with revenue KPIs, 12-month stacked-area trend, recent invoices, managed-services queue, quick links.
  • Accounts — list with status filters and search; detail page with all 9 tabs (Settings, Users, Campaigns, Ad Units, Advertisers, Billing History, Permissions & Entitlements, Plan Info, Managed Services).
  • Billing — Overview (KPIs + trend/breakdown chart toggle + collections progress + aging snapshot), Invoices, Account Revenue History (12-month per-account grid), Processing controls, Delinquent, Aging Summary.
  • Tools — Preview Link Lookup, User Lookup, Ad Type Utilization, Ad Type Performance, Cognito Sync, Special Permissions catalog.

Stack chosen and proven: Vite 8 + React 19 + TypeScript + Tailwind 3 + shadcn-flavored primitives + TanStack Router (code-based) + TanStack Query + Recharts. Brand tokens are HSL CSS variables lifted directly from brandguide.adventive.com/dashboard (dark theme, primary purple 250 100% 70%, radius 0.75rem, Stripe-style accent palette, Inter font).

The prototype’s mock data layer at src/lib/mock/ is deliberately structured to mirror the API contract this project will define — replacing it with TanStack Query hooks against adventive-admin-api is the primary handoff between Phase 1 and Phase 2.

Update — 2026-04-30: Phase 2 locked decisions

Section titled “Update — 2026-04-30: Phase 2 locked decisions”

Three open API/architecture questions were closed during the Phase 1 hand-off conversation and are now locked:

  1. API surface shape — hybrid REST + composite endpoints. Strict REST per resource (GET /accounts, /campaigns, /invoices, /users, etc.) plus a small set of composite endpoints for heavy screens. The canonical example: GET /accounts/:id?include=users,campaigns,adunits,advertisers,invoices,managedjobs,permissions,plan returns the full Account Detail screen in one round-trip. Avoids the 9-call waterfall a strict-REST design would force on the legacy /account/detail tabbed page without over-fitting the API to the UI.

  2. Auth posture — Cloudflare Access JWT + Worker session for CSRF/idempotency. Cloudflare Access at the edge handles authn (JumpCloud SAML + WebAuthn replaces the legacy LDAP + Duo flow entirely; the JumpCloud Admin Dashboard group becomes the Access policy). The Worker layers a lightweight signed-cookie session on top — KV-backed — that issues CSRF tokens for mutations, idempotency keys for billing actions, and seeds an audit trail. Defense-in-depth that matters specifically for the billing/processing surface.

  3. Billing scope — read-only in v1, mutations deferred to Stripe Billing Transition. Read-only billing endpoints (overview, invoice list, account history, aging, delinquent) ship in admin-api Phase 2 so the prototype becomes review-able end-to-end. Mutating billing endpoints (test invoice generation, QuickBooks export, dunning sweep, year-end rollover) stay mocked in the UI until the Stripe Billing Transition project provides their replacement. Documented as a deliberate handoff to that project.

These three decisions, plus the existing ADRs for Cloudflare Access, the React SPA stack, and the no-CodeIgniter-retrofit deployment strategy, fully constrain the Phase 2 implementation. The adventive-admin-api Worker scaffold at ~/Repositories/GitHub/Adventive/adventive-admin-api/ reflects them in code.

Update — 2026-04-30: Phase 2 prerequisites surfaced by deeper analysis

Section titled “Update — 2026-04-30: Phase 2 prerequisites surfaced by deeper analysis”

A deep code analysis pass (Phase 2 kickoff) added findings beyond §01:

  • Auth replacement is cleaner than originally assumed. Legacy uses JumpCloud LDAP bind + Admin Dashboard group check + Duo Universal Prompt 2FA. Cloudflare Access fully replaces all three layers with a single JumpCloud SAML/SCIM connector and WebAuthn second-factor. The new admin-api never ships with Auth.php, Jc_auth library, or the Duo SDK — entire surface area removed.
  • Four-database topology, not one. Beyond console (Hyperdrive DB_CONSOLE already provisioned at 059838c4abb64a92a4aece2a6a533a29 for dev), the legacy admin reads from billing (invoicing/payments), aggregate_ro (KPI event metrics), and data_warehouse (Redshift, used only by Report.php). Phase 2 dev needs only DB_CONSOLE. Stg/prd planning must provision additional Hyperdrives for billing and aggregate_ro — captured in §03’s environment matrix.
  • 20+ snowflake customer reports do not migrate as-is. Report.php (~5200 LOC) contains custom SQL for Sonoma, HighSnobiety, Pulpo, NPM, FarmJournal, Afar, Bridgetower, DoubleVerify, Plugshare, Atlantic, Boston Globe, SF Gate, etc. Each is a one-off CSV export with 3–5 join queries. Recommendation: move to versioned templates or a BI tool; do not port to admin-api. Deferred to a Phase 4 analytics-service evaluation.
  • External microservice coupling. Three internal HTTP services (billing_service, account_service, asset_service) are called from PHP via raw curl with no timeout, retry, or circuit-breaker. Phase 2 wraps these with proper resilience (timeout + exponential backoff + bulkhead). The billing_service boundary is co-owned with the Stripe Billing Transition project.
  • Hardcoded production secrets in application/config/adventive.php — full inventory now visible: AWS IAM key/secret, Mailgun API key, Duo secret key, JumpCloud bind password, Bitbucket OAuth token. All require rotation at migration cutover; remediation is a hard prerequisite, not a Phase 2 task. The new admin-api has no equivalent secrets — Cloudflare Access replaces auth credentials, Hyperdrive replaces DB credentials at the binding layer, Mailgun and S3 calls are deferred to billing/billing_service.

These prerequisites are captured downstream: §02 details the new Worker’s surface; §03 details the binding/secret topology; §05 captures the rotation and decommission plan.

Reference — Legacy adventive-admin architecture (current state)

Section titled “Reference — Legacy adventive-admin architecture (current state)”

Source: Deep code analysis performed 2026-04-30 against ~/Repositories/BitBucket/adventive-admin/ (CodeIgniter, version 3.3.9). Promoted from personal Claude memory into this chapter on 2026-06-02 so the reference is team-visible. Treat the legacy repository as read-only until decommissioned.

  • CodeIgniter 3.1.x on PHP 7.3.
  • Composer dependencies that affect the migration: aws/aws-sdk-php, duosecurity/duo_universal_php, chriskacerguis/codeigniter-restserver, phpoffice/phpspreadsheet (PHPExcel is still imported in legacy code paths), firebase/php-jwt, paragonie/sodium_compat.
  • Front end: jQuery + Bootstrap + DataTables + AmCharts + ACE editor + Select2. Versions are not pinned.
  • Sessions: PHP native file handler on the filesystem — does not scale across replicas.

JumpCloud LDAP bind → JumpCloud Admin Dashboard group check → Duo Universal Prompt 2FA → session sets logged_in=true and 2fa_authd=true. Every controller’s __construct() calls jc_auth->is_authenticated(). No CSRF tokens. No fine-grained roles — JumpCloud group membership is the only RBAC.

Migration implication: Cloudflare Access replaces the entire chain. JumpCloud becomes the SAML IdP (or SCIM); Access enforces the Admin Dashboard group; WebAuthn replaces Duo. The Auth.php controller, the Jc_auth library, and the Duo SDK are removed from the new admin-api entirely.

Four databases are configured in application/config/database.json:

  • console — primary; account, campaign, and ad metadata. Hyperdrive DB_CONSOLE exists at id 059838c4abb64a92a4aece2a6a533a29 for dev.
  • billing — invoicing and payments. No Hyperdrive provisioned yet.
  • aggregate_ro / aggregate_rw — event metrics read replicas (KPI tables). No Hyperdrive provisioned yet.
  • data_warehouse — Redshift via PostgreSQL PDO; analytics for some customer reports. Out of scope for admin-api; do not access from a Worker.

Stg and prd planning must provision Hyperdrives for billing and aggregate_ro before reaching parity with legacy.

  • AWS Cognito — user-pool sync via AwsCognito.php library. Operations: adminUpdateUserAttributes(), getUser(). Entry points: Tools/cognitoCheckUser, Tools/cognitoSyncUser. JWTs are stored in the cookie aws-cognito-app-access-token.
  • AWS S3 — override-file versioning via AmazonS3.php library. CRUD operations on the overrides bucket (domain overrideXXX.adventivecdn.com). Entry point: Override/* controller.
  • AWS EC2 + SSMAppDeploy.php library, used for the “deployment users” sidebar list and SSM parameter fetching. Likely deprecated in the cloud-native admin.
  • MailgunMailgun.php curl-based wrapper. Templates: invoice_new, invoice_notify, invoice_pastdue. Most email sends are invoked indirectly via the external billing_service.
  • Duo Security — replaced by Cloudflare Access WebAuthn in the new admin.
  • JumpCloud LDAP — replaced by Cloudflare Access SAML/SCIM in the new admin.
  • billing_service — invoices, payments, revenue history. Owned by the Stripe Billing Transition project. The new admin-api should consume read-only endpoints during transition; mutations move to billing_service entirely.
  • account_service — account creation (Account/validateNewAccount POST).
  • asset_service — ad-thumbnail generation (Campaign/copyAdOrTemplate*).

All three are called with raw curl, no timeout, no retry, and no circuit breaker. Phase 2 wraps these with proper resilience (timeout + exponential backoff + bulkhead).

HTML and JSON are mixed in single controllers. The only true REST endpoints are Campaign_Controller and Managedservices_Controller (both subclass REST_Controller). Everything else returns HTML or returns JSON via json_encode.

Controllers worth porting: Account (list, detail, getUsers, updateAccountAdmin, getCountryRegions), Billing (read-only: index, invoices, history, viewDelinquentInvoices; write paths defer to billing_service), Tools (lookup, userlookup, adtypes, videoBenchmarks, specialPermissions, userSearch, cognito*), Campaign (detail, copyAdOrTemplate*), Override (S3 file CRUD), ManagedServiceJob (create/update), Dashboard, Chart (data-only endpoints).

Controllers NOT to port as-is: Report (~5200 LOC, 20+ customer-specific raw-SQL queries — Sonoma, HighSnobiety, Pulpo, NPM, FarmJournal, Afar, Bridgetower, DoubleVerify, Plugshare, Atlantic, BostonGlobe, SfGate, etc.). Move to a data warehouse plus BI tool, or to versioned templates. Deferred to a Phase 4 analytics-service evaluation.

  • Hardcoded AWS IAM keys, Mailgun API key, Duo secret key, JumpCloud bind password, and Bitbucket OAuth tokens in application/config/adventive.php. Rotate at migration cutover.
  • SQL-injection risk in Report_model (string concatenation for table and column names).
  • No CSRF protection on forms.
  • DataTables endpoint disables the PHP memory limit (ini_set('memory_limit', -1)).
  • No rate limiting on report endpoints.
  • No audit logging on mutations.
  • Account/detail — O(n) queries for accounts, campaigns, ads. No pagination.
  • Billing/history — aggregates all-time invoice data; no date-range filter on the base query.
  • Billing/getMonthSummary — 6+ nested queries in a loop; builds the XLSX in memory.
  • Tools/videoBenchmarks — KPI-table aggregation without obvious indexing.
  • Campaign_Controller/get — DataTables loads the full result into memory.

~/Repositories/BitBucket/adventive-admin/ — read-only reference until decommissioned. Do not modify.