Skip to content

Standalone Admin Application -- Feature Exploration & PRD

Product: Objectuve (codename: Enkidu) Feature: admin_dashboard/ -- standalone operator application Status: Shipped — April 2026 Date: April 2026 Type: Infrastructure PRD


1. Problem Statement

Objectuve ships with an 8-tab admin surface at /admin inside the Ionic mobile app. Today (pre-launch, zero users), that arrangement is defensible — the solo founder is the only operator, there are no user reports to triage, and no support tickets to answer. But the very first week of public launch will break every assumption baked into the current design, and within 6-12 months the seams will split wide enough to materially harm the business.

Seven concrete ways the current in-app admin breaks under load:

  1. Wrong form factor. Moderation, user investigation, billing reconciliation, and log inspection are desktop workloads. Triaging a content flag on a 375px iPhone viewport means pinching, scrolling, and losing context. Ionic's segmented-tab pattern collapses at 8 tabs even on tablet. When the support queue hits 50 pending items, this is not a cosmetic issue — it's an hourly productivity tax.

  2. Frontend-filtered user search. AdminUsersTable.vue fetches the entire user list and filters client-side. At 500 users (Oct 2026 low end) it's wasteful. At 5,000 users (Apr 2027 low end) it's broken. At 100,000 users (Apr 2029) it won't load. The fix requires a backend search API — not a frontend tweak.

  3. Single admin boolean = no delegation. User.admin is a binary switch. You cannot hire a moderator without giving them the power to delete users, roll back deploys, or view billing. The first support hire in Q4 2026 requires RBAC, not another boolean.

  4. No audit trail. When a moderator approves a flag, bans a user, or changes a feedback post's status, nothing is logged. For a Public Benefit Company with GDPR and right-to-be-forgotten obligations, "who deleted this user and why" is not a nice-to-have. It's a compliance requirement.

  5. Admin code ships to every mobile device. Every Admin*.vue component is bundled into the Ionic app that end users install. It's behind an auth gate, but it still bloats the bundle, increases build time, and creates a larger attack surface on user-owned devices than is necessary. Removing it shrinks the mobile app.

  6. No surface for the next seven PRDs. Meet Coach needs persona cost attribution. Accountability Partners needs mutual-streak reconciliation. Dedicated AI Service needs LiteLLM dashboards. Phase 7 Teams needs billing webhook log + refund workflows. None of these have a home in the current Admin.vue, and each tab we bolt on makes the Ionic segmented control more unusable.

  7. The original Item 59 plan is too late. Roadmap Item 59 defers "Standalone Admin Portal" to Phase 7 (Q1-Q2 2027). That means we launch publicly in Jun-Aug 2026, acquire 500-2K users, hire a support person, take our first GDPR request, process our first content moderation appeal, and onboard our first paying Supporters — all while operating the platform from an 8-tab view inside a mobile app. The strategic risk of that posture exceeds the cost of extraction.

What breaks if we keep this arrangement: Moderation SLA slips past the first weekend of launch. The first GDPR data-export request reveals we have no tooling and scramble to write rake tasks on the fly. The first support hire costs a day of scaffolding a "moderator" role that doesn't exist. An AI cost spike goes unnoticed for a week because nobody checks LiteLLM directly. The first Stripe webhook failure corrupts a Supporter subscription, and we find out from the user. None of these are hypothetical — each is a known failure mode of the current architecture, and each is cheaper to prevent than to recover from.

What a standalone admin app solves: one operational surface, desktop-first, separately deployed, RBAC-gated, audit-logged, extensible module-by-module as the business grows. It is the operational backbone of a platform that intends to serve 1.5M users and 8K teams by 2031.


2. Goals & Success Metrics

Primary Goals

  1. Unblock public launch with a moderation + support surface that a solo founder (and later a small support team) can actually operate efficiently.
  2. Enable role delegation so the first support/moderator hire can be productive without full super-admin access.
  3. Close compliance gaps (GDPR export, right-to-be-forgotten, admin audit log) that are preconditions for launching publicly as a Public Benefit Company.
  4. Create a home for every PRD's admin requirements so Meet Coach, Partners, Teams, and AI-service features can ship their admin surfaces into a real application instead of bolting tabs onto a mobile app.
  5. Remove admin weight from the mobile bundle so the end-user app gets smaller and more secure over time.

Success Metrics

Metrics here do NOT ladder up to time-on-tool. Per the anti-social philosophy in mission.md, the goal is operator efficiency and platform health, not admin engagement.

MetricPhase 0 target (Sep 2026)Phase 2 target (Dec 2026)Phase 4 target (Jun 2027)
Time to triage a content flag (p50)< 90 sec< 60 sec< 30 sec
Time to find a user by email/username (p50)< 5 sec< 3 sec< 2 sec
% of admin actions with audit log entry100%100%100%
GDPR data export turnaround (p95)< 72 hours< 24 hours< 4 hours
GDPR deletion turnaround (p95)< 72 hours< 48 hours< 24 hours
Moderation SLA (time from flag creation to review, p95)< 24 hours< 12 hours< 4 hours
Distinct admin roles in production1 (super_admin)3 (super, mod, support)6 (add billing, read_only, team_admin)
Bundle size removed from ionic_frontend0 KB (Phase 0 still dual-hosted)~50 KB gz (admin extracted)~50 KB gz (fully removed)
Admin app p95 page load< 2 s< 1.5 s< 1 s
Admin cost/month (incremental infrastructure)< $5< $10< $20

Guardrails

  • No ad-hoc privileged actions. Every destructive action is logged. Every bulk action requires typed confirmation. No "select all + delete" without audit context.
  • Admin app never weakens user-facing security. The admin app hits the same GraphQL endpoint with the same authorization rails; RBAC is enforced in the backend, never only in the frontend.
  • Admin queries do not degrade user-facing performance. Reports that touch 90 days of growth data must be cached or moved to a read replica before they block a user check-in.
  • Admin work is bounded by its own anti-social norm: target < 30 min/day of operator time at 10K MAU. If the tool needs more, it's the wrong shape.

3. Strategic Rationale

Why standalone, not "another tab in Ionic"

The existing ionic_frontend is a mobile-first Vue 3 + Ionic app. Ionic's opinionated component library (ion-segment, ion-content, ion-page) is optimized for 375-428px viewports. Admin work wants dense tables, multi-column layouts, keyboard shortcuts, large text inputs, file uploads, and persistent sidebars. Every admin tab we've built so far has fought Ionic's defaults — compare AdminUsersTable.vue (scrollable 7-column table shoehorned into ion-card) against what you'd want for triaging 200 users. The form factor mismatch compounds with every new feature.

A standalone Vue 3 + Vite + Tailwind app — same conventions as ionic_frontend, minus Ionic — gives us desktop-native layouts without rebuilding the skill tree.

Why now, not Phase 7 like Item 59 suggests

