GradKit Platform
Cloud-Native AI Resume Builder & Document Processing Engine
- Node.js
- Neon PostgreSQL
- Redis
- Azure
- Docker
- GitHub Actions
- Nginx
- DeepSeek AI
- Paynow
- Playwright
Trade-off Analysis & Lessons Learned
What Worked
- Scale-to-zero cut idle cost without dropping steady-state traffic
- The Nginx edge absorbed AI-triggered burst traffic before the app ever saw it
- Playwright PDFs rendered in memory and vanished on completion
What Failed
- The first deployment exposed the app directly, before the Nginx WAF landed
- DeepSeek latency spikes taught us to queue document jobs, not run them inline
- PDF generation initially staged files to disk, which privacy review rejected
Architectural Iterations
- Direct app exposure replaced by a hardened Nginx reverse proxy
- Inline AI document generation replaced by queued processing with Redis
- On-disk PDF staging replaced by transient in-memory rendering
Lessons Learned
- Scale-to-zero is only free if cold starts are acceptable
- Security headers belong at the edge, not the application
- Server-to-server payments mean the server owns the money movement, not the browser
Overview
GradKit is a cloud-native AI resume builder and document processing engine — a commercial SaaS platform at gradkit.co.zw running on Node.js, Neon PostgreSQL, Redis, and Azure Container Apps. AI document drafting runs on DeepSeek AI, payments go through server-to-server Paynow Express Checkout (EcoCash and InnBucks USD), and PDF rendering happens transiently in memory via Playwright Chromium with zero client-data persistence.
Engineered by Super Washington Banga (DevOps, Security & Cloud Architecture) with co-developers Julius Marandure and Anesu Matanhire.
Architecture
GradKit is a three-tier platform: a hardened Nginx reverse proxy at the edge, the Node.js application on scale-to-zero Azure Container Apps, and managed data services behind it. AI document generation runs through a Redis-backed job queue so model latency never blocks the request path, and PDF rendering is an ephemeral, in-memory step that leaves nothing behind. The diagram below is interactive — zoom with the buttons or the mouse wheel.
Serverless-First Platform
The application runs on Azure Container Apps with scale-to-zero: when no one is building a resume, the platform costs nothing. Traffic is small but bursty — exam season spikes, term-time silence — and scale-to-zero turns that shape into a bill that tracks actual use.
The trade-off is cold starts: the first request after idle pays a startup latency tax. The Nginx edge keeps the warm path cached where possible, and the Redis job queue decouples AI work from the request lifecycle entirely — a cold start delays a job, never a user's session.
Hardened Edge with Nginx
Every request crosses a hardened Nginx reverse proxy before the application sees it. The edge enforces rate limits per client IP, security headers (CSP, HSTS), and proxy behavior — the application never has to defend the wire:
server {
listen 443 ssl;
# Strict transport security and a locked-down content policy.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'; base-uri 'self'" always;
# Per-IP burst protection in front of the app.
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://gradkit-app:3000;
}
}
Security headers at the edge mean the response is hardened before the application code runs, and rate limiting absorbs AI-triggered burst traffic before a single request reaches the container.
Paynow Express Checkout
Payments are server-to-server: the browser never touches the payment provider. The server builds the signed Paynow payload, redirects the user to the checkout, and verifies the return transaction hash server-side before crediting the order:
const payload = {
result_url: `${APP_URL}/api/payments/return`,
return_url: `${APP_URL}/checkout/complete`,
reference: `GRADKIT-${order.id}`,
amount: order.amountUsd,
email: order.email,
};
const signature = crypto
.createHmac("sha512", PAYNOW_INTEGRATION_KEY)
.update(Object.entries(payload).map(([k, v]) => `${k}=${v}`).join("&"))
.digest("hex")
.toUpperCase();
// POST /paynow/transactions/init — server owns the money movement.
const { browserurl, status, hash } = await initTransaction({ ...payload, signature });
Verification happens in the return handler with a freshly recomputed hash, so a forged callback cannot credit an order. EcoCash and InnBucks USD both route through the same signed flow.
Transient PDF Rendering
Resumes and documents are rendered to PDF with Playwright Chromium in a transient, in-memory step — the HTML is painted in a headless browser, the PDF is streamed to the user, and nothing is written to disk:
const browser = await chromium.launch({ args: ["--disable-dev-shm-usage"] });
try {
const page = await browser.newPage({ viewport: { width: 794, height: 1123 } });
await page.setContent(html, { waitUntil: "networkidle" });
const pdf = await page.pdf({ format: "A4", printBackground: true });
return new Response(pdf, {
headers: { "Content-Type": "application/pdf" },
});
} finally {
await browser.close(); // nothing persisted — zero client data on disk
}
Privacy review drove this: the first design staged PDFs to disk, and the review rejected it. Now the document lives only in memory, from render to response — the platform holds no residual copies of user documents.
Deployment Pipeline
Every merge to main builds the container, runs the smoke suite, and deploys to Azure Container Apps via GitHub Actions. Infrastructure — container environment, PostgreSQL, Redis, scale rules — is declared in the repository, so the platform is reproducible from a clean checkout.
Status
Live at v1.0.0. Commercial and closed source; the platform is a paid SaaS
with Paynow Express Checkout processing EcoCash and InnBucks USD payments.
The edge, the queue, and the in-memory renderer keep the platform cheap at
rest, hardened at the wire, and empty of user data after every request.