Skip to content

Cross-Site Architecture and Deployment

This page is the starting infrastructure reference for work that spans multiple NASPO-supported websites or shared services. Use it before creating local clones, changing deployment automation, moving secrets, or modifying transactional mail.

Last verified: August 12, 2026.

Start Here for Cross-Site Work

Before planning or changing a cross-site project, review OSKB for:

  • Repository mapping
  • Frontend/backend split
  • Hosting provider or server
  • Branch/environment mapping
  • Deployment trigger and process
  • Plugin and package management
  • Server filesystem paths
  • Secrets boundary
  • Rollback procedure
  • Ownership and contact notes, where present

If a detail is missing, document it as Unknown/TODO rather than guessing. Do not create alternate local clones when the canonical working copy already exists under ~/Sites/naspo.

Canonical Local Workspace

Use ~/Sites/naspo as the preferred local working-copy root for NASPO website and mail infrastructure work.

Local path GitHub repository Purpose
~/Sites/naspo/naspo.org NASPO-ValuePoint/naspo naspo.org public Next.js frontend
~/Sites/naspo/naspovaluepoint.org NASPO-ValuePoint/NVP NASPO ValuePoint WordPress site
~/Sites/naspo/joinppa.org NASPO-ValuePoint/joinppa JoinPPA WordPress-managed themes/plugins deployment repository
~/Sites/naspo/uppcc.org NASPO-ValuePoint/uppcc UPPCC repository placeholder; local clone currently has no project files
~/Sites/naspo/rfxpremier.org NASPO-ValuePoint/rfxpremier RFxPremier repository
~/Sites/naspo/naspo-secure-mail NASPO-ValuePoint/naspo-secure-mail NASPO-owned WordPress mail transport plugin
~/Sites/naspo/naspo-mail-gateway NASPO-ValuePoint/naspo-mail-gateway Cloudflare Worker mail gateway
~/Sites/naspo/oskb NASPO-ValuePoint/OSKB This knowledge base

Website Architecture and Deployment Summary

Property Current verified architecture Repository and local path Deployment notes
www.naspo.org Public frontend is Next.js on Vercel. Production WordPress backend is Bitnami WordPress on AWS Lightsail at cms.naspo.org. Frontend repo: NASPO-ValuePoint/naspo; local path: ~/Sites/naspo/naspo.org. Staging frontend repo: NASPO-ValuePoint/naspo-staging. Public production site is www.naspo.org. Production CMS is cms.naspo.org. Staging frontend is sandbox.naspo.org; staging CMS uses its own WordPress host and staging mail gateway identity. See NASPO.org.
naspovaluepoint.org WordPress site with production, staging, and development environments. Repo: NASPO-ValuePoint/NVP; local path: ~/Sites/naspo/naspovaluepoint.org. Existing OSKB deployment model: dev auto-deploys to Development, staging auto-deploys to Staging, and main is production-ready but production deploys manually from GitHub Actions. The automation branch is for the cross-environment refresh workflow, not normal app deployment.
joinppa.org WordPress site with managed theme/plugin deployment through Composer and GitHub Actions. Production runs on AWS Lightsail through Cloudflare-proxied DNS. Repo: NASPO-ValuePoint/joinppa; local path: ~/Sites/naspo/joinppa.org. dev and staging auto-deploy from matching branches. Production is workflow_dispatch from main only. Production DNS was corrected August 12, 2026 so joinppa-prod is the live origin. Confirm DNS/origin routing rather than inferring role from the Lightsail instance name. See JoinPPA.org.
uppcc.org Bitnami WordPress on AWS Lightsail. Repo: NASPO-ValuePoint/uppcc; local path: ~/Sites/naspo/uppcc.org. Local clone is initialized but currently has no commits checked out and no project files. Treat repository/deployment source of truth as Unknown/TODO until confirmed. See UPPCC.org.
rfxpremier.org Unknown/TODO; preserve any special application and data integrations already documented for this site. Repo: NASPO-ValuePoint/rfxpremier; local path: ~/Sites/naspo/rfxpremier.org. Local repo is on main; remote branches include dev-deployment-setup and local-dev-configuration. No .github/workflows directory was present in the local checkout when checked. Do not assume it matches JoinPPA.

NASPO.org Frontend and WordPress Backend Split

www.naspo.org has a split architecture:

Public website request
  -> Next.js frontend on Vercel project naspo
  -> separate WordPress backend for content/backend behavior
  -> WordPress wp_mail() calls for transactional mail

The Vercel/Next.js frontend and the WordPress backend are separate operational surfaces. The frontend deployment is currently verified as:

  • Platform: Vercel
  • Vercel project: naspo
  • Production source branch: main
  • Production domain: www.naspo.org
  • Frontend GitHub repository: NASPO-ValuePoint/naspo
  • Canonical local frontend path: ~/Sites/naspo/naspo.org

Transactional mail belongs to the WordPress backend because WordPress remains the source of wp_mail() calls. The Vercel frontend must not receive:

  • SendGrid API credentials
  • NASPO Secure Mail HMAC secrets
  • WordPress backend-only mail configuration