Roadmap Item 59 was scoped when admin work was purely internal. Three things changed that make the Q1-Q2 2027 timing too late:

  1. GDPR compliance is a launch gate, not a Phase 7 nicety. The first public signup creates a right-to-be-forgotten obligation. The current admin has zero tooling for that — it would fall back to rake tasks written in panic mode. Extracting admin before public launch is the cheapest path to having a GDPR wizard that actually works.
  2. Moderation at 500+ MAU needs bulk actions and pattern detection. The existing single-flag review flow is fine at 0 flags/day. It breaks around 20/day, which arrives with the first subreddit post. Bulk ops + pattern detection need a desktop UI to be usable.
  3. Every PRD in the queue depends on admin surfaces. Meet Coach persona analytics, Partners abuse detection, Dedicated AI Service cost dashboards, Phase 5 auto-adjustment audit, Phase 7 billing console, Teams admin dashboards — every one of these has "admin surface for X" in its scope. Deferring the admin app to Phase 7 means the admin surfaces for those features either ship late or get jammed into Ionic tabs and then rewritten twice. Extracting the shell now lets each PRD land its admin work in the right place the first time.

Why a new app in the monorepo, not a new repo

Keep admin_dashboard/ in the same monorepo as ionic_frontend, rails_api, marketing_landing, and docs_site. The monorepo already has per-app GitHub Actions workflows, per-app Firebase Hosting targets, and a docker-compose pattern for local dev. A sibling directory is the lowest-friction place to put it and reuses every existing pattern. It also keeps GraphQL type changes and admin UI changes in the same commit when they co-evolve, which they will constantly.

Why not deferred beyond RBAC

You could argue we should at least wait until we need the second admin role before extracting. Two problems: (1) the current in-app admin has architectural debt (client-side user search, no audit log, no GDPR tooling) that needs to be fixed anyway, and (2) fixing that debt inside Ionic produces throwaway work. Extracting first, then fixing, does the work once.

What this is not

  • Not a rewrite of end-user features. The user-facing Ionic app is untouched.
  • Not a new backend service. The admin app is a static Vue bundle hitting the existing rails_api GraphQL endpoint. No new Cloud Run service.
  • Not a permanent home for every operator tool. Some tools (Sidekiq web, LiteLLM admin UI) stay where they are and get deep-linked. The admin app is a hub, not a monolith.

4. Architecture Design

4a. Application Structure

New directory at the monorepo root: /Users/joshualockhart/code/enkidu/admin_dashboard/

admin_dashboard/
  src/
    main.ts
    App.vue
    router/
      index.ts                    role-gated routes
      guards.ts                   RBAC enforcement
    stores/
      auth.ts                     Clerk + current admin user
      audit.ts                    action logging helpers
    lib/
      apollo.ts                   Apollo client (same patterns as ionic_frontend)
      clerk.ts                    Clerk initialization
      graphql/                    generated types
    components/
      layout/
        AppShell.vue              sidebar + topbar + content slot
        Sidebar.vue
        Topbar.vue
        CommandPalette.vue        Cmd+K
      users/
      moderation/
      ai-ops/
      billing/
      teams/
      audit/
    views/
      LoginView.vue
      DashboardView.vue           home: north-star metric + alerts
      users/
        UserSearchView.vue
        UserDetailView.vue
      moderation/
        QueueView.vue
        PatternsView.vue
      ai-ops/
        CostDashboardView.vue
        PromptsView.vue
      ...
    constants/
      graphql/                    query/mutation constants (ionic_frontend pattern)
      roles.ts
  public/
  index.html
  vite.config.ts
  tailwind.config.js             extends design_system/ tokens
  tsconfig.json
  package.json
  .storybook/                    same shadcn-style Storybook as ionic_frontend

Reuses monorepo conventions:

  • Vue 3 + TypeScript + Vite (same toolchain as ionic_frontend minus Ionic).
  • Apollo Client 3 (same patterns, same query constants style).
  • Pinia for state.
  • Tailwind with the same design tokens from design_system/.
  • Storybook for component development.
  • Sentry for error tracking with a separate project DSN.
  • Vitest for unit tests; Cypress for E2E.

Rejects from ionic_frontend:

  • No Ionic. No @ionic/vue, no ion-* components, no Capacitor. Desktop-first.
  • No mobile router hacks; standard vue-router with history mode.

4b. Authentication & Access Control

Clerk integration

A separate Clerk application (or a separate frontend instance of the same Clerk app, if Clerk supports multi-origin) lets admin.objectuve.com authenticate independently from app.objectuve.com. Admin uses email/password + SSO (Google Workspace for the founder initially; SAML/OIDC SSO added in Phase 5 for enterprise).

Defense in depth — authentication has three independent gates:

  1. Clerk gate. User must be signed in via Clerk on the admin origin. Unauthenticated users never see the admin shell.
  2. Email allowlist gate. Frontend checks the signed-in Clerk user's email against an allowlist (env var at build time for Phase 0, database table from Phase 1 onward). Non-matching emails see an access denied screen and cannot fetch GraphQL.
  3. Backend RBAC gate. Every admin GraphQL operation calls a new authorize_admin_role!(role) helper in QueryType/MutationType that looks up AdminRole records for the current user and returns GraphQL::ExecutionError.new('FORBIDDEN') if the role is insufficient. The frontend gate is a UX convenience; the backend gate is the security boundary.

Role-based access control

Replace User.admin boolean with an AdminRole join table (detailed in §6). Initial role catalog:

RoleDescriptionCapabilities
super_adminFounder / engineering leadEverything, including role management, hard-delete, deploy rollback
adminGeneralist operatorEverything except role management, hard-delete, deploy rollback
moderatorContent moderation onlyReview flags/reports, ban users (soft), community health actions
supportUser support onlyView user details, read-only on moderation, respond in support inbox
billingBilling/finance operatorSubscription and webhook inspection, refund workflow with dual confirmation
team_admin(Phase 3) Scoped to one teamTeam admin operations for a specific team only
read_onlyObserver (e.g., investor view)All read-only queries, no mutations

Roles are additive: a user can hold multiple. moderator + support + billing is a common shape for a senior operator without full admin.

User.admin boolean is not removed immediately — it becomes a derived "has any admin role" field for backward compatibility while the Ionic /admin route is being deprecated. Migration details in §6 and §7.

Additional controls

  • Two-factor for destructive operations. Hard-delete, role grant/revoke, and refund > $100 require re-verification (Clerk step-up auth or a TOTP prompt).
  • Typed confirmation. Destructive actions require typing the target identifier (e.g., type username to confirm ban).
  • IP allowlist for super admin (Phase 5). Cloud Armor rule on the admin hosting target for sensitive super-admin operations.
  • Session timeout. 4-hour idle timeout on admin sessions (Clerk-configurable).

4c. Layout & Navigation

