← All work

2025–26Solo, product, full-stack + AI

VisaNav

A free public service for immigrants: plain-language answers grounded in official USCIS data, plus a public directory of resources for real help. Built first as a legal-tech SaaS on a full AWS stack, then pivoted into an open-source public good with community-curated country data, cheap enough to run for free.

Pydantic-AIClaudePineconeDjangoVueTerraformAWS
Idle hosting per month, before and after
$100 → ~$5

Idle hosting per month, before and after

USCIS chunks in the index
6,600+

USCIS chunks in the index

License for the open-source version
AGPL-3.0

License for the open-source version

VisaNav, an immigration-resources RAG assistant
VisaNav, an immigration-resources RAG and an aspiring open-source project

The problem

Immigration guidance is high-stakes, constantly changing, and buried in dense official sources. The people who need it most are often the least equipped to find it: the answers live in English legalese spread across USCIS, the State Department, and the immigration courts, and a wrong answer can cost someone years. So people guess, ask a cousin, or pay for answers they could have found if the rules were easier to read. I wanted an assistant that answers from real USCIS data instead of a model's memory, and that points people to real help when a chatbot isn't enough.

Listening before building

I didn't start in the editor. I started by talking with organizations that serve immigrant communities, to understand what people actually walk in asking for and where the existing help falls short.

The plan at the time was a legal SaaS: a trusted assistant plus a referral network, serving individuals for free while the organizations and firms that work with immigrants paid for it.

Those conversations shaped the product directly. People need a plain-language answer about where they stand, and right after that they need a person: a clinic, a pro-bono attorney, an accredited representative. That is why VisaNav is a grounded chat and a public directory of resources, not just a chatbot.

What I built

The chat. Javi, the assistant, opens with a single question box and no account required.
The chat. Javi, the assistant, opens with a single question box and no account required.

VisaNav (codenamed Project Raul while I built it) is a full product, not a notebook:

  • Retrieval. Scrapy spiders pull USCIS forms, instructions, and pages; a PySpark job cleans and enriches the text; the chunks are embedded into a Pinecone index, 6,600 of them for the US.
  • The agent. A Pydantic-AI agent with one tool, semantic search. When a question needs facts, a small summary model condenses the conversation into a single search query, the tool pulls the top matches, and the agent writes from those, citing form numbers and official pages. The system prompt draws hard lines: no tax, no medical, no guarantees of approval, and always "I am not a lawyer."
  • The product. A Vue chat interface with streaming replies and visible status states, on a Django API with JWT auth, anonymous conversations, and rate limiting. Containerized with Docker, provisioned with Terraform.
  • The public directory. A searchable directory of nonprofits, clinics, and pro-bono organizations, seeded from cleaned public lists, so the answer to "now what?" is a phone number.
Status states stream over Server-Sent Events while the agent decides whether it needs to search.
Status states stream over Server-Sent Events while the agent decides whether it needs to search.
A grounded answer: the agent searched the USCIS corpus, then wrote the steps and forms from what it found.
A grounded answer: the agent searched the USCIS corpus, then wrote the steps and forms from what it found.
The public directory: every conversation can end with a real organization to call.
The public directory: every conversation can end with a real organization to call.

Building for trust

An immigration assistant that hallucinates is worse than useless: it is harmful. So the design leans hard on grounding: retrieve first, cite the source, and prefer "here is where to look" over a confident wrong answer. The tool results are typed, the prompt fences the scope, and the corpus is only official text.

The enterprise build

Because I was building a legal SaaS, I built it the way a company would.

The enterprise build

Terraform | AWS | ~$100–120 / mo idle

Built the way a company would build a legal SaaS: everything private, everything managed, everything billing around the clock. Chips are the rough monthly cost with zero users.

Edge

Route 53 + ACM
visanav.app, TLS
CloudFront + WAF~$5+
SPA CDN, managed rule sets
S3
SPA, uploads, logs, scraped data

VPC, private subnets, two AZs

NAT gateway~$32
egress for private subnets
Aurora Serverless v2~$43
Postgres, 0.5 ACU floor
App Runner~$5–25
Django API container
ECS Fargate~$10–15
Celery worker, always on
ElastiCache Redis~$12–15
cache + Celery results
SQS~$0
Celery broker
ECS Fargate
Scrapy task, monthly EventBridge cron
Secrets Manager
DB password, API keys

Ingestion, retrieval, models

Gluecrawler + PySpark clean & enrich job
Pineconeembeddings, one uscis namespace
OpenAI + Bedrockchat model + search-query summary

GitHub Actions via OIDCECR images: api, workers, scraperTerraform modules per service

The two killers: the NAT gateway and Aurora's 0.5-ACU floor, about $75 a month for existing.

Everything in that diagram is real Terraform in the repo: a VPC with private subnets and a NAT gateway, Aurora Serverless Postgres, ElastiCache Redis, SQS as the Celery broker, an always-on Fargate worker for chat jobs, a second Fargate task for the monthly scrape, App Runner for the API, CloudFront with WAF managed rules in front of the SPA, Glue for the ingestion job, Secrets Manager, and GitHub Actions deploying through OIDC into ECR. It was a good stack. It was also $100 to $120 a month with zero users, and about $75 of that was the NAT gateway and Aurora's 0.5-ACU floor, two services that bill for existing.

The road bump

The legal SaaS route stalled for three reasons, and none of them were technical.