For verified production and staging backend details, see NASPO.org. Unknown/TODO: confirm the production WordPress backend repository status if any, plugin management workflow, and deployment process.

Shared NASPO Secure Mail and Mail Gateway

NASPO Secure Mail replaces direct WordPress SendGrid credentials with a signed gateway transport:

WordPress wp_mail()
  -> NASPO Secure Mail plugin
  -> site/environment HMAC signature
  -> NASPO Mail Gateway Cloudflare Worker
  -> SendGrid Mail Send API

The WordPress plugin is naspo-secure-mail version 0.1.0. It intercepts normal wp_mail() calls, signs the request with HMAC-SHA256, sends to the gateway endpoint, and returns failure to WordPress if configuration, payload preparation, transport, or gateway validation fails.

The gateway is the only component that should hold SendGrid credentials.

The plugin repository is NASPO-ValuePoint/naspo-secure-mail. The gateway repository is NASPO-ValuePoint/naspo-mail-gateway.

Environment Worker URL
Production https://naspo-mail-gateway.naspo.workers.dev
Development https://naspo-mail-gateway-dev.naspo.workers.dev
Staging https://naspo-mail-gateway-staging.naspo.workers.dev

The full mail endpoint includes /v1/mail, for example:

https://naspo-mail-gateway.naspo.workers.dev/v1/mail

Secrets Boundary

The canonical SendGrid API key is named NASPO Secure Mail Gateway. It is a Restricted Access SendGrid API key with Mail Send permission only.

The SendGrid API key must be stored only as Cloudflare Worker secret(s). It must not be stored in:

  • WordPress
  • Git
  • wp-config.php
  • the WordPress database
  • Vercel frontend configuration
  • screenshots, logs, OSKB pages, pull request bodies, or chat messages

The gateway administrative credential is separate from the per-site HMAC secrets. The administrative credential authorizes operator actions such as creating, inspecting, updating, revoking, and rotating site identities and policies. Each site's HMAC secret authenticates signed mail requests from that individual WordPress site and environment to the gateway.

A WordPress site does not receive or use the gateway administrative credential. Do not store the administrative credential or per-site HMAC secrets in OSKB, Git, WordPress options, logs, screenshots, pull request bodies, or chat messages.

Each site/environment identity has its own HMAC secret. Do not reuse HMAC secrets between sites or environments.

Store site configuration outside Git, outside the WordPress database, and outside web-managed content. The production pattern used across migrated sites is:

/opt/naspo/secure-mail/<site-environment>.php

WordPress should reference that file with NASPO_SECURE_MAIL_CONFIG_FILE in wp-config.php. The protected config contains the site ID, gateway URL, HMAC secret, timeout, and attachment limit. Never print or document the secret value.

Gateway Policy Model

The gateway stores one policy per site identity. Policy includes:

  • Site ID
  • Environment
  • Delivery mode
  • Allowed From addresses
  • Recipient limits
  • Rate limits
  • Revocation state
  • HMAC secret
  • Nonce history

Policy inspection uses the gateway admin GET /v1/sites/{siteId} endpoint. It returns non-secret policy and configuration metadata for a known site identity and does not return the site's HMAC secret. The gateway currently does not provide a GET /v1/sites list-all-sites endpoint; a collection request returns 404. Inspect a known site identity directly.

Policy changes that do not rotate the HMAC secret use the gateway admin PATCH /v1/sites/{siteId} endpoint. HMAC rotation uses POST /v1/sites/{siteId}/rotate-secret. Site revocation uses POST /v1/sites/{siteId}/revoke.

The gateway enforces sender policy:

  • NASPO Secure Mail 0.1.0 parses an explicit WordPress From header when one is supplied and passes that sender in the signed gateway payload.
  • When WordPress supplies no explicit From header, the payload may contain no sender.
  • The gateway owns sender authorization and defaulting.
  • If WordPress supplies no From header, the gateway uses the policy's defaultFrom when configured.
  • If WordPress supplies a From header listed in allowedFrom, the gateway accepts it.
  • If WordPress supplies a From header not listed in allowedFrom, the gateway rejects the request.

The plugin does not independently rewrite every WordPress From address. User-submitted or customer email addresses should normally remain Reply-To values. Do not configure contact forms to send as the submitter's address.

Environment Safety

capture, redirect, and send are server-side gateway policy. A WordPress mail request cannot select, promote, or override its delivery mode.

Use the following delivery modes:

Environment Mode Behavior
Development capture Gateway validates, authenticates, and normalizes mail requests, then returns success without calling SendGrid.
Staging redirect Gateway calls SendGrid, but rewrites all To, CC, and BCC recipients to the server-side configured redirect recipient. The WordPress request cannot choose or override the redirect target.
Production send Gateway sends to the requested recipients only after site policy and signature checks pass.

Changing an environment from one delivery behavior to another is an operator-controlled gateway policy action, not a WordPress or application request property.

The gateway rejects dev/staging configurations that use arbitrary external delivery. redirect and send fail safely with sendgrid_unavailable if the SendGrid secret is absent.

