Work
OwnOwn product · Electronic signing
Signperu

Signperu

Electronic signing for PDF documents: the sender builds an envelope, signers sign from a link with an emailed code, and the final PDF is sealed and can be verified publicly.

Screenshot of Signperu
SYSTEM.md

The problem

A contract sent by message leaves each party waiting blind, with no versions and no clear close, and nothing stops the file from changing after it is signed.

What I built

I built a modular monolith (Spring Boot 4, Java 21, hexagonal DDD) with an Angular 22 frontend. Signers get in without an account through a link and an emailed code; on completion the system stamps the signatures, adds an evidence page and seals the PDF with PAdES and a time stamp.

Outcome

An own product with private access, covering the full cycle: envelope, signing, sealing and public verification.

Decisions that mattered

  1. 01

    Evidence, not just an image

    Each signature records the verified email, IP, browser, time, PDF hash and the consent version accepted. The audit log is hash-chained: altering one record breaks the chain.

  2. 02

    The final PDF proves itself

    It is sealed with PAdES B-T: a signature with its own certificate plus an RFC 3161 time stamp from an external authority, with a backup one. Without a stamp the envelope stays in SELLANDO and retries.

  3. 03

    The link is not enough

    The token travels in the URL fragment, only its hash is stored, and a 6-digit code is required, with at most 5 attempts.

  4. 04

    Events with an outbox

    Every state change writes its events to an outbox table in the same transaction. Background tasks use FOR UPDATE SKIP LOCKED and can also be triggered from Cloud Scheduler.

  5. 05

    A monolith, on purpose

    No microservices, Kafka or Redis: cost with no benefit at this volume. Contexts (envelopes, documents, identity, notifications, audit) talk through facades and events.

  6. 06

    Built for Cloud Run

    A non-root Docker image, PDFs in a private bucket, the sealing key in Secret Manager and Cloud Scheduler triggering background tasks.

Technologies

  • Java 21
  • Spring Boot 4
  • PostgreSQL
  • Angular 22
  • PDFBox
  • BouncyCastle
  • Resend
  • Cloud Run
  • Cloud Storage

C4 model. The pulsing line is the main path of a request.

Level 1 · who uses the system and what it connects to

HTTPS · session + TOTPHTTPS · link + code1Public checkRFC 31612HTTPS · webhookStorage APISenderBuilds the envelope andsends itSignerSigns from a link, noaccountThird partyVerifies a documentSignperuElectronic signing withevidenceResend[Resend]Email and bounce noticesTime-stampauthority[RFC 3161]External RFC 3161 stampPDF store[Google Cloud Storage · bucketprivado]Private bucket withretention

Level 2 · the applications and data stores it is made of

HTTPSHTTPS ·link1HTTPSREST ·/api2HTTP · tokenenv · secretsJDBC · outbox3StorageAPIRFC 31614HTTPS · webhookSenderBuilds the envelope andsends itSignerSigns from a link, noaccountThird partyVerifies a documentWeb app[Angular 22 · Tailwind 4 · GSAP]Dashboard, signing andverificationSecret Manager[Secret Manager]Keys and credentialsCloud Scheduler[Cloud Scheduler]Triggers background tasksSignperu API[Spring Boot 4 · Java 21 · CloudRun]Modular monolith, hexagonalDDDResend[Resend]Email and bounce noticesDatabase[PostgreSQL 16 · Flyway]Envelopes, signers, auditand outboxPDF store[Google Cloud Storage · bucketprivado]Private bucket withretentionTime-stampauthority[RFC 3161]External RFC 3161 stamp

Level 3 · the components inside the API

sessionvalidates access1eventseventshandlerHTTPSACL · facade2port3RFC 31614portIdentity[Bounded context]Argon2id, TOTP and sessionsSigning access[Aggregate]Token, code and shortsessionAudit[Hash chain]Hash-chained logResend[Resend]Email and bounce noticesPDF store[Google Cloud Storage · bucketprivado]Private bucket withretentionEnvelopes[Bounded context]Lifecycle, signers andfieldsOutbox and tasks[Outbox · SKIP LOCKED]Events and background workNotifications[Bounded context]Emails with retriesDocuments[Bounded context]Validates, stamps and sealsthe PDFPAdES sealer[PDFBox · BouncyCastle]Signature and time stampTime-stampauthority[RFC 3161]External RFC 3161 stamp
  • Person
  • System
  • Container
  • Database
  • External
  • Main path
  1. 01Sender uploads the PDF
  2. 02Envelope and fields
  3. 03Link and code
  4. 04Signature with evidence
  5. 05PAdES sealing
  6. 06Public verification

CV

Get my CV

Leave your email and I will send the CV there. First I check the address is yours with a code.