doc-assembly

module
v0.0.0-...-4a1d7bd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 4, 2026 License: MIT

README

doc-assembly

Multi-tenant document template builder with digital signature delegation.

Go 1.25 · React 19 · PostgreSQL 16 · Typst


Table of Contents

Overview

doc-assembly lets organizations build reusable document templates with a rich text editor, inject dynamic data through a variable system, render production-quality PDFs via Typst, and delegate digital signatures to external providers.

The platform is multi-tenant by design: a three-level RBAC model (System, Tenant, Workspace) controls access, while the backend enforces tenant isolation across every query.

Key Features
  • Rich text template editor (TipTap) with injectable variables and signature blocks
  • PDF rendering powered by Typst (no browser/Chromium dependency)
  • Digital signature delegation to Documenso (PandaDoc and DocuSign interfaces planned)
  • Three-level RBAC: System, Tenant, and Workspace roles
  • Template versioning with publish/archive lifecycle
  • Extensibility system via code-generated injectors and mappers
  • Internationalization (English & Spanish)
  • Folder and tag organization for templates

Monorepo Structure

doc-assembly/
  core/       Go backend (Hexagonal Architecture, Gin, Wire DI)
  app/        React SPA (TanStack Router, Zustand, TipTap)
  db/         Liquibase migrations (PostgreSQL 16)
  docs/       All project documentation
  scripts/    Tooling (docml2json, etc.)
Component Stack Docs
doc-engine Go 1.25, Gin, pgx/v5, Wire Backend docs
web-client React 19, TypeScript, TanStack Router, Zustand, TipTap 3 Frontend docs
db Liquibase, PostgreSQL 16, pgcrypto DATABASE.md
scripts Python 3 tooling docml2json

Architecture

Request Flow (Hexagonal)
HTTP Request
 -> Middleware (JWT auth, tenant context, operation ID)
   -> Controller (parse DTO, validate)
     -> UseCase interface
       -> Service (business logic)
         -> Port interface
           -> Repository (SQL via pgx)
             -> PostgreSQL
Multi-Tenant Hierarchy
System
 +-- Tenant A
 |    +-- Workspace 1
 |    +-- Workspace 2
 +-- Tenant B
      +-- Workspace 3
RBAC

Three authorization levels with seven roles:

Level Roles
System SUPERADMIN, PLATFORM_ADMIN
Tenant OWNER, ADMIN
Workspace OWNER, ADMIN, EDITOR, OPERATOR, VIEWER

SUPERADMIN auto-elevates to OWNER in any tenant or workspace.

Quick Start

Prerequisites
Tool Version Install
Go 1.25+ go.dev/dl
Node.js 20+ nodejs.org
pnpm 9+ npm i -g pnpm
Docker latest docker.com
Typst 0.13+ typst.app
golangci-lint latest golangci-lint.run
Wire latest go install github.com/google/wire/cmd/wire@latest

Run make doctor to verify all dependencies are installed.

1. Clone and install dependencies
git clone https://github.com/your-org/doc-assembly.git
cd doc-assembly
pnpm install --dir apps/web-client
go -C apps/doc-engine mod download
2. Start PostgreSQL
docker compose -f docker-compose.dev.yml up -d

This starts PostgreSQL 16 on port 5432.

3. Run database migrations
cd db && ./run-migrations.sh && cd ..
4. Start dev servers
make dev-dummy

[!TIP] dev-dummy bypasses JWT authentication so you can develop without setting up Keycloak. The backend runs on :8080 and the frontend on :3001.

The app is now available at http://localhost:3001. The first user to sign up is automatically promoted to SUPERADMIN.

Available Commands

Run make help for the full list.

Command Description
make dev Hot reload backend + frontend
make dev-dummy Dev with dummy auth (no Keycloak needed)
make build Build backend + frontend
make test Run unit tests
make test-integration Run integration tests (Docker required)
make lint Lint backend + frontend
make gen Codegen: Wire DI + Swagger + Extensions
make doctor Check system dependencies
make clean Remove all build artifacts

