Documenso is an open-source, AGPL-3.0 document e-signing platform built as a React Router application with a Prisma/PostgreSQL backend, implementing PAdES-standard PDF signatures with local or Google Cloud KMS-backed keys.
- Language
- TypeScript
- Database
- PostgreSQL
- Hosting
- Docker
- License
- AGPL-3.0
- Running for
- 1 year 2 months
- Team
- 2-5 people
Why this architecture
Signing logic is isolated in its own package with a swappable transport (local certificate or Google Cloud KMS) so self-hosters can choose key custody, while Prisma and a generated Kysely client cover both everyday CRUD and the raw SQL that document/signature queries need.
Tech stack by layer
9 technologies · audited Sep 17, 2026- ReactUI library for the main app, rendered through React Router in framework mode.
- React Router (framework mode)Routing, SSR, and build tooling for the main app; formerly a Remix app, now on @react-router/dev and @react-router/serve.
- Radix UIUnstyled accessible primitives (dialog, dropdown-menu, select, tabs, toast, etc.) behind the design system.
- Tailwind CSSUtility-first styling shared across apps via the packages/tailwind-config workspace package.
- HonoLightweight server framework mounted inside the React Router app via hono-react-router-adapter.
- tRPCTyped API layer (packages/trpc) consumed by the React app and used for document/signing procedures.
- InngestBackground job runner for async workflows (email sending, document processing) outside the request cycle.
- PAdES signing (packages/signing)Implements PDF Advanced Electronic Signatures; supports local certificates and Google Cloud KMS as a remote signer.
- PostgreSQLPrimary database, accessed through Prisma; schema and migrations live in packages/prisma.
- PrismaORM and migration tool; prisma-kysely also generates a Kysely-typed client for raw query access.
- KyselyTyped SQL query builder generated from the Prisma schema via prisma-kysely, used for queries Prisma cannot express efficiently.
- Dockerdocker/Dockerfile and docker/Dockerfile.chromium build self-hosting images; docker/production holds deploy assets.
- RenderOne-click deploy target; render.yaml provisions the web service and a managed Postgres database.
- RailwayAlternative one-click deploy target configured at the repo root.(inferred)
- TurborepoOrchestrates builds, dev servers, and tests across the npm workspaces (apps/*, packages/*).
- PlaywrightEnd-to-end test runner for signing and document flows.
- BiomeLint and format tool run via biome check across the monorepo, replacing ESLint/Prettier.
Documenso architecture diagram
Open SVGDiagram as text
- Web app (React Router · Radix) → Server (Hono · tRPC): tRPC
- Server (Hono · tRPC) → PostgreSQL (Prisma · Kysely): Prisma
- Server (Hono · tRPC) → PAdES signing (packages/signing): seal PDF
- PAdES signing (packages/signing) → Signing keys (local cert · Google KMS): sign
- Server (Hono · tRPC) → Background jobs (Inngest): enqueue
- Background jobs (Inngest) → Email (notifications): send
Key architectural decisions
6 decisions- 01
The app migrated from Next.js to React Router in framework mode
apps/remix/package.json is named @documenso/remix but depends on @react-router/dev, @react-router/serve, and react-router ^7, with build/dev scripts calling react-router build and react-router dev rather than next; a Hono server (hono-react-router-adapter) is mounted for custom routes.
- 02
Signing keys can be local certificates or a remote KMS
packages/signing depends on @google-cloud/kms and @google-cloud/secret-manager alongside its local transport, so a self-hoster can keep a signing certificate on disk or delegate signing operations to Google Cloud KMS without touching the signing call sites.
- 03
Prisma is paired with a generated Kysely client for typed raw queries
packages/prisma/package.json depends on both @prisma/client and kysely, wired together by prisma-extension-kysely and prisma-kysely, so code can use Prisma's high-level API for CRUD and drop into Kysely-typed SQL for queries Prisma's query builder cannot express well.
- 04
Async work runs through Inngest, not a custom queue
The root package.json depends on inngest and inngest-cli (with an inngest:dev script pointed at /api/jobs), so background jobs like emailing signers or post-signing processing are defined as Inngest functions rather than a bespoke Redis/BullMQ queue.
- 05
Passwordless auth uses WebAuthn alongside traditional login
apps/remix/package.json depends on @simplewebauthn/browser and @simplewebauthn/server, adding passkey-based authentication as an option next to the email/password and OAuth flows.
- 06
The monorepo ships two independent one-click deploy configs
render.yaml (Render, provisioning a managed Postgres database and running prisma migrate deploy on start) and railway.toml (Railway) both live at the repo root, so self-hosters can deploy without hand-writing infrastructure config for either platform.
How Documenso is built
How Documenso is structured
Documenso is an npm workspace (apps/*, packages/*) orchestrated by Turborepo (turbo.json). apps/remix is the main application; apps/docs and apps/openpage-api are supporting apps. Shared code is split into focused packages: packages/prisma (schema and generated clients), packages/trpc (typed API routers), packages/auth, packages/signing (PAdES signature logic), packages/email, packages/ee (enterprise-edition features), packages/lib (shared business logic), and packages/ui (component library). docker/ holds self-hosting build scripts and Dockerfiles, and render.yaml/railway.toml at the repo root configure managed-platform deploys.
Frontend
apps/remix/package.json is named @documenso/remix but is built on react-router 7 in framework mode (@react-router/dev, @react-router/serve), not Next.js — build:app runs react-router build and dev runs react-router dev. React 19 and React DOM are the rendering layer. Styling uses Tailwind CSS 3 through the shared packages/tailwind-config, with Radix UI primitives (accordion, dialog, dropdown-menu, select, tabs, toast, and more, declared in the root package.json) as the accessible base for the component library in packages/ui. Internationalization runs through Lingui (@lingui/core, @lingui/react, lingui.config.ts), and PDF rendering in the browser uses @cantoo/pdf-lib and pdfjs-dist.
Backend & APIs
The server layer is Hono, mounted into the React Router app via hono-react-router-adapter and @hono/node-server, with @hono/trpc-server exposing the tRPC routers defined in packages/trpc. Authentication combines traditional email/password and OAuth with WebAuthn passkeys (@simplewebauthn/browser, @simplewebauthn/server) and low-level crypto primitives from @oslojs/crypto/@oslojs/encoding. Background jobs — sending signing emails, post-signature processing — run through Inngest (inngest, inngest-cli, with an inngest:dev script pointed at /api/jobs) rather than a custom queue. Document signing itself is isolated in packages/signing, which depends on @google-cloud/kms and @google-cloud/secret-manager so a certificate can be held locally or signing can be delegated to a managed KMS key.
Data & persistence
PostgreSQL is the database, accessed through Prisma (packages/prisma, @prisma/client ^6.19). The same package also generates a Kysely-typed client via prisma-kysely and wires it in with prisma-extension-kysely, so the codebase can use Prisma's high-level API for standard CRUD and fall back to typed raw SQL through Kysely where needed. prisma-json-types-generator and zod-prisma-types add typed JSON columns and Zod schema generation from the same Prisma schema, and prisma:seed runs seed-database.ts for local data.
Build, test & deploy
Turborepo (turbo.json) drives build, dev, lint, and test across the workspace, with npm run dev compiling Lingui translations first. Linting and formatting go through Biome (biome.json, biome check .) instead of ESLint/Prettier. Husky and lint-staged run pre-commit hooks (commitlint.config.cjs enforces Conventional Commits). .github/workflows includes ci.yml, e2e-tests.yml (Playwright), codeql-analysis.yml, and deploy.yml, along with translation sync workflows (translations-pull.yml, translations-upload.yml) that integrate with Crowdin (crowdin.yml).
Self-hosting notes
docker/Dockerfile (plus docker/Dockerfile.chromium for the PDF-rendering variant) builds a self-hosting image; docker/development and docker/production hold compose assets for each environment, and docker/start.sh is the container entrypoint. For managed-platform deploys, render.yaml defines a Render web service that runs npx prisma migrate deploy --schema packages/prisma/schema.prisma before starting the app via Turborepo, provisions a managed Postgres database, and exposes environment variables for SMTP, Stripe billing, S3-compatible upload storage, and encryption keys. railway.toml provides an equivalent one-click path for Railway.
What to copy (and what not to)
Isolating signing behind packages/signing with a transport abstraction (local certificate vs. Google Cloud KMS) is a strong pattern for any product handling cryptographic material that self-hosters may want full custody over — it keeps the choice out of call sites. Pairing Prisma with a generated Kysely client is worth copying if you hit Prisma's query-builder limits often enough to justify maintaining two typed query layers against one schema; for simpler CRUD-heavy apps, Prisma alone is less to maintain. The Next.js-to-React-Router migration visible in the apps/remix naming is a one-time transitional cost rather than a design choice to emulate — new projects should just start on whichever framework they intend to stay on.
Sources & repo audit
- apps/remix/package.json (React Router 7, Hono, WebAuthn, Radix dependencies)
- Root package.json (npm workspaces, Turborepo, Inngest, Biome, Prisma)
- packages/prisma/package.json (Prisma + Kysely + prisma-extension-kysely)
- packages/signing/package.json (Google Cloud KMS signing transport)
- render.yaml (Render web service + managed Postgres, prisma migrate deploy on start)
Independent analysis of repository at github.com/documenso/documenso. Spotted an inaccuracy? Use the claim form to request a correction.
Show this stack on your README or website
[](https://stackitfast.com/project/documenso)Paste it into your README. It shows your current stack and links to this page.
Scaffold it with your agent
Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with Documenso's architecture.
- 1Copy the promptThe full markdown spec, with every layer and decision.
- 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
- 3Paste and scaffoldUse it as the first instruction; review before you ship.
# MISSION: Scaffold "Documenso" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **Documenso**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Documenso
- **What It Does**: Documenso is an open-source, AGPL-3.0 document e-signing platform built as a React Router application with a Prisma/PostgreSQL backend, implementing PAdES-standard PDF signatures with local or Google Cloud KMS-backed keys.
- **Domain & Category**: E-signature SaaS
- **Production Scale**: 2-5 people
- **Development Mode**: AI-AGENT
- **Architectural Rationale**: Signing logic is isolated in its own package with a swappable transport (local certificate or Google Cloud KMS) so self-hosters can choose key custody, while Prisma and a generated Kysely client cover both everyday CRUD and the raw SQL that document/signature queries need.
- **Live Website Reference**: https://documenso.com
- **Source Repository**: https://github.com/documenso/documenso
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: React, TypeScript, React Router, Prisma, PostgreSQL, tRPC, Tailwind CSS, Turborepo, Docker
- **Primary Language(s)**: TypeScript, MDX
- **License of the reference repo**: AGPL-3.0
- **Frontend**: React — UI library for the main app, rendered through React Router in framework mode.; React Router (framework mode) — Routing, SSR, and build tooling for the main app; formerly a Remix app, now on @react-router/dev and @react-router/serve.; Radix UI — Unstyled accessible primitives (dialog, dropdown-menu, select, tabs, toast, etc.) behind the design system.; Tailwind CSS — Utility-first styling shared across apps via the packages/tailwind-config workspace package.
- **Backend & APIs**: Hono — Lightweight server framework mounted inside the React Router app via hono-react-router-adapter.; tRPC — Typed API layer (packages/trpc) consumed by the React app and used for document/signing procedures.; Inngest — Background job runner for async workflows (email sending, document processing) outside the request cycle.; PAdES signing (packages/signing) — Implements PDF Advanced Electronic Signatures; supports local certificates and Google Cloud KMS as a remote signer.
- **Data & persistence**: PostgreSQL — Primary database, accessed through Prisma; schema and migrations live in packages/prisma.; Prisma — ORM and migration tool; prisma-kysely also generates a Kysely-typed client for raw query access.; Kysely — Typed SQL query builder generated from the Prisma schema via prisma-kysely, used for queries Prisma cannot express efficiently.
- **Infrastructure & deploy**: Docker — docker/Dockerfile and docker/Dockerfile.chromium build self-hosting images; docker/production holds deploy assets.; Render — One-click deploy target; render.yaml provisions the web service and a managed Postgres database.; Railway — Alternative one-click deploy target configured at the repo root.
- **Tooling, testing & ops**: Turborepo — Orchestrates builds, dev servers, and tests across the npm workspaces (apps/*, packages/*).; Playwright — End-to-end test runner for signing and document flows.; Biome — Lint and format tool run via biome check across the monorepo, replacing ESLint/Prettier.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/documenso/documenso @ e658cc5)
1. **The app migrated from Next.js to React Router in framework mode**: apps/remix/package.json is named @documenso/remix but depends on @react-router/dev, @react-router/serve, and react-router ^7, with build/dev scripts calling react-router build and react-router dev rather than next; a Hono server (hono-react-router-adapter) is mounted for custom routes.
2. **Signing keys can be local certificates or a remote KMS**: packages/signing depends on @google-cloud/kms and @google-cloud/secret-manager alongside its local transport, so a self-hoster can keep a signing certificate on disk or delegate signing operations to Google Cloud KMS without touching the signing call sites.
3. **Prisma is paired with a generated Kysely client for typed raw queries**: packages/prisma/package.json depends on both @prisma/client and kysely, wired together by prisma-extension-kysely and prisma-kysely, so code can use Prisma's high-level API for CRUD and drop into Kysely-typed SQL for queries Prisma's query builder cannot express well.
4. **Async work runs through Inngest, not a custom queue**: The root package.json depends on inngest and inngest-cli (with an inngest:dev script pointed at /api/jobs), so background jobs like emailing signers or post-signing processing are defined as Inngest functions rather than a bespoke Redis/BullMQ queue.
5. **Passwordless auth uses WebAuthn alongside traditional login**: apps/remix/package.json depends on @simplewebauthn/browser and @simplewebauthn/server, adding passkey-based authentication as an option next to the email/password and OAuth flows.
6. **The monorepo ships two independent one-click deploy configs**: render.yaml (Render, provisioning a managed Postgres database and running prisma migrate deploy on start) and railway.toml (Railway) both live at the repo root, so self-hosters can deploy without hand-writing infrastructure config for either platform.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
- Structure as a Turborepo monorepo with strict package boundaries:
- `apps/web`: Application UI, routing, layouts, and server endpoints.
- `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
- `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
- `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
- Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
- Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
- Isolate interactive UI state to leaf components. Keep core pages lightweight and performant.
4. **Design System & Aesthetics**:
- Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
- Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
- Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
- Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (PostgreSQL, Prisma, Kysely): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.Frequently asked about Documenso
What is Documenso built with?
The app (apps/remix) is a React Router 7 application in framework mode, styled with Tailwind and Radix UI, running on a Hono server. Data lives in PostgreSQL via Prisma (with a generated Kysely client for raw queries), the typed API layer is tRPC, and background jobs run through Inngest.
Does Documenso need a backend?
Yes. Documenso is a full application, not a static frontend: the React Router app talks to a Hono/tRPC backend that requires PostgreSQL, and document signing depends on the packages/signing service for PAdES certificate or Google Cloud KMS-based signatures.
Can I self-host Documenso?
Yes. docker/Dockerfile builds a self-hosting image, and the repo ships one-click deploy configs for both Render (render.yaml, which provisions a managed Postgres database and runs prisma migrate deploy on boot) and Railway (railway.toml).
How does Documenso sign documents?
packages/signing implements PAdES (PDF Advanced Electronic Signatures); the signing transport can use a certificate stored locally or delegate to Google Cloud KMS and Secret Manager (@google-cloud/kms, @google-cloud/secret-manager) for remote key management.
One email a month: new deep dives and stack trends
New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.
Similar architectures
- Cal.comClassicScheduling SaaS · 6-20 peopleShares TypeScript · React · PostgreSQL
- FormbricksAI-agentSurvey & Feedback SaaS · 2-5 peopleShares TypeScript · Prisma · PostgreSQL
- PlaneClassicProject Management · 6-20 peopleShares TypeScript · React Router · PostgreSQL
- TypebotAI-agentConversational Form Builder · SoloShares TypeScript · React · Tailwind CSS
- TwentyHybridCRM SaaS · 6-20 peopleShares TypeScript · React · PostgreSQL
- UmamiHybridWeb Analytics · SoloShares TypeScript · React · Prisma