documenso/documenso

The Open Source DocuSign Alternative.

12,695 stars TypeScript 7 components

13 hidden assumptions · 6-stage pipeline · 7 components

Like any codebase, this fullstack makes assumptions it never checks — most are routine. The ones worth your attention are below.

Handles document signing workflows with PDF processing, recipient management, and electronic signatures

Document signing workflows begin when users upload PDF files, which get processed into Envelope records with embedded document data. Recipients are added with specific roles (signer/viewer) and positioned Fields define where signatures or form inputs appear on pages. The system sends email invitations containing unique tokens, recipients authenticate using various methods (email verification, passkeys, 2FA), complete their signing actions which update Field records as 'inserted', and finally the envelope transitions to completed status triggering audit log generation and signed document storage.

Under the hood, the system uses 3 feedback loops, 3 data pools, 4 control points to manage its runtime behavior.

A 7-component fullstack. 1821 files analyzed. Data flows through 6 distinct pipeline stages.

Hidden Assumptions

Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".

Worth your attention first

if a dataset has fewer data points than labels, array access will return undefined and push operations will create sparse arrays with holes, corrupting chart visualization

Worth your attention first

in non-English locales, parsing fails silently returning invalid DateTime, causing the function to return early without zero-padding, breaking chart consistency

Worth your attention first

if token expires or becomes invalid mid-session, subsequent authentication operations fail silently or with confusing errors, leaving users unable to complete signing

Show everything (10 more)
Ordering

assumes labels array contains chronologically ordered month strings, but never validates this ordering

If this fails: if months are out of order, the minus/plus month calculations add zero months in wrong positions, creating nonsensical time series with months appearing multiple times

apps/openpage-api/lib/add-zero-month.ts:addZeroMonth
Resource

assumes window.location.href assignment in signOut/signOutSession methods will always succeed and complete navigation

If this fails: in restricted iframe contexts or when CSP blocks navigation, the redirect fails silently leaving users in inconsistent auth state thinking they're signed out but still having active sessions

packages/auth/client/index.ts:AuthClient
Temporal

assumes subscription.status reflects real-time subscription state, but status is only updated through webhook events or periodic syncing

If this fails: recently cancelled or expired subscriptions may show as active for minutes or hours, allowing users to exceed limits they should be restricted to, potentially causing billing discrepancies

packages/ee/server-only/limits/server.ts:getServerLimits
Environment

assumes NEXT_PRIVATE_DATABASE_REPLICA_URLS contains valid, reachable database URLs when present, but never validates URL format or connectivity

If this fails: invalid replica URLs cause Prisma to fail at runtime during read operations, potentially taking down the entire application when read replicas are configured incorrectly

packages/prisma/index.ts:prismaWithReplicas
Scale

assumes remember() utility creates singleton instances that are safe across concurrent requests, but doesn't handle memory cleanup or connection pool limits

If this fails: in high-concurrency scenarios, the global prisma client singleton may exhaust database connection pools or accumulate memory, causing degraded performance or crashes

packages/prisma/index.ts:remember
Domain

assumes email.toLowerCase() is sufficient for email normalization, but doesn't handle unicode characters, punycode domains, or plus-addressing

If this fails: users with identical emails in different unicode forms or with plus-addressing can create duplicate accounts, causing authentication confusion and potential data leakage between accounts

packages/prisma/seed/users.ts:seedUser
Contract

assumes AppError.parseError can handle any error format returned by the session endpoint, but error response shapes may vary by HTTP status or server state

If this fails: unexpected error formats cause AppError.parseError to throw its own errors, masking the original authentication failure and making debugging authentication issues extremely difficult

packages/auth/client/index.ts:getSession
Environment

assumes environment variable contains a valid number string when present, but Number() conversion can return NaN for invalid inputs

If this fails: invalid size limits cause document upload validation to use NaN, which fails all comparisons, either blocking all uploads or allowing unlimited sizes depending on comparison logic

packages/lib/constants/app.ts:NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT
Temporal

assumes passkey data from trpc queries remains valid throughout the signing session without checking for revoked or expired passkeys

If this fails: if passkeys are revoked on another device or expire during a long signing session, authentication attempts fail with cryptic WebAuthn errors instead of prompting for alternative auth methods