Persistent three-zone layout (desktop-first, 1280×800 minimum, responsive down to 768px for tablet-on-couch scenarios, not optimized below that):

  • Left sidebar (240px): logo, search bar (Cmd+K), primary nav grouped by module (Dashboard, Users, Moderation, Content, AI Ops, Billing, Teams, Support Inbox, System, Analytics, Audit). Current admin user + role badge at bottom with sign-out.
  • Top bar (56px): breadcrumbs, environment indicator ("production" red / "staging" yellow), notification bell (queue counts), keyboard shortcut hint.
  • Main content (remaining): route-based pages. Tables, detail panels, empty states all use the same Tailwind + design-token system as ionic_frontend.
  • Command palette (Cmd+K): global search across users/communities/flags/tickets plus navigation and quick actions ("ban user", "find flag by ID"). Modeled on Linear's palette.

4d. URL & Routing

  • New subdomain: admin.objectuve.com (plus staging.admin.objectuve.com for the staging environment).
  • Firebase Hosting target: enkidu-admin (and enkidu-admin-staging), added to firebase.json alongside existing enkidu-app, enkidu-app-staging, enkidu-marketing, enkidu-docs, enkidu-api-docs, enkidu-storybook, enkidu-design.
  • Role-gated routes via a beforeEach guard that reads the signed-in admin user's roles from the Apollo cache:
/                           dashboard (any role)
/users                      requires support|admin|super_admin
/users/:id                  requires support|admin|super_admin
/moderation                 requires moderator|admin|super_admin
/moderation/:flagId         requires moderator|admin|super_admin
/communities                requires moderator|admin|super_admin
/ai-ops                     requires admin|super_admin
/billing                    requires billing|admin|super_admin
/teams                      requires admin|super_admin (Phase 3)
/teams/:id                  requires team_admin(:id)|admin|super_admin
/support                    requires support|moderator|admin|super_admin
/system                     requires admin|super_admin
/audit                      requires admin|super_admin
/analytics                  requires read_only|admin|super_admin
  • Deep links are first-class: admin.objectuve.com/users/abc123, admin.objectuve.com/moderation/flag-456. Shareable in Slack/email/tickets.

5. Feature Scope (Module-by-Module)

Each module below lists: pages, existing vs. new GraphQL operations, permissions, key UI elements. "Existing" means the operation is already in rails_api/app/graphql/ today.

5a. User Management

Pages:

  • /users — search and list
  • /users/:id — full user detail
  • /users/:id/timeline — activity timeline
  • /users/:id/ai-history — every Coach interaction for this user
  • /users/:id/impersonate — confirmation dialog for "view as user"

GraphQL:

  • Existing: users(...) (client-side only today — needs backend rework)
  • New searchUsers(query, filters, first, after) with cursor pagination, filters for: admin role, banned, soft-deleted, plan (free/supporter/team), country, signup date range, last active date range, flag count. Backend implements Postgres full-text search over users.email, users.username, users.display_name with trigram indexes.
  • New user(id) that returns the full admin view (preferences, subscription, AI history summary, flag count, ban history, role assignments).
  • New userTimeline(userId, types, first, after) returning a merged activity stream across goals, events, mood logs, AI interactions, flags, admin actions.
  • New mutations: banUser(userId, reason, durationDays), unbanUser(userId, reason), softDeleteUser(userId, reason), restoreUser(userId), hardDeleteUser(userId, gdprRequestId, typedConfirmation), grantAdminRole(userId, role, expiresAt), revokeAdminRole(userId, role, reason), forcePrivacyToggle(userId, field, value, reason), startImpersonation(userId, reason), endImpersonation().

Permissions: search visible to support|admin|super_admin; ban/unban to admin|super_admin; hard-delete to super_admin only and requires typed confirmation + GDPR request link; impersonation to admin|super_admin and auto-terminates on tab close.

Key UI elements:

  • Backend-paginated table with sort + filter.
  • User detail panel with tabbed sections (Profile, Activity, AI, Flags, Subscription, Roles, Audit).
  • "Impersonate" button that opens app.objectuve.com in a new tab with a short-lived impersonation token; banner on user side says "an operator is viewing as you" (user-visible transparency is a PBC obligation); auto-logs an AdminAction entry with type impersonation_started and impersonation_ended.

5b. Content Moderation

Pages:

  • /moderation — queue with filters
  • /moderation/:flagId — flag detail with full context
  • /moderation/patterns — pattern detection view
  • /moderation/rules — custom rule management
  • /moderation/appeals — appeals queue

GraphQL:

  • Existing: moderationQueue, reviewContentFlag, reviewContentReport (all in rails_api/app/interactions/content_moderation/, with GraphQL wrappers at rails_api/app/graphql/mutations/review_content_flag.rb, review_content_report.rb).
  • New moderationQueue(filters: {severity, source, contentType, ageHours, flaggedUserId}, first, after) with pagination and richer filters.
  • New mutation bulkReviewContentFlags(flagIds, decision, reason) with per-action audit entries.
  • New mutation bulkActionOnUserContent(userId, action: DELETE|HIDE|RESTORE, scope: {types}, reason) — bulk action across a user's posts, comments, and media. Requires typed confirmation.
  • New query flaggedUserPatterns(windowDays) returning users with flag-count anomalies.
  • New mutations createModerationRule(pattern, action), updateModerationRule(id, ...), deleteModerationRule(id, reason).
  • New query contentAppeals(status, first, after) and mutation reviewContentAppeal(id, decision, reason) (requires building a user-facing appeals surface in Ionic later).

Permissions: moderator or above; bulk user content actions are admin or above.

Key UI elements: filter bar (severity/source/age), multi-select row actions, detail panel with the flagged content rendered in-place (with a clear "flagged content preview" label), side-by-side "similar flags" panel. Pattern view shows a heatmap of flags per user over time.

5c. Community Management

Pages:

  • /communities — list with health scores
  • /communities/:id — detail with members, goals, posts, audit events
  • /communities/:id/members — member management
  • /communities/:id/restore — restore soft-deleted communities

