cd /news/artificial-intelligence/building-a-passwordless-gemini-advis… · home topics artificial-intelligence article
[ARTICLE · art-44124] src=dev.to ↗ pub= topic=artificial-intelligence verified=true sentiment=· neutral

Building a passwordless, Gemini-advised dashboard on the "zero stack"

A developer built Kajota Pulse, a Bloomberg-terminal-style dashboard for African micro-commerce co-sellers that uses Gemini 2.5 Flash to recommend what stock to buy. The app runs on the 'zero stack'—Vercel for compute and AWS Aurora Serverless v2 for state—with no servers to manage. Key engineering challenges included mandatory IAM database authentication, Lambda environment variable shadowing, and handling MongoDB Extended JSON format from production data triggers.

read4 min views1 publishedJun 29, 2026

I built Kajota Pulse and wrote this article as my entry for the AWS × Vercel "H0: Hack the Zero Stack" hackathon (#H0Hackathon). Live app: kajota-pulse.vercel.app · Code: github.com/KaJota-inc/kajota-pulse

Across African micro-commerce, "co-sellers" buy stock from wholesalers and resell to their network for a markup. There's a whole industry of tools for writing the listing. There's almost nothing for the question that actually decides whether a co-seller makes money: what should I stock this week?

So we built Kajota Pulse — a Bloomberg-terminal-style dashboard that watches the marketplace and, in one click, tells a seller what to buy and why. It's the "monitor" pillar of a three-app stack: Coach drafts the listing, Pulse says what to stock, Mesh settles the deal on-chain.

The hackathon constraint was the fun part: build it on the zero stack — Vercel for compute, an AWS database for state, no servers to manage. Here's what that actually took.

Next.js 16 (App Router) on Vercel → AWS Aurora Serverless v2 (PostgreSQL) for every number on the dashboard → Gemini 2.5 Flash for the advice → MongoDB Atlas Database Triggers streaming the real Kajota catalogue in. Five Postgres tables, two SQL views, two Gemini endpoints, one ingest endpoint. No VPC, no connection pooler, no server.

The interesting engineering wasn't the UI. It was three things that don't show up in tutorials.

We provisioned Aurora Serverless v2 with the new internet-access-gateway networking model so Vercel could reach it without VPC plumbing. Then every password connection failed with PAM authentication failed

.

The new model mandates IAM database authentication — and as a bonus, it doesn't support the RDS Data API either. So instead of a stored password, every connection mints a short-lived (15-minute) IAM auth token:

const signer = new Signer({ hostname, port, username, region, credentials });
pool = new Pool({
  host, port, user, database,
  password: () => signer.getAuthToken(), // fresh token at each handshake
  ssl: { rejectUnauthorized: false },
});

pg

supports an async password

callback, so this is clean. And the security property is genuinely nice: there is no long-lived database password anywhere — not in Vercel, not in the repo, not in a secret manager.

This one cost an hour. The IAM signer needs AWS credentials to sign the token. We set AWS_ACCESS_KEY_ID

/ AWS_SECRET_ACCESS_KEY

in Vercel… and it still failed.

Vercel functions run on Lambda, and the Lambda runtime injects its own execution-role AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY, which shadow yours. The signer was minting tokens with the wrong (no-

rds-db:connect

) identity.The fix: use custom env names and pass them explicitly to the signer.

function signerCredentials() {
  const accessKeyId = process.env.PULSE_AWS_ACCESS_KEY_ID;
  const secretAccessKey = process.env.PULSE_AWS_SECRET_ACCESS_KEY;
  return accessKeyId && secretAccessKey ? { accessKeyId, secretAccessKey } : undefined;
}

A dedicated IAM user with only rds-db:connect

on the cluster's dbuser resource, surfaced under PULSE_AWS_*

, and the shadowing problem disappears.

The dashboard is only as good as its data, so we wired three MongoDB Atlas Database Triggers on the real Kajota collections (products

, cosell_products

, orders

). Each trigger POSTs its change event to /api/ingest

, which upserts into Aurora. Hooking this to production data immediately surfaced three bugs that a seed file would never reveal:

_id

arrives as {"$oid":"…"}

and a price as {"$numberInt":"9500"}

— not as a string and a number. Without decoders you get id="[object Object]"

and price=NaN

in your database. Two small helpers (ejsonId

, ejsonNum

) fixed it.cosell_products

(underscore), but our router matched cosellproducts

. Events silently fell through as "ignored." Now the router normalizes names.None of these reproduce against fixtures. They only appear when real production writes hit your pipeline — which is exactly why we wired it to live data instead of demoing on a seed.

A dashboard shows you numbers and makes you do the synthesis. We wanted Pulse to answer the question. So /api/recommend

pulls the live signals — trending demand, category margins, competitor stock-outs, price position — and hands them to Gemini 2.5 Flash with a structured-output schema:

Organic Shea Butter→Stock 10–15 units before the weekend.

"+27 favorites, sits in the high-margin Beauty category (18%), and a competitor just ran out of a similar cream."

Two details that matter for a demo that can't break:

responseMimeType: "application/json"

  • a responseSchema

) means we render a clean ranked list, not parse prose.node scripts/verify-live.mjs

checks the live landing page, the Aurora badge, both Gemini endpoints, the ingest auth gate, and a real IAM-authenticated row count — 5/5. Anyone can run it.Live: kajota-pulse.vercel.app · Code: github.com/KaJota-inc/kajota-pulse · Built on Next.js 16 (Vercel) + Aurora Serverless v2 + Gemini 2.5 Flash.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @kajota pulse 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-a-passwordl…] indexed:0 read:4min 2026-06-29 ·