From 0b7d73ce06536801a94054803d8752d904a706dd Mon Sep 17 00:00:00 2001 From: Michael Griffin Date: Thu, 16 Jul 2026 19:16:17 -0400 Subject: [PATCH] Initial production deployment and signing certificate documentation --- .gitignore | 21 ++++++++++ README.md | 25 ++++++++++++ compose.yml | 78 +++++++++++++++++++++++++++++++++++++ docs/SIGNING-CERTIFICATE.md | 49 +++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 compose.yml create mode 100644 docs/SIGNING-CERTIFICATE.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54b9458 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Environment +.env +.env.* +!.env.example + +# Certificates +*.p12 +*.pem +*.key +*.crt + +# Databases / dumps +*.sql +*.dump + +# Logs +*.log + +# Docker overrides +docker-compose.override.yml +compose.override.yml diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e13458 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# SkyCloud Digital - Documenso + +This repository contains the production Docker Compose deployment for Documenso. + +## Purpose + +This repository tracks: + +- compose.yml +- deployment documentation +- recovery procedures +- operational notes +- production changes + +This repository intentionally does NOT track: + +- .env +- certificates +- databases +- uploaded documents +- secrets + +Production deployment directory: + +/home/michael/docker/documenso diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..0aa7ad6 --- /dev/null +++ b/compose.yml @@ -0,0 +1,78 @@ +name: documenso-production + +services: + database: + restart: unless-stopped + image: postgres:15 + environment: + - POSTGRES_USER=${POSTGRES_USER:?err} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?err} + - POSTGRES_DB=${POSTGRES_DB:?err} + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER}'] + interval: 10s + timeout: 5s + retries: 5 + volumes: + - database:/var/lib/postgresql/data + + documenso: + restart: unless-stopped + image: documenso/documenso:latest + depends_on: + database: + condition: service_healthy + environment: + - PORT=${PORT:-3000} + - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:?err} + - NEXT_PRIVATE_ENCRYPTION_KEY=${NEXT_PRIVATE_ENCRYPTION_KEY:?err} + - NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=${NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY:?err} + - NEXT_PRIVATE_GOOGLE_CLIENT_ID=${NEXT_PRIVATE_GOOGLE_CLIENT_ID} + - NEXT_PRIVATE_GOOGLE_CLIENT_SECRET=${NEXT_PRIVATE_GOOGLE_CLIENT_SECRET} + - NEXT_PUBLIC_WEBAPP_URL=${NEXT_PUBLIC_WEBAPP_URL:?err} + - NEXT_PRIVATE_INTERNAL_WEBAPP_URL=${NEXT_PRIVATE_INTERNAL_WEBAPP_URL:-http://localhost:$PORT} + - NEXT_PRIVATE_DATABASE_URL=${NEXT_PRIVATE_DATABASE_URL:?err} + - NEXT_PRIVATE_DIRECT_DATABASE_URL=${NEXT_PRIVATE_DIRECT_DATABASE_URL:-${NEXT_PRIVATE_DATABASE_URL}} + - NEXT_PUBLIC_UPLOAD_TRANSPORT=${NEXT_PUBLIC_UPLOAD_TRANSPORT:-database} + - NEXT_PRIVATE_UPLOAD_ENDPOINT=${NEXT_PRIVATE_UPLOAD_ENDPOINT} + - NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE=${NEXT_PRIVATE_UPLOAD_FORCE_PATH_STYLE} + - NEXT_PRIVATE_UPLOAD_REGION=${NEXT_PRIVATE_UPLOAD_REGION} + - NEXT_PRIVATE_UPLOAD_BUCKET=${NEXT_PRIVATE_UPLOAD_BUCKET} + - NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID=${NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID} + - NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY=${NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY} + - NEXT_PRIVATE_SMTP_TRANSPORT=${NEXT_PRIVATE_SMTP_TRANSPORT:?err} + - NEXT_PRIVATE_SMTP_HOST=${NEXT_PRIVATE_SMTP_HOST} + - NEXT_PRIVATE_SMTP_PORT=${NEXT_PRIVATE_SMTP_PORT} + - NEXT_PRIVATE_SMTP_USERNAME=${NEXT_PRIVATE_SMTP_USERNAME} + - NEXT_PRIVATE_SMTP_PASSWORD=${NEXT_PRIVATE_SMTP_PASSWORD} + - NEXT_PRIVATE_SMTP_APIKEY_USER=${NEXT_PRIVATE_SMTP_APIKEY_USER} + - NEXT_PRIVATE_SMTP_APIKEY=${NEXT_PRIVATE_SMTP_APIKEY} + - NEXT_PRIVATE_SMTP_SECURE=${NEXT_PRIVATE_SMTP_SECURE} + - NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS=${NEXT_PRIVATE_SMTP_UNSAFE_IGNORE_TLS} + - NEXT_PRIVATE_SMTP_FROM_NAME=${NEXT_PRIVATE_SMTP_FROM_NAME:?err} + - NEXT_PRIVATE_SMTP_FROM_ADDRESS=${NEXT_PRIVATE_SMTP_FROM_ADDRESS:?err} + - NEXT_PRIVATE_SMTP_SERVICE=${NEXT_PRIVATE_SMTP_SERVICE} + - NEXT_PRIVATE_RESEND_API_KEY=${NEXT_PRIVATE_RESEND_API_KEY} + - NEXT_PRIVATE_MAILCHANNELS_API_KEY=${NEXT_PRIVATE_MAILCHANNELS_API_KEY} + - NEXT_PRIVATE_MAILCHANNELS_ENDPOINT=${NEXT_PRIVATE_MAILCHANNELS_ENDPOINT} + - NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN=${NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN} + - NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR=${NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR} + - NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY=${NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY} + - NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT=${NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT} + - NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY} + - NEXT_PUBLIC_DISABLE_SIGNUP=${NEXT_PUBLIC_DISABLE_SIGNUP} + - NEXT_PUBLIC_DISABLE_EMAIL_PASSWORD_SIGNUP=${NEXT_PUBLIC_DISABLE_EMAIL_PASSWORD_SIGNUP} + - NEXT_PUBLIC_DISABLE_GOOGLE_SIGNUP=${NEXT_PUBLIC_DISABLE_GOOGLE_SIGNUP} + - NEXT_PUBLIC_DISABLE_MICROSOFT_SIGNUP=${NEXT_PUBLIC_DISABLE_MICROSOFT_SIGNUP} + - NEXT_PUBLIC_DISABLE_OIDC_SIGNUP=${NEXT_PUBLIC_DISABLE_OIDC_SIGNUP} + - NEXT_PRIVATE_ALLOWED_SIGNUP_DOMAINS=${NEXT_PRIVATE_ALLOWED_SIGNUP_DOMAINS} + - NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=${NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH:-/opt/documenso/cert.p12} + - NEXT_PRIVATE_SIGNING_PASSPHRASE=${NEXT_PRIVATE_SIGNING_PASSPHRASE} + - NEXT_PUBLIC_USE_INTERNAL_URL_BROWSERLESS=${NEXT_PUBLIC_USE_INTERNAL_URL_BROWSERLESS} + ports: + - ${PORT:-3000}:${PORT:-3000} + volumes: + - /opt/documenso/cert.p12:/opt/documenso/cert.p12:ro + +volumes: + database: diff --git a/docs/SIGNING-CERTIFICATE.md b/docs/SIGNING-CERTIFICATE.md new file mode 100644 index 0000000..c3eeedc --- /dev/null +++ b/docs/SIGNING-CERTIFICATE.md @@ -0,0 +1,49 @@ +# Signing Certificate + +## Incident + +Date: 2026-07-16 + +Symptoms: + +- Documents remained in PENDING after every signer completed. +- Audit logs confirmed recipients signed. +- Background jobs repeatedly failed during document sealing. + +Root Cause: + +The signing certificate was not usable by the Documenso application. + +Verified production configuration: + +Certificate: + +/opt/documenso/cert.p12 + +Permissions: + +-rw-r--r-- + +Owner: + +node:node + +Environment Variable: + +NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=/opt/documenso/cert.p12 + +Verification: + +Container reported: + +CERTIFICATE_READABLE: YES + +Documenso reported: + +{"isAvailable":true} + +Result: + +The previously stuck document completed successfully after the certificate configuration was corrected. + +This document serves as the authoritative recovery reference if document sealing ever fails again.