PRIV ProtocolPRIV Docs
Guides

Deployment

Production deployment checklist and best practices for your PRIV integration.

Overview

Before deploying your PRIV integration to production, follow this comprehensive checklist to ensure security, performance, and reliability.

Security First

Verify all security configurations are properly set.

Performance Optimized

Ensure minimal impact on your application's performance.


Pre-Deployment Checklist

API Keys

  • Switch to production keys: Replace pk_test_ with pk_live_ keys
  • Store keys securely: Use environment variables, not hardcoded values
  • Verify key permissions: Ensure keys have minimum required permissions
  • Set up key rotation plan: Document how to rotate keys if compromised
# Production environment variables
NEXT_PUBLIC_PRIV_KEY=pk_live_xxxxxxxxxxxxx
PRIV_SECRET_KEY=sk_live_xxxxxxxxxxxxx

Security Headers

Ensure your application has proper security headers:

// next.config.ts
const securityHeaders = [
  {
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000; includeSubDomains; preload',
  },
  {
    key: 'X-Frame-Options',
    value: 'DENY',
  },
  {
    key: 'X-Content-Type-Options',
    value: 'nosniff',
  },
  {
    key: 'X-XSS-Protection',
    value: '1; mode=block',
  },
  {
    key: 'Referrer-Policy',
    value: 'strict-origin-when-cross-origin',
  },
  {
    key: 'Content-Security-Policy',
    value: `
      default-src 'self';
      script-src 'self' 'unsafe-inline' https://cdn.priv.io;
      connect-src 'self' https://api.priv.io wss://realtime.priv.io;
      img-src 'self' data: https:;
      style-src 'self' 'unsafe-inline';
    `.replace(/\n/g, ''),
  },
]

export default {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: securityHeaders,
      },
    ]
  },
}

Domain Whitelisting

Register your production domains in the PRIV dashboard:

  1. Go to Settings then Domains
  2. Add all production domains
  3. Add staging/preview domains if needed
Allowed domains:
- example.com
- www.example.com
- app.example.com
- *.vercel.app (for preview deployments)

Environment Configuration

Vercel

# Install Vercel CLI
npm i -g vercel

# Set environment variables
vercel env add NEXT_PUBLIC_PRIV_KEY production
vercel env add PRIV_SECRET_KEY production
vercel env add PRIV_WEBHOOK_SECRET production

# Deploy
vercel --prod

Docker

# Dockerfile
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

FROM node:20-alpine AS runner

WORKDIR /app

ENV NODE_ENV=production

COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

# Environment variables should be injected at runtime
# Never bake secrets into the image

EXPOSE 3000

CMD ["node", "server.js"]
# docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_PRIV_KEY=${NEXT_PUBLIC_PRIV_KEY}
      - PRIV_SECRET_KEY=${PRIV_SECRET_KEY}
      - PRIV_WEBHOOK_SECRET=${PRIV_WEBHOOK_SECRET}

AWS/GCP/Azure

Use your cloud provider's secrets manager:

// AWS Secrets Manager example
import {
  SecretsManagerClient,
  GetSecretValueCommand,
} from '@aws-sdk/client-secrets-manager'

async function getSecrets() {
  const client = new SecretsManagerClient({ region: 'us-east-1' })

  const response = await client.send(
    new GetSecretValueCommand({
      SecretId: 'priv/production',
    })
  )

  return JSON.parse(response.SecretString!)
}

SDK Configuration

Production Settings

import { Priv } from '@priv/sdk'

const priv = new Priv({
  apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,

  // Production settings
  debug: false,              // Disable debug logging
  batchSize: 20,             // Larger batches for efficiency
  flushInterval: 10000,      // 10 second flush interval

  // Privacy
  respectDNT: true,          // Honor Do Not Track
  anonymizeIP: true,         // Hash IP addresses

  // Error handling
  onError: (error) => {
    // Send to your error tracking service
    Sentry.captureException(error)
  },
})

Error Tracking Integration

// Integration with Sentry
import * as Sentry from '@sentry/nextjs'
import { Priv } from '@priv/sdk'

const priv = new Priv({
  apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
  onError: (error) => {
    Sentry.captureException(error, {
      tags: {
        sdk: 'priv',
      },
      extra: {
        eventType: error.event?.type,
      },
    })
  },
})

Performance Optimization

Bundle Size

The PRIV SDK is designed to be lightweight (under 5KB gzipped). Verify your bundle:

# Check bundle size
npx next build
npx @next/bundle-analyzer

Lazy Loading

Load the SDK only when needed:

// Lazy load for non-critical pages
const loadPriv = async () => {
  const { Priv } = await import('@priv/sdk')
  return new Priv({
    apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
  })
}

// Use in component
useEffect(() => {
  loadPriv().then(priv => {
    priv.trackPageView()
  })
}, [])

Server-Side Considerations

For SSR applications, ensure SDK only runs on client:

'use client'

import { useEffect } from 'react'
import { Priv } from '@priv/sdk'

export function Analytics() {
  useEffect(() => {
    // Only run on client
    if (typeof window === 'undefined') return

    const priv = new Priv({
      apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
    })

    priv.trackPageView()

    return () => {
      priv.flush()
    }
  }, [])

  return null
}

