Portfolio

Own product

Pullstok: a complete SaaS ERP with an online store

My own product: a multi-tenant SaaS ERP I design, build and run. Point of sale, stock, ARCA electronic invoicing, accounting (in development), an online store per subdomain, Mercado Pago, AI bots and a local print agent. A monorepo with 5 apps and a Node API with Prisma and PostgreSQL.

2025 Side project In progress
ReactNode.jsExpressPrismaPostgreSQLAstroMulti-tenant SaaSARCA

Tech stack

The technologies used and why.

UI React
Build Vite
Ruteo React Router
Data fetching TanStack Query
Estilos Tailwind CSS
Lenguaje TypeScript
API Express
ORM Prisma
Base de datos PostgreSQL
Validación Zod
Landing y tienda Astro
Testing Jest
Runtime Node.js
Componentes shadcn/ui
Tiempo real Socket.io
Testing Vitest
Observabilidad Sentry
Storage Cloudflare R2
Pagos Mercado Pago
IA Groq
Servidor Nginx
CI/CD GitHub Actions

Screenshots

Pantalla de login de PullstokPanel de productos y control de stockGestión de categorías de productosMódulo de presupuestosMódulo de pedidosMódulo de ventasMódulo de remitosGestión de clientesTienda online conectada al catálogoMódulo de facturaciónPanel de superadmin para gestionar comercios


The problem

A company that sells, retail or wholesale, with one or many branches, usually has its operation scattered: stock in a spreadsheet, invoicing in another system, QR payments in another app, accounting somewhere else and the online store on its own. Every separate tool is one more place to enter the same data twice and one more source of inconsistencies.

Pullstok is my answer to that: a multi-tenant SaaS ERP that brings a company’s whole operation into one place, from the counter to the books. It is not client work: it is my own product, which I design, build, deploy and run end to end.

One system

From quote to sale and from sale to electronic invoice, without entering anything twice. The next step, in development: every operation posting its own accounting entry.

Every business, isolated

All businesses share the platform, but each one only sees its own products, customers and sales.

Connected to the counter

Thermal printers, weight-label scales, camera barcode scanning and Mercado Pago QR payments.

What it does

Sales and counter

Commercial flow

Stock and catalog

Tax and accounting

Digital channels

Platform

Architecture

Pullstok is a pnpm workspaces monorepo with five apps, each with a clear responsibility:

LandingAstro
ERP appReact · Vite · TanStack Query
StoreAstro SSR · React islands
Print agentNode SEA · Windows
HTTPS · JWT · Socket.io
APIExpress · TypeScript · Zod · Prisma 7
data and integrations
PostgreSQL70 models
Cloudflare R2images and backups
ARCAWSAA · WSFEv1 · registry
Mercado PagoQR · OAuth
GroqLLM for the bots

Multi-tenancy and security

Key technical decisions

01

Tenant isolation in the ORM

In a multi-tenant SaaS, the worst possible bug is one business seeing another’s data. Instead of relying on remembering to filter every query, the filter lives in a Prisma extension fed by AsyncLocalStorage. Forgetting the filter is no longer possible: the query fails.

02

ARCA from scratch, over SOAP

No third-party SDK: the access ticket is signed as CMS/PKCS#7 with node-forge, WSAA handles authentication and WSFEv1 authorizes invoices. The ticket is cached only in memory and each business’s private key is stored encrypted with AES-256-GCM. Each business uploads its own certificate and can test in the sandbox before going to production.

03

A cloud to local hardware bridge

A browser cannot print RAW to a thermal printer. The print agent is a single Node executable (SEA) that listens only on localhost with an origin allowlist and sends ESC/POS bytes to the Windows spooler. To print from a phone, the agent pairs with a code and polls the API for print jobs, with heartbeat and backoff.

04

Money in cents, fiscally correct

The sale to invoice conversion derives the net amount from VAT-inclusive prices and turns discounts and surcharges into their own lines, so what is invoiced matches exactly what was charged. Accounting entries come from pure rules, all in integer cents.

05

Sales that never get lost

The POS keeps selling without internet and syncs later, idempotently. A Mercado Pago QR charge is created, polled until it is processed and only then atomically claimed by the sale, so a payment can never be attached to two sales.

Quality and testing

Infra and deploy

How I build it

Every change starts with a written spec: goal, decisions, findings from exploring the code, what is in and out of scope, and a task list. I work with a spec-driven, AI-assisted workflow where I define the architecture and the decisions, and use agents to explore, implement and review, with persistent memory across sessions and a review before every push. Commits follow conventional commits.

Numbers

1,100+ commits

With conventional commits and continuous deploy to production.

70 data models

Around 100 Prisma migrations on PostgreSQL.

~280 endpoints

Spread across about 57 API routers.

500+ test files

Across the API, the front and the print agent.

Status

Pullstok is in production and under active development. The current focus is the accounting module. Every new module (scales, the offline POS, QR payments, accounting) comes from real needs of the businesses already using it.