Skip to content

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SignFlow

Stages Completed: Stage 1, Stage 2, and Stage 3 (All milestones, including Database Persistence and Real-time Polling bonuses, are fully implemented and verified)

A Setu-powered contract upload and Aadhaar eSign platform. Upload a PDF, send it for legally valid e-signature, track status in real time, and download the signed document, all through a clean, self-serve interface.


System Architecture

graph TD
    subgraph Client ["Client Side (Browser)"]
        A[Next.js Frontend]
        C[Clerk Auth SDK]
    end

    subgraph Server ["Server Side (FastAPI / local or cloud)"]
        B[FastAPI Backend]
        D[PostgreSQL DB]
    end

    subgraph External ["Third-Party Services"]
        E[Setu Aadhaar eSign Sandbox]
        F[Clerk Identity Provider]
    end

    A -->|1. Authenticate| C
    C -->|JWT| A
    A -->|2. Proxy Request with Bearer Token| B
    B -->|3. Verify Session Key| F
    B -->|4. Persist Metadata| D
    B -->|5. Outbound HTTP Call| E
Loading

The frontend never communicates with Setu directly. All API calls (upload, create signature request, check status, and download) are proxied through the FastAPI backend. Setu credentials live only in the backend .env and are never exposed to any client.


E2E Sequence Diagram

sequenceDiagram
    autonumber
    actor User as Operations User (Priya)
    participant Front as Next.js Frontend
    participant Clerk as Clerk Auth
    participant Back as FastAPI Backend
    participant DB as PostgreSQL Database
    participant Setu as Setu Aadhaar eSign API
    actor Signer as Signer (Candidate)

    User->>Front: Sign in & upload PDF + enter Signer Mobile
    Front->>Clerk: Get Session Token
    Clerk-->>Front: JWT Token
    Front->>Back: POST /api/upload-contract (File + Signer Details + JWT)
    Back->>Back: Verify Clerk JWT & validate PDF (magic bytes + size)
    Back->>Back: Save PDF to temp/uploads storage
    Back->>Setu: POST /api/documents (Upload PDF)
    Setu-->>Back: Return setu_document_id
    Back->>DB: Insert Document (status="uploaded")
    Back->>Setu: POST /api/signature (Create Signature Request)
    Setu-->>Back: Return setu_signature_id & signer_url
    Back->>DB: Insert SignatureRequest & Signer (status="pending")
    Back-->>Front: Return metadata & signer_url
    Front-->>User: Display signing link & show "Pending" status

    Note over User, Signer: User shares signing link with Signer
    Signer->>Setu: Open signer_url, enter OTP, complete eSign
    Setu-->>Signer: Redirect back to SignFlow / setu.co

    Note over User, Front: Status checking (Manual or Poll every 8s)
    Front->>Back: GET /api/signature-status/:id
    Back->>Setu: GET /api/signature/:id
    Setu-->>Back: Return latest status (e.g. "sign_complete")
    Back->>DB: Update status to "signed" & save signed_at
    Back-->>Front: Return updated status & details
    Front-->>User: Update UI to "Signed" and show download button

    User->>Front: Click "Download signed document"
    Front->>Back: GET /api/download/:id
    Back->>Setu: GET /api/documents/:id/download (Setu PDF bytes)
    Setu-->>Back: Return signed PDF binary
    Back-->>Front: Stream signed PDF binary
    Front-->>User: Save signed PDF to disk
Loading

Tech Stack

Layer Choice Why
Backend FastAPI (Python) Async-native; built-in Pydantic validation; auto-generated OpenAPI docs
Frontend Next.js 15 (TypeScript) App Router; file-based routing; trivial Vercel deployment
Database PostgreSQL Relational integrity for documents → signature requests → signers
ORM / Migrations SQLAlchemy + Alembic Versioned migrations; alembic upgrade head setup
Auth Clerk (magic-link) Passwordless (no password database to leak; hosted provider)
HTTP Client httpx Async-native; used for all Setu API calls in the backend

Repository Structure

signflow/
├── backend/          # FastAPI app, Setu integration, DB models
├── frontend/         # Next.js app, UI components, design system
├── docs/             # All spec and architecture documents
└── README.md

Quick Start

Backend

cd backend
python -m venv venv && venv\Scripts\activate   # Windows
pip install -r requirements.txt
cp .env.example .env                            # fill in credentials
alembic upgrade head
uvicorn app.main:app --reload
# → http://localhost:8000/docs

Frontend

cd frontend
npm install
cp .env.local.example .env.local               # fill in API URL + Clerk key
npm run dev
# → http://localhost:3000

Database Schema

Three tables reflect the real-world relationship: a document is uploaded once; a signature request is created for it; each request has one or more signers.

documents (1) ──── (1..n) signature_requests (1) ──── (1..n) signers
Table Key fields
documents id, setu_document_id, owner_id, original_filename, file_path, uploaded_at
signature_requests id, document_id, setu_signature_id, status, created_at, updated_at
signers id, signature_request_id, identifier (mobile), signer_url, status, signed_at

Full schema: docs/Technical_Architecture_SignFlow.md


Security Considerations

  • Credentials server-side only: Setu's x-client-id, x-client-secret, and x-product-instance-id exist only in backend/.env. They are never referenced in any frontend file and never returned in any API response.
  • Unguessable signing links: Every signing link uses a long random token, not a sequential ID. Guessing /status/2 does not expose another user's contract.
  • Row-level ownership: Every database query filters on owner_id (the authenticated Clerk user). No query path can return another user's documents.
  • CORS locked to one origin: The backend only accepts requests from FRONTEND_URL. Wildcard (*) is never used.
  • Content-type validation: Uploaded files are validated by magic bytes (%PDF-), not just extension.
  • No raw errors to the client: A global exception handler ensures stack traces and DB errors never reach the browser.

Secrets in production

For this assignment, secrets live in the platform's encrypted environment variable store (Render / Railway built-in). In a production system they would move to a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault, or Doppler) with automatic rotation and audit logging. The SETU_WEBHOOK_SECRET placeholder in .env.example shows where webhook verification would be added.


Docs

Document Description
PRD Product requirements, personas, MVP scope
Technical Architecture Stack, folder structure, DB schema, env vars
Frontend Specification Design system, components, Setu API shapes
Security & Access Auth, roles, RLS, error handling, edge cases
Feature Tickets Ordered build tickets with acceptance criteria
Build Plan Commit discipline and agent workflow

Deployment & Live Links

Service Platform Notes
Backend Local 24/7 Server Hosted on an Indian server and tunneled via Ngrok
Frontend Vercel Zero-config Next.js deploys
Database Neon Postgres Serverless PostgreSQL database

Note

Setu API Geo-Blocking and our Solution: Setu processes regulated identity and Aadhaar-based transactions, which are subject to strict Indian data localization guidelines. Consequently, Setu's sandbox gateway firewall employs strict geo-blocking, immediately rejecting requests originating outside India (such as standard Render/Railway US/EU data centers).

To bypass this restriction for the live deployed application, the backend is hosted on a 24/7 server located in India and exposed via Ngrok. Because all API calls originate from an Indian IP range, the complete end-to-end eSign flow (upload, OTP verification, status polling, and downloads) works fully on the live Vercel URL.

About

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages