The Pew Research Center Digital Publishing Platform runs on WordPress VIP, with multiple environments, automated deployments, and a modular monorepo.
Git lives on Cursor Origin. CI and deploys run on Depot. VIP is what is live. See RELEASING.md for the deploy-manager CLI.
See the README in the repo root for monorepo structure, Node.js workspaces, and dependency setup. The most recent version and release name are there as well.
Architecture overview
The PRC Platform follows a WordPress Multisite architecture with subdirectory configuration, hosted on WordPress VIP. It is a monorepo of modular plugins and a design-system theme.
Infrastructure Diagram
flowchart TB
subgraph dx ["Developer experience"]
DEV[Developer workstation]
LOCAL[VIP CLI local<br/>Docker]
CLOUD[Cursor Cloud]
DEV --> LOCAL
DEV --> CLOUD
end
subgraph scm ["Git and CI"]
ORIGIN[Cursor Origin<br/>git]
DEPOT[Depot CI]
MIRROR[WPCOMVIP repo<br/>source mirror only]
ORIGIN --> DEPOT
ORIGIN --> MIRROR
end
LOCAL --> ORIGIN
CLOUD --> ORIGIN
subgraph depotjobs ["Depot workflows"]
RESET[reset-alpha-beta-branch]
STAGE[deploy-to-staging]
PUB[publish-release]
BUILD[build-release]
RESET --> STAGE
PUB --> BUILD
end
DEPOT --> RESET
DEPOT --> STAGE
DEPOT --> PUB
subgraph vip ["WordPress VIP"]
ALPHA[alpha]
BETA[beta]
CANARY[canary]
PROD[production]
STAGE --> ALPHA
STAGE --> BETA
STAGE --> CANARY
BUILD --> PROD
end
subgraph stack ["WordPress stack"]
EDGE[Edge cache / Varnish]
WP[WordPress 6.8<br/>Multisite]
PHP[PHP 8.4]
DB[(MariaDB)]
ES[VIP Search]
MC[Memcached]
ALPHA --> EDGE
BETA --> EDGE
CANARY --> EDGE
PROD --> EDGE
EDGE --> WP
WP --> PHP
PHP --> DB
WP --> ES
WP --> MC
end
subgraph app ["PRC Platform"]
PLUGINS[Plugins and blocks]
THEME[prc-design-system]
WP --> PLUGINS
WP --> THEME
end
subgraph fb ["Firebase"]
AUTH[Auth]
RTDB[Named Realtime Databases]
PLUGINS --> AUTH
PLUGINS --> RTDB
end
subgraph ext ["Other integrations"]
LINEAR[Linear]
SLACK[Slack]
MAIL[Mailchimp]
APPLE[Apple News]
PARSELY[Parse.ly]
CF[Cloudflare Turnstile]
SHOT[ScreenshotOne / Cloudflare]
PLUGINS --> SLACK
PLUGINS --> MAIL
PLUGINS --> APPLE
PLUGINS --> PARSELY
PLUGINS --> CF
PLUGINS --> SHOT
ORIGIN -.-> LINEAR
DEPOT -.-> LINEAR
end
Origin is git. Depot is CI. VIP is live. The WPCOMVIP repository is an archival source mirror. Production deploys use vip app deploy from Depot build-release.yml. They do not git-track on the VIP app.
Firebase is the outside-user data plane. Auth and named Realtime Databases back accounts, quizzes, interactives, datasets, and data tables. MySQL stays the editorial store.
Core Technologies
- WordPress: 6.8 (Latest)
- PHP: 8.4 with php-fpm (local VIP dev-env; see
.wpvip/vip-dev-env.yml) - Database: MariaDB/MySQL cluster
- Web Server: Nginx
- Search: Elasticsearch (VIP Search)
- Caching: Edge cache (Varnish) + Memcached object cache
- Outside-user data: Firebase Auth + named Realtime Databases
Environment Structure
1. Local Development
- VIP Dev Environment (Preferred)
- Docker-based local environment using VIP CLI
- Configuration:
.wpvip/vip-dev-env.yml - Includes: Xdebug, Mailpit, phpMyAdmin, Elasticsearch
- Commands:
npm run vip:start,npm run vip:stop,npm run vip:sync
- Cursor Cloud (autonomous agents)
- Cloud VMs boot the same VIP CLI dev-env via
.cursor/environment.json - Elasticsearch is disabled in cloud (cgroup limits)
- See Cursor Cloud agents
- Cloud VMs boot the same VIP CLI dev-env via
2. Staging Environments
Alpha Environment
- Purpose: Development testing and QA
- Alias:
@pewresearch.alpha - Deploy Trigger: Push to
alpha(mirrorstrunk) - Workflow:
.depot/workflows/deploy-to-staging.yml(BUILD_MODE: all+ Turbo cache)
Beta Environment
- Purpose: Staging and content review
- Alias:
@pewresearch.beta - Deploy Trigger: Push to active
release/x.y(mirrors beta) - Workflow:
.depot/workflows/deploy-to-staging.yml
Canary Environment
- Purpose: Mutable experimental sandbox — stack ad hoc experiments without landing them on
trunkfirst - Alias:
@pewresearch.canary(https://canary-pewresearch.go-vip.net/— temporary convenience domain untilcanary.[REDACTED]DNS is ready) - Deploy Trigger: Push to
canary(a manual reset or an ad hoc experimental merge) - Reset: Manual only — dispatch
.depot/workflows/reset-alpha-beta-branch.ymlwithtarget_branch=canaryto force-pushtrunkontocanary. There is no auto-reset on push. - Promotion: Experiments promote via a normal PR to
trunkwith a changeset. Never mergecanary→trunk. - Workflow:
.depot/workflows/deploy-to-staging.yml - Data sync:
config/.vip.pewresearch.canary.yml; pull locally withnpm run vip:sync:canary
3. Production Environment
- Purpose: Live public site
- Alias:
@pewresearch.production - Deploy Trigger: Dispatch Depot
publish-release.ymlfor a production git tag (vMAJOR.MINOR.PATCH) with matchingtagandconfirm - Workflow:
.depot/workflows/build-release.yml - Urgent fixes: Ship a patch on the release line through the same prep + publish path. There is no separate emergency hotfix workflow. See RELEASING.md.
WordPress VIP Platform Services
Edge & Caching Layer
- Edge Cache (Varnish CDN): Global content delivery with intelligent caching
- Object Cache (Memcached): In-memory database result caching
- Vary Cache: Geo-location and device-specific caching
- Photon: Automatic image optimization and WebP conversion
Search & Data
- VIP Search (Elasticsearch): Enhanced search with faceted filtering
- VIP Block Data API: REST API for block-based content
- Geo Uniques: Location-based content targeting using
GEOIP_COUNTRY_CODE
Security & Performance
- Two-Factor Authentication: Enforced on production environment
- VIP Login Limiter: Brute force protection with rate limiting
- Block Governance: Content block security and compliance
- Smart Redirects: SEO-friendly URL redirects with spam protection
Retired VIP Node.js
VIP Node.js microservices are being sunset. The old Asana–GitHub sync and Node screenshot service are not part of the live architecture. Chart PNGs now go through prc-chart-builder screenshot providers (ScreenshotOne, Cloudflare Browser Rendering, or Firebase). Project tracking is Linear.
Deployment Pipeline
Source Control Flow
Developer → Cursor Origin (git) → Depot CI → VIP app deploy
↘ Firebase functions + Slack app manifests
↘ WPCOMVIP source mirror (archival)
Do not use gh workflow run. Cut, prep, publish, and staging dispatch through npm run deploy-manager. Origin is git. Depot is CI. VIP is live. Firebase functions and Slack app manifests ship from the same staging and publish workflows via .depot/workflows/deploy-platform-services.yml.
Automated Deployment Process
- Code push: Developer pushes to a feature branch on Origin.
- PR: Pull request targets
trunk(or arelease/**maintenance branch as described in RELEASING.md). - Merge: Merging to
trunkupdates thealphamirror and redeploys alpha; merging to the activerelease/x.yupdatesbetaand redeploys beta (see .depot/workflows/reset-alpha-beta-branch.yml and .depot/workflows/deploy-to-staging.yml). - Production: Dispatching Depot
.depot/workflows/publish-release.ymlfor a production git tag publishes announce notes to DocsPress, posts Slack, then runs.depot/workflows/build-release.yml(VIP zip) in parallel with.depot/workflows/deploy-platform-services.yml(Firebase functions and Slack manifests). Operators check live status withnpm run deploy-manager -- status(see RELEASING.md). - WPCOMVIP mirror: On merged PRs, .depot/workflows/copy-to-wpcomvip.yml force-pushes a source-only snapshot of the merged branch to
wpcomvip/pewresearch-org(nonode_modules, no Composervendor/, no webpackbuild/outputs). VIP production does not consume this mirror for deploys; it is archival/reference. The built tree for a release is pushed to WPCOMVIP as part of thebuild-release.ymljob after publish.
PRC Platform Application Stack
WordPress Multisite Configuration
- Primary public site ID: 20 (
www.[REDACTED], local/pewresearch-org) - Docs site ID: 1 (
PRC_DOCS_SITE_ID, network root). DocsPress Pages live at/docs. Production: platform.[REDACTED]/docs. Local:https://prc-platform.vipdev.lndo.site/docs. - Multisite Style: Subdirectory
- Default Theme:
prc-design-systemon the public site. Docs use the DocsPress theme.
The old wiki site is gone. Help Center reads DocsPress pages from the docs site, not a separate wiki.
Core Platform Components
Must-Use Plugins (client-mu-plugins/)
- plugin-loader.php: Bootstrap and plugin orchestration
- prc-vip-dev-env: Development-specific configurations
- prc-bimi: Brand indicator email authentication
Core Plugins (50+ modular plugins)
- prc-platform-core: Foundation utilities and shared functionality
- prc-firebase: Shared Firebase Auth and Realtime Database access
- prc-block-library: Custom Gutenberg blocks for content creation
- prc-analytics: Tracking and measurement integration
- prc-chart-builder: Data visualization tools
- prc-quiz-builder: Interactive quiz system with WordPress Interactivity API
- prc-datasets: Data management and publishing
- prc-elasticpress: Enhanced search functionality
- prc-staff-bylines: Author and contributor management
Firebase
Firebase is the platform's outside-user data plane. Editorial content stays in MySQL. Simultaneous writes from many clients go to Firebase.
| Surface | Role |
|---|---|
| Firebase Auth | Sign-in for prc-user-accounts (magic link and federated providers). REST mutations send X-PRC-User-Id and X-PRC-User-Token. |
| Named Realtime Databases | Separate RTDBs for auth, interactives, and data-table-builder. Constants live in vip-config/ (PRC_PLATFORM_FIREBASE_*_DB and __DEV variants). |
| PHP SDK | prc-firebase initializes Kreait from private/firebase-service-account.json (never commit; generate with npm run gen:firebase-sa). |
| JS module | New blocks import @prc/firebase. Older interactives still use the legacy firebase script handle. |
Consumers include prc-user-accounts, prc-quiz-builder, prc-quiz-cast, prc-datasets, prc-block-tables, and interactives. Chart screenshot capture can also use a Firebase provider. See PRC Firebase and dependency auth.
External Service Integrations
Communication & Publishing
- Slack: Development notifications and alerts
- Apple News: Automated article publishing
- Mailchimp: Email marketing and newsletter management
Project tracking
- Linear: Issues, releases, and backport association. Release workflows sync Linear from git history.
Analytics, screenshots, and protection
- Parse.ly: Content analytics
- VIP APM & log shipping: Application performance and centralized logs on VIP
- Google Analytics: Web analytics and user tracking
- Cloudflare Turnstile: CAPTCHA and bot protection
- ScreenshotOne / Cloudflare Browser Rendering: Chart featured-image PNGs via
prc-chart-builder(not a VIP Node service)
Development Tools & Workflow
Command Line Tools
# VIP CLI for environment management
npm install -g @automattic/vip
vip dev-env create && vip dev-env start
# Development commands
npm run vip:start # Start local VIP environment
npm run vip:sync # Sync database from beta
npm run test # Run Playwright tests
npm run deploy-manager -- status # VIP live deploys + Depot CI overlay (see RELEASING.md)
# Plugin docs: use .cursor/skills/prc-plugin-docs (see docs/plugins/README.md)
Development Environment Features
- Xdebug: PHP debugging with IDE integration
- Mailpit: Email testing and preview (ports 8025/1025)
- phpMyAdmin: Database administration interface (port 81)
- Query Monitor: WordPress debugging and performance analysis
- WP-CLI: WordPress command-line management
Code Quality & Standards
- PHP: WordPress VIP coding standards with PHPCS
- JavaScript/TypeScript: ESLint with WordPress standards
- CSS/SCSS: Stylelint with BEM methodology
- Git Hooks: Automated via Husky for pre-commit checks
Configuration Management
Environment Variables
- Local:
vip-env-vars.local.php(git-ignored, generated from 1Password Platform Secrets orPRC_PLATFORM_*env vars vianpm run gen:vip-env-vars) - Cursor Cloud: Same generators; secrets come from the cloud environment
- Depot CI: Platform Secrets items tagged
depotvia.depot/workflows/load-depot-secrets - VIP Environments: VIP platform environment variables
Key Configuration Files
- vip-config/vip-config.php: WordPress and VIP-specific settings
- vip-config/keys-and-tokens.php: API keys and credentials
- vip-config/redirects.php: Platform URL redirects and security rules (pre-WordPress)
- vip-config/server-redirects.php: Allowlist loader for plugin-owned server redirect contributors
- .wpvip/vip-dev-env.yml: Local development environment config
PRC server redirects
High-traffic string-only redirects that must exit before WordPress boots live in vip-config. Plugins own their rules by committing a pure-PHP contributor file; vip-config loads those files via an explicit allowlist (not a glob).
Contributor path: plugins/prc-<name>/vip-config/server-redirects.php (monorepo). On VIP the same files are served from wp-content/plugins/prc-<name>/vip-config/server-redirects.php. The loader tries both roots.
Contract:
- Pure PHP only (
$_SERVER,$_GET,header(),exit/return) - No WordPress APIs, Composer autoload, plugin bootstrap, or
ABSPATH - Register the relative plugin path + callback in the allowlist in
vip-config/server-redirects.php - Keep query-aware redirects on
template_redirect/ Safe Redirect Manager
Current plugin owners:
| Rule | Contributor |
|---|---|
/interactives → /feature | plugins/prc-interactive-features/vip-config/server-redirects.php |
/category/ → /topic/ | plugins/prc-taxonomies/vip-config/server-redirects.php |
?s= → /search/{term} (+ ep_filter_*) | plugins/prc-elasticpress/vip-config/server-redirects.php |
Platform spam rules, lowercase normalization, staff/domain collapses stay in vip-config/redirects.php. Editorial / long-tail rules stay in Safe Redirect Manager (vip-config/srm-redirects-import.csv).
Verify contributors with:
npm run audit:server-redirects
Monitoring & Maintenance
Performance Monitoring
- VIP APM & log shipping: Application performance metrics and centralized logs (production)
- Query Monitor: Development database and query analysis
- VIP Dashboard: Infrastructure metrics and alerts
Maintenance Features
- Maintenance Mode: Site-wide lockdown capability
- Database Sync: Automated staging-to-local data sync
- Backup & Recovery: Automated VIP platform backups
- Health Checks: Automated monitoring and alerting
Security Features
- Two-Factor Authentication: Required for production access
- VIP Login Protection: Automated brute force prevention
- Firewall Rules: Custom security rules in
vip-config/firewall.php - Spam Protection: Anti-spam redirects and filtering
Network Sites
The platform operates as a WordPress multisite network:
Primary Sites
- www.[REDACTED] (Site ID: 20) – Main public website
- platform.[REDACTED]/docs (Site ID: 1) – DocsPress documentation
Local Development URLs
- Main Site:
https://prc-platform.vipdev.lndo.site/pewresearch-org - Docs:
https://prc-platform.vipdev.lndo.site/docs - Network Admin:
https://prc-platform.vipdev.lndo.site/wp-admin/network
Troubleshooting & Support
Common Issues
- Database Sync: Use
npm run vip:syncfor fresh local data - Environment Reset:
npm run vip:destroy && npm run vip:start - Plugin Conflicts: Check
client-mu-plugins/plugin-loader.phpload order
Support Contacts
- Technical Lead: srubenstein@[REDACTED]
- Chart Technical: bwormald@[REDACTED]
- Editorial Features (Interactives) Technical: cbaronavski@[REDACTED]
Resources
- VIP Documentation: vip.wordpress.com/documentation
- Release runbook: RELEASING.md
- Local docs: https://prc-platform.vipdev.lndo.site/docs
- Production docs: platform.[REDACTED]/docs