GraphQL:

  • New adminCommunities(filters, first, after) returning paginated list with health scores (reuses existing Community#health_score).
  • New adminCommunity(id) returning full admin view (members, recent posts, flag count, member growth rate, deleted_at).
  • New mutations restoreCommunity(id, reason), featureCommunity(id, untilAt), unfeatureCommunity(id, reason), forceRemoveCommunityMember(communityId, userId, reason), changeCommunityRole(communityId, userId, role, reason).

Permissions: moderator or above.

5d. Goals & Events (Audit & Recovery)

Pages:

  • /users/:id → Goals tab — goal list including soft-deleted
  • /goals/:id/audit — full audit of changes and auto-adjustments
  • /streaks/audit — streak integrity report

GraphQL:

  • New adminGoal(id) including soft-deleted state and full event history.
  • New mutation restoreGoal(id, reason), restoreGoalEvent(id, reason).
  • New query autoAdjustmentAudit(userId?, windowDays) (Phase 5 dependency — Phase 5 Intelligence PRD introduces goal auto-adjustment).
  • New query mutualStreakAudit(partnershipId) (Accountability Partners PRD dependency — needs to verify that mutual streak counters are consistent across both users' views).
  • New mutation resetMutualStreak(partnershipId, reason).

Permissions: admin or above.

5e. AI Operations (LiteLLM Cockpit)

Hooks into Dedicated AI Service PRD — see docs/product/dedicated-ai-service-prd.md.

Pages:

  • /ai-ops — cost dashboard (per virtual key, per feature, per persona)
  • /ai-ops/budgets — budget alerts + kill switches
  • /ai-ops/models — model performance comparison
  • /ai-ops/prompts — prompt version tracking
  • /ai-ops/ab-tests — A/B test analysis
  • /ai-ops/users/:id — per-user AI interaction history

GraphQL:

  • New aiCostReport(start, end, groupBy: VIRTUAL_KEY|FEATURE|PERSONA|USER).
  • New aiBudgets / setAiBudget(virtualKey, monthlyLimitUsd, alertThresholds).
  • New aiModelPerformance(start, end) returning latency, error rate, cost/request per model.
  • New coachPromptVersions / coachPromptVersion(id) — tracks which prompt template was used when.
  • New aiAbTest(id) returning variant vs. control metrics (persona adoption, engagement, retention).
  • New userAiHistory(userId, first, after) returning Coach service call log.
  • New mutation killSwitchAiFeature(feature, reason) — disables a feature flag across the platform.

Direct links: card on this page linking to the LiteLLM admin UI (read-only) and to the Rails Sidekiq web interface.

Permissions: admin or above; kill switches require super_admin.

5f. Billing & Subscriptions (Phase 7)

Hooks into Phase 7 Monetization & Teams PRD.

Pages:

  • /billing — overview (MRR, churn, active subscriptions across Supporter + Teams)
  • /billing/webhooks — webhook event log and replay
  • /billing/subscriptions/:id — subscription detail
  • /billing/refunds — refund workflow
  • /billing/reconciliation — monthly reconciliation report
  • /billing/seats — team seat reconciliation

GraphQL:

  • Existing: Billing::CreateCheckout (rails_api/app/interactions/billing/create_checkout.rb).
  • New billingOverview(period), billingWebhooks(status, first, after), billingWebhookDetail(id), replayBillingWebhook(id, reason), subscriptionDetail(id), createRefund(subscriptionId, amount, reason, typedConfirmation), reconcileStripeSubscription(id, reason) (manually sync from Stripe/Clerk Billing to our DB), reconcileTeamSeats(teamId), monthlyBillingReport(month).

Permissions: billing or above; refunds > $100 require two-factor.

Key UI elements: webhook event table with replay button, refund form with typed confirmation, reconciliation diff view showing "Stripe says X / we say Y / resolution".

5g. Team Admin Operations (Phase 7)

Hooks into Roadmap Items 52, 55 and Phase 7 Monetization & Teams PRD.

Pages:

  • /teams — list with health scores
  • /teams/:id — detail (members, goals, subscription, activity)
  • /teams/:id/manager-view — Manager View (Item 55) — grid of all members with streak, goal completion, health signal
  • /teams/:id/billing — team subscription management
  • /teams/:id/moderation — team-scoped moderation

GraphQL:

  • New adminTeams(filters, first, after), adminTeam(id), teamManagerView(id), teamHealthSignals(id), teamCsvExport(id, period).
  • New mutations for team lifecycle: suspendTeam(id, reason), restoreTeam(id, reason).

Permissions: team_admin(:id) or admin or super_admin. team_admin is scoped — a team admin for team X cannot see team Y.

5h. Support Inbox

Pages:

  • /support — unified inbox (feedback posts + content reports + appeals + future direct tickets)
  • /support/:id — ticket detail
  • /support/templates — response template management

GraphQL:

  • Existing: feedbackStats, updateFeedbackPostStatus (rails_api/app/interactions/feedback/update_feedback_post_status.rb), Feedback::NotifyVotersJob.
  • New supportInbox(status, assignee, first, after) — unified query across feedback, reports, appeals.
  • New mutations assignSupportTicket(id, assigneeId, reason), updateSupportTicketStatus(id, status, reason), replySupportTicket(id, templateId?, body).
  • New CRUD: supportTemplates, createSupportTemplate, etc.

Permissions: support or above.

5i. System Operations

Pages:

  • /system/health — extends current Monitoring tab
  • /system/jobs — Sidekiq integration (embedded iframe or deep link)
  • /system/scheduled-jobs — Crono job status (rails_api uses the Crono gem — see rails_api/config/initializers/crono.rb)
  • /system/queries — read-only database query inspector with safety limits
  • /system/flags — feature flag management
  • /system/env — environment variable viewer (secrets redacted)
  • /system/deploys — deploy history and rollback trigger

GraphQL:

  • Existing: /health endpoint (not GraphQL — stays REST) exposes DB/Redis/Sidekiq/LiteLLM status.
  • New systemHealth, sidekiqStats, cronoJobs, featureFlags, setFeatureFlag(key, value, reason), environmentVariables (redacted), deployHistory, triggerRollback(deployId, reason).

Permissions: admin or above; triggerRollback and setFeatureFlag for kill-switch flags require super_admin + two-factor.

5j. Analytics & Reporting

Pages:

  • /analytics/north-star — cumulative goals completed (the north-star metric). This is the admin home dashboard.
  • /analytics/cohorts — cohort retention curves
  • /analytics/activation — activation funnel
  • /analytics/pbc-report — PBC impact report generator

GraphQL:

  • Existing: adminStats (currently backed by AdminStatsService with no caching; 90 days × 2 queries = 180 queries per request). Rework to read from materialized views or a nightly-refreshed cache.
  • New goalsCompletedTrend(start, end, resolution), cohortRetention(cohort, resolution, weeks), activationFunnel(cohort), pbcImpactReport(year), customQuery(name, params).

Permissions: read_only or above can view; only admin or above can export CSV or run the PBC report.

Key UI elements: large dashboard card showing cumulative goals completed trending toward the north-star numbers (2.5K→1.5M→10M). Every decision in the product eventually surfaces here.

5k. Audit & Compliance

Pages:

  • /audit — universal admin action log with filters
  • /audit/roles — role assignment history
  • /gdpr — GDPR request queue
  • /gdpr/:id/export — data export wizard
  • /gdpr/:id/delete — deletion wizard (orchestrates hard-delete cascade)

GraphQL:

  • New adminActions(filters: {adminId, action, targetType, start, end}, first, after).
  • New gdprRequests(status, first, after), gdprRequestDetail(id), createGdprRequest(userEmail, type, requestorEmail), fulfillGdprExport(requestId), fulfillGdprDeletion(requestId, typedConfirmation).

Permissions: audit visible to admin or above; GDPR fulfillment requires super_admin; privacy audit is read_only+.

Key UI elements: audit log table with before/after JSON diff viewer; GDPR wizard that walks through which tables contain user data and shows cascade preview before execution.


6. Data Model Changes

6a. Replace User.admin boolean with role system

New migration (in rails_api/db/migrate/):

ruby
class CreateAdminRoles < ActiveRecord::Migration[8.0]
  def change
    create_table :admin_roles do |t|
      t.references :user, null: false, foreign_key: true, index: true
      t.string :role, null: false
      t.references :granted_by, null: false, foreign_key: { to_table: :users }
      t.datetime :granted_at, null: false
      t.datetime :revoked_at
      t.references :revoked_by, foreign_key: { to_table: :users }
      t.text :grant_reason
      t.text :revoke_reason
      t.references :scoped_team, foreign_key: { to_table: :teams }   # Phase 3 (team_admin)
      t.jsonb :metadata, default: {}
      t.timestamps
    end

    add_index :admin_roles, [:user_id, :role, :revoked_at], name: "idx_active_admin_roles"
    add_index :admin_roles, :role
  end
end

New model rails_api/app/models/admin_role.rb with enum for role values:

ruby
class AdminRole < ApplicationRecord
  ROLES = %w[super_admin admin moderator support billing team_admin read_only].freeze

  belongs_to :user
  belongs_to :granted_by, class_name: "User"
  belongs_to :revoked_by, class_name: "User", optional: true
  belongs_to :scoped_team, class_name: "Team", optional: true

  validates :role, inclusion: { in: ROLES }
  validate :team_admin_requires_scoped_team

  scope :active, -> { where(revoked_at: nil) }
end

Backfill migration: for every User.admin = true today, insert AdminRole(role: "super_admin", granted_by: <founder user id>, grant_reason: "migrated from User.admin"). Keep User.admin column for one release as a derived read-only shim (def admin?; admin_roles.active.exists?; end), then drop it.

6b. Admin action audit log

ruby
class CreateAdminActions < ActiveRecord::Migration[8.0]
  def change
    create_table :admin_actions do |t|
      t.references :admin_user, null: false, foreign_key: { to_table: :users }, index: true
      t.string :action, null: false
      t.references :target, polymorphic: true, index: true
      t.jsonb :before_state, default: {}
      t.jsonb :after_state, default: {}
      t.text :reason
      t.string :ip_address
      t.string :user_agent
      t.string :request_id
      t.datetime :created_at, null: false
    end

    add_index :admin_actions, [:admin_user_id, :created_at]
    add_index :admin_actions, [:action, :created_at]
    add_index :admin_actions, :created_at
  end
end

New model rails_api/app/models/admin_action.rb and a base interaction concern (rails_api/app/interactions/admin/logged.rb) that every admin interaction includes. The concern wraps execution in a transaction that appends an AdminAction row capturing before_state (pre-execution DB snapshot of the target), after_state (post-execution snapshot), reason (from input), and request context (IP + UA + request_id) from GraphQL context.

This means audit logging is enforced at the interaction layer — you cannot write a new admin mutation without it.

Retention: 7 years for super_admin actions and role grants; 2 years for everything else (configurable). Old rows migrate to GCS cold storage via a nightly Crono job.

6c. Backend search infrastructure

Phase 0 ships the searchUsers query using Postgres full-text search:

ruby
class CreateUserSearchIndex < ActiveRecord::Migration[8.0]
  def change
    enable_extension "pg_trgm" unless extension_enabled?("pg_trgm")

    add_index :users, "lower(email) gin_trgm_ops", using: :gin, name: "idx_users_email_trgm"
    add_index :users, "lower(username) gin_trgm_ops", using: :gin, name: "idx_users_username_trgm"
    add_index :users, "lower(display_name) gin_trgm_ops", using: :gin, name: "idx_users_display_name_trgm"
  end
end

Trigram indexes scale to ~1M users with sub-100ms queries. Past 1M users, revisit with a dedicated search service (Typesense/Meilisearch/Elasticsearch) — but that's Phase 5+ concern.

Cursor pagination via created_at + id tiebreaker (same pattern as GoalEvent feed).

6d. Content moderation rules

ruby
create_table :moderation_rules do |t|
  t.string :name, null: false
  t.string :pattern_type   # "regex", "substring", "ai_score_threshold"
  t.text :pattern
  t.string :action         # "flag", "auto_reject", "notify"
  t.string :severity       # "low", "medium", "high", "critical"
  t.references :created_by, foreign_key: { to_table: :users }
  t.boolean :enabled, default: true
  t.timestamps
end

6e. GDPR requests

ruby
create_table :gdpr_requests do |t|
  t.references :user, foreign_key: true
  t.string :request_type       # "export", "deletion"
  t.string :status             # "received", "processing", "fulfilled", "failed"
  t.string :requestor_email
  t.datetime :received_at, null: false
  t.datetime :due_by
  t.datetime :fulfilled_at
  t.references :fulfilled_by, foreign_key: { to_table: :users }
  t.string :export_file_url    # signed GCS URL, expires
  t.text :notes
  t.timestamps
end

6f. Support inbox (Phase 1)

ruby
create_table :support_tickets do |t|
  t.references :user, foreign_key: true
  t.references :assignee, foreign_key: { to_table: :users }
  t.string :source                    # "feedback_post", "content_report", "appeal", "direct"
  t.string :source_id
  t.string :status                    # "new", "in_progress", "waiting_user", "resolved"
  t.integer :priority                 # 1-5
  t.text :last_message
  t.timestamps
end

6g. New GraphQL operations (summary)

See §5 for per-module detail. High level:

  • Queries added: searchUsers, userTimeline, userAiHistory, moderationQueue (refactored), flaggedUserPatterns, contentAppeals, adminCommunities, adminCommunity, adminTeams, adminTeam, teamManagerView, aiCostReport, aiBudgets, aiModelPerformance, coachPromptVersions, billingOverview, billingWebhooks, subscriptionDetail, supportInbox, supportTemplates, systemHealth, sidekiqStats, cronoJobs, featureFlags, environmentVariables, deployHistory, adminActions, gdprRequests, goalsCompletedTrend, cohortRetention, activationFunnel, pbcImpactReport, autoAdjustmentAudit, mutualStreakAudit.
  • Mutations added: see module-by-module lists in §5. All wrapped by the Admin::Logged interaction concern.

6h. Performance: read replica for admin

At 5K MAU (Apr 2027 low), admin analytics queries start competing with user-facing traffic. Add a Cloud SQL read replica and route all admin read queries to it via a Rails multi-DB role (ActiveRecord::Base.connected_to(role: :reading) scoped in a middleware for the admin GraphQL origin). Phase 3 item.


7. Migration Plan

Four-wave migration from Ionic /admin to admin.objectuve.com:

Wave 1 — Shell up, nothing migrated (Phase 0, weeks 1-2). New admin_dashboard/ app deployed to staging.admin.objectuve.com with Clerk auth, email allowlist, empty navigation, a dashboard view rendering the north-star metric query, and one working module end-to-end (Users) as a proof of the full stack. Existing Ionic /admin continues to work unchanged.

Wave 2 — Move existing tabs module by module (Phase 0, weeks 3-6). Port each existing admin component from ionic_frontend/src/components/admin/ into admin_dashboard/src/components/, rewriting ion-* elements to plain Tailwind equivalents. Order:

  1. Stats (dashboard home) — lowest risk, all read-only, reuses existing adminStats.
  2. Growth charts — same, adds Chart.js or Recharts instead of Ionic's chart wrapper.
  3. Users — rewrite for backend search (this is the real unlock — don't skip).
  4. Activity feed — straight port.
  5. Monitoring — extend to full System module.
  6. Moderation — port + add bulk actions.
  7. Feedback — fold into Support Inbox.
  8. Themes / word cloud — port into Analytics module.

Each module goes live on admin.objectuve.com as it's finished. The Ionic /admin remains functional in parallel.

Wave 3 — Deprecate Ionic /admin (Phase 1, launch readiness sprint). Once all 8 original tabs are ported and the new modules (user detail, audit log, GDPR, support inbox) are live, redirect ionic_frontend/src/views/Admin.vue to a single view that displays "This admin dashboard has moved to admin.objectuve.com" with a link. Keep the Vue file for one release in case of rollback.

Wave 4 — Remove admin code from Ionic bundle (Phase 2). Delete ionic_frontend/src/views/Admin.vue, ionic_frontend/src/components/admin/*, their stories, and the admin route. Remove AdminStatsCards, AdminUsersTable, etc. from Vite's build graph. Shrinks the mobile bundle by roughly 50 KB gzipped and reduces the user-side attack surface.

Rollback plan: at each wave, the previous state is one commit and one deploy away. Wave 3's redirect is reversible by toggling a feature flag. Wave 4 is destructive but happens after 4+ weeks of stable Wave 3 operation.


8. Phased Implementation

Six phases, mapped to the product roadmap.

Phase 0 — Extraction & Foundation (May-Jun 2026, pre-public launch)

Goal: Get off Ionic before we launch publicly.

  • New admin_dashboard/ app: Vue 3 + Vite + TypeScript + Tailwind (no Ionic).
  • Clerk auth with email allowlist (env var for Phase 0).
  • Apollo client + auth guards + layout shell + command palette.
  • Migrate admin_roles and admin_actions tables.
  • Implement Admin::Logged interaction concern.
  • Port Stats, Growth, Users, Activity, Monitoring, Moderation, Feedback, Themes tabs.
  • Ship the backend user search API — this is the critical unblocker.
  • Firebase Hosting target enkidu-admin + enkidu-admin-staging.
  • GitHub Actions workflow admin.yml (sibling to existing staging.yml/production.yml).
  • Docker-compose admin service.
  • Storybook for admin components.
  • Deploy to staging.admin.objectuve.com, founder-tested.

Exit criterion: every existing admin workflow works on admin.objectuve.com and is audit-logged.

Phase 1 — Launch Readiness (Jul-Sep 2026, ships before public launch)

Goal: Ship everything needed to operate a public platform without embarrassment.

  • User detail view (profile, activity timeline, AI history, flags, subscription).
  • Bulk moderation actions + pattern detection view.
  • Support Inbox (unified feedback + reports + appeals).
  • GDPR request queue + export wizard + deletion wizard. This is the compliance gate for launch.
  • Audit log view with before/after JSON diff.
  • Support templates.
  • Sentry alerts on failed admin auth attempts and on AdminAction with action = "hard_delete_user".

Exit criterion: founder is confident taking public launch traffic without needing to touch rake tasks.

Phase 2 — Operational Maturity (Oct-Dec 2026)

Goal: Scale operator productivity as user base grows from 500 → 2K MAU.

  • User impersonation ("view as user") with user-visible banner and audit entry.
  • Soft-delete restoration UI for every acts_as_paranoid model (Goal, GoalEvent, Community, GoalMedia, UserDevice, FeedbackComment, FeedbackPost, GoalEventReaction, GoalEventEncouragement, UserGoalFollow).
  • AI cost dashboards (LiteLLM integration) — wires into Dedicated AI Service PRD.
  • Feature flag management UI.
  • Sidekiq web integration (either embedded or deep-linked).
  • Second and third admin roles go live (moderator, support) — first support hire gets a delegated account.
  • Remove admin code from ionic_frontend bundle (Wave 4 of migration).

Exit criterion: first support hire is productive without full admin access.

Phase 3 — Teams Foundation (Jan-Mar 2027, prep for Teams launch)

Goal: Have admin tooling ready the day Teams launches.

  • Team admin views + Manager View (Item 55).
  • team_admin scoped role.
  • Billing webhook log and replay.
  • Manual Stripe/Clerk Billing sync tool.
  • Phase 5 Intelligence admin hooks: auto-adjustment audit, mood correlation review.
  • Phase 6 Growth admin hooks: shared celebration card monitoring, seasonal event reconciliation.
  • Accountability Partners admin hooks: mutual streak audit, manual reset.
  • Cloud SQL read replica + admin traffic routed to it.

Exit criterion: Teams launch has no admin dependencies left.

Phase 4 — Teams Commercial (Apr-Jun 2027, Teams launch)

Goal: Full billing console + PBC impact report v1 + first annual audit.

  • Full billing console (subscriptions, refunds, reconciliation, seat sync).
  • Refund workflow with dual confirmation and typed amount.
  • Team-level moderation tools.
  • PBC impact report generator v1 (north-star metric history + PBC obligations dashboard).
  • First year-over-year cohort retention report.

Exit criterion: can publish the first annual PBC impact report straight from the admin app.

Phase 5 — Scale & Compliance (Jul 2027 - Dec 2028, 5K → 75K MAU)

Goal: Scale beyond the founder-and-a-support-hire shape.

  • Advanced cohort + funnel analytics.
  • Custom query builder (with safety limits, read-replica only).
  • Distributed moderation workflows (moderator queues, escalation paths).
  • Federated SSO (SAML/OIDC) for enterprise teams admins.
  • IP allowlist for super admin actions.
  • Automated rules engine (moderation rules + anomaly detection triggers).
  • Multi-region admin support (EU data residency).
  • Move audit logs to GCS cold storage after 90 days.

Exit criterion: admin operations scale to 75K MAU without adding operators linearly.

Phase 6 — Intelligence Layer (Jan 2029 - Apr 2031, 75K → 1.5M MAU)

Goal: Admin app becomes partly autonomous.

  • ML-assisted moderation (classifier for obvious spam/abuse, human confirms edge cases).
  • Anomaly detection (flag-rate spikes, cost anomalies, retention cliffs).
  • Predictive churn alerts surfaced to retention operators.
  • Enterprise compliance reports (SOC 2, GDPR audit exports).
  • Partnership API oversight (external integrations from Phase 5 dedicated AI service).

Exit criterion: platform at 1.5M MAU operated by a team of 10-20 without operational drag.


9. Edge Cases & Open Questions

  1. Day 1 access. Only the founder's email is in the allowlist for Phase 0. First additional admin requires a super_admin to grant via grantAdminRole. Bootstrap problem solved by a rails_api/lib/tasks/bootstrap_super_admin.rake that accepts EMAIL= and inserts the first role when AdminRole.count == 0 — same pattern as the existing users:make_admin rake task. Rake task is removed in Phase 2 once the system is stable.

  2. Admin app downtime. If admin.objectuve.com is down (Firebase Hosting outage or a broken deploy), the founder can still access critical operations via rails_api rake tasks: rails users:ban EMAIL=, rails moderation:review_all, rails gdpr:export USER_ID=. Rake tasks remain as fallback forever, not as primary interface.

  3. Audit log retention. 7 years for super_admin actions and role grants (PBC board-review need + typical compliance horizon); 2 years for routine moderation; 90 days warm in Postgres then GCS cold storage. Configurable per action type. Open question: do we need a tamper-evident chain (hash-linked entries) for highest-trust ops? Decision deferred to Phase 5 when enterprise compliance requires it.

  4. IP allowlist for super admin. Phase 5 item. Adds friction for the founder but is a reasonable cost at 100K+ MAU. Use Cloud Armor on the Firebase Hosting target.

  5. Two-factor for destructive ops. Clerk step-up auth is the preferred mechanism (user re-enters password + TOTP before specific mutations). Backend mutations accept a stepUpToken parameter that must be validated.

  6. Read replica routing. Middleware for admin origin routes all reads to the replica. But not all reads — admin actions that immediately verify their own effect (e.g., a ban followed by a read to confirm the ban) need read-your-writes, which means a small allowlist of queries bypasses the replica.

  7. Impersonation + audit + user transparency. User sees a banner on their own app that says "an operator is viewing as you". PBC transparency obligation; this is not negotiable. Open question: does impersonation allow mutations as the user, or read-only? Recommendation: read-only in Phase 2, write-enabled with additional confirmation in Phase 3 (useful for "help me fix my account" support flows).

  8. Deleted user data in audit logs. After a GDPR hard-delete, what remains in admin_actions about that user? Recommendation: strip PII from before_state/after_state JSON (replace email/name with hash), keep the action record with target_user_id set to NULL but target_user_hash present. Preserves accountability for the admin without retaining deleted user's PII.

  9. Dual-write during migration. During Wave 2 (parallel Ionic admin + new admin), admin actions could happen in either place. Solution: both paths go through the same backend interactions, both log to admin_actions. No dual-write risk.

  10. Clerk seat cost. Clerk charges per-MAU; do admins count twice (once on app.objectuve.com, once on admin.objectuve.com)? Answer: depends on Clerk configuration. If using a separate Clerk app, yes. If multi-origin within one Clerk app, no. Verify with Clerk pricing before Phase 0 kickoff.


10. Security Considerations

  • Defense in depth at three layers: Clerk auth + frontend email allowlist + backend RBAC. Each layer independently rejects unauthorized access.
  • Every mutation is RBAC-gated at the resolver level. Frontend gates are UX affordances, not security boundaries.
  • Typed confirmation for destructive actions. Ban, hard-delete, refund, kill-switch flags, deploy rollback — all require typing an identifier.
  • Two-factor (step-up) for the most sensitive ops. Role grant/revoke, hard-delete, deploy rollback, large refunds.
  • Every admin action logs IP, user agent, request_id, before_state, after_state, reason. Enforced at the interaction concern layer — impossible to add an admin mutation without logging.
  • Rate limiting on admin endpoints. Rack::Attack rules for admin origin: 60 req/min per admin user for queries, 20/min for mutations.
  • No PII in URLs. User IDs are opaque internal IDs (not emails). No query string parameters with user data.
  • Secrets never displayed. systemEnvironmentVariables query redacts every key whose name matches /key|secret|token|password|dsn/i. Values shown as [REDACTED – 47 chars].
  • Sentry alerts:
    • Failed admin auth attempts (5+ in 10 min) → page the founder.
    • Any hard_delete_user action → page the founder.
    • Any role grant of super_admin → page the founder.
    • Any kill-switch flag flip → page the founder.
  • CSP on admin origin. Strict Content-Security-Policy blocking third-party scripts. Only Clerk and Apollo origins allowed.
  • No storage of admin cookies in the Ionic app. Admin session is origin-isolated to admin.objectuve.com. Ionic cannot read admin cookies.
  • Admin Apollo errors never leak to end users. Sentry project is separate; errors on admin origin go to the admin Sentry project, never the user-facing one.

11. Local Development

  • docker-compose.yml addition: admin service running Vite dev server on port 5174 (ionic_frontend uses 5173).
  • Shared patterns from ionic_frontend:
    • Same Apollo client config (same GraphQL endpoint, different auth header).
    • Same Pinia store conventions.
    • Same vitest setup.
    • Same Storybook pattern.
  • Clerk dev mode with allowlisted dev emails (founder+dev@objectuve.com, claude+dev@objectuve.com).
  • Dev seed helper: rails_api/lib/tasks/admin_seed.rake creates an admin user with all roles for local development.
  • Storybook: admin_dashboard/.storybook/ with mock Apollo decorators. Same shadcn-style component story pattern as ionic_frontend.
  • Cypress E2E: admin-specific specs under tests/e2e/admin/ covering the GDPR wizard, bulk moderation, and role assignment flows.

12. Cost Analysis

Direct infrastructure cost

ItemPhase 0-2Phase 3-4Phase 5-6
Firebase Hosting (admin.objectuve.com)~$0~$0< $5/mo
Firebase Hosting (admin-staging)~$0~$0~$0
Cloud SQL read replica$0~$40/mo (db-custom-2-7680)~$80/mo
GCS cold storage for audit logs$0< $1/mo< $5/mo
Audit log Postgres growth~100 MB/mo at 1K users~1 GB/mo at 10K users~10 GB/mo at 100K users
Sentry (separate admin project)free tierfree tier~$26/mo
Clerk (if separate billing)free tierfree tierpossibly ~$25/mo
Total incremental< $5/mo~$50/mo~$150/mo

Compared to the alternative of continuing to bolt admin onto Ionic and dealing with the compounding drag (support rescales, missed flags, compliance risk, rewrites later), this is trivial.

Engineering cost

PhaseEst. effort
Phase 0 (Extraction & Foundation)4-6 weeks solo, 2-3 weeks with Claude pair
Phase 1 (Launch Readiness)3-4 weeks
Phase 2 (Operational Maturity)4 weeks
Phase 3 (Teams Foundation)4-6 weeks (parallel with Teams PRD work)
Phase 4 (Teams Commercial)3-4 weeks
Phases 5-6ongoing, bounded by PBC obligations and scale needs

Phase 0 + Phase 1 represent the critical pre-launch investment — roughly 7-10 weeks of work that unblocks launch and permanently lowers the operational tax on every subsequent phase.


13. Critical Files to Modify / Create

New monorepo app (entire new directory):

  • /Users/joshualockhart/code/enkidu/admin_dashboard/ — the whole Vue 3 + Vite + Tailwind app

Rails API new files:

  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_create_admin_roles.rb
  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_create_admin_actions.rb
  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_add_user_search_trgm_indexes.rb
  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_create_gdpr_requests.rb
  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_create_support_tickets.rb
  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_create_moderation_rules.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/models/admin_role.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/models/admin_action.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/models/gdpr_request.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/models/support_ticket.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/logged.rb — audit concern
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/search_users.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/ban_user.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/grant_admin_role.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/impersonate_user.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/gdpr/* — export + delete wizards
  • /Users/joshualockhart/code/enkidu/rails_api/app/graphql/mutations/admin/ (new namespace)
  • /Users/joshualockhart/code/enkidu/rails_api/app/graphql/types/admin/ (new namespace)
  • /Users/joshualockhart/code/enkidu/rails_api/lib/tasks/bootstrap_super_admin.rake
  • /Users/joshualockhart/code/enkidu/rails_api/lib/tasks/admin_seed.rake

Rails API modified files:

  • /Users/joshualockhart/code/enkidu/rails_api/app/graphql/types/query_type.rb — extend authorize_admin! into authorize_admin_role!(role) that checks AdminRole; keep old helper as a derived shim.
  • /Users/joshualockhart/code/enkidu/rails_api/app/models/user.rb — add has_many :admin_roles and derived admin? method.
  • /Users/joshualockhart/code/enkidu/rails_api/app/services/admin_stats_service.rb — add caching layer (Rails.cache with 10-minute TTL) to stop the 180-query-per-request problem.
  • /Users/joshualockhart/code/enkidu/rails_api/config/routes.rb — add admin-origin health/impersonation helpers.

Monorepo infrastructure:

  • /Users/joshualockhart/code/enkidu/firebase.json — add enkidu-admin and enkidu-admin-staging hosting targets.
  • /Users/joshualockhart/code/enkidu/.github/workflows/admin.yml (new, modeled on staging.yml and production.yml).
  • /Users/joshualockhart/code/enkidu/docker-compose.yml — add admin service on port 5174.
  • /Users/joshualockhart/code/enkidu/deploy-sites.sh — add admin hosting deploy step.

Eventually deprecated:

  • /Users/joshualockhart/code/enkidu/ionic_frontend/src/views/Admin.vue — Wave 3 redirect, Wave 4 delete.
  • /Users/joshualockhart/code/enkidu/ionic_frontend/src/components/admin/* (10 files) — Wave 4 delete.
  • /Users/joshualockhart/code/enkidu/ionic_frontend/src/components/admin/*.stories.ts (8 files) — Wave 4 delete.
  • /Users/joshualockhart/code/enkidu/ionic_frontend/src/router/index.ts — remove /admin route in Wave 3.

Documentation:

  • /Users/joshualockhart/code/enkidu/docs/features/admin-dashboard.md — update to reference new architecture.
  • /Users/joshualockhart/code/enkidu/docs/product/standalone-admin-app-prd.md — this PRD itself, once promoted from exploration.
  • /Users/joshualockhart/code/enkidu/docs/product/roadmap.md — update Item 59 to reference this PRD and the new Phase 0-1 timeline.
  • /Users/joshualockhart/code/enkidu/CLAUDE.md — add admin_dashboard/ to the monorepo apps list.

14. How This PRD Ladders Up to the North Star

Per north-star.md, the one metric that matters is cumulative goals completed. An admin app does not directly create goal completions. But it does three things that indirectly protect and compound the north-star number:

  1. Protects the platform from operational failure (moderation SLA, GDPR compliance, billing accuracy, cost control). A failure in any of these is a retention event that erases goal completions. An admin app that catches them early preserves goal completions.
  2. Enables the PBC model. A PBC must be able to publish annual impact reports, honor right-to-be-forgotten, and prove operational transparency. The admin app is the tool that makes those obligations affordable. Without it, the PBC commitment is unfunded.
  3. Unlocks operator leverage. One founder can operate a platform for the first 1K users. After that, every additional operator who can be hired and onboarded into a delegated role (moderator, support) is a multiplier on goal completions per operator-hour. RBAC + audit log + support inbox + training templates is how you go from "founder does everything" to "a small team operates a 100K-MAU platform without drowning."

The north-star panel on the admin dashboard home renders exactly one chart: cumulative goals completed, with the 5-year target overlay (2.5K → 1.5M → 10M). Every time an operator opens the admin app, that number is the first thing they see. That's the reminder that this tool exists to serve the mission, not to maximize itself.


  • /Users/joshualockhart/code/enkidu/docs/product/north-star.md — Vision + metric the admin app ladders up to.
  • /Users/joshualockhart/code/enkidu/docs/product/mission.md — PBC obligations.
  • /Users/joshualockhart/code/enkidu/docs/product/roadmap.md — Item 52 (Team Admin Dashboard), Item 55 (Manager View), Item 59 (Standalone Admin Portal — now rescoped to this PRD).
  • /Users/joshualockhart/code/enkidu/docs/product/meet-coach-prd.md — persona analytics admin surface (lands in §5e).
  • /Users/joshualockhart/code/enkidu/docs/product/accountability-partners-prd.md — mutual streak audit (§5d).
  • /Users/joshualockhart/code/enkidu/docs/product/privacy-controls-prd.md — Go It Alone enforcement validation.
  • /Users/joshualockhart/code/enkidu/docs/product/dedicated-ai-service-prd.md — LiteLLM cockpit (§5e).
  • /Users/joshualockhart/code/enkidu/docs/product/phase-5-intelligence-prd.md — auto-adjustment audit (§5d, §5e).
  • /Users/joshualockhart/code/enkidu/docs/product/phase-6-growth-prd.md — seasonal event reconciliation (§5c, §5j).
  • /Users/joshualockhart/code/enkidu/docs/product/phase-7-monetization-teams-prd.md — billing console + team admin (§5f, §5g).
  • Admin Dashboard — feature doc for the shipped implementation

Objectuve Softworks, LLC (Delaware Public Benefit LLC; principal office in Chicago, IL)Last updated: April 2026


Critical Files for Implementation

  • /Users/joshualockhart/code/enkidu/rails_api/app/graphql/types/query_type.rb
  • /Users/joshualockhart/code/enkidu/rails_api/app/interactions/admin/logged.rb (NEW — audit concern that wraps every admin interaction)
  • /Users/joshualockhart/code/enkidu/firebase.json (add enkidu-admin hosting target)
  • /Users/joshualockhart/code/enkidu/admin_dashboard/src/router/index.ts (NEW — role-gated routes)
  • /Users/joshualockhart/code/enkidu/rails_api/db/migrate/<timestamp>_create_admin_roles.rb (NEW — RBAC foundation)

Last updated: 2026-07-21

Loading…