apps/remix/app/components/general/document-signing/document-signing-auth-provider.tsx:passkeyData
Ordering

assumes organisation.subscription and organisation.organisationClaim are always present when organisation exists, based on database relations

If this fails: if database integrity is compromised or migrations create orphaned organisations, accessing subscription or claim properties throws null reference errors during limit checks, blocking document operations

packages/ee/server-only/limits/server.ts:getServerLimits

Open the standalone hidden-assumptions report for documenso →

How Data Flows Through the System

Document signing workflows begin when users upload PDF files, which get processed into Envelope records with embedded document data. Recipients are added with specific roles (signer/viewer) and positioned Fields define where signatures or form inputs appear on pages. The system sends email invitations containing unique tokens, recipients authenticate using various methods (email verification, passkeys, 2FA), complete their signing actions which update Field records as 'inserted', and finally the envelope transitions to completed status triggering audit log generation and signed document storage.

  1. Document upload and processing — User uploads PDF file through Remix app, system stores document data as Buffer in Envelope record, extracts page count and dimensions for field positioning [PDF file → Envelope]
  2. Recipient and field configuration — User adds recipients with email addresses and roles, places signature/text/date fields on specific pages with pixel coordinates, authOptions configured per recipient and document level [Envelope → Recipient, Field]
  3. Document sending and invitation — System generates unique tokens per recipient, sends email invitations using React Email templates with signing links, envelope status changes to PENDING [Envelope, Recipient → Email notifications]
  4. Recipient authentication — DocumentSigningAuthProvider orchestrates auth flow, validates tokens, handles passkey challenges or 2FA verification, checks recipient access permissions before allowing signing [Recipient, DocumentSigningAuthContextValue → Authenticated session]
  5. Signature and form completion — Authenticated recipient views document with positioned fields, enters signatures/text/dates, each completed field marked as inserted: true, signing interface validates all required fields completed [Field, Recipient → Field (updated)]
  6. Document finalization — When all recipients complete actions, envelope status becomes COMPLETED, audit log generated, final signed PDF created with embedded signatures and stored [Envelope, Field → Signed document]

Data Models

The data structures that flow between stages — the contracts that hold the system together.

Envelope packages/prisma/schema.prisma
Database model with id: string, status: EnvelopeStatus, documentData: Buffer, recipients: Recipient[], fields: Field[], authOptions: DocumentAuth configuration, createdAt/updatedAt timestamps
Created when document uploaded, populated with recipients and fields, transitions through DRAFT → PENDING → COMPLETED states as recipients sign
Recipient packages/prisma/schema.prisma
Database model with email: string, name: string, token: string, role: RecipientRole (SIGNER|VIEWER|CC), authOptions: RecipientAuth, signedAt: Date, envelopeId: foreign key
Added to envelope during creation, receives signing invitation email, authenticates using token, completes signing or viewing action
Field packages/prisma/schema.prisma
Database model with type: FieldType (SIGNATURE|TEXT|DATE|CHECKBOX|RADIO|DROPDOWN), page: number, positionX/Y: number, width/height: number, recipientId: foreign key, inserted: boolean, customText: string
Created when placing fields on document template, rendered on PDF pages, filled by recipients during signing, marked as inserted when completed
DocumentSigningAuthContextValue apps/remix/app/components/general/document-signing/document-signing-auth-provider.tsx
React context with executeActionAuthProcedure: function, documentAuthOptions: Envelope['authOptions'], recipient: SigningAuthRecipient, derivedRecipientAccessAuth/ActionAuth: auth type arrays, passkeyData: PasskeyData object
Initialized when recipient accesses signing page, manages auth state throughout signing process, handles passkey enrollment and verification
TransformedData apps/openpage-api/lib/add-zero-month.ts
Chart data structure with labels: string[] (month names), datasets: Array<{label: string, data: number[]}> for metrics visualization
Generated from raw GitHub/community metrics, transformed to add zero months for consistent charting, returned via openpage API endpoints

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

Document Storage (database)
PostgreSQL database storing envelopes with embedded document data, recipient information, field positions, and signing status across document lifecycle
Session Store (cache)
Active user sessions with authentication state, permissions, and temporary signing context during document workflows
Email Queue (queue)
Outgoing email notifications for signing invitations, document completion alerts, and system notifications using React Email templates

Feedback Loops

Delays

Control Points

Technology Stack

Remix (framework)
Full-stack React framework powering the main signing application with SSR and file-based routing
Prisma (database)
Database ORM managing PostgreSQL schema, migrations, and type-safe database operations
tRPC (framework)
Type-safe API layer enabling client-server communication with automatic TypeScript inference
React Email (library)
Email template system creating HTML emails for signing invitations and notifications
Playwright (testing)
End-to-end testing framework validating complete signing workflows and user interactions
Turbo (build)
Monorepo build system orchestrating package builds, dev servers, and dependency caching
Hono (framework)
Lightweight web framework handling authentication routes and API endpoints
Tailwind CSS (library)
Utility-first CSS framework providing consistent styling across UI components
Kysely (database)
SQL query builder integrated with Prisma for complex database operations requiring raw SQL
Luxon (library)
Date manipulation library handling timezone-aware date operations and formatting

Key Components

Package Structure

remix (app)
Main web application providing the document signing interface, user dashboard, and document management features
docs (app)
Documentation site built with Next.js and MDX for user guides and API documentation
openpage-api (app)
Public-facing API for community metrics and statistics display
api (library)
REST API contracts and schemas for external integrations using ts-rest
auth (library)
Authentication system handling sessions, email/password auth, passkeys, and 2FA
prisma (library)
Database layer with Prisma ORM, migrations, and Kysely integration for PostgreSQL
lib (shared)
Core utilities, constants, and business logic shared across the application
trpc (library)
Type-safe API layer using tRPC for internal client-server communication
email (library)
Email template system using React Email for notifications and signing invitations
ui (library)
Shared UI component library built with React and Tailwind CSS
signing (library)
Digital signature implementation and PDF manipulation for document sealing
ee (library)
Enterprise edition features including usage limits, billing, and advanced functionality
app-tests (tooling)
End-to-end testing suite using Playwright for workflow validation
assets (shared)
Static assets and media files shared across applications
tailwind-config (config)
Shared Tailwind CSS configuration for consistent styling

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Fullstack Repositories

Frequently Asked Questions

What is documenso used for?

Handles document signing workflows with PDF processing, recipient management, and electronic signatures documenso/documenso is a 7-component fullstack written in TypeScript. Data flows through 6 distinct pipeline stages. The codebase contains 1821 files.

How is documenso architected?

documenso is organized into 4 architecture layers: Applications, API Layer, Business Logic, Data & Infrastructure. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through documenso?

Data moves through 6 stages: Document upload and processing → Recipient and field configuration → Document sending and invitation → Recipient authentication → Signature and form completion → .... Document signing workflows begin when users upload PDF files, which get processed into Envelope records with embedded document data. Recipients are added with specific roles (signer/viewer) and positioned Fields define where signatures or form inputs appear on pages. The system sends email invitations containing unique tokens, recipients authenticate using various methods (email verification, passkeys, 2FA), complete their signing actions which update Field records as 'inserted', and finally the envelope transitions to completed status triggering audit log generation and signed document storage. This pipeline design reflects a complex multi-stage processing system.

What technologies does documenso use?

The core stack includes Remix (Full-stack React framework powering the main signing application with SSR and file-based routing), Prisma (Database ORM managing PostgreSQL schema, migrations, and type-safe database operations), tRPC (Type-safe API layer enabling client-server communication with automatic TypeScript inference), React Email (Email template system creating HTML emails for signing invitations and notifications), Playwright (End-to-end testing framework validating complete signing workflows and user interactions), Turbo (Monorepo build system orchestrating package builds, dev servers, and dependency caching), and 4 more. This broad technology surface reflects a mature project with many integration points.

What system dynamics does documenso have?

documenso exhibits 3 data pools (Document Storage, Session Store), 3 feedback loops, 4 control points, 3 delays. The feedback loops handle retry and circuit-breaker. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does documenso use?

5 design patterns detected: Monorepo with shared packages, Type-safe API layer, Context-based state management, Database ORM abstraction, Feature flag architecture.

Analyzed on April 20, 2026 by CodeSea. Written by .