Operations Manual
Operations
This section covers everything required to keep the SONAN DIGITAL CRM running reliably in production. It is written for the engineering and operations team โ the people responsible for deploying, monitoring, maintaining, and responding to incidents on the platform.
Audience
| Role | Relevant Pages |
|---|---|
| Engineers | All pages |
| Operations / DevOps | Manual, Environment, Monitoring, Cron Jobs, Incident Response, Maintenance |
| Team Leads / Admins | Onboarding guides, Manual (overview sections) |
Pages in This Section
Operations Manual
The authoritative system overview. Covers what the CRM is, who uses it, the full infrastructure table, versioning and branching strategy, deployment frequency, support responsibilities, and escalation contacts. Start here if you are new to the platform.
Environment Configuration
Complete listing of all environment variables used by the application โ names, descriptions, where to find each value, which are public vs. server-only, and how to set up a local .env.local for development.
Secrets Management
How application secrets are stored, who has access, rotation schedules for every secret, the emergency rotation procedure for compromised credentials, and recommendations for upgrading to a secrets manager.
Monitoring & Logging
How to use Sentry for error tracking, how to access Vercel function logs, recommended UptimeRobot checks, alert escalation paths, cron job monitoring, and the weekly monitoring review checklist.
Cron Jobs
Documentation for all scheduled background jobs: the invoice overdue check, recurring invoice generation, and the appointments cron. Covers authentication, Vercel config, local testing, and what to do when a job fails.
Incident Response
The incident classification table (P1โP4), the end-to-end response process from detection through post-mortem, escalation paths, and ready-to-use communication templates for P1 client outages.
Maintenance Schedule
Checklists for monthly, quarterly, and annual maintenance tasks โ dependency updates, security audits, RLS policy reviews, secret rotation, backup testing, and more.
Client Onboarding
The 8-step admin procedure for onboarding a new client: creating their CRM record, setting up portal access, assigning an account manager, and briefing the client. Includes a quick-reference checklist and common issue fixes.
Employee Onboarding
The step-by-step admin procedure for onboarding a new team member โ sending the invite, configuring their role and MFA, assigning projects, and briefing on time logging. Also covers the offboarding checklist.
Read the Operations Manual first for a full system overview, then review Environment Configuration to understand how the application is configured across environments.
Operations Manual
This document is the authoritative reference for operating the SONAN DIGITAL CRM in production. It covers system purpose, infrastructure, versioning, deployment, support responsibilities, and escalation contacts.
1. System Overview
The SONAN DIGITAL CRM is a multi-tenant web application built to manage the full client lifecycle for a digital agency. It serves two classes of users:
- Admin / Staff users โ the agency's internal team. They use the CRM to manage leads, clients, projects, tasks, time logs, proposals, invoices, contracts, and support tickets.
- Client users โ agency clients who log in to a self-service portal to view proposals, sign contracts, pay invoices, track project progress, and submit support requests.
The application is a single Next.js 15 codebase deployed on Vercel. All data lives in a Supabase PostgreSQL database, with row-level security (RLS) enforcing tenant isolation. Authentication is handled by Supabase Auth. File uploads (contracts, attachments) go to Supabase Storage. Transactional emails are sent via Resend. Payments and subscriptions are handled by Stripe. Errors are tracked in Sentry.
The system is designed to be always-on โ clients interact with the portal and expect real-time access to invoices and project status. Any unplanned downtime directly impacts client trust.
2. Infrastructure
| Service | Provider | Region | Plan | URL / Identifier | Criticality |
|---|---|---|---|---|---|
| App hosting | Vercel | Global Edge (CDN) | Team | sonan-digital.vercel.app + custom domain |
Critical |
| Database | Supabase (PostgreSQL) | us-east-1 | Pro | Supabase project dashboard | Critical |
| Auth | Supabase Auth | Same project | Included in Pro | โ | Critical |
| Storage | Supabase Storage | Same project | Included in Pro | โ | High |
| Email delivery | Resend | Global | Scale plan | resend.com dashboard |
High |
| Payments | Stripe | Global | Standard | dashboard.stripe.com |
High |
| Error tracking | Sentry | Global | Team | sentry.io โ SONAN DIGITAL org |
High |
| DNS / CDN | Cloudflare | Global | Free/Pro | Cloudflare dashboard | High |
| Docs portal | Cloudflare Pages | Global | Free | Separate Cloudflare Pages project | Low |
All Next.js routes and pages use export const runtime = 'edge'. This means API routes run on Vercel's Edge network โ not Node.js lambdas. Keep this in mind when debugging: function logs appear under the Edge Logs filter in the Vercel dashboard, not Function Logs.
3. Versioning
The CRM follows semantic versioning (MAJOR.MINOR.PATCH):
| Increment | When to use |
|---|---|
PATCH |
Bug fixes, copy changes, minor UI tweaks |
MINOR |
New features, non-breaking API changes, new CRM modules |
MAJOR |
Breaking changes, major architectural overhauls, multi-tenant schema changes |
Current version: v1.0.0
Version is tracked in package.json. On each release, tag the commit in Git:
git tag -a v1.2.0 -m "Release v1.2.0: recurring invoices, wiki module"
git push origin v1.2.0
4. Branching Strategy
| Branch | Purpose | Deploys to |
|---|---|---|
main |
Production code | Vercel Production environment |
dev |
Staging / preview | Vercel Preview environment |
| Feature branches | Work in progress | Vercel Preview (per-branch) |
Rules:
- Never commit directly to
mainfor non-emergency changes. - All changes go through a pull request:
feature/* โ dev โ main. devis used for QA and client-facing preview URLs before production release.- Emergency hotfixes can be pushed directly to
mainwith a post-merge PR for review โ document in the incident report.
The development environment uses a FUSE-mounted filesystem. Always follow the FUSE-safe git commit pattern (documented in CLAUDE.md) to avoid blob truncation. Never use git add directly on mounted paths โ use the git plumbing workflow via /tmp.
5. Deployment
Deployment is triggered automatically by a push to the relevant branch:
| Event | Result |
|---|---|
Push to main |
Production deployment on Vercel |
Push to dev |
Preview deployment on Vercel |
| Open PR | Ephemeral preview deployment for that PR |
Deployment frequency: As needed. There is no fixed release schedule โ features and fixes are deployed when ready and tested on dev.
Deployment checklist before merging to main:
- [ ] All new API routes include
export const runtime = 'edge' - [ ] Database migrations have been applied to the Supabase production project
- [ ] Any new environment variables have been added to Vercel (Production + Preview)
- [ ] The change has been tested on the
devpreview environment - [ ] No new TypeScript errors (
npx tsc --noEmit) - [ ] Sentry is not reporting new errors from the preview environment
Rollback: Vercel retains all previous deployments. To roll back, go to the Vercel dashboard โ Deployments โ select the last known-good deployment โ "Promote to Production". This is the fastest recovery path for bad deployments.
6. Support Responsibilities
| Area | Responsible Party | Notes |
|---|---|---|
| Application bugs | Engineering team | Triaged via Sentry; P1/P2 get immediate response |
| Database / schema changes | Engineering team | Via Supabase SQL editor; must update RLS policies |
| Email delivery issues | Engineering team + Resend support | Check Resend logs first; escalate to Resend support if delivery is failing at provider level |
| Payment issues | Engineering team + Stripe support | Check Stripe dashboard for webhook failures; Stripe has 24/7 support for payment disputes |
| Client portal access issues | Admin team (first line) โ Engineering (second line) | Admins can resend invites and reset passwords; engineering handles auth bugs |
| Infrastructure / hosting | Vercel support | Raise a support ticket via Vercel dashboard for platform-level issues |
| SSL / DNS | Cloudflare support | Auto-renewed SSL; DNS changes via Cloudflare dashboard |
| Sentry / error tracking | Engineering team | Weekly review cadence |
7. Escalation Contacts
Replace the placeholder names and contacts below with actual team members.
| Role | Name | Contact | Escalation Trigger |
|---|---|---|---|
| Primary Engineer | {{ PRIMARY_ENGINEER_NAME }} |
{{ PRIMARY_ENGINEER_EMAIL }} |
All P1/P2 incidents |
| Tech Lead / CTO | {{ TECH_LEAD_NAME }} |
{{ TECH_LEAD_EMAIL }} |
P1 incidents, security breaches |
| Operations Manager | {{ OPS_MANAGER_NAME }} |
{{ OPS_MANAGER_EMAIL }} |
Client-facing P1/P2 communication |
| Vercel Support | Vercel team | vercel.com/support |
Platform / hosting failures |
| Supabase Support | Supabase team | supabase.com/support |
Database / auth failures |
| Stripe Support | Stripe team | support.stripe.com |
Payment processing failures |
| Resend Support | Resend team | resend.com/support |
Email delivery failures |
For a full production outage (P1), contact the Primary Engineer and Tech Lead simultaneously โ do not wait for the Primary Engineer to be unreachable before looping in the Tech Lead.
Environment Configuration
This page documents all environment variables used by the SONAN DIGITAL CRM, how they are organized across environments, where to find their values, and how to set up a local development environment.
1. Vercel Environments
The application runs in three Vercel environments. Each has its own set of environment variable values:
| Environment | Branch | URL | Purpose |
|---|---|---|---|
| Production | main |
https://yourdomain.com |
Live system โ real clients, real payments |
| Preview | dev + all PRs |
Auto-generated Vercel preview URL | QA and staging โ safe to test with real Stripe test keys |
| Development | Local machine | http://localhost:3000 |
Engineer local development |
Production and Preview environments should use different Stripe keys. Production uses Stripe live keys (sk_live_...). Preview/Development should use Stripe test keys (sk_test_...). Using live keys in preview environments risks accidental real charges.
To manage environment variables, go to the Vercel Dashboard โ Project โ Settings โ Environment Variables.
2. Environment Variable Reference
Supabase
| Variable | Value | Scope | Description |
|---|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
{{ SUPABASE_PROJECT_URL }} |
Public | The Supabase project URL, used by both the client and server SDK. Found in Supabase Dashboard โ Project Settings โ API โ Project URL. |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
{{ SUPABASE_ANON_KEY }} |
Public | The Supabase anonymous (public) key. Used by the browser client. This key is safe to expose โ it is restricted by RLS. Found in Supabase Dashboard โ Project Settings โ API โ Project API keys โ anon public. |
SUPABASE_SERVICE_ROLE_KEY |
{{ SUPABASE_SERVICE_ROLE_KEY }} |
Server only | The Supabase service role key. Bypasses RLS โ used for admin operations only. Found in Supabase Dashboard โ Project Settings โ API โ Project API keys โ service_role secret. Never expose this key client-side. |
Email โ Resend
| Variable | Value | Scope | Description |
|---|---|---|---|
RESEND_API_KEY |
{{ RESEND_API_KEY }} |
Server only | API key for sending transactional emails via Resend. Found in Resend Dashboard โ API Keys. |
Payments โ Stripe
| Variable | Value | Scope | Description |
|---|---|---|---|
STRIPE_SECRET_KEY |
{{ STRIPE_SECRET_KEY }} |
Server only | Stripe secret key used for creating payment intents, managing subscriptions, and other server-side Stripe API calls. Found in Stripe Dashboard โ Developers โ API keys โ Secret key. Use sk_live_... for Production, sk_test_... for Preview/Dev. |
STRIPE_WEBHOOK_SECRET |
{{ STRIPE_WEBHOOK_SECRET }} |
Server only | Signing secret for verifying Stripe webhook payloads. Found in Stripe Dashboard โ Developers โ Webhooks โ select the webhook endpoint โ Signing secret. |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
{{ STRIPE_PUBLISHABLE_KEY }} |
Public | Stripe publishable key used in the browser to initialize Stripe.js. Found in Stripe Dashboard โ Developers โ API keys โ Publishable key. Use pk_live_... for Production, pk_test_... for Preview/Dev. |
Background Jobs
| Variable | Value | Scope | Description |
|---|---|---|---|
CRON_SECRET |
{{ CRON_SECRET }} |
Server only | A long random string (minimum 32 characters) used to authenticate requests to cron job endpoints. Generate with openssl rand -hex 32. Must match the Authorization: Bearer header sent by Vercel Cron. |
Error Tracking โ Sentry
| Variable | Value | Scope | Description |
|---|---|---|---|
NEXT_PUBLIC_SENTRY_DSN |
{{ SENTRY_DSN }} |
Public | The Sentry Data Source Name (DSN) for your project. Used to report errors from both client and server code. Found in Sentry Dashboard โ Project Settings โ Client Keys (DSN). |
SENTRY_AUTH_TOKEN |
{{ SENTRY_AUTH_TOKEN }} |
Server only | Sentry authentication token with org:ci scope โ used during the build process to upload source maps. Found in Sentry Dashboard โ Settings โ Auth Tokens. Required for production-quality stack traces. |
3. Public vs. Server-Only Variables
| Prefix | Exposure | Risk if leaked |
|---|---|---|
NEXT_PUBLIC_ |
Bundled into client JavaScript โ visible to anyone | Low (these are designed to be public) |
| No prefix | Never sent to the browser โ server/edge only | High โ treat as secrets |
Variables without the NEXT_PUBLIC_ prefix must never be referenced in client components ('use client'), passed as props to client components, or logged anywhere. If you accidentally expose SUPABASE_SERVICE_ROLE_KEY or STRIPE_SECRET_KEY client-side, rotate them immediately.
4. Where to Find Each Value
| Variable | Location |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Supabase Dashboard โ Project Settings โ API โ Project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase Dashboard โ Project Settings โ API โ anon public key |
SUPABASE_SERVICE_ROLE_KEY |
Supabase Dashboard โ Project Settings โ API โ service_role key |
RESEND_API_KEY |
Resend Dashboard โ API Keys โ Create API Key |
STRIPE_SECRET_KEY |
Stripe Dashboard โ Developers โ API keys โ Secret key |
STRIPE_WEBHOOK_SECRET |
Stripe Dashboard โ Developers โ Webhooks โ [endpoint] โ Signing secret |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY |
Stripe Dashboard โ Developers โ API keys โ Publishable key |
CRON_SECRET |
Generated locally: openssl rand -hex 32 |
NEXT_PUBLIC_SENTRY_DSN |
Sentry โ Project Settings โ Client Keys (DSN) |
SENTRY_AUTH_TOKEN |
Sentry โ Settings โ Auth Tokens โ Create New Token (scope: org:ci) |
5. Startup Validation
The application performs implicit validation at build time via TypeScript and at runtime via the Supabase and Stripe SDK initializers. If a required variable is missing:
NEXT_PUBLIC_SUPABASE_URLorNEXT_PUBLIC_SUPABASE_ANON_KEYmissing โ Supabase client will throw during initialization. The application will fail to render.SUPABASE_SERVICE_ROLE_KEYmissing โcreateServiceClient()will throw on first admin API call.STRIPE_SECRET_KEYmissing โ Stripe SDK initialization will throw; all payment routes will return 500.CRON_SECRETmissing โ Cron endpoints will return 401 for all requests.SENTRY_AUTH_TOKENmissing โ The build will succeed but source maps will not be uploaded. Stack traces in Sentry will show minified code.
To verify all variables are loaded in a Vercel deployment:
- Go to Vercel Dashboard โ Project โ Deployments โ select the deployment.
- Click View Build Logs โ any missing
NEXT_PUBLIC_variable will appear asundefinedin the build output. - Click View Function Logs and trigger an authenticated API request โ check for
undefinedor initialization errors in the log output.
6. Local Development Setup
Create a .env.local file in the project root. This file is git-ignored and should never be committed.
# .env.local โ Local development only. Never commit this file.
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...
# Resend (use a test API key or the real key for local testing)
RESEND_API_KEY=re_...
# Stripe โ use TEST keys for local development
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
# Cron protection
CRON_SECRET=your-local-random-secret-at-least-32-chars
# Sentry (optional for local dev โ can be omitted)
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...
SENTRY_AUTH_TOKEN=sntrys_...
Starting the dev server:
npm install
npm run dev
# App available at http://localhost:3000
To test Stripe webhooks in local development, install the Stripe CLI and forward events to your local server:
stripe listen --forward-to localhost:3000/api/stripe/webhook
The CLI will print a webhook signing secret (whsec_...) โ use that as your local STRIPE_WEBHOOK_SECRET.
You can use the shared Supabase cloud project for local development (easiest setup) or run a local Supabase stack with supabase start. For most development work, pointing local dev at the cloud dev/staging project is sufficient.
Secrets Management
This page documents how application secrets are stored, who has access to them, the rotation schedule for each secret, the procedure for emergency rotation, and recommendations for upgrading the secrets infrastructure.
1. Where Secrets Are Stored
All secrets are stored as Vercel Environment Variables, which are:
- Encrypted at rest using Vercel's infrastructure-level encryption.
- Scoped per environment (Production, Preview, Development).
- Never written to disk or committed to version control.
- Accessible only to authenticated Vercel team members with the appropriate role.
Rules that must never be broken:
- No secret is ever written into source code.
- No
.envfile containing real secrets is ever committed to git..env.localis in.gitignoreโ verify this before anygit add .. - Secrets are never logged, printed, or included in error messages.
- Secrets are never passed as URL query parameters.
If a secret is accidentally committed to git โ even on a private repo โ treat it as compromised. Immediately follow the Emergency Rotation Procedure for that secret. Removing it from git history (via git filter-branch or BFG) does not guarantee it was not already read or cached.
2. Secrets Inventory
| Secret Name | Provider | Purpose | Rotation Recommended | Last Rotated |
|---|---|---|---|---|
SUPABASE_SERVICE_ROLE_KEY |
Supabase | Admin database access, bypasses RLS | Every 6 months | {{ DATE_LAST_ROTATED }} |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase | Browser client (public, restricted by RLS) | On suspected compromise | {{ DATE_LAST_ROTATED }} |
RESEND_API_KEY |
Resend | Transactional email sending | Every 12 months | {{ DATE_LAST_ROTATED }} |
STRIPE_SECRET_KEY |
Stripe | Payment API (live) | Every 12 months | {{ DATE_LAST_ROTATED }} |
STRIPE_WEBHOOK_SECRET |
Stripe | Webhook payload verification | On endpoint URL change | {{ DATE_LAST_ROTATED }} |
CRON_SECRET |
Self-generated | Cron job endpoint authentication | Every 6 months | {{ DATE_LAST_ROTATED }} |
SENTRY_AUTH_TOKEN |
Sentry | Source map upload during CI | Every 12 months | {{ DATE_LAST_ROTATED }} |
NEXT_PUBLIC_SUPABASE_URL and the Stripe publishable key are not secret โ they are intentionally public and do not need rotation unless the project or account changes.
3. Rotation Schedule
SUPABASE_SERVICE_ROLE_KEY โ Every 6 months
This key bypasses all RLS policies and has full database access. It is the highest-risk secret in the system.
- Go to Supabase Dashboard โ Project Settings โ API.
- Scroll to Project API keys โ click the service role key's Reveal button.
- Click Reset (Supabase will generate a new key; the old key becomes invalid immediately).
- Copy the new key and update it in Vercel (see rotation steps below).
- Trigger a Vercel redeploy to load the new value.
STRIPE_SECRET_KEY โ Every 12 months or on suspected compromise
- Go to Stripe Dashboard โ Developers โ API keys.
- Click Roll key next to the secret key. Stripe lets you set a 24-hour delay before the old key is invalidated โ use this window to update and test before the old key expires.
- Update
STRIPE_SECRET_KEYin Vercel. - Redeploy and verify Stripe API calls succeed.
RESEND_API_KEY โ Every 12 months or on suspected compromise
- Go to Resend Dashboard โ API Keys.
- Create a new API key.
- Update
RESEND_API_KEYin Vercel. - Redeploy and send a test email to confirm delivery.
- Delete the old API key from the Resend dashboard.
CRON_SECRET โ Every 6 months
- Generate a new secret locally:
bash openssl rand -hex 32 - Update
CRON_SECRETin Vercel (Production and Preview). - Update the secret in
vercel.jsonunder the cron authorization header if it is stored there (it should not be โ the cron routes read it from the environment). - Redeploy and verify a cron manual trigger works.
STRIPE_WEBHOOK_SECRET โ On endpoint URL change
The webhook signing secret is tied to the registered endpoint URL in Stripe. If the production URL changes:
- Go to Stripe Dashboard โ Developers โ Webhooks.
- Add the new endpoint URL.
- Copy the new signing secret.
- Update
STRIPE_WEBHOOK_SECRETin Vercel. - Delete the old webhook endpoint from Stripe.
SENTRY_AUTH_TOKEN โ Every 12 months
- Go to Sentry โ Settings โ Auth Tokens.
- Create a new token with
org:ciscope. - Update
SENTRY_AUTH_TOKENin Vercel. - Delete the old token.
4. Emergency Rotation Procedure
Use this procedure when a secret is known or suspected to be compromised โ leaked in logs, committed to git, exposed in an error message, or accessed by an unauthorized party.
Treat every suspected compromise as a confirmed compromise. Do not wait for confirmation.
Step-by-Step
- Revoke the old key immediately in the provider dashboard โ do not wait.
- Supabase service role key: Supabase Dashboard โ API โ Reset
- Stripe key: Stripe Dashboard โ Developers โ API keys โ Roll key (set 0 delay)
- Resend key: Resend Dashboard โ API Keys โ Delete
- Cron secret: Skip to step 2 (self-generated, just replace it)
-
Sentry token: Sentry โ Settings โ Auth Tokens โ Revoke
-
Generate the new key/secret in the provider dashboard.
-
Update in Vercel โ go to Vercel Dashboard โ Project โ Settings โ Environment Variables:
-
Update the value for Production, Preview, and Development scopes as applicable.
-
Trigger a redeploy โ in Vercel Dashboard โ Deployments โ click Redeploy on the latest production deployment. The new environment variable is picked up immediately.
-
Verify the application works โ check the feature that uses the rotated secret (e.g., send a test email after rotating
RESEND_API_KEY, make a test payment after rotatingSTRIPE_SECRET_KEY). -
Check for unauthorized activity โ review provider dashboards for any suspicious API calls during the window when the key may have been compromised:
- Supabase: Database โ Logs
- Stripe: Dashboard โ Developers โ Events
-
Resend: Dashboard โ Emails โ Sent
-
Document the rotation โ update the Last Rotated date in the Secrets Inventory table above, and record the incident in the incident log with: date discovered, suspected exposure window, scope of potential access, and actions taken.
-
Notify the team โ send an internal notification to all engineers noting the rotated secret and confirming the old key is no longer valid.
Every minute a compromised key remains active is a minute it can be used. Revoke first, then sort out the replacement. It is acceptable to have a brief (minutes) service degradation while a new key is set up โ it is not acceptable to leave a compromised key active while you investigate.
5. Vault and Secrets Manager Options
Current Setup: Vercel Environment Variables
Vercel's built-in environment variables are encrypted at rest and access-controlled by Vercel team roles. This is appropriate for small teams and single-project setups.
Limitations: - No audit log of who accessed a secret's value. - No automatic rotation. - Rotation requires a manual redeploy.
Recommended Upgrade for Growing Teams
If the team expands beyond ~5 engineers or begins handling particularly sensitive client data, consider migrating to a dedicated secrets manager:
| Tool | Pros | Cons |
|---|---|---|
| Infisical | Open-source option; self-hostable; Vercel integration; audit logs | Requires setup and maintenance if self-hosted |
| Doppler | Seamless Vercel sync; per-environment scoping; access logs; auto-rotation hooks | Paid for team features |
| HashiCorp Vault | Industry standard; very powerful | Complex setup; overkill for this scale |
For the current scale, Vercel environment variables remain appropriate. Re-evaluate when the team adds a dedicated DevOps role.
6. Access Control
Access to Vercel environment variables is controlled by Vercel team roles:
| Role | Can view secret values | Can edit secrets | Can delete secrets |
|---|---|---|---|
| Owner | Yes | Yes | Yes |
| Member (Admin) | Yes | Yes | Yes |
| Member (Developer) | No (masked) | No | No |
Only team members who actively need to rotate or manage secrets should have Owner or Admin roles on the Vercel project. Engineers who only deploy code can work with Developer access.
To review current team access: Vercel Dashboard โ Team Settings โ Members.