Skip to content
Back to all work
Full-Stack / Workflow Engineering2025–2026Client work

SignFlow

An internal e-signature and approval platform — memos routed through an approval chain, signed in-browser, sealed as a PDF with an audit certificate and integrity hash.

Role
Architect & sole engineer
Client
an investment firm
Scale
112 commits · 1,362-line workflow engine
TypeScriptReact 19Express 5MongoDBpdf-libPuppeteerAzure AD SSOAWS S3

Client system. The problem, the architecture and my contribution only — no source, no screenshots, no client data.

The frozen chain, the request-changes state and the double hash are running and screenshotted in Sealed, a demo built over invented data.

The problem

An investment firm's internal paperwork — memos, expense claims, bank transfer instructions — moved by email and printed signature. The approval chain lived in convention and in people's heads: who signs first, who is merely copied, what happens when someone asks for changes rather than rejecting outright.

Two things were missing. There was no single artifact at the end that proved who approved what and when. And there was no way to answer "where is this right now" without asking someone.

What I built

A workflow engine. 1,362 lines that resolve an approval chain from the document's From / To / Through / CC convention, then freeze a snapshot of that chain at submission time. This detail matters more than it sounds: if a person's role changes next month, the record of who was required to approve last month must not change with it. Each stage supports approve, reject, or request-changes, and every transition is written to an immutable trail.

A PDF sealing pipeline. Composed memos render through Puppeteer to PDF, signatures stamp on via pdf-lib, and an audit-certificate page is appended listing every action with its timestamp, actor, role and IP. The original and the final document are both SHA-256 hashed, so the sealed artifact can be verified later without trusting the system that produced it.

Closed-system authentication. Magic-link and OTP over JWT, optional Microsoft Azure AD SSO, and a bcrypt local-password fallback — all restricted to admin-provisioned addresses. Nobody self-registers.

Design decisions worth naming

  • The snapshot over the live lookup. Resolving the chain at read time would have been simpler and quietly wrong.
  • Request-changes as a first-class state. Modelling it as "reject, then resubmit" loses the thread of the conversation, which is usually the thing people actually need to see.
  • Hash the input as well as the output. Sealing only the final PDF proves the document wasn't altered after signing; hashing the original proves it wasn't altered before.
  • S3 with a local fallback. The system had to keep working during a storage outage, degrading rather than stopping.

Architecture

An npm-workspaces monorepo in TypeScript: Express 5 and Mongoose 8 over MongoDB on the server, React 19 with Vite 6, Tailwind, MUI and React Router 7 on the client, with a shared render package so a memo looks identical in the browser and in the sealed PDF. TipTap for composition, exceljs and libreoffice-convert for attachments, Resend for transactional mail with Teams webhooks for notifications. Deployed on Railway and Vercel with auto-deploy.

Designed and built single-handedly.

You have a process thatshould be a system.

Tell me what arrives, who has to act on it, and where it currently falls over. That conversation is usually enough to scope the build.