Contents
Target Audience: Incoming Software Engineer, Church Administrator, or IT Support Lead.
System: CPCana (CPCana Church & Organizational Workflow Engine)
Deployment: Chesterfield Presbyterian Church (Deployment #1)
Last Updated: October 5, 2026 (checked against the code); §2.4 (outbound email) rewritten October 3, 2026
Executive Summary & Tech Stack
CPCana is a dedicated, single-tenant operations and task management system built specifically for church and non-profit ministry operations. It handles task tracking, multi-project workflows, repeating task resets, automated morning email digests, physical facility printouts, child protection compliance, and staff onboarding.
Technology Overview
| Component | Technology | Provider / Hosting |
|---|---|---|
| Application Framework | Next.js 14 (App Router, Server Actions) | Vercel |
| Language & Typing | TypeScript 5.5+ (Strict mode) | Node.js 20+ |
| Database | PostgreSQL | Neon (Serverless Postgres) |
| ORM & Migrations | Prisma ORM 5.22 | @prisma/adapter-neon |
| Authentication | NextAuth.js 4 (Credentials + Emergency HMAC Support) | Native in-app |
| Outbound Email | Nodemailer (SMTP) | Google Workspace, smtp.gmail.com with an app password (§2.4) |
| File Attachments | @vercel/blob | Vercel Blob Storage |
| Styling & UI | Tailwind CSS 3.4 | Dynamic brand palette |
| Testing Suite | Vitest | npm test; the whole suite must pass before deploying |
Critical Ownership & Account Transfer Checklist
Before the departing lead departs, the following external platforms must have ownership transferred or administrative co-owners added:
1. Git Repository (GitHub)
- Current Remotes:
origin:https://github.com/jblizard30-wq/CPCana.gitchurch:https://github.com/cpcoffice/CPCana.gitcentral-admin:https://github.com/jblizard30-wq/central-admin.git- Confirm which one Vercel deploys from (Project Settings → Git) before transferring anything.
- Action:
- Transfer repository ownership from
jblizard30-wqto a Church GitHub Organization (e.g.,github.com/chesterfieldpres) or a shared church IT account (it@yourchurch.org). - GitHub: Repo Settings → General → Danger Zone → Transfer ownership.
- Ensure at least two technical church leaders or committee members have Admin permissions on the repository.
- In branch settings, ensure
mainbranch is protected against accidental force-pushes.
- Transfer repository ownership from
2. Deployment & Hosting (Vercel)
- Project:
cpcana - Vercel Team:
team_Ih9cpCCbR4JmD2KTwuyKbN0V - Action:
- In Vercel: Team Settings → Members, invite church administrator or IT lead as Owner.
- In Vercel: Team Settings → Billing, confirm that the payment method on file is an official church card.
- If the GitHub repo is transferred to a new organization, go to Project Settings → Git in Vercel and reconnect to the new repository URL to maintain automatic deployments on push to
main. - Export all production environment variables into the church's credential vault.
3. Database (Neon Serverless Postgres)
- Dashboard: console.neon.tech
- Action:
- Invite the incoming technical lead or church IT contact to the Neon Project as an Admin / Owner.
- Switch billing to the church's payment method.
- Verify access to connection strings (Pooled connection for serverless queries; Direct connection for schema migrations).
- Note the point-in-time restore (PITR) backup settings in the Neon dashboard.
4. Outbound Email (Google Workspace SMTP)
Status as of October 3, 2026: production has no email settings, so CPCana sends no email at all: no morning digests, no reminders, no setup invites, no password-reset links. In-app notifications still work. The decision is to send through CPC's Google Workspace using an app password on a chespres.org account. It's free and needs no DNS changes, and because Google sends the mail, it passes chespres.org's sender checks.
Who does what
| Step | Who | About |
|---|---|---|
| A. Allow 2-Step Verification | Google Workspace admin for chespres.org | 2 min |
| B. Create the app password | Whoever can sign in as the sending account | 3 min |
| C. Add the settings to Vercel and redeploy | Vercel project owner (Justin Blizard) | 5 min |
| D. Send a test email | Any CPCana admin | 1 min |
Step A: Google Workspace admin (admin.google.com)
- Pick the sending account. A dedicated account such as
cpcana@chespres.orgis best: digests don't arrive from one staff member's name, and email keeps working if that person leaves. Creating a new user may use a Workspace license. If that isn't possible, an existing staff account works. - Go to Menu → Security → Authentication → 2-step verification.
- On the left, select the organizational unit that contains the sending account (or the top-level organization).
- Check Allow users to turn on 2-Step Verification, then Save. Don't change Enforcement.
- Check two things that block app passwords:
- If enforcement is on, its Methods setting must not be Only security key for this account.
- The account must not be enrolled in Advanced Protection.
Step B: signed in as the sending account
An admin can't create an app password for someone else; it has to be made from the account itself.
- Go to myaccount.google.com → Security, and turn on 2-Step Verification.
- Go to myaccount.google.com/apppasswords, enter the name
CPCana, and click Create. - Copy the 16-character code and save it in the church password manager. Share it with the Vercel project owner through the password manager, never by email or text.
Step C: Vercel project owner (cpcana → Settings → Environment Variables)
Add each of these with the environment set to Production:
| Variable | Value |
|---|---|
SMTP_HOST | smtp.gmail.com |
SMTP_PORT | 587 |
SMTP_USER | the sending account's full address, e.g. cpcana@chespres.org |
SMTP_PASSWORD | the 16-character code with spaces removed (mark it Sensitive) |
EMAIL_FROM | CPCana · Chesterfield Presbyterian <cpcana@chespres.org> (same address as SMTP_USER) |
Leave SMTP_REQUIRE_TLS unset; with a password present, CPCana already requires an encrypted connection on port 587. Then go to Deployments, open the current production deployment, and choose Redeploy. New variables take effect only after a redeploy.
Step D: test (any CPCana admin)
Sign in to CPCana and go to Settings → Developer → Outbound Email & SMTP. The status should read Configured. Enter your address and click Send Test, which checks the connection and then sends. It should arrive within a minute, from the sending account.
Things to know
- Daily limit: Google allows the account 2,000 messages a day. A few staff generate a few dozen.
- From address: it must be the sending account or one of its Gmail Send mail as aliases. Otherwise Gmail replaces it with the account's own address.
- Password changes: changing the sending account's Google password, or suspending the account, stops CPCana's email. If the password changes, repeat Step B and update
SMTP_PASSWORD(then redeploy). - Common errors:
- "Username and Password not accepted": the app password is wrong or was revoked, or 2-Step Verification was turned off.
- App passwords page missing or unavailable: Step A isn't done for that account, or the account is limited to security keys or enrolled in Advanced Protection.
5. Domain Registrar & DNS
- Action:
- Document where the church's domain is registered (e.g., Cloudflare, GoDaddy, Squarespace/Google Domains).
- Confirm that church IT has credentials to manage DNS records pointing to Vercel (
cname.vercel-dns.comor Vercel A records).
6. Credentials Vault
- All secrets must be recorded in the church's password manager (e.g. 1Password, Bitwarden):
- GitHub credentials / Deploy keys
- Vercel login
- Neon Postgres credentials
- The sending account's Google app password (§2.4)
- Production
NEXTAUTH_SECRET,CRON_SECRET,HQ_SUPPORT_SECRET
Environment Variables Reference
These variables are defined in .env.example and configured in Vercel under Project Settings → Environment Variables:
| Variable | Required | Description | Example / Notes |
|---|---|---|---|
DATABASE_URL | Yes | PostgreSQL connection string from Neon | postgresql://user:pass@ep-...neon.tech/cpcana?sslmode=require |
NEXTAUTH_SECRET | Yes | Encryption key for session cookies | Generate via openssl rand -base64 32 |
NEXTAUTH_URL | Yes | Canonical base URL of the live site | https://cpcana.yourchurch.org |
CRON_SECRET | Yes | Secures /api/cron/* endpoints against unauthorized triggers | Generate via openssl rand -base64 32 |
HQ_SUPPORT_SECRET | Optional (needed for central-admin support login) | Shared HMAC secret for emergency support login from the control plane. Unset disables support login; nothing else depends on it | Generate via openssl rand -base64 32 |
BLOB_READ_WRITE_TOKEN | Yes | Vercel Blob access token for task file uploads | Copied from Vercel Storage dashboard tab |
ORG_NAME | Optional | Display name for the organization | "Chesterfield Presbyterian Church" |
BRAND_COLOR | Optional | Primary hex color (generates 50–950 Tailwind palette) | "#003366" |
LOGO_URL | Optional | URL or relative path to church logo | "/logo.png" |
NEXT_PUBLIC_HANDBOOKS_URL | Optional | Base URL of this deployment's public handbook library. Shows Handbooks & Guides in the sidebar and search, Settings → Help & Handbooks, and the guide links on individual pages. Unset or not an http(s) URL hides all of them. Read at build time, so redeploy after changing it | https://handbooks.cpcana.app (CPC's library) |
EMAIL_FROM | Needed for email | From address for notifications; must match SMTP_USER or one of its Gmail aliases (see §2.4) | "CPCana · Chesterfield Presbyterian <cpcana@chespres.org>" |
SMTP_HOST | Needed for email | SMTP server hostname. Unset means no email is sent at all | smtp.gmail.com |
SMTP_PORT | Needed for email | SMTP port (587 for STARTTLS, 465 for SSL) | 587 |
SMTP_USER | Needed for email | The sending Google Workspace account | cpcana@chespres.org |
SMTP_PASSWORD | Needed for email | That account's 16-character app password (§2.4, Step B) | Secret; mark Sensitive |
SMTP_REQUIRE_TLS | Optional | STARTTLS is already required whenever a password is set; "false" only for a trusted internal relay | Leave unset |
CHILD_PROTECTION_CRON_PROJECT_ID | Needed for the compliance sweep | Project where the Monday child-protection renewal tasks are filed. Unset means the sweep files nothing | a project id |
CHILD_PROTECTION_CRON_SECTION_ID | Optional | Section in that project for those tasks; defaults to its first section. A section from another project stops the sweep | a section id |
GOOGLE_CALENDAR_CLIENT_ID / GOOGLE_CALENDAR_CLIENT_SECRET, OUTLOOK_CALENDAR_CLIENT_ID / OUTLOOK_CALENDAR_CLIENT_SECRET | Optional | Enable the Settings → Integrations connect buttons (connecting doesn't sync events yet) | Leave blank |
APP_URL / NEXT_PUBLIC_APP_URL | Optional | Fallbacks for the base URL in emailed links when NEXTAUTH_URL is unset | https://cpcana.yourchurch.org |
ENABLED_MODULES | Yes (to show add-on modules) | Add-on modules this deployment shows. Unset or empty enables none. An explicit list is frozen — modules added to the code later stay off until it's edited | all (recommended), or a subset such as inventory,meetups,raci,xp |
Deployment & Build Pipeline
Production Deployment Flow
- A commit is merged or pushed to the
mainbranch on GitHub. - Vercel automatically detects the commit and triggers a deployment.
- The build command executed by Vercel is:
prisma migrate deploy && next build- Important: Any pending Prisma migrations in
prisma/migrationsare automatically applied to the Neon database before the Next.js app builds. - If migrations fail, the deployment is aborted, preventing broken code from hitting production.
- Preview deployments skip the migration step. For a branch or pull request (Vercel's
VERCEL_ENVispreview), the build prints a note and goes straight tonext build, because no database is attached to previews (DATABASE_URLis set for Production only). A preview can never change the production database. Everywhere else, including local builds, the command above runs unchanged.
- Important: Any pending Prisma migrations in
Local Development Setup
To run CPCana locally on a developer machine:
# 1. Clone repository
git clone https://github.com/<org>/CPCana.git
cd CPCana
# 2. Install dependencies
npm install
# 3. Configure environment
cp .env.example .env
# Edit .env with your local or development Postgres connection string
# 4. Run database migrations
npm run prisma:migrate
# 5. Run test suite to verify health
npm test
# 6. Start development server
npm run devVerification & Quality Commands
Before deploying any custom changes, always verify:
npm test # Runs the full Vitest suite
npm run lint # Next.js ESLint verification
npx tsc --noEmit # TypeScript strict compiler checkScheduled Background Jobs (Vercel Cron)
CPCana schedules 7 automated jobs via vercel.json. Every cron requires Authorization: Bearer <CRON_SECRET>. Vercel Cron sends it automatically once CRON_SECRET is set; if it's missing, every job returns 401 and nothing runs.
| Path | Schedule (UTC) | Local (Central Time) | Purpose |
|---|---|---|---|
/api/cron/materialize-recurrence | 0 7 * * * | 1:00 AM CST / 2:00 AM CDT | Resets due repeating tasks in place (back to the base To Do column, subtasks unchecked, due date advanced). No copies are created. |
/api/cron/purge-trash | 0 8 * * * | 2:00 AM CST / 3:00 AM CDT | Permanently deletes soft-deleted tasks older than the retention window. |
/api/cron/child-protection-renewals | 0 9 * * 1 | 3:00 AM CST / 4:00 AM CDT (Mon) | Files one unassigned review task per expiring or lapsed person (at most every 30 days) into CHILD_PROTECTION_CRON_PROJECT_ID. Does nothing if that variable is unset or the child_protection module is off. |
/api/cron/automations | 0 13 * * * | 7:00 AM CST / 8:00 AM CDT | Evaluates due-date-approaching automation rules. |
/api/cron/due-date-reminders | 0 13 * * * | 7:00 AM CST / 8:00 AM CDT | Notifies assignees of tasks due today, due tomorrow, or overdue, plus blocker alerts (in-app, and emailed when their notification settings allow). |
/api/cron/digest | 0 14 * * * | 8:00 AM CST / 9:00 AM CDT | Compiles and sends morning digest emails based on user preferences. |
/api/cron/send-reminders | 0 15 * * * | 9:00 AM CST / 10:00 AM CDT | Sends explicit user-scheduled task reminder emails. |
Note: All cron handlers are idempotent. If a cron fires twice, conditional atomic claims prevent duplicate emails or duplicate tasks.
Church Application Governance & User Administration
Promoting Staff to ADMIN
- Go to
/admin/usersas an existing Admin. - In the staff member's row, change the Role dropdown to Admin. It saves immediately. (You can't change your own role; to change several people at once, tick their rows and use Set role.) Ensure at least two active church staff members hold the Admin role.
Onboarding New Staff or Volunteers
- There is no public self-signup in production.
- To add a user: go to User Management (
/admin/users) → + New user. - Enter their name, church email, and role, leave Send first-time login invitation email with setup link checked, and click Create user. (Unchecked, you must set a temporary password of 8+ characters instead.)
- A dialog shows their 7-day
/set-password?token=...link with a Copy button, for Slack, Teams, or text. The row's Invite button resends it later.
Resetting a Password
- If a user forgets their password:
- They can use Forgot password? on the sign-in page (rate-limited via Postgres).
- An Admin can go to
/admin/users, click Reset pass in the user's row, and either set a temporary password (Set manually) or send a 24-hour reset link (Send email link → Send reset email; the link also appears on screen to copy).
Break-Glass Troubleshooting Runbook
Issue 1: The application displays a 500 error or "Database connection failed"
- Check Neon Postgres status:
- Log into console.neon.tech.
- Check if the database instance is in an active state or suspended due to billing/resource limits.
- Test connecting with
npm run prisma:studiolocally with the production connection string to verify access.
- Check Vercel Function Logs:
- Open Vercel dashboard → Click the
cpcanaproject → Click Logs. - Filter by status
500to inspect stack traces.
- Open Vercel dashboard → Click the
Issue 2: Emails, morning digests, or reminders are not sending
- Check Vercel Cron Runs:
- Go to Vercel →
cpcana→ Settings → Cron Jobs. - Check the status column to see if the cron job succeeded (200 OK) or timed out.
- Go to Vercel →
- Check the email settings (§2.4):
- In CPCana, go to Settings → Developer → Outbound Email & SMTP. Disabled / "Not configured (SMTP_HOST is unset)" means
SMTP_HOSTis missing from Production. Otherwise click Send Test; it checks the connection, then sends. - An authentication error usually means the app password was revoked, often because the sending account's Google password changed. Repeat §2.4 Step B, update
SMTP_PASSWORD, and redeploy. - Check that the sending account hasn't been suspended or hit Google's 2,000-messages-a-day limit.
- In CPCana, go to Settings → Developer → Outbound Email & SMTP. Disabled / "Not configured (SMTP_HOST is unset)" means
- Verify in-app settings:
- Check that the user has email notifications turned ON in
/settings/notifications. - If the user clicked Unsubscribe from all emails in a past message, every email toggle and their digest were switched off. Have them turn the ones they want back on in
/settings/notifications.
- Check that the user has email notifications turned ON in
Issue 3: An important task was deleted by mistake
- Go to Admin → All Trash (
/admin/trash). - Find the task. Newest deletions are at the top; your browser's Find (⌘/Ctrl+F) helps on a long list.
- Click Restore and confirm. The task returns to its original project and section with its tags and custom field values. Its subtasks are separate trash entries (listed as "Parent / Subtask"); restore those too.
- Note: If a task has been in the trash longer than the 30-day retention window, it may have been purged by the nightly cleanup cron.
- A deleted project is gone for good: it skips the trash. Recovering one means a Neon point-in-time restore (§2.3).
Issue 4: A volunteer cannot access their RSVP or meetup sheet
- Volunteer links: guest sign-up uses one shared link per meetup, with no login. If a volunteer lost it, a Manager, Admin, or the meetup's organizer opens the meetup (
/meetups/[id]) and clicks Share with Guests to copy a fresh link (good for 30 days). - Volunteer Check-in Sheets: If paper clipboards are needed on Sunday morning, click "Print Check-in Sheet" on the Meetup to generate an instant physical sheet with supervisor sign-off blocks.
Cross-Repository Boundary: central-admin (Control Plane)
The repository connects to an optional control plane (central-admin):
- Support Login: An administrator at
central-admincan access CPCana using a signed HMAC token via/support-login. This usesHQ_SUPPORT_SECRET. - Health Probing: The
/api/healthendpoint responds with{ status: "ok" }for external uptime monitors. - Independence: CPCana functions completely independently of
central-admin. All day-to-day church operations, user management, and passwords are fully self-contained.
Key Documentation Files in Repository
All team members should be pointed to the following resources in the repository:
docs/admin-guide.md— Detailed instructions for Admins (User management, Teams, Automations, Print tools).docs/user-manual.md— Comprehensive user manual for staff and ministry leads.docs/workflow-strategy-guide.md— Ministry playbooks (Guest assimilation, Facilities, Children's ministry).docs/automation-workflow-guide.md— Click-by-click process stages and automation rules.docs/field-cards.md— Printable worksheets: Sunday prep, child-protection requirements, onboarding.docs/handbooks/— Builds the CPCana Handbooks Library, the copies of these guides staff actually read: claude.ai artifacts for people with a Claude login, and a public copy at https://handbooks.cpcana.app for everyone else (its own Vercel project,cpcana-handbooks, separate fromcpcana). CI checks both are current; see its README for republishing. The app links to the public copy throughNEXT_PUBLIC_HANDBOOKS_URL(§3), so if that site moves, update the variable and redeploy.src/lib/handbooks.test.tsfails if a guide's id, title, or public flag inlibrary.json, or a heading the app links to, changes without the app's copy insrc/lib/handbooks.ts.CLAUDE.md— Architectural invariants for developers modifying the code.