Proof status last verified August 12, 2026:

  • JoinPPA development: capture verified
  • JoinPPA staging: redirect verified with controlled real delivery to the configured redirect recipient
  • JoinPPA production: send verified with controlled real delivery, including WordPress transactional mail and Contact Form 7 mail
  • NASPO ValuePoint development: capture verified
  • NASPO ValuePoint staging: redirect verified with controlled real delivery to the configured redirect recipient
  • NASPO ValuePoint production: send verified with controlled real delivery

Do not treat a proof on one site as proof that another site has matching deployment, plugin management, backend hosting, or rollback behavior. Verify each site independently before rollout.

Adding an Authorized Sender

Adding a legitimate sender to an existing site identity is a policy change, not a secret rotation.

  1. Confirm the sender address or domain is authorized for the shared SendGrid account.
  2. Retrieve the current non-secret site policy from the gateway admin endpoint.
  3. Add the address to allowedFrom.
  4. Update defaultFrom only when the new address should become the default sender.
  5. Preserve the existing HMAC secret unless rotation is intentional.
  6. Do not expose the admin token or HMAC secret in documentation, logs, screenshots, or shell history.
  7. Verify the updated non-secret policy.
  8. Tail the Worker while testing.
  9. Send a controlled WordPress wp_mail() test.
  10. Confirm gateway mail_sent, SendGrid delivery, inbox receipt, and the actual application path that uses the sender.

Provisioning a Site Identity

For a new WordPress site or environment:

  1. Choose a canonical site ID, such as site-prod, site-staging, or site-dev.
  2. Generate a high-entropy per-site HMAC secret.
  3. Register the site identity in the matching gateway environment.
  4. Define allowed sender identities, default sender, recipient limit, payload limit, rate limits, delivery mode, and sandbox behavior.
  5. Store the HMAC secret in a protected server-side config file.
  6. Point WordPress to that file with NASPO_SECURE_MAIL_CONFIG_FILE.
  7. Install NASPO Secure Mail from the canonical plugin artifact or repository workflow.
  8. Activate NASPO Secure Mail only after configuration is readable from WordPress.
  9. Send a controlled transport test.
  10. Test the real application path, such as password reset, Contact Form 7, or Gravity Forms.
  11. Deactivate the legacy mail plugin only after proof is complete.
  12. Record the site/environment details in OSKB.

Use the plugin and gateway repositories as the source of truth for exact command and API syntax.

Revoking a Site Identity

Revoke a site identity when a site and environment should no longer be able to send through the gateway, such as after suspected HMAC exposure, decommissioning, or a cutover where the old identity must be disabled.

Use the authenticated gateway administrative operation POST /v1/sites/{siteId}/revoke. Revocation is distinct from rotating the site's HMAC secret. Rotation keeps the identity usable after WordPress and the gateway share the new secret; revocation changes the identity status so the site can no longer send.

After revocation:

  1. Inspect the known site identity with GET /v1/sites/{siteId}.
  2. Confirm the non-secret policy shows status as revoked.
  3. Confirm a signed mail request for that identity is rejected with site_revoked.
  4. Preserve gateway request IDs, WordPress wp_mail_failed evidence, and the operator notes needed to explain why the identity was revoked.

Revocation notes should record the action and non-secret status only. Do not print HMAC secrets, gateway administrative credentials, message bodies, or recipient lists in tickets, OSKB, logs, screenshots, or pull request bodies.

Troubleshooting Mail

Troubleshoot mail in layers:

  1. Confirm the WordPress plugin state.
  2. Confirm NASPO_SECURE_MAIL_CONFIG_FILE is defined.
  3. Confirm the protected config file is readable by WordPress.
  4. Confirm site ID, gateway URL, secret presence, and expected secret length without printing the secret.
  5. Confirm the gateway site identity exists in the correct Worker environment.
  6. Confirm allowedFrom and defaultFrom.
  7. Run a controlled wp_mail() test.
  8. Watch Worker tail for SiteGuard.getConfig, SiteGuard.verifyAndReserve, POST /v1/mail, and mail_sent.
  9. Verify SendGrid processed the message.
  10. Verify inbox receipt.
  11. Test the actual application path.

A direct wp_mail() test can succeed while a form still fails. In that case, inspect the form's From address, Reply-To handling, frontend validation, and backend endpoint before treating the gateway as failed.

Rollback Pattern

During the proof window, keep the previous mail plugin installed but inactive when practical.

If Secure Mail needs temporary rollback:

  1. Deactivate NASPO Secure Mail.
  2. Reactivate the previous known-good mail transport if its configuration remains valid.
  3. Confirm plugin states.
  4. Send a controlled wp_mail() test.
  5. Preserve gateway request IDs and WordPress wp_mail_failed evidence for diagnosis.

Do not delete the protected Secure Mail config or gateway identity merely to perform a temporary transport rollback.

Remaining Unknowns

Track these until confirmed:

  • NASPO.org production backend deployment process and plugin management process.
  • UPPCC remote branch state, active codebase location, and deployment process.
  • RFxPremier application architecture, deployment process, and special data/application integrations.
  • Production owner/approver and rollback contact notes for each site where they are not already documented.