Monitoring Setup

Health Checks

Implement health check endpoints:

// app/api/health/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  const checks = {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    services: {
      priv: await checkPrivConnection(),
      database: await checkDatabaseConnection(),
    },
  }

  const allHealthy = Object.values(checks.services).every(s => s.status === 'healthy')

  return NextResponse.json(checks, {
    status: allHealthy ? 200 : 503,
  })
}

async function checkPrivConnection() {
  try {
    const response = await fetch('https://api.priv.io/v1/health', {
      headers: {
        'Authorization': `Bearer ${process.env.PRIV_SECRET_KEY}`,
      },
    })
    return {
      status: response.ok ? 'healthy' : 'unhealthy',
      latency: response.headers.get('x-response-time'),
    }
  } catch (error) {
    return { status: 'unhealthy', error: String(error) }
  }
}

Logging

Set up structured logging:

import pino from 'pino'

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  formatters: {
    level: (label) => ({ level: label }),
  },
})

// Log PRIV events
priv.on('eventSent', (event) => {
  logger.debug({ event }, 'PRIV event sent')
})

priv.on('error', (error) => {
  logger.error({ error }, 'PRIV SDK error')
})

Metrics

Export metrics for monitoring:

import { Counter, Histogram } from 'prom-client'

const eventsSent = new Counter({
  name: 'priv_events_sent_total',
  help: 'Total number of events sent to PRIV',
  labelNames: ['event_type'],
})

const eventLatency = new Histogram({
  name: 'priv_event_latency_seconds',
  help: 'Event send latency',
  buckets: [0.1, 0.5, 1, 2, 5],
})

// Track in SDK callbacks
priv.on('eventSent', (event) => {
  eventsSent.inc({ event_type: event.type })
})

priv.on('batchSent', (batch, duration) => {
  eventLatency.observe(duration / 1000)
})

Webhook Configuration

Production Webhook Setup

  1. Create production endpoint:

    • Use HTTPS only
    • Implement signature verification
    • Add rate limiting
  2. Register in PRIV dashboard:

    • Go to Settings then Webhooks
    • Add production URL
    • Select required events
    • Copy signing secret
  3. Set up retry handling:

    • Implement idempotency
    • Handle duplicate deliveries
    • Log all webhook events
// Production webhook handler
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'

export async function POST(request: NextRequest) {
  const body = await request.text()
  const signature = request.headers.get('x-priv-signature')
  const timestamp = request.headers.get('x-priv-timestamp')

  // Verify signature
  if (!verifySignature(body, signature, timestamp)) {
    logger.warn('Invalid webhook signature')
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
  }

  const event = JSON.parse(body)

  // Log for audit trail
  logger.info({ eventId: event.id, type: event.type }, 'Webhook received')

  try {
    // Process with idempotency check
    await processWebhookIdempotent(event)

    return NextResponse.json({ received: true })
  } catch (error) {
    logger.error({ error, eventId: event.id }, 'Webhook processing failed')
    return NextResponse.json({ error: 'Processing failed' }, { status: 500 })
  }
}

Final Checklist

Security

  • Production API keys configured
  • Secret keys not exposed in client code
  • Security headers configured
  • CSP allows PRIV domains
  • Webhook signatures verified
  • Rate limiting implemented

Configuration

  • Domains whitelisted in dashboard
  • Environment variables set correctly
  • Debug mode disabled
  • Error handling configured

Privacy

  • Consent management implemented
  • DNT (Do Not Track) respected
  • Privacy policy updated
  • Data processing agreements in place

Performance

  • Bundle size verified
  • Lazy loading configured (if needed)
  • Batch settings optimized

Monitoring

  • Health checks configured
  • Error tracking integrated
  • Logging set up
  • Alerts configured

Testing

  • Production smoke tests passed
  • Webhook delivery verified
  • Events appearing in dashboard

Post-Deployment

Verify Integration

After deployment, verify everything is working:

// Production verification script
async function verifyDeployment() {
  console.log('Verifying PRIV integration...')

  // 1. Check SDK connection
  const priv = new Priv({
    apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
  })

  // 2. Send test event
  await priv.track('deployment_verification', {
    environment: 'production',
    timestamp: new Date().toISOString(),
  })
  await priv.flush()

  console.log('Test event sent')

  // 3. Verify in dashboard
  console.log('Check dashboard for event: deployment_verification')
}

Monitor First 24 Hours

  • Watch error rates in your monitoring
  • Check PRIV dashboard for incoming events
  • Verify webhook delivery logs
  • Monitor for any rate limiting issues

Rollback Plan

If issues arise, have a rollback plan ready:

// Feature flag to disable PRIV
const priv = new Priv({
  apiKey: process.env.NEXT_PUBLIC_PRIV_KEY!,
  disabled: process.env.DISABLE_PRIV === 'true',
})

// Or wrap in try-catch
function safeTrack(event: string, properties?: Record<string, unknown>) {
  try {
    priv.track(event, properties)
  } catch (error) {
    console.error('PRIV tracking failed:', error)
    // Continue without tracking
  }
}

Next Steps