Admin Dashboard (Phenom Global Command Center)
Overview
The Phenom Global Command Center is a comprehensive admin dashboard built to manage and monitor UAP (Unidentified Anomalous Phenomena) data globally. Located at admin_sandbox/phenom/ in the phenom-backend repository, this is a modern React application providing real-time visualization and management capabilities.
Current Version: v1.3.0 (April 2026)
Access
URL: https://nest.thephenom.app/
Authentication: AWS Cognito User Pool (phenom-dev-local, us-east-1_AkG9mnbjA). Email + password sign-in, with sign-up, OTP verification, forgot-password, and reset-password flows.
Access gate: CF Access gates entry to N.E.S.T. domains at the network level. All API routes use Cognito Bearer tokens exclusively.
Technology Stack
Frontend Framework
- React 19 - Latest React with concurrent features
- TypeScript - Type-safe development
- Vite - Lightning-fast build tool and dev server
- TanStack Router - Type-safe routing with code-splitting
UI & Styling
- Shadcn UI - High-quality component library
- Tailwind CSS - Utility-first CSS framework
- Radix UI - Accessible component primitives
- Lucide React - Beautiful icon library
- next-themes - Dark/light mode support
State & Data Management
- Zustand — Lightweight state management (used for the Cognito
authStorethat holds the ID token) - TanStack React Query — Async state and caching
- Axios / fetch — HTTP clients for the
nest-apiWorker amazon-cognito-identity-js— Cognito SDK for sign-in, sign-up, OTP, forgot/reset password
Development Tools
- ESLint - Code linting
- cz-git - Conventional commits
- PostCSS - CSS processing
Key Features
1. Dashboard Overview
The main dashboard provides:
- System Status: Real-time health monitoring
- Quick Stats: Key metrics and analytics
- Recent Activity: Latest phenomenon events
- Task Overview: Pending and completed tasks
2. Map View
Interactive Global Map featuring:
- Real-time Aircraft Tracking: Integration with OpenSky Network API
- Phenomenon Locations: Pinpointed UAP events on map
- Geofencing: Define areas of interest
- Layer Control: Toggle different data layers
- Search & Filter: Find specific events or locations
Technical Details:
- Provider: Google Maps API + CesiumJS 3D globe
- Data Source: Hasura
phenomanddropstables, fetched viaGET /api/eventson thenest-apiWorker. ADSB aircraft data via OpenSky Network. - Proxy: Cloudflare Functions for OpenSky API;
nest-apiWorker for Hasura - Updates:
PhenomFetcherpolls/api/eventsevery 60 seconds withAuthorization: Bearer ${idToken}
3. Globe View
3D Earth Visualization with:
- Interactive 3D Globe: Rotate and zoom
- Phenomenon Markers: Global UAP event visualization
- Location Data: Timestamp and coordinates
- Visual Effects: Atmospheric rendering
4. User Management
Comprehensive User Administration:
- User List: View all team members
- CRUD Operations: Create, read, update, delete users
- Role Assignment: Manage user permissions
- Activity Tracking: Monitor user actions
- Search & Filter: Quick user lookup
User Fields:
- Name, Email, Phone
- Role (admin, editor, viewer, etc.)
- Status (active, inactive, suspended)
- Created/Updated timestamps
- Activity logs
5. Task Management
Built-in Task System:
- Task Creation: Assign tasks to team members
- Status Tracking: Todo, In Progress, Completed
- Priority Levels: High, Medium, Low
- Due Dates: Deadline management
- Comments: Task discussion threads
6. Settings & Configuration
Application Settings:
Appearance:
- Theme: Light, Dark, System
- Font Size: Adjust text scaling
- Color Scheme: Customize accent colors
Profile:
- Personal Information: Name, email, bio
- Avatar: Profile picture upload
- Preferences: Language, timezone
Account:
- Security: Password change, 2FA
- Privacy: Data visibility settings
- Notifications: Email and push preferences
7. Global Search
Command Palette (keyboard shortcut):
- Universal Search: Find users, tasks, phenomena
- Quick Actions: Navigate anywhere instantly
- Keyboard Navigation: Full keyboard support
- Recent Searches: Quick access to history
Application Architecture
Directory Structure
admin_sandbox/phenom/
├── src/
│ ├── features/ # Feature modules
│ │ ├── auth/ # Authentication
│ │ ├── dashboard/ # Main dashboard
│ │ ├── settings/ # Settings pages
│ │ ├── tasks/ # Task management
│ │ └── users/ # User management
│ ├── components/
│ │ ├── layout/ # App layout components
│ │ ├── search/ # Global search
│ │ └── ui/ # Shadcn UI components
│ ├── lib/
│ │ └── utils.ts # Utility functions
│ ├── stores/
│ │ └── auth-store.ts # Zustand auth store
│ ├── routes/ # TanStack router config
│ └── main.tsx # App entry point
├── public/ # Static assets
└── package.json # Dependencies
Routing Structure
/ # Dashboard home
├── /dashboard # Main dashboard view
├── /users # User management
│ ├── /users/new # Create new user
│ └── /users/:id/edit # Edit user
├── /tasks # Task management
│ ├── /tasks/new # Create task
│ └── /tasks/:id # Task details
├── /settings # Settings hub
│ ├── /settings/profile
│ ├── /settings/account
│ └── /settings/appearance
└── /auth # Authentication flows
├── /auth/login
└── /auth/register
Data Source: Hasura via nest-api Worker
All dashboard data flows through the nest-api Worker to Hasura:
Browser → nest-api Worker → Hasura → PostgreSQL
The frontend never talks to Hasura directly for events — it always goes through the Worker so that:
- The
/api/eventsresponse shape stays stable (the Worker normalizes Hasura columns into the legacy shape the dashboard already consumed) - Drop media URLs can be presigned server-side using AWS credentials that are not exposed to the browser
- The Worker is the single place to add caching, rate limiting, or auth-driven filtering later
Request flow
- User signs in via Cognito (
amazon-cognito-identity-js). The ID token is stored in the ZustandauthStore. PhenomFetcher(anduseNestUsers) callsfetch('/api/events', { headers: { Authorization: \Bearer ${idToken}` } })`.- The
nest-apiWorker (admin_sandbox/nest-api/src/auth.ts) verifies the JWT signature against the Cognito JWKS endpoint and stores the raw token on theAuthUser. - The Worker (
admin_sandbox/nest-api/src/routes/events.ts) issues a single GraphQL query against Hasura, forwarding the same JWT as aBearertoken. Hasura validates the JWT against the same Cognito JWKS and applies row-level security. - Phenom and drop rows are mapped into the response shape and returned.
Hasura tables consumed by /api/events
| Table | Columns read |
|---|---|
phenom | id, reported_at, lat, lng, calculated_location_address, reporter_name, reporter_email, video_url, sensors_data, airnav |
drops | id, created_at, filename, file_type, file_size, file_hash, lat, lng, location_source, c2pa_status, c2pa_issuer, ai_detected, claim_generator, media_url, s3_key, sensors_data, planes |
phenom.airnav is already pre-normalized to the dashboard’s expected shape by the migration script (hasura/scripts/migrate_firebase_to_postgres.py). drops.planes is still stored as raw OpenSky state[] arrays and the Worker normalizes it at read time.
Response shape
{
events: Array<
| {
id: string
source: 'phenom'
timestamp: string // ISO from phenom.reported_at
owner: { name: string; email?: string }
calculatedLocation: { lat: number; lng: number }
calculatedLocationAddress: string | null
video: string | null // phenom.video_url
sensorsData: object | null
airnav: Plane[]
}
| {
id: string
source: 'drop'
timestamp: string // ISO from drops.created_at
filename: string | null
fileType: string | null
fileSize: number | null
fileHash: string | null
calculatedLocation: { lat: number; lng: number }
locationSource: string | null
c2paStatus: string | null
c2paIssuer: string | null
aiDetected: boolean | null
claimGenerator: string | null
mediaUrl: string | null
sensorsData: object | null
airnav: Plane[] // normalized from drops.planes
owner: { name: 'Drop Submission' }
}
>,
fetchedAt: string // ISO
}
The frontend useNestUsers hook derives a user list and per-user upload counts from this same response.
OpenSky Network Integration
Purpose
Real-time aircraft tracking to correlate UAP sightings with known aircraft positions.
Implementation
Cloudflare Function Proxy (functions/opensky/[[path]].js):
// Proxies requests to OpenSky API with:
- Token caching (memory + Cloudflare KV)
- CORS handling
- Automatic token refresh
- Request forwarding
API Client:
- Client ID:
jonathan_at_phenom-api-client - Endpoint:
https://opensky-network.org/api/ - Features: Live aircraft positions, flight data, historical tracking
Usage in Dashboard
// Fetch aircraft data via proxy
const response = await fetch('/opensky/states/all')
const aircraftData = await response.json()
// Display on map
aircraftData.states.forEach(aircraft => {
// Plot aircraft position on map
addAircraftMarker(aircraft)
})
Authentication Flow
Cognito Authentication
The dashboard authenticates users directly against AWS Cognito using amazon-cognito-identity-js. There is no longer a Firebase or Cloudflare Access step in the user-facing path.
- User visits
nest.thephenom.app. The/_authenticated/route.tsxroute guard callsuseAuthStore.restoreSession()which checks for an existing Cognito session in localStorage (the SDK persists refresh tokens there). - If no valid session, the route redirects to
/sign-in. - The user enters email + password.
useAuthStore.login()callsauthenticateUser()insrc/lib/cognito.ts, which uses Cognito’sUSER_SRP_AUTHflow. - On success, the ID token JWT is stored in the Zustand
authStorealong with the decodedemail. - Protected routes render. Any API call to
/api/events(and future Cognito-migrated routes) includesAuthorization: Bearer ${idToken}.
Auxiliary flows
| Flow | Route | Cognito API |
|---|---|---|
| Sign up | /sign-up | userPool.signUp(email, password, ...) |
| OTP verification | /otp?email=... | user.confirmRegistration(code, true, ...) |
| Forgot password | /forgot-password | user.forgotPassword(...) |
| Reset password | /reset-password?email=... | user.confirmPassword(code, newPassword, ...) |
| Sign out | sidebar dropdown | user.signOut() then navigate to /sign-in |
Implementation lives under admin_sandbox/phenom/src/features/auth/. The Cognito SDK requires the global polyfill in Vite — vite.config.ts sets define: { global: 'globalThis' }.
Session Management
Auth Store (Zustand) — src/stores/authStore.ts:
interface AuthState {
user: { email: string } | null
idToken: string | null
isAuthenticated: boolean
isLoading: boolean
login: (email: string, password: string) => Promise<void>
logout: () => void
restoreSession: () => Promise<void>
}
Token storage:
- ID token: in-memory, in the Zustand store. Re-derived on page load via
restoreSession(). - Refresh token: persisted to localStorage by
amazon-cognito-identity-js(standard SDK behavior). - Expiration: ID tokens last 1 hour; the SDK transparently refreshes using the refresh token.
When the dashboard receives a 401 from any nest-api endpoint, the global axios error handler in src/main.tsx calls useAuthStore.getState().logout() and navigates back to /sign-in.
Development Setup
Prerequisites
Node.js 18+
npm or pnpm
Installation
cd admin_sandbox/phenom
npm install
Environment Variables
Copy .env.example to .env and fill in the values:
# Cognito User Pool
VITE_COGNITO_USER_POOL_ID=us-east-1_AkG9mnbjA
VITE_COGNITO_CLIENT_ID=2eq1vf0nvl5o3rha2vshm8j0mn
These values are not secrets — Cognito Pool ID and Client ID are visible in any browser request to Cognito. They are committed to .env.example so onboarding requires no out-of-band credential exchange.
Switching pools: For staging set
VITE_COGNITO_USER_POOL_ID=us-east-1_n8gO6SbP6and use the matching client ID fromphenom-staging(e.g.,phenom-dev-hasura-client).
The nest-api Worker has its own configuration in admin_sandbox/nest-api/wrangler.toml:
[vars]
HASURA_GRAPHQL_URL = "https://api-staging.thephenom.app/v1/graphql"
COGNITO_REGION = "us-east-1"
COGNITO_USER_POOL_ID = "us-east-1_AkG9mnbjA"
Worker secrets (set via wrangler secret put): GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY.
Development Server
npm run dev
# Opens at http://localhost:5173
Build for Production
npm run build
# Output: dist/
Deployment
Build Configuration
Netlify (netlify.toml):
[build]
command = "npm run build"
publish = "dist"
functions = "functions"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Cloudflare Functions
Functions deployed alongside static assets:
- Automatic deployment with Netlify/Cloudflare Pages
- Cloudflare KV for token caching
- Worker bindings configured in dashboard
Security Considerations
Access Control
- Zero Trust: Cloudflare Access at perimeter
- Cognito Auth: Application-level authentication via AWS Cognito
- Hasura row-level security: Permissions evaluated against Cognito JWT claims
- Role-Based: Granular permissions per feature
Data Security
- Encryption: TLS/SSL in transit
- Hasura row-level security: Permissions evaluated against the Cognito JWT claims at query time
- No service account in request path: The Worker forwards the user’s JWT rather than holding admin credentials. A compromised Worker secret cannot read the database directly.
- API tokens: OpenSky token cached in Cloudflare KV; AWS S3 signing keys held as Worker secrets, never exposed to the browser
Credential Management
The Cognito Pool ID and Client ID are not secrets — they live in .env.example and are committed to the repo. Provisioning a new developer requires only:
cp admin_sandbox/phenom/.env.example admin_sandbox/phenom/.env- Get a Cognito user added to the
phenom-dev-localpool by an existing admin - Sign in via
npm run dev
The Worker’s secrets (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY) are managed via wrangler secret put.
Known Limitations
- OAuth Incomplete: Phenomenon deletion disabled
- Local URLs: Development configured for localhost
- Token Refresh: Manual intervention may be needed
Performance Optimizations
Code Splitting
- Route-based: Lazy loading for each page
- Component-level: Dynamic imports where needed
- Bundle Size: Optimized with Vite
Caching Strategy
- OpenSky Data: Cached in Cloudflare KV (configurable TTL)
- Static Assets: CDN caching via Cloudflare
Monitoring
- React Query DevTools: Debug data fetching
- Error Reporting: Client-side error logs
Troubleshooting
Common Issues
“Authentication Failed”
- Verify GitHub organization membership
- Check
INTteam membership in phenom-earth org - Clear browser cookies and try again
“Map Not Loading”
- Check Google Maps API key validity
- Verify API quotas not exceeded
- Check browser console for errors
“Aircraft Data Not Updating”
- Verify OpenSky API credentials
- Check Cloudflare Function logs
- Confirm KV namespace binding
“401 Unauthorized from /api/events”
- Confirm the user is signed in to Cognito (
useAuthStore.getState().idTokenshould be a non-null string in DevTools) - Confirm the request actually carries the
Authorization: Bearer ...header (Network tab) - Check the Worker logs for
[events] Error:— Hasura JWT validation failures surface here
“global is not defined” in console after a new install
- The Cognito SDK requires a
globalpolyfill. Confirmvite.config.tshasdefine: { global: 'globalThis' }and restart the dev server.
User Content Curation
Overview
Authenticated users can create named curated lists of phenom/drop content and share them with other team members.
Data Layer
- Storage: Hasura/PostgreSQL (tables:
lists,list_items,list_shares,item_shares) - API: nest-api Worker REST endpoints (
/api/lists/*,/api/shares/*) - Auth: Cognito Bearer token → forwarded to Hasura for row-level security
User Features
- Create lists: Named collections (e.g., “East Coast Events”, “High Confidence”)
- Add items: From either phenom sightings or drop submissions
- Share lists: With other team members (read or write permission)
- Share items: Quick “send this to someone” with optional note
- Notifications: Badge on profile avatar for unseen shares
Profile Hub
The lower-left profile dropdown shows:
- User identity (from Cloudflare Access GitHub OAuth)
- My Lists with item counts
- Shared With Me (items and lists from other users)
- Create New List button
Mobile Responsive Map
On screens below 768px, the event sidebar is replaced with a draggable bottom sheet:
- Collapsed: handle bar showing event count
- Peek: 2-3 items visible, map mostly visible
- Expanded: full scrollable list
- Item tap: centers map on marker
Future Enhancements
Planned Features:
- Enhanced OAuth implementation for secure deletions
- Advanced data analytics dashboard
- Multi-language support
- Webhook integrations
- Email notifications for shared content (Brevo)
Support & Resources
Documentation:
- Shadcn UI Components
- TanStack Router
- Amazon Cognito User Pools
- Hasura JWT Authentication
- OpenSky Network API
Internal Resources:
- GitHub: phenom-backend/admin_sandbox
- Team:
INTmembers inphenom-earthorganization
For technical support, contact the development team or file an issue in the GitHub repository.
Related Documentation
- Infrastructure & Service Map — service architecture, API endpoints, DNS records
- Quality Dashboard — defect tracking and sprint metrics
- N.E.S.T. User Guide — end-user documentation
- ADS-B Aircraft Tracking — aircraft data integration
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.