Trust in AI wasn't there yet. In 2025 the organizations I wanted as customers were rightly cautious about putting an AI between them and a client whose immigration status was on the line. "Grounded in USCIS data" is a strong engineering answer; it is not yet a strong answer for a legal director.

The regulatory bar is high. Anything that looks like case-specific advice brushes against the unauthorized practice of law, and the data involved is some of the most sensitive a person has. Doing that properly means entity structure, counsel, compliance, and insurance before the first paying customer.

It was far more than I wanted to take on alone. I was paying for an enterprise stack nobody used while looking at a wall of legal, financial, and technical hurdles for one person. So I tore the stack down.

The pivot: an open-source public good

Tearing it down didn't sit right either. The project was too far along, with too much genuinely useful work in it, to let nobody use it. So the question changed from "how do I sell this" to "how do I make this free, cheap enough to run out of my own pocket, and something other people can build on."

The insight that reframed it: the code is not the valuable part. A RAG chatbot is a commodity now. The valuable, hard, unglamorous part is a curated, cited, current corpus of immigration knowledge and a directory of real help, per country. That is exactly the kind of work a community does better than one engineer, and it is the part lawyers, paralegals, law students, and people who have been through the process can contribute to without ever opening the application code.

So the project is becoming immigration-ai, licensed AGPL-3.0 so anyone who runs a modified hosted version has to share it back. The heart of the repo is a folder per country:

  • Knowledge docs in Markdown with frontmatter: how a process, category, form, or fee works, in plain language, with a source URL.
  • Resource lists in YAML: legal aid, clinics, accredited nonprofits, with contact details and a source URL.
  • Rules that keep it trustworthy, enforced in CI: every entry cites a source; anything marked official must cite a domain that country declares as a government authority; everything carries a last-verified date.
  • Automation gates format, humans gate truth. A country maintainer reviews the substance and merges, and the merge ingests the knowledge into that country's own vector namespace.

Adding Germany means adding files under one new country folder. Nothing else changes.

Cheap enough to just run

The other half of the pivot was making the thing affordable to keep online, for free, for people who need it, indefinitely.

The public-good build

immigration-ai | AGPL-3.0 | ~$5–15 / mo

Web plus Postgres on one box, a free vector index, and a cheap model. The queue is gone because a chat turn is mostly waiting on I/O; one async process holds hundreds of in-flight turns.

Static host

Vue SPA$0
Cloudflare Pages or any static host

One small machine

Django under uvicorn (ASGI)~$5–15
async chat view streams SSE, no queue
Postgres
conversations, users, resource directory
In-process cache
per-client rate limit, spend safety net

Pay per use

Pinecone$0
free tier, one namespace per country
Claude Haiku 4.5~$0.10 / user
answers + search-query summary

The data loop, no code required

01Contributor adds filesdata/countries/<code>/ knowledge .md + resources .yaml
02CI validatesschema, source_url, official domains, last_verified
03Maintainer reviewsa human gates truth, then merges
04Ingestchunks embedded into that country’s namespace
Removed:NAT gatewayAuroraRedisSQSCelery workersApp RunnerWAFGlue

The biggest saving was architectural, not a cheaper host. Celery, Redis, SQS, and the always-on worker existed to run one thing: the chat turn. A chat turn is almost entirely waiting on the model and the vector store, so I replaced the whole queue with an async Django view under uvicorn that streams the reply over Server-Sent Events. One small process can hold hundreds of in-flight turns, and the frontend went from polling a task endpoint to reading a stream. Three services and a worker became web plus Postgres.

One chat turn

Server-Sent Events | status, delta, done

Retrieve first, then write. The status frames are what the shimmer in the chat shows while the agent works.

01QuestionPOST /api/chat, anonymous or JWT
02Agent decidesPydantic-AI on Claude Haiku 4.5
status: thinking
03Condense historysummary model writes one search query
status: researching
04RetrievePinecone, top 3 chunks, uscis namespace
05Write from sourcestokens stream as SSE deltas
status: writing
06Persistrequest, tool call, reply saved to Postgres
the person askingmodel stepsstorage

The model got cheaper too. A provider factory lets the agent run on Anthropic, OpenAI, or Bedrock from one environment variable, and the default is Claude Haiku 4.5 for both the answer and the search-query summary. At roughly 4k input and 500 output tokens a turn, a turn costs about half a cent to a cent, a whole conversation five to ten cents, and 200 users a month lands around $20 in tokens. Rate limits cap the downside. Hosting is one small machine at $5 to $15 a month, and the vector index fits Pinecone's free tier. A year of serving a couple hundred people a month is about $600 in credits, small enough that I can carry it myself.

What's next

  • Publish the public repo from clean history, with the contribution guide, the validator, and the US as the worked example.
  • Now that the lean stack is live, put it in front of the organizations I talked to at the start and fold their feedback back in.
  • Recruit country maintainers from legal-aid clinics and diaspora communities, and add the second country.

What I learned

I built the company before I built the community. The product was never the bottleneck: the agent, the pipeline, the infrastructure, all of it worked. What stalled was trust and distribution, and those are not problems a better Terraform module solves. Open source turns the legal, financial, and technical hurdles that were a wall for one person into shared work, and it makes the actual asset, a trustworthy corpus of immigration knowledge, something that can outlive any one deployment. The model is the easy part. The value is in the grounding, the data, and the people willing to keep it true.

Let's build something.

Product engineerDesign + build + AI

© 2026 Victor ValdezBuilt with Nuxt