iToverDose/Software· 30 JUNE 2026 · 00:01

Zero-trust analytics: How a passwordless dashboard predicts retail stock

A zero-stack dashboard built for African micro-sellers predicts profitable stocking decisions using real-time market signals and AI. Here’s how passwordless IAM, change-stream fixes, and zero server management came together in five days.

DEV Community5 min read0 Comments

The most profitable question in African micro-commerce isn’t how to list a product — it’s what to stock this week. For co-sellers juggling thin margins and shifting demand, guesswork costs money. That’s why we built Kajota Pulse: a terminal-style dashboard that monitors live marketplace data, analyzes trends, and delivers one-click stocking decisions with AI-generated reasoning.

Kajota Pulse is the second app in a three-part stack — Coach drafts listings, Pulse decides inventory, and Mesh settles transactions on-chain. Built for the AWS × Vercel H0: Hack the Zero Stack hackathon, it runs entirely on serverless infrastructure: Vercel for compute, AWS Aurora Serverless v2 for data, and no managed servers or connection pools. Here’s how we pulled it off — and why three hidden engineering hurdles nearly derailed the project.

Building on the “zero stack”: no servers, no secrets, no passwords

The hackathon’s defining constraint was the zero stack: use Vercel for deployment, AWS for persistent state, and avoid managing any infrastructure. For a real-time analytics app, that meant relying on serverless compute and a managed database.

The architecture is intentionally minimal:

  • Next.js 16 (App Router) on Vercel for the frontend and API routes
  • AWS Aurora Serverless v2 (PostgreSQL) as the single source of truth
  • Gemini 2.5 Flash for AI-powered stocking recommendations
  • MongoDB Atlas Database Triggers streaming live data from the Kajota catalogue

Total footprint: five PostgreSQL tables, two SQL views, two Gemini endpoints, and one real-time ingestion API. No VPC configuration. No connection pooling. No password files.

But the most surprising outcome wasn’t the speed — it was the security.

Why passwordless auth isn’t just safer — it’s required

When we provisioned Aurora Serverless v2 using the new internet-access-gateway networking model, every password-based connection failed with PAM authentication failed. The model enforces IAM database authentication, which means no static passwords are allowed — and it also disables the RDS Data API.

Instead of storing a long-lived secret, every database connection uses a short-lived IAM authentication token generated at runtime:

const signer = new Signer({ hostname, port, username, region, credentials });

const pool = new Pool({
  host,
  port,
  user,
  database,
  password: () => signer.getAuthToken(), // fresh token every time
  ssl: { rejectUnauthorized: false }
});

The pg library supports an async password callback, so the token refreshes seamlessly with each new connection. The result: no database password exists anywhere — not in environment variables, not in code, not in a secrets manager. Zero secrets, zero risk.

When AWS credentials aren’t yours: the Lambda shadowing trap

The IAM signer needs AWS credentials to mint tokens. We set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in Vercel’s environment variables. That should have worked — except Vercel functions run on AWS Lambda, and Lambda injects its own execution-role credentials, overriding ours.

The signer was signing tokens with the wrong identity — one without the required rds-db:connect permission on the Aurora cluster. After an hour of debugging, we discovered the issue and switched to custom environment names:

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;
}

We created a dedicated IAM user with only rds-db:connect on the cluster’s database user, exposed under non-standard variable names. The Lambda shadowing problem vanished — and the passwordless connection remained secure.

Real data breaks assumptions: three bugs that seed data never shows

We wired Kajota Pulse to live MongoDB Atlas change streams on three collections: products, cosell_products, and orders. Each change event triggers a POST to /api/ingest, which upserts into Aurora. While this setup is fast and clean, real production data revealed three bugs that unit tests and seed files never catch.

  • Extended JSON serialization. Atlas change events use EJSON, so a MongoDB _id arrives as {"$oid":"abc123"} and a price as {"$numberInt":"9500"}. Without proper decoding, the dashboard renders id="[object Object]" and price=NaN. Two small utilities fixed it:
const ejsonId = (obj) => obj.$oid || obj;
const ejsonNum = (obj) => Number(obj.$numberInt || obj);
  • Collection name mismatches. The real collection is cosell_products (with an underscore), but our API router was matching cosellproducts. Events silently bypassed ingestion. We now normalize collection names before routing.
  • Foreign key constraints under event disorder. Change streams don’t guarantee order. A co-seller listing can arrive before its referenced product. Our foreign key constraints rejected these rows. The fix: drop the FKs and treat each table as an independent projection.

These issues only surface under real load. That’s why we built the integration early — to catch problems before users did.

From dashboard to advisor: turning data into decisions

Dashboards show numbers. Advisors answer questions. Kajota Pulse was designed to do both — and to do it reliably, even when AI is unavailable.

The /api/recommend endpoint pulls live signals: trending demand, category margins, competitor stock-outs, and price positioning. It then sends this context to Gemini 2.5 Flash with a structured-output schema:

{
  "product": "Organic Shea Butter",
  "recommendation": "Stock 10–15 units before the weekend",
  "reasoning": "+27 favorites this week, sits in the high-margin Beauty category (18%), and a competitor just ran out of a similar cream"
}

Two design choices ensure robustness:

  • Structured JSON output using responseMimeType: "application/json" and a responseSchema guarantees clean parsing and rendering, not fragile prose extraction.
  • A deterministic fallback. If Gemini is unreachable, a heuristic ranking (demand × margin × opportunity) replaces AI decisions. The stocking card never shows an empty state — even during demos.

What zero stack buys: speed, safety, and zero ops

In five days, we built a production-ready analytics advisor that:

  • Runs entirely serverless — no servers to patch, scale, or monitor
  • Uses no long-lived secrets — passwords are impossible, and credentials rotate automatically
  • Processes live data with minimal latency — change streams arrive in near real time
  • Delivers AI-powered stocking advice that degrades gracefully

The zero stack didn’t just simplify deployment — it forced us to think about security, data integrity, and reliability in ways that traditional stacks often obscure. For teams building in fast-moving markets, that’s not just a hackathon trick. It’s a blueprint.

The next step? Bringing Pulse to real co-sellers and turning AI-driven stocking from a demo into a daily habit.

AI summary

Vercel ve AWS Aurora Serverless kullanarak sunucu yönetimi gerektirmeyen, sifresiz ve yapay zeka destekli bir gösterge paneli geliştirme rehberi. Kajota Pulse projesi detayları.

Comments

00
LEAVE A COMMENT
ID #5GRCBE

0 / 1200 CHARACTERS

Human check

9 + 9 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.