The Database That Makes an AI Honest: Building Continuity on Amazon Aurora + Vercel
I built Continuity and wrote this post as my submission for the AWS × Vercel “Hack the Zero Stack” (H0) Hackathon. #H0Hackathon
Most demos of AI agents are forgiving. If a chatbot invents a detail, you shrug and re-prompt. But I wanted to build for a domain where a confident hallucination has a physical cost: board-level electronics repair. If an AI tells a technician the wrong component failed, they desolder a good part, scrap a repairable board, or — worse — send a latent fault back to a customer.
So the central question of this project wasn’t “how do I make the model smarter?” It was: how do I make it impossible for the model to lie?
The answer turned out to live almost entirely in the database. This is the story of how Amazon Aurora (PostgreSQL) became the integrity layer of an AI product — not its storage bucket — and how it deploys on Vercel with no database password anywhere in the stack.
The thesis: the database is the only thing the agent is allowed to speak from
Continuity takes a dead board from a plain-English symptom (“no power”) to the one failed component and the exact pin to probe. Under the hood there’s a tool-using LLM. But the agent has no free-text knowledge of any board. Its only way to perceive a circuit is to call tools — and every tool is a parameterized query against Aurora.
That single design decision is the whole product:
- If the agent wants to trace a power rail, it runs a query.
- If it wants to inspect a component, it looks up a row.
- If there’s no row for a part, there is no such part — the tool returns nothing, and the agent has nothing to cite.
Referential integrity, the most boring feature in databases, becomes a hallucination guardrail. The model can reason about what the rows mean, but it cannot conjure a component that the schema doesn’t contain.
The board is a graph, and the graph is the schema
A circuit board is naturally a graph: components connect to nets (electrical nodes) through pins, and nets relate to each other through typed edges — powers, enables, clocks. I modeled exactly that in Aurora as relational tables (components, nets, pins, edges).
The payoff: “trace where this power rail comes from” isn’t a prompt to an LLM. It’s a recursive query.
WITH RECURSIVE power_path AS (
SELECT e.from_net, e.to_net, e.kind, 1 AS depth
FROM edges e
WHERE e.to_net = (SELECT id FROM nets WHERE name = 'PP5V0_SYS')
UNION ALL
SELECT e.from_net, e.to_net, e.kind, pp.depth + 1
FROM edges e
JOIN power_path pp ON e.to_net = pp.from_net
WHERE pp.depth < 12
)
SELECT * FROM power_path;Postgres walks the board from the dead rail back to the USB-C input port, through the regulator, in milliseconds. The agent isn’t reasoning about the circuit — it’s querying it, and reporting rows. That’s the difference between a plausible answer and a correct one.
The model proposes; the database decides
Grounding stops the agent from inventing parts. But I also wanted a check on its conclusions. So every finding the agent proposes is re-checked by a deterministic verifier — plain SQL, no model involved. It confirms the cited component exists, sits on the rail in question, and that the recorded measurements actually support the diagnosis (e.g., a 5 V rail reading 0 V with a fraction of an ohm to ground is a real short). Only findings that pass get the VERIFIED ✓ stamp a technician sees.
The model gets to be creative. It does not get to be the source of truth.
Compounding memory with pgvector + HNSW
A repair shop’s real asset is its accumulated experience. I wanted that to compound — in the database.
Get Cmandikonza’s stories in your inbox
Join Medium for free to get updates from this writer.
When a technician confirms a root cause, Continuity embeds the case (symptom + confirmed component) and stores the vector on the finding using pgvector, indexed with HNSW for sub-linear similarity search:
CREATE INDEX ON findings
USING hnsw (embedding vector_cosine_ops);Now retrieval is semantic. A week later a different technician types “five-volt rail won’t come up” — different words, same fault — and the agent finds the earlier case by meaning, not keywords:
SELECT c.refdes, r.symptom,
1 - (f.embedding <=> $1) AS similarity
FROM findings f
JOIN repairs r ON r.id = f.repair_id
LEFT JOIN components c ON c.id = f.component_id
WHERE f.embedding IS NOT NULL
AND f.status = 'confirmed'
AND r.tenant_id = $2 -- scoped to this shop only
ORDER BY f.embedding <=> $1 -- cosine distance, HNSW-accelerated
LIMIT 8;The bench literally gets smarter every week it’s used — and that memory is private to each shop.
Multi-tenant by design — and a privacy boundary you can audit
Continuity is a B2B product: a shop is a team, and shops must never see each other’s data. Each shop’s rows are scoped to its tenant, with PostgreSQL Row-Level Security policies as a guardrail and explicit per-shop scoping on every query.
But there’s a twist that makes the data model interesting. I also wanted a cross-shop benchmark — “C29 is the #1 cause of no-power failures across all shops” — which is genuinely useful, but must not leak any individual shop’s rows. The solution is a SECURITY DEFINER aggregate that crosses the tenant boundary on purpose, and returns only percentages:
CREATE FUNCTION fleet_failure_rate(p_symptom text)
RETURNS TABLE (refdes text, rate numeric, n int)
LANGUAGE sql
SECURITY DEFINER -- runs with the owner's privileges, reads all shops
SET search_path = public
AS $$
SELECT c.refdes,
round(count(*)::numeric / sum(count(*)) OVER (), 2) AS rate,
count(*)::int AS n
FROM findings f
JOIN repairs r ON r.id = f.repair_id
JOIN components c ON c.id = f.component_id
WHERE f.status = 'confirmed' AND r.symptom = p_symptom
GROUP BY c.refdes
ORDER BY rate DESC;
$$;The aggregate crosses the boundary; the rows never do. It’s privacy-preserving network intelligence, expressed as a database function you can read and audit. For a panel of database engineers, that — not the chat UI — is the interesting part.
Passwordless Aurora on Vercel
Continuity’s front end is Next.js on Vercel, and the most satisfying piece of plumbing is that there is no database password anywhere. The app authenticates to Aurora with short-lived IAM tokens, minted per-request from Vercel’s OIDC identity:
import { Signer } from '@aws-sdk/rds-signer'
import { awsCredentialsProvider } from '@vercel/functions/oidc'const signer = new Signer({
hostname: process.env.PGHOST!,
port: 5432,
username: process.env.PGUSER!,
region: process.env.AWS_REGION!,
credentials: awsCredentialsProvider({ roleArn: process.env.AWS_ROLE_ARN! }),
})
const token = await signer.getAuthToken() // expires in 15 minutesNo secret to leak, no .env with a connection string — Vercel's serverless identity assumes an AWS role, and Aurora trusts the IAM token. It's the right way to run a managed database from the edge.
Why this stack, and what I learned
Building on Aurora + Vercel let me ship a real, multi-tenant product in a weekend, and it pushed me toward an idea I’ll keep: constraints are features. “The agent may only cite rows” sounds limiting. It’s the entire value proposition. The database design did things prompt engineering never could:
- Referential integrity → a hallucination guardrail.
- A recursive CTE → a circuit-tracing engine.
- pgvector + HNSW → compounding institutional memory.
- A
SECURITY DEFINERaggregate → an auditable privacy boundary. - IAM auth → a database with no password.
What’s next: netlist/boardview ingestion so any device becomes a queryable graph, a parts-inventory join, and billing wired to the per-shop usage meter that already lives in Aurora.
If you take one thing from this: when you’re building an AI product for a domain where being wrong is expensive, don’t ask the model to be trustworthy. Make the database the thing it has to tell the truth about.
Continuity is live at continuity-instrument.vercel.app. I built it and wrote this post for the AWS × Vercel “Hack the Zero Stack” (H0) Hackathon. #H0Hackathon