Pass DUMMY=1 to any target to enable dummy auth: make run DUMMY=1.

Configuration

Backend

Configuration is loaded from apps/doc-engine/settings/app.yaml and can be overridden with environment variables following the pattern DOC_ENGINE_<SECTION>_<KEY>.

Copy the example env file and fill in values:

cp apps/doc-engine/.env.example apps/doc-engine/.env

Key variables:

Variable Required Description
DOC_ENGINE_DATABASE_PASSWORD Yes PostgreSQL password
DOC_ENGINE_AUTH_JWKS_URL Yes* Keycloak JWKS endpoint
DOC_ENGINE_AUTH_ISSUER Yes* Keycloak issuer URL
DOC_ENGINE_AUTH_AUDIENCE Yes* JWT audience claim
DOC_ENGINE_AUTH_DUMMY No Set true to bypass JWT
DOC_ENGINE_DOCUMENSO_API_KEY No Documenso API key (for signing)

* Not required when DOC_ENGINE_AUTH_DUMMY=true.

[!NOTE] See apps/doc-engine/settings/app.yaml for all available options including storage, logging, scheduler, Typst renderer, and notification settings.

Frontend
cp apps/web-client/.env.example apps/web-client/.env
Variable Default Description
VITE_API_URL /api/v1 Backend API base URL
VITE_KEYCLOAK_URL http://localhost:8180 Keycloak server URL
VITE_KEYCLOAK_REALM doc-assembly Keycloak realm
VITE_KEYCLOAK_CLIENT_ID web-client Keycloak client ID
VITE_USE_MOCK_AUTH true Bypass Keycloak in development

Digital Signatures

doc-assembly delegates digital signatures to external providers. Each document has a shared public URL (/public/doc/{id}) that recipients use to verify their email and receive a signing link.

Template (published)
  -> Admin creates document
    -> Recipients notified with public URL (/public/doc/{id})
      -> Recipient visits URL, enters email
        -> System verifies email, sends token link (/public/sign/{token})
          -> Path A (no interactive fields): PDF preview -> Sign
          -> Path B (interactive fields): Fill form -> PDF preview -> Sign
            -> Signing provider handles signature -> Webhooks update status -> Sealed PDF stored

For detailed flow documentation with sequence diagrams, see Public Signing Flow.

Supported Providers
Provider Status
Documenso Implemented
PandaDoc Interface defined
DocuSign Interface defined
Local Documenso Setup
docker compose -f docker-compose.documenso.yml up -d

[!IMPORTANT] The compose file includes a documenso-cert-init service that auto-generates a self-signed P12 certificate for document sealing. No manual certificate setup is needed.

This starts:

  • Documenso on http://localhost:3000
  • MailPit (SMTP) on http://localhost:8025 (web UI) and :1025 (SMTP)
  • PostgreSQL for Documenso on port 5433

Configure the webhook in Documenso to point to http://host.docker.internal:8080/webhooks/signing/documenso.

Background Workers (River)

When all recipients sign a document, the system needs to notify SDK consumers asynchronously. This is powered by River, a PostgreSQL-native job queue that runs inside the same Go process — no external broker (Redis, RabbitMQ) needed.

Why River?

The critical requirement is transactional atomicity: the document status update (COMPLETED) and the job enqueue happen in a single PostgreSQL transaction. This guarantees no "completed document without job" or "job without completed document" — even on crashes.

Signing Provider webhook
  -> DocumentService: all recipients signed
    -> BEGIN transaction
      -> UPDATE document SET status = 'COMPLETED'
      -> INSERT INTO river_job (document_completed args)
    -> COMMIT
  -> River Worker (async): polls job, builds event, invokes SDK handler
SDK Usage
import "github.com/rendis/doc-assembly/core/sdk"

