Hanzo Sign - Open Source Document Signing
Hanzo Sign is an open-source electronic document signing platform — React Router and Hono frontend, tRPC and ts-rest APIs, Prisma ORM, and PDF signing via P12 certificates or Google Cloud KMS. Live at sign.hanzo.ai. AGPL-3.0.
Overview
Hanzo Sign is an open-source DocuSign alternative for electronic document signing. Turborepo monorepo with a React Router (Remix) + Hono server frontend, tRPC + ts-rest APIs, Prisma ORM, and PDF signing via P12 certificates or Google Cloud KMS. Live at sign.hanzo.ai. Version 2.7.1. Licensed AGPL-3.0.
Why Hanzo Sign?
- Self-hostable: Full control over your signing infrastructure
- Open source: Inspect and audit the entire signing pipeline
- Multiple signing providers: Local P12 certificates or Google Cloud HSM
- Swappable storage: Database or S3-compatible object storage
- API-first: REST (V1) and tRPC (V2) APIs with OpenAPI spec
- Enterprise features: Teams, custom branding, webhooks, bulk sending
- i18n: Multi-language via Lingui
- AI integration: Google Vertex AI (Gemini) for document analysis
Tech Stack
- Frontend: React 18, React Router v7 (Remix), Tailwind CSS, shadcn/ui, Radix UI
- Server: Hono (on Node.js)
- Database: PostgreSQL 15 (Prisma ORM + Kysely)
- API: tRPC (V2, current), ts-rest (V1, deprecated)
- Auth: Arctic (OAuth), WebAuthn/Passkeys, Hanzo IAM (hanzo.id)
- Email: React Email, Nodemailer (SMTP, Resend, MailChannels)
- PDF: @libpdf/core, pdfjs-dist, PDF-Lib
- Jobs: Inngest or local database-backed queue
- Build: Turborepo + npm workspaces, Vite
- Testing: Playwright (E2E)
OSS Base
Repo: hanzoai/sign.
When to use
- Sending documents for electronic signature
- Building document signing workflows
- Self-hosting a DocuSign-like service
- Creating document templates for recurring signatures
- Bulk sending documents via API
- Embedding signing into existing applications
Hard requirements
- Node.js v22 or above
- PostgreSQL database
- SMTP server or email provider (Resend, MailChannels)
- P12 signing certificate (or Google Cloud KMS)
Quick reference
| Item | Value |
|---|---|
| UI | https://sign.hanzo.ai (port 3000) |
| API V2 | /api/v2/* (tRPC + OpenAPI) |
| API V1 | /api/v1/* (ts-rest, deprecated) |
| Auth | Arctic OAuth + WebAuthn + Hanzo IAM |
| DB | PostgreSQL (Prisma + Kysely) |
| Root package | @hanzo/sign-root |
| Version | 2.7.1 |
| License | AGPL-3.0 |
| Repo | github.com/hanzoai/sign |
One-file quickstart
Developer Quickstart (Docker)
# Clone and setup
git clone https://github.com/hanzoai/sign.git
cd sign
cp .env.example .env
# One command: install, start db/mail containers, migrate, seed, dev
npm run dManual Setup
npm install
cp .env.example .env
# Set required env vars:
# NEXTAUTH_SECRET, NEXT_PUBLIC_WEBAPP_URL
# NEXT_PRIVATE_DATABASE_URL, NEXT_PRIVATE_DIRECT_DATABASE_URL
# NEXT_PRIVATE_SMTP_FROM_NAME, NEXT_PRIVATE_SMTP_FROM_ADDRESS
npm run prisma:migrate-dev
npm run translate:compile
npm run devDocker (Production)
docker run -d --restart=unless-stopped \
-p 3000:3000 \
-v hanzo-sign:/app/data \
--name hanzo-sign \
hanzo-sign:latestCore Concepts
Architecture
Remix App (Hono Server)
apps/remix
/api/v1/* /api/v2/* /api/trpc/* /api/jobs/* React Router UI
(ts-rest) (tRPC) (tRPC) (Jobs API)
| | | |
@hanzo/sign-api @hanzo/sign-trpc @hanzo/sign-lib @hanzo/sign-email
|
+--------------+--------------+
| | |
Storage Jobs PDF Signing
Provider Provider Provider
| | |
DB or S3 Inngest/Local Local P12 or GCloud HSMMonorepo Packages
Applications (apps/):
| Package | Description | Port |
|---|---|---|
@hanzo/sign-remix | Main app (React Router + Hono) | 3000 |
@hanzo/sign-documentation | Documentation site (Nextra) | 3002 |
@hanzo/sign-openpage-api | Public analytics API | 3003 |
Core Packages (packages/):
| Package | Description |
|---|---|
@hanzo/sign-lib | Core business logic (server, client, universal) |
@hanzo/sign-trpc | tRPC API layer with OpenAPI (V2) |
@hanzo/sign-api | REST API layer via ts-rest (V1) |
@hanzo/sign-prisma | Database layer (Prisma ORM + Kysely) |
@hanzo/sign-ui | UI components (shadcn + Radix + Tailwind) |
@hanzo/sign-email | Email templates (React Email) |
@hanzo/sign-auth | Auth (Arctic OAuth, WebAuthn/Passkeys) |
@hanzo/sign-signing | PDF signing (Local P12, Google Cloud KMS) |
@hanzo/sign-ee | Enterprise Edition features |
@hanzo/sign-assets | Static assets |
Swappable Providers
Storage (NEXT_PUBLIC_UPLOAD_TRANSPORT):
database-- Store files as Base64 in PostgreSQL (default)s3-- S3-compatible storage (Hanzo S3, AWS S3, or any S3 endpoint)
PDF Signing (NEXT_PRIVATE_SIGNING_TRANSPORT):
local-- P12 certificate file (default)gcloud-hsm-- Google Cloud KMS Hardware Security Module
Email (NEXT_PRIVATE_SMTP_TRANSPORT):
smtp-auth-- Standard SMTP with credentials (default)smtp-api-- SMTP with API keyresend-- Resend APImailchannels-- MailChannels API
Background Jobs (NEXT_PRIVATE_JOBS_PROVIDER):
local-- Database-backed queue (default)inngest-- Managed cloud service
Document Signing Flow
- Upload document to storage provider (DB or S3)
- Add recipients (signers, viewers, approvers)
- Add signature fields (positions on PDF)
- Send document (triggers email job)
- Recipient signs via unique link
seal-documentjob finalizes the PDF- Signing provider cryptographically signs the PDF
- Signed PDF stored back to storage provider
Auth / IAM Integration
Hanzo Sign supports Hanzo IAM (hanzo.id) for production auth:
IAM_URL="https://hanzo.id"
IAM_CLIENT_ID="<your-client-id>"
IAM_CLIENT_SECRET="<your-client-secret>"Also supports Google OAuth, Microsoft OAuth, and generic OIDC providers.
Signing Certificates
For local signing, generate a P12 certificate:
openssl genrsa -out private.key 2048
openssl req -new -x509 -key private.key -out certificate.crt -days 365
read -s -p "Enter certificate password: " CERT_PASS
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt \
-password env:CERT_PASS -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1Place at apps/remix/resources/certificate.p12.
Environment Variables
| Variable | Purpose | Options |
|---|---|---|
NEXTAUTH_SECRET | Session encryption | Random string |
NEXT_PUBLIC_WEBAPP_URL | Public URL | https://sign.hanzo.ai |
NEXT_PRIVATE_DATABASE_URL | PostgreSQL connection | Connection string |
NEXT_PUBLIC_UPLOAD_TRANSPORT | Storage provider | database, s3 |
NEXT_PRIVATE_SIGNING_TRANSPORT | Signing provider | local, gcloud-hsm |
NEXT_PRIVATE_SMTP_TRANSPORT | Email provider | smtp-auth, smtp-api, resend, mailchannels |
NEXT_PRIVATE_JOBS_PROVIDER | Jobs provider | local, inngest |
IAM_URL | Hanzo IAM endpoint | https://hanzo.id |
Development Services (Docker)
| Service | Port |
|---|---|
| PostgreSQL | 54320 |
| Inbucket (Mail UI) | 9000 |
| Inbucket (SMTP) | 2500 |
| S3 dashboard | 9001 |
| S3 API | 9002 |
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| No emails received | Using dev quickstart | Check Inbucket at http://localhost:9000 |
| "Failed to get private key bags" | P12 cert has no password | Re-create with password (min 4 chars) |
| Can't see env vars in scripts | Env not loaded | Wrap with npm run with:env -- npm run myscript |
| IPv6 deployment | Default binds IPv4 | Pass npm run start -- -H :: |
| Lingui errors | Translations not compiled | Run npm run translate:compile |
Related Skills
hanzo/hanzo-id.md- IAM and authentication (hanzo.id)hanzo/hanzo-kms.md- Secret managementhanzo/hanzo-platform.md- PaaS deploymenthanzo/hanzo-commerce.md- E-commerce (contracts, checkout)
How is this guide?