documenso/documenso
The Open Source DocuSign Alternative.
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".
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
in non-English locales, parsing fails silently returning invalid DateTime, causing the function to return early without zero-padding, breaking chart consistency
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)
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
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
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
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
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
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
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
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
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
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.
- 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]
- 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]
- 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]
- 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]
- 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)]
- 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.
packages/prisma/schema.prismaDatabase 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
packages/prisma/schema.prismaDatabase 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
packages/prisma/schema.prismaDatabase 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
apps/remix/app/components/general/document-signing/document-signing-auth-provider.tsxReact 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
apps/openpage-api/lib/add-zero-month.tsChart 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
PostgreSQL database storing envelopes with embedded document data, recipient information, field positions, and signing status across document lifecycle
Active user sessions with authentication state, permissions, and temporary signing context during document workflows
Outgoing email notifications for signing invitations, document completion alerts, and system notifications using React Email templates
Feedback Loops
- Authentication retry flow (retry, balancing) — Trigger: Failed authentication attempt (wrong passkey, expired 2FA). Action: DocumentSigningAuthProvider re-prompts for credentials, increments attempt counter, may lock out after threshold. Exit: Successful authentication or maximum attempts exceeded.
- Usage limit enforcement (circuit-breaker, balancing) — Trigger: User/organization approaching or exceeding plan limits. Action: getServerLimits checks subscription status and quota usage, blocks actions when limits reached, prompts for upgrade. Exit: Subscription upgrade or limit reset period.
- Document completion polling (polling, reinforcing) — Trigger: Envelope in PENDING status with incomplete signatures. Action: System periodically checks recipient completion status, sends reminder emails, updates UI state for document owner. Exit: All required recipients complete signing or document expires.
Delays
- Email delivery processing (async-processing, ~seconds to minutes) — Recipients receive signing invitations after document creation, may affect time-sensitive signing workflows
- PDF processing and field rendering (batch-window, ~milliseconds to seconds) — Document upload and field positioning requires PDF parsing, affects initial document setup time
- Database replica consistency (eventual-consistency, ~milliseconds) — Read operations may see slightly stale data when using database replicas for scaling
Control Points
- NEXT_PUBLIC_FEATURE_BILLING_ENABLED (feature-flag) — Controls: Enables/disables billing and subscription features, affects enterprise limits enforcement. Default: boolean environment variable
- Document upload size limit (threshold) — Controls: NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT sets maximum PDF file size in MB. Default: 50MB default
- Authentication method selection (architecture-switch) — Controls: Choice between email verification, passkey authentication, or 2FA based on recipient and document authOptions. Default: configurable per document/recipient
- Database replica routing (runtime-toggle) — Controls: NEXT_PRIVATE_DATABASE_REPLICA_URLS enables read replica distribution for database scaling. Default: optional comma-separated URLs
Technology Stack
Full-stack React framework powering the main signing application with SSR and file-based routing
Database ORM managing PostgreSQL schema, migrations, and type-safe database operations
Type-safe API layer enabling client-server communication with automatic TypeScript inference
Email template system creating HTML emails for signing invitations and notifications
End-to-end testing framework validating complete signing workflows and user interactions
Monorepo build system orchestrating package builds, dev servers, and dependency caching
Lightweight web framework handling authentication routes and API endpoints
Utility-first CSS framework providing consistent styling across UI components
SQL query builder integrated with Prisma for complex database operations requiring raw SQL
Date manipulation library handling timezone-aware date operations and formatting
Key Components
- DocumentSigningAuthProvider (orchestrator) — Coordinates authentication flow during document signing, managing passkey data, auth options, and recipient verification across the signing workflow
apps/remix/app/components/general/document-signing/document-signing-auth-provider.tsx - ApiContractV1 (gateway) — Defines REST API contracts using ts-rest for external document operations like create, send, sign, and download with type-safe request/response schemas
packages/api/v1/contract.ts - AuthClient (adapter) — Client-side authentication interface handling sign-in/out, session management, passkey operations, and two-factor authentication using Hono client
packages/auth/client/index.ts - prisma (store) — Database connection manager with Prisma ORM, read replicas extension, and Kysely integration for PostgreSQL operations across the application
packages/prisma/index.ts - getServerLimits (validator) — Enforces usage limits and quota management for enterprise features, checking subscription status and organization claims to determine allowed actions
packages/ee/server-only/limits/server.ts - addZeroMonth (transformer) — Normalizes time-series chart data by adding zero values for missing months and extending to current month for consistent metric visualization
apps/openpage-api/lib/add-zero-month.ts - seedUser (factory) — Creates test users with organizations, hashed passwords, and configurable roles for development and testing environments
packages/prisma/seed/users.ts
Package Structure
Main web application providing the document signing interface, user dashboard, and document management features
Documentation site built with Next.js and MDX for user guides and API documentation
Public-facing API for community metrics and statistics display
REST API contracts and schemas for external integrations using ts-rest
Authentication system handling sessions, email/password auth, passkeys, and 2FA
Database layer with Prisma ORM, migrations, and Kysely integration for PostgreSQL
Core utilities, constants, and business logic shared across the application
Type-safe API layer using tRPC for internal client-server communication
Email template system using React Email for notifications and signing invitations
Shared UI component library built with React and Tailwind CSS
Digital signature implementation and PDF manipulation for document sealing
Enterprise edition features including usage limits, billing, and advanced functionality
End-to-end testing suite using Playwright for workflow validation
Static assets and media files shared across applications
Shared Tailwind CSS configuration for consistent styling
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated 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 Karolina Sarna.