handler := func(ctx context.Context, ev sdk.DocumentCompletedEvent) error {
    log.Printf("Document %s completed in tenant %s", ev.DocumentID, ev.TenantCode)
    for _, r := range ev.Recipients {
        log.Printf("  %s (%s) signed at %v", r.Name, r.RoleName, r.SignedAt)
    }
    return nil // return error to retry
}
Key Properties
Property Behavior
Atomicity Status update + job enqueue in single transaction
Deduplication Same document produces at most 1 job per hour (ByArgs + ByPeriod)
Retries Handler errors and panics trigger exponential backoff retries
Fallback Without SetCompletionNotifier, plain repo.Update() — no jobs enqueued
Insert-only mode worker.enabled: false inserts jobs but never processes them
Configuration
worker:
  enabled: false        # DOC_ENGINE_WORKER_ENABLED
  max_workers: 10       # DOC_ENGINE_WORKER_MAX_WORKERS

For architecture diagrams, SDK type reference, and integration test details, see Worker Queue Guide.

Database

PostgreSQL 16 with five schemas:

Schema Purpose
tenancy Tenants, workspaces, memberships
identity Users, access history
organizer Folders, tags
content Templates, versions, injectables, signer roles
execution Documents, recipients, events

Migrations are managed with Liquibase:

cd db && ./run-migrations.sh

[!WARNING] Do not modify migration files in db/src/ directly. Suggest changes and create new changesets instead.

See db/DATABASE.md for the complete schema documentation.

Deployment

Docker Build
docker build -f apps/doc-engine/Dockerfile -t doc-engine .

The Dockerfile uses a multi-stage build:

  1. Builder: golang:1.25-alpine compiles the binary
  2. Runtime: alpine:3.21 with Typst v0.13.1 and ca-certificates

The container exposes port 8080.

Required Environment Variables (Production)
Category Variables
Database DOC_ENGINE_DATABASE_HOST, _PORT, _USER, _PASSWORD, _NAME
Auth DOC_ENGINE_AUTH_JWKS_URL, _ISSUER, _AUDIENCE
Signing DOC_ENGINE_DOCUMENSO_API_URL, _API_KEY, _WEBHOOK_SECRET
Storage DOC_ENGINE_STORAGE_BUCKET, _REGION (for S3)

Documentation

Document Path
Backend Architecture docs/backend/architecture.md
Authentication Guide docs/backend/authentication-guide.md
Authorization Matrix docs/backend/authorization-matrix.md
Public Signing Flow docs/backend/public-signing-flow.md
Template Preview Flow docs/template-preview-flow.md
Internal API Document Creation Flow docs/internal-api-document-creation-flow.md
Public Signing Flow (Flow Detail) docs/public-signing-flow-detail.md
Extensibility Guide docs/backend/extensibility-guide.md
Worker Queue Guide (River) docs/backend/worker-queue-guide.md
Frontend Architecture docs/frontend/architecture.md
Design System docs/frontend/design-system.md
Database Schema db/DATABASE.md
OpenAPI Spec core/docs/swagger.yaml
docml2json Reference scripts/docml2json/DOCML-REFERENCIA.md

License

This project is licensed under the MIT License.

Directories

Path Synopsis
cmd
init command
core
cmd/api command
docs
Package docs Code generated by swaggo/swag.
Package docs Code generated by swaggo/swag.
internal/adapters/secondary/database/postgres/common
Package common provides shared utilities for PostgreSQL repositories.
Package common provides shared utilities for PostgreSQL repositories.
internal/core/entity/portabledoc
Package portabledoc defines types for the Portable Document Format (PDF-JSON).
Package portabledoc defines types for the Portable Document Format (PDF-JSON).
internal/core/port
Document completion ports for the River worker queue.
Document completion ports for the River worker queue.
internal/core/service/template/contentvalidator
Package contentvalidator provides content structure validation for template versions.
Package contentvalidator provides content structure validation for template versions.
internal/infra/riverqueue
Package riverqueue implements background job processing for document completion events using River, a PostgreSQL-native job queue.
Package riverqueue implements background job processing for document completion events using River, a PostgreSQL-native job queue.
sdk
Package sdk provides the public API for doc-assembly.
Package sdk provides the public API for doc-assembly.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL