# Harjot Singh Rana - full site

> Full-stack and AI product engineer helping early-stage teams turn ideas, Figma designs, and half-built products into shipped SaaS and AI systems.

This document concatenates every public page of https://www.harjotrana.com as Markdown. Each section starts with its canonical URL. The compact guide with when-to-use notes is https://www.harjotrana.com/llms.txt.

## Contents

- Harjot Singh Rana: https://www.harjotrana.com/
- Work with Harjot Singh Rana: https://www.harjotrana.com/hire
- About Harjot Singh Rana: https://www.harjotrana.com/about
- Contact Harjot Singh Rana: https://www.harjotrana.com/contact
- harjotrana.com developer resources: API, MCP server, llms.txt: https://www.harjotrana.com/developers
- Privacy Policy: https://www.harjotrana.com/privacy-policy
- Projects: https://www.harjotrana.com/projects
- Moonshift: https://www.harjotrana.com/projects/Moonshift
- Reinstate: https://www.harjotrana.com/projects/Reinstate
- SignVault.io: https://www.harjotrana.com/projects/SignVault.io
- Orchastra: https://www.harjotrana.com/projects/Orchastra
- DevSynq: https://www.harjotrana.com/projects/DevSynq
- Weavestore: https://www.harjotrana.com/projects/Weavestore
- Zyflo: https://www.harjotrana.com/projects/Zyflo
- Writing: https://www.harjotrana.com/blog
- How I Scored 100/100 on Is Agentic by Vercel: https://www.harjotrana.com/blog/scoring-100-on-is-agentic
- Locum: Giving Grok Bot a Stand-In: https://www.harjotrana.com/blog/locum-grok-bot-provider-adapter
- The Practical Vercel Turborepo Cheatsheet: https://www.harjotrana.com/blog/turborepo-cheatsheet
- uv and Ruff: A Practical Python Tooling Cheatsheet: https://www.harjotrana.com/blog/uvx-and-ruff-cheatsheet
- From Zero to npm in 7 Hours: Building Weavestore: https://www.harjotrana.com/blog/7-hours-to-mvp-weavestore
- Building Zyflo: An Accessible, Motion-First React Library: https://www.harjotrana.com/blog/building-zyflo-ui-library
- Next.js and MDX: A Practical Setup Guide: https://www.harjotrana.com/blog/getting-started-nextjs-mdx
- Choosing a Stack for a Portfolio Site: https://www.harjotrana.com/blog/building-portfolio-website
- MVP Scope Estimator: https://www.harjotrana.com/tools/mvp-scope-estimator


---

# Harjot Singh Rana - Full-Stack and AI Product Engineer

Canonical URL: https://www.harjotrana.com/

> I build software from first idea to production.

- I ship multi-agent AI products and multi-tenant SaaS end to end, using TypeScript and Python.
- Founding engineer at [Moonshift](https://moonshift.io); raised generation success 46% → 76% for 1,200+ users.
- Full-stack engineer at SignVault.io, owning the API, permissions, and billing.

Bring me anything: an idea, a Figma design, or a half-built product. I can help scope it, ship it, or take ownership of the next stage.

- Work with me: https://www.harjotrana.com/hire
- Résumé (PDF): https://www.harjotrana.com/resume.pdf
- Email: me@harjotrana.com
- Status: Available for selected product work

## Selected work

Six systems I designed, built, and shipped. Each one links straight to the running product or the source.

### Moonshift

*Production · Founding engineer*

AI app builder that turns a prompt into a deployed full-stack app. I rebuilt the multi-agent generation pipeline, taking end-to-end success from 46.2% to 76.7% and cutting median prompt-to-live time 32%, to 14.9 minutes. 1,200+ users, 800+ apps generated.

- Detail page: https://www.harjotrana.com/projects/Moonshift
- Live: https://moonshift.io
- Stack: TypeScript, Python, Next.js, PostgreSQL, SSE, Docker

### Reinstate

*Open source · Solo · Apache-2.0*

Go CLI that keeps Claude Code and Codex sessions alive across machines. Zero-knowledge age encryption into your own S3/R2 bucket, plus OS-aware path remapping so resume still works after switching from Windows to macOS. 23/23 cross-platform acceptance tests.

- Detail page: https://www.harjotrana.com/projects/Reinstate
- Code: https://github.com/HarjjotSinghh/reinstate
- Live: https://reinstate.dev
- Stack: Go, age, S3, GitHub Actions

### SignVault.io

*Production · Full-stack engineer*

Multi-tenant e-signature SaaS (shut down Nov 2025). I owned the REST API, ABAC permissions engine, and Stripe billing.

- Detail page: https://www.harjotrana.com/projects/SignVault.io
- Stack: Next.js, Express, PostgreSQL

### Zyflo

*Open source · Component library*

Accessible, motion-first React component library. 10+ components, MDX docs, showcased at GitHub Universe 2025.

- Detail page: https://www.harjotrana.com/projects/Zyflo
- Code: https://github.com/HarjjotSinghh/zyflo
- Live: https://zyflo.vercel.app
- Stack: React, TypeScript, Framer Motion

### Orchastra

*AI product · Visual orchestration*

No-code canvas for multi-agent AI workflows. Swap between OpenAI, Claude, Grok, and Gemini. 140+ early adopters.

- Detail page: https://www.harjotrana.com/projects/Orchastra
- Live: https://www.orchastra.org
- Stack: Next.js, FastAPI, LangChain

### DevSynq

*Desktop tool · Developer experience*

Desktop app that syncs MCP servers, API keys, and settings across Cursor, Windsurf, and VS Code. 219+ developers.

- Detail page: https://www.harjotrana.com/projects/DevSynq
- Code: https://github.com/HarjjotSinghh/devsynq
- Live: https://www.devsynq.app
- Stack: Electron, React, TypeScript

All projects: https://www.harjotrana.com/projects

## Experience

### [Moonshift](https://moonshift.io) | Founding Engineer (Jan 2026 - Aug 2026)

Built and shipped the AI app builder end to end - multi-agent pipeline, SSE observability, prompt-to-deploy - to 1,200+ users.

### SignVault.io | Full-Stack Software Engineer (Nov 2024 - Nov 2025)

Owned the e-signature REST API, ABAC engine, Stripe billing, and document workflows for a multi-tenant SaaS.

### [LawSikho](https://lawsikho.com) | Software Engineer Intern (Sep 2024 - Nov 2024)

Multithreaded Python pipeline that scraped and deduplicated 100,000+ job listings, deployed on AWS EC2.

### [Mindcase](https://mindcase.co) | Full-Stack Engineer Intern (Aug 2024 - Sep 2024)

Built an AI blog-generation platform on the OpenAI and Reddit APIs, from interface to content preview.

Full résumé: https://www.harjotrana.com/resume.pdf

## Education

- B.Tech, Computer Science and Engineering, Guru Tegh Bahadur Institute of Technology (GGSIPU, New Delhi), 2022 - 2026

## Recognition

- Avalanche ecosystem mini-grant: $10K. One of 10 teams selected across India.
- OpenCode Buildathon: Top 120 of 20,000 participants.
- [GitHub Universe 2025](https://zyflo.vercel.app): Zyflo showcased during the event.
- [LeetCode](https://leetcode.com/u/HarjjotSinghh): 770+ problems solved.

## Writing

- [How I Scored 100/100 on Is Agentic by Vercel](https://www.harjotrana.com/blog/scoring-100-on-is-agentic) (23 Aug 2026, 21 min): My portfolio started at 75 on Vercel's agent-readiness scanner. Two rounds later it scored 100. Here is every change that moved the number: Markdown content negotiation, a hand-rolled MCP server, RFC 9457 errors, RateLimit headers, an llms.txt that says when to use the site, and the one header Next.js would not let me set.
- [Locum: Giving Grok Bot a Stand-In](https://www.harjotrana.com/blog/locum-grok-bot-provider-adapter) (22 Aug 2026, 20 min): Grok Bot can burn a week of quota in an hour. Locum hands the work to the Claude Code or Codex CLI already logged in on your machine, through documented extension points, without ever touching a credential. Six releases in a day, including the one where the tool found five real bugs in its own code.
- [The Practical Vercel Turborepo Cheatsheet](https://www.harjotrana.com/blog/turborepo-cheatsheet) (08 Dec 2025, 23 min): Task graphs, remote caching, and the pipeline config that actually keeps a monorepo fast.

All writing: https://www.harjotrana.com/blog

## Stack

- **Core**: TypeScript, Python, React, Next.js, Node.js, PostgreSQL
- **Backend**: Express, FastAPI, Redis, SSE, Socket.IO, GraphQL
- **AI systems**: OpenAI, Anthropic, LangChain
- **Infrastructure**: Docker, AWS, Vercel, Stripe, Prisma, Drizzle
- **Also**: Go, Rust, MongoDB, Supabase, Bun, Move

## Open-source activity

GitHub contribution graph for the last 12 months: https://github.com/HarjjotSinghh

## Contact

Building something and need a product engineer? Email me@harjotrana.com or use the form at https://www.harjotrana.com/hire#start. Share the product, the stage, and what you need next. I reply within one business day.

- GitHub: https://github.com/HarjjotSinghh
- LinkedIn: https://www.linkedin.com/in/HarjjotSinghh
- Twitter: https://x.com/HarjjotSinghh
- LeetCode: https://leetcode.com/u/HarjjotSinghh
- Behance: https://www.behance.net/harjjot

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Work with Harjot Singh Rana - Product engineering for early-stage teams

Canonical URL: https://www.harjotrana.com/hire

> Bring me the idea. I'll help you ship the product.

I'm Harjot, a full-stack and AI product engineer. If you have a Figma, a rough brief, or a half-built product, I'll help turn it into something real, live, and maintainable. I built Moonshift, a multi-agent app builder that served 1,200+ users, so I know the gap between a demo and a product people can actually use.

Remote from New Delhi (IST) · Fixed-scope and ongoing engagements · Replies within one business day

- Start a project: https://www.harjotrana.com/hire#start
- Book a call: https://cal.com/harjot
- Email: me@harjotrana.com

## What are you trying to ship?

The best fit is a real product problem with a decision-maker, a useful first milestone, and a willingness to keep scope honest.

- **You have an idea**: You need a small, useful first version and a technical partner who can help decide what not to build yet.
- **You have a product**: You need an AI feature, integration, reliability pass, or another pair of hands to move the roadmap forward.
- **Your codebase is stuck**: The prototype works in a demo but is slow, fragile, insecure, or difficult to take to production.

## Ways I can help

Every engagement starts with a written scope. The right shape and price depend on the product, the deadline, and what already exists.

### Idea to live MVP

*For founders without a technical co-founder*

Scope the smallest useful product, build the core experience, connect the backend, and deploy it with a handover you can own.

Shape: Fixed-scope sprint · timeline depends on scope

### AI product sprint

*For teams adding AI to an existing product*

Turn an LLM, agent, RAG, or automation idea into a working feature with sensible evaluation, observability, and cost decisions.

Shape: Fixed-scope sprint · timeline depends on scope

### Product or codebase rescue

*For half-built or AI-generated products*

Find the highest-risk problems, make the next fixes concrete, and create a path from fragile prototype to shippable product.

Shape: Diagnostic first · implementation quoted separately

### Fractional product engineering

*For small teams that need ownership*

Take responsibility for a product area, ship continuously, and give a founder or product team dependable technical momentum without adding a full-time hire immediately.

Shape: Ongoing monthly engagement · explicit capacity

## Proof of shipped systems

These are the systems and outcomes I can show, not a list of tools I have tried.

- **[Moonshift](https://moonshift.io)**: Founding engineer on an AI app builder for 1,200+ users. Improved end-to-end generation success from 46.2% to 76.7% and reduced median prompt-to-live time by 32%.
- **SignVault.io**: Owned the REST API, ABAC permissions engine, Stripe billing, and document workflows for a multi-tenant e-signature SaaS.
- **[Reinstate](https://reinstate.dev)**: Built a Go CLI for encrypted cross-machine AI coding-session sync, with 23/23 cross-platform acceptance tests.
- **[Orchastra](https://orchastra.org) and [DevSynq](https://devsynq.app)**: Shipped a multi-agent workflow canvas with 140+ early adopters and a developer tool used by 219+ developers.

Project details: https://www.harjotrana.com/projects

## A straightforward process

1. **Understand the problem**: A short call or written brief clarifies the user, desired outcome, constraints, urgency, and what success means.
2. **Write the smallest useful scope**: You receive a clear proposal with deliverables, exclusions, milestones, dependencies, and a fixed price or monthly arrangement.
3. **Build in visible increments**: Work is shipped in small steps with a written update each week and decisions recorded when scope changes.
4. **Launch and hand over**: The product is deployed to your infrastructure, with documentation, a walkthrough, and an agreed support window.
5. **Decide what comes next**: We close the loop with the next product priorities, a maintenance plan, or a clean handoff to your team.

## Before we start

### How does remote collaboration work?

I work remotely from New Delhi (IST). We agree on overlap for important decisions, use written updates for the rest, and keep dependencies visible so timezone differences do not become a surprise.

### How are scope and payments handled?

The proposal defines what is included, what is not, milestones, payment timing, ownership, and support. Changes are quoted separately instead of being hidden in an open-ended invoice.

### What if I am not sure what to build?

That is fine. Share the idea, the customer, and what you have tried. I can help turn it into a smaller first milestone before we commit to a build.

### Do you also consider long-term roles?

Yes. The client path stays focused on product work, while the [resume](https://www.harjotrana.com/resume.pdf) contains the fuller employment history.

## Tell me what you are building

A few useful details are enough to start: the product, the current stage, the outcome you want, and when it matters. I will reply with a practical next step within one business day.

- Inquiry form: https://www.harjotrana.com/hire#start
- MVP Scope Estimator: https://www.harjotrana.com/tools/mvp-scope-estimator
- Book a call: https://cal.com/harjot
- Email: mailto:me@harjotrana.com?subject=Project%20inquiry

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# About Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/about

> A full-stack and AI product engineer who takes early-stage products from a rough idea, a Figma, or a half-built codebase to something live and maintainable.

## Who I am

I'm Harjot Singh Rana, a full-stack and AI product engineer based in New Delhi, India (IST). I work with TypeScript and Python, and I ship multi-agent AI products, multi-tenant SaaS, real-time backends, and developer tooling end to end: the product surface, the API, the data model, the billing, and the unglamorous production hardening in between.

This site is the canonical public record of that work. Every claim on it resolves to something you can open: a live product, a repository, a package, a post, or the [résumé](/resume.pdf).

## What I have shipped

- [Moonshift](https://moonshift.io): founding engineer on an AI app builder. I rebuilt the multi-agent generation pipeline, raising end-to-end success from 46.2% to 76.7% and cutting median prompt-to-live time 32%, for 1,200+ users generating 800+ apps.
- [SignVault.io](/projects/SignVault.io): full-stack engineer on a multi-tenant e-signature SaaS (operations concluded November 2025). I owned the REST API, the ABAC permissions engine, Stripe billing, and document workflows.
- [Reinstate](https://reinstate.dev): an open-source Go CLI for encrypted, cross-machine Claude Code and Codex session continuity, with 23/23 cross-platform acceptance tests passing.
- [Orchastra](https://www.orchastra.org) (140+ early adopters), [DevSynq](https://www.devsynq.app) (219+ developers), and [Zyflo](https://zyflo.vercel.app), a React component library showcased at GitHub Universe 2025.

The full list, with stack and outcomes, is on the [projects page](/projects).

## Background

B.Tech in Computer Science and Engineering from Guru Tegh Bahadur Institute of Technology (GGSIPU, New Delhi), 2022 to 2026. Before Moonshift I was a full-stack engineer at SignVault.io, a software engineering intern at LawSikho, where I built a multithreaded Python pipeline that scraped and deduplicated 100,000+ job listings, and a full-stack intern at Mindcase.

Recognition that other people can check: a $10K Avalanche ecosystem mini-grant (one of 10 teams selected across India), a top-120 finish out of 20,000 participants in the OpenCode Buildathon, and 770+ problems solved on [LeetCode](https://leetcode.com/u/HarjjotSinghh).

## How I work

I work remotely with founders, operators, and small product teams on fixed-scope sprints and ongoing fractional engagements. Every engagement starts with a written scope that says what is included, what is not, and what happens when that changes. The ways I can help, and the process, are described on the [work with me](/hire) page.

Questions, introductions, or a project brief: see the [contact page](/contact).


---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Contact Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/contact

> Email is the most reliable route. Share the product, the stage it is at, and what you need next, and I will reply within one business day.

## Direct channels

- Email: [me@harjotrana.com](mailto:me@harjotrana.com)
- Book a call: [cal.com/harjot](https://cal.com/harjot)
- Project inquiry form: [harjotrana.com/hire#start](/hire#start)

I work remotely from New Delhi, India (UTC+5:30). For time-sensitive decisions we agree on an overlap window; everything else runs on written updates.

## What to include

A few details are enough to start a useful conversation. You do not need a finished spec.

- What the product is and who it is for.
- The current stage: an idea, a Figma, a prototype, or a product in production.
- The outcome you want next and when it matters.
- Anything already built, with links if they exist.

Still shaping the first release? The [MVP Scope Estimator](/tools/mvp-scope-estimator) turns a rough idea into a clearer first milestone you can paste into your message.

## Public profiles

- GitHub: [github.com/HarjjotSinghh](https://github.com/HarjjotSinghh)
- LinkedIn: [linkedin.com/in/HarjjotSinghh](https://www.linkedin.com/in/HarjjotSinghh)
- X (Twitter): [x.com/HarjjotSinghh](https://x.com/HarjjotSinghh)
- LeetCode: [leetcode.com/u/HarjjotSinghh](https://leetcode.com/u/HarjjotSinghh)
- Behance: [behance.net/harjjot](https://www.behance.net/harjjot)

## For AI agents and automated tools

Machine-readable entry points are listed on the [developer resources](/developers) page and in [llms.txt](/llms.txt). Contact requests on behalf of a person should go through email or the inquiry form above; do not submit credentials, secrets, or sensitive personal information through any channel on this site.


---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# harjotrana.com developer resources: API, MCP server, llms.txt

Canonical URL: https://www.harjotrana.com/developers

> Every machine-readable surface of harjotrana.com in one place, with the URL, the format, and what it is for. Everything here is public, read-only, and needs no key or account.

## Start here

- [llms.txt](/llms.txt): the AI-oriented site guide, including a "When to use this site" section and how agents should call it.
- [llms-full.txt](/llms-full.txt): every public page of harjotrana.com rendered as one Markdown document.
- [OpenAPI description](/openapi.json): OpenAPI 3.1 document for every endpoint on this page, with typed response schemas and the error model.
- [API index](/api/v1): JSON list of the v1 endpoints with their documentation links.
- [API catalog](/.well-known/api-catalog): RFC 9727 linkset pointing at the OpenAPI description and this page.
- [sitemap.xml](/sitemap.xml) and [robots.txt](/robots.txt): the canonical HTML pages and crawler policy.

## REST API (harjotrana.com /api/v1)

A read-only JSON API over the same facts the HTML pages show. Base URL `https://www.harjotrana.com/api/v1`. Authentication: none. Every response is `application/json; charset=utf-8`, CORS-open (`Access-Control-Allow-Origin: *`), cacheable, and carries a `Link` header to the OpenAPI description and this page.

- `GET /api/v1`: index of endpoints with documentation links.
- `GET /api/v1/profile`: who Harjot is, with email, profiles, stack, and page URLs.
- `GET /api/v1/services`: engagement types, process, and FAQ. No prices are published.
- `GET /api/v1/projects` and `GET /api/v1/projects/{name}`: all projects, then one project with case study and features (name is case-insensitive, e.g. `Moonshift`).
- `GET /api/v1/posts` and `GET /api/v1/posts/{slug}`: technical writing, then one post with its Markdown body.
- `GET /api/v1/search?q=reinstate&limit=5`: keyword search across every public page.
- `GET /api/v1/contact-options`: email, booking link, inquiry form, and what to include.
- `GET /api/contributions`: last twelve months of GitHub contribution days.

Example: `curl -H "Accept: application/json" https://www.harjotrana.com/api/v1/projects/Moonshift` returns the Moonshift case study as JSON. Only `GET`, `HEAD`, and `OPTIONS` are supported; other methods return a `405` problem with an `Allow` header. There are no write endpoints, webhooks, or OAuth flows: to contact or hire Harjot, hand the user the links from `/api/v1/contact-options`.

## Errors

Every 4xx and 5xx from `/api/*`, `/mcp`, the discovery documents, and content negotiation is an RFC 9457 problem-details object served as `application/problem+json`. Members: `type` (a URI that resolves to the matching entry below), `title`, `status`, `detail`, `instance` (the request path), plus two extension members: `code`, a stable machine-readable identifier, and `hint`, what to do next. Validation failures add `invalidParams`.

- **bad_request (400)**: A query or path parameter is missing or invalid; `invalidParams` names it. Fix the request.
- **not_found (404)**: No endpoint or resource at that path. The `hint` names where to list valid ones. Unknown `/api/*` paths always return this, never an HTML page.
- **method_not_allowed (405)**: The endpoint is read-only. The `Allow` header lists supported methods.
- **not_acceptable (406)**: The `Accept` header on a page request allowed neither `text/html` nor `text/markdown`; `available` lists both.
- **rate_limited (429)**: Quota exhausted. Wait `Retry-After` seconds, then retry; reuse cached responses instead of re-fetching.
- **internal_error (500)**: The server failed to produce a response. Retry once; if it persists, email me@harjotrana.com with the URL.
- **upstream_unavailable (502)**: A third-party source the endpoint depends on (GitHub for `/api/contributions`) did not answer. Retry in a few minutes.

The MCP endpoint keeps JSON-RPC semantics: a malformed body is a JSON-RPC `-32700` error object with HTTP 400, unknown methods are `-32601`, and tool-level failures set `isError: true` on the result.

## Rate limits

`/api/*`, `/mcp`, and the discovery documents allow 120 requests per 60 seconds per client address. Every response carries the IETF RateLimit header fields (draft-ietf-httpapi-ratelimit-headers): `RateLimit-Policy: "default";q=120;w=60` declares the quota, and `RateLimit: "default";r=<remaining>;t=<seconds until reset>` reports where you stand. Exceeding it returns a `429` problem with `Retry-After`, which takes precedence over `RateLimit`.

The counter is per server instance, so treat the headers as a live advisory signal rather than a single global cap. Text documents (`/llms.txt`, `/llms-full.txt`, `/feed.xml`, `/sitemap.xml`) and HTML pages are outside the policy.

## Versioning and deprecation

The REST API is versioned in the path: `/api/v1`. Additive changes (new fields, new endpoints) ship in place; breaking changes ship under a new prefix (`/api/v2`) and never alter v1 responses. The MCP server negotiates its protocol version on `initialize` and advertises supported versions in its server card.

Deprecation policy: an endpoint or version scheduled for removal announces it at least 90 days in advance with the `Deprecation` response header (RFC 9745) and a `Sunset` header (RFC 8594) giving the removal date, and is listed here with its replacement. Nothing is deprecated today.

## MCP server

harjotrana.com runs a read-only Model Context Protocol server over Streamable HTTP at [https://www.harjotrana.com/mcp](/mcp). It exposes the portfolio as tools: profile, services, projects, writing, search, and contact options. No authentication, no session state, no side effects.

- Endpoint: `POST https://www.harjotrana.com/mcp` (JSON-RPC 2.0, Streamable HTTP transport, protocol versions 2025-03-26, 2025-06-18, and 2025-11-25). Responses are `application/json`; the server never opens server-initiated streams, so `GET /mcp` returns a `405` problem as the transport spec allows. The legacy HTTP+SSE transport is not offered.
- Server card: [/mcp/server-card](/mcp/server-card) (`application/mcp-server-card+json`, SEP-2127), mirrored with connection hints at [/.well-known/mcp.json](/.well-known/mcp.json) and listed in [/.well-known/ai-catalog.json](/.well-known/ai-catalog.json).
- Tools: `get_profile`, `get_services`, `list_projects`, `get_project`, `list_posts`, `get_post`, `get_contact_options`, `search_site`. Resources: llms.txt, llms-full.txt, openapi.json.
- Client config: `{ "mcpServers": { "harjotrana": { "type": "http", "url": "https://www.harjotrana.com/mcp" } } }`.

## Markdown content negotiation

Every public page on harjotrana.com also has a Markdown representation, following the acceptmarkdown.com convention. Send `Accept: text/markdown` to any page URL and you get `Content-Type: text/markdown; charset=utf-8` with `Vary: Accept`. Appending `.md` to a page path (for example `/blog/turborepo-cheatsheet.md`, or `/index.md` for the home page) returns Markdown unconditionally. Requests that accept neither HTML nor Markdown receive a `406` problem. Unknown paths return a real `404` with a short Markdown body pointing back to the sitemap and llms.txt.

## Usage policy

All of the above is free to read and cache. The HTML pages are canonical; JSON, Markdown, llms.txt, and MCP responses are derived from the same source files at build time and must not be quoted as if they contained facts the HTML does not. Do not infer employment availability, client relationships, testimonials, pricing, or outcomes beyond what the pages state. Questions about these resources: [me@harjotrana.com](mailto:me@harjotrana.com).


---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Privacy Policy

Canonical URL: https://www.harjotrana.com/privacy-policy

> Privacy and data-handling information for harjotrana.com.

Effective date · 07 August 2026

## 1. Analytics

This website uses PostHog for explicit product-analytics events and Vercel Analytics and Speed Insights for aggregate traffic and performance diagnostics. PostHog is configured with its EU host.

- Page views and referring/campaign information
- Opted-in navigation, CTA, project, resume, and calendar clicks
- Form-start, submission, failure, and safe qualification events
- Aggregate tool-start, completion, copy, reset, and handoff events without raw tool answers

DOM-wide autocapture and session replay are disabled. The analytics events do not include names, email addresses, message bodies, or other raw contact-form values, estimator selections, or generated scope briefs.

## 2. Contact form

If you submit the project form, the information you provide, such as your email address, product context, and message, is used to respond to your inquiry and discuss whether the work is a fit.

Do not submit confidential credentials, secrets, or sensitive personal information through the form.

## 3. Portfolio and project pages

The website [www.harjotrana.com](https://www.harjotrana.com) is a personal portfolio for sharing software projects, technical writing, and a resume.

The separate User Data Puller demonstration is an educational utility and is not part of the indexable portfolio funnel.

## 4. Automated and agent access

Public pages, the Markdown representations served through content negotiation, llms.txt, the RSS feed, the sitemap, and the read-only MCP server expose only information that is already published on this site. These endpoints do not set cookies, do not require accounts, and do not collect personal information beyond standard server request logs.

## 5. Contact information

If you have any questions regarding this privacy policy, feel free to contact me at:

Email: [me@harjotrana.com](mailto:me@harjotrana.com)


---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Projects by Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects

> Production work, open-source tools, and experiments across AI, SaaS, developer tooling, and real-time systems. Every entry links to the running product or the source.

## Production & open source

### Moonshift

*Production · Founding engineer*

AI app builder that turns a prompt into a deployed full-stack app. I rebuilt the multi-agent generation pipeline, taking end-to-end success from 46.2% to 76.7% and cutting median prompt-to-live time 32%, to 14.9 minutes. 1,200+ users, 800+ apps generated.

- Detail page: https://www.harjotrana.com/projects/Moonshift
- Live: https://moonshift.io
- Stack: TypeScript, Python, Next.js, PostgreSQL, SSE, Docker

### Reinstate

*Open source · Solo · Apache-2.0*

Go CLI that keeps Claude Code and Codex sessions alive across machines. Zero-knowledge age encryption into your own S3/R2 bucket, plus OS-aware path remapping so resume still works after switching from Windows to macOS. 23/23 cross-platform acceptance tests.

- Detail page: https://www.harjotrana.com/projects/Reinstate
- Code: https://github.com/HarjjotSinghh/reinstate
- Live: https://reinstate.dev
- Stack: Go, age, S3, GitHub Actions

### SignVault.io

*Production · Full-stack engineer*

Multi-tenant e-signature SaaS (shut down Nov 2025). I owned the REST API, ABAC permissions engine, and Stripe billing.

- Detail page: https://www.harjotrana.com/projects/SignVault.io
- Stack: Next.js, Express, PostgreSQL

### Zyflo

*Open source · Component library*

Accessible, motion-first React component library. 10+ components, MDX docs, showcased at GitHub Universe 2025.

- Detail page: https://www.harjotrana.com/projects/Zyflo
- Code: https://github.com/HarjjotSinghh/zyflo
- Live: https://zyflo.vercel.app
- Stack: React, TypeScript, Framer Motion

### Orchastra

*AI product · Visual orchestration*

No-code canvas for multi-agent AI workflows. Swap between OpenAI, Claude, Grok, and Gemini. 140+ early adopters.

- Detail page: https://www.harjotrana.com/projects/Orchastra
- Live: https://www.orchastra.org
- Stack: Next.js, FastAPI, LangChain

### DevSynq

*Desktop tool · Developer experience*

Desktop app that syncs MCP servers, API keys, and settings across Cursor, Windsurf, and VS Code. 219+ developers.

- Detail page: https://www.harjotrana.com/projects/DevSynq
- Code: https://github.com/HarjjotSinghh/devsynq
- Live: https://www.devsynq.app
- Stack: Electron, React, TypeScript

## Archive

### Weavestore

A TypeScript-based database abstraction layer published on NPM that provides traditional database operations (CRUD) on top of the IPFS/IPNS decentralized storage network. Features hierarchical organization, type-safe API, and Better Auth adapter for decentralized authentication.

- Detail page: https://www.harjotrana.com/projects/Weavestore
- npm: https://www.npmjs.com/package/weavestore
- Stack: TypeScript, Bun, IPFS, IPNS, Lighthouse


---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Moonshift - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/Moonshift

> AI app builder that turns prompts into deployed full-stack applications. Multi-agent generation pipeline, SSE real-time observability, and production usage by 1,200+ users generating 800+ apps.

## Links

- Live: https://moonshift.io

## Case study

### Situation

Moonshift helps founders and developers turn a natural-language idea into a launched product on their own infrastructure. A successful run had to produce more than a demo: a real app, a GitHub repository, a Vercel deployment, and a launch kit.

### Constraint

Multiple agents and external side effects had to work together without hiding cost, reliability, or publishing decisions from the user. Deployment and social publishing needed explicit approval gates.

## Stack

TypeScript, Python, Next.js, PostgreSQL, Node.js, OpenAI, Docker

## What I built

- Rebuilt the multi-agent pipeline around research, planning, contracts, validation, and bounded fixer loops so generated code had a checkable path to production.
- Added live SSE run observability for phase progress, agent state, generated assets, deployment, and the human approval gate.
- Hardened the ship path with cost caps, resumable runs, and GitHub, Vercel, Turso, Playwright, and Docker integrations.

## Outcome

Raised end-to-end generation success from 46.2% to 76.7%, cut median prompt-to-live time from 22.1 to 14.9 minutes, and supported 1,200+ users generating 800+ apps. The run dashboard streamed 7.3M+ SSE events across 2,000+ runs.

- Role: Founding Engineer
- Timeline: April–July 2026
- Technical detail: Bun + TypeScript monorepo; Next.js dashboard; orchestrator-v2; SSE; Drizzle/Turso; Vercel; GitHub; Playwright; Docker.

Discuss a similar product: https://www.harjotrana.com/hire

## Key features

- Prompt-to-deploy full-stack app generation
- Multi-agent LLM orchestration pipeline
- Real-time SSE observability at multi-million event scale
- Production use: 1,200+ users, 800+ generated apps

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Reinstate - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/Reinstate

> Open-source Go CLI for coding-agent session continuity. Finds and resumes Claude Code and Codex sessions locally, and syncs them across macOS, Windows, Linux, and WSL2 through client-side age encryption and your own S3-compatible storage—with OS-aware path remapping so resume works after a machine switch.

## Links

- GitHub: https://github.com/HarjjotSinghh/reinstate
- Live: https://reinstate.dev

## Case study

### Situation

Reinstate addresses a specific gap in AI-assisted development: the code survives a device switch, but the agent's working context does not. A session started on one machine should be findable and resumable on another without leaving the agent's local storage behind.

### Constraint

Agent sessions are vendor-specific and contain absolute paths, sensitive files, and state that should not be copied blindly. Sync needed to preserve native resume while handling path remapping, credential exclusion, conflicts, backups, and offline use.

## Stack

Go, age, S3, GitHub Actions

## What I built

- Built Claude Code and Codex adapters that discover sessions, map them to projects, and restore them to the native locations those agents already understand.
- Added client-side age encryption and bring-your-own R2/S3 storage so the remote bucket stores ciphertext, not a Reinstate-held copy of the work.
- Hardened restore with OS-aware path remapping, credential denylisting, dry-run checks, atomic writes, backups, and conflict forks.

## Outcome

Passed 23/23 Mac–Windows acceptance tests for path remapping and conflict-safe restore. The result is an Apache-2.0 CLI that keeps the user's bucket, keys, and agent sessions under their control.

- Role: Creator and maintainer
- Timeline: July 2026 · ongoing
- Technical detail: Go; Cobra; age; S3-compatible storage; OS keyring; path mapping; atomic restore; GitHub Actions.

Discuss a systems build: https://www.harjotrana.com/hire

## Key features

- Configless local index and search across sessions, prompts, files, projects
- Native resume, last, and fork for Claude Code and Codex
- Encrypted push/pull via age and user-owned S3-compatible storage
- OS-aware path remapping so --resume works after switching machines
- Pre-launch checks for workspace, agent layout, and capabilities
- Interactive TTY switcher with deterministic JSON for automation
- Read-only discovery for Gemini CLI and OpenCode sessions
- Apache-2.0: no Reinstate account; your bucket, your keys

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# SignVault.io - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/SignVault.io

> SignVault.io was a multi-tenant SaaS platform for secure document management (operations shut down November 2025). Sophisticated tools for complex organizational structures, with a powerful ABAC engine and data provenance tracking.

## Links

- GitHub: https://github.com/SignVault

## Case study

### Situation

SignVault was a multi-tenant e-signature SaaS for organizations managing secure document workflows, complex team structures, and subscriptions.

### Constraint

The product had to keep tenant data and permissions isolated while coordinating document signing, billing, real-time collaboration, key management, and audit provenance across the API and customer UI.

## Stack

Next.js, Express.js, Tailwind CSS, ShadcnUI, Redis, PostgreSQL, Docker, Stripe, GraphQL

## What I built

- Owned the Express.js + Drizzle REST API and PostgreSQL data layer, including multi-tenant organization and workspace isolation.
- Built the ABAC permissions engine and provenance tracking so resource access and document history stayed explicit and auditable.
- Connected the Next.js workflows to Stripe billing, Socket.IO notifications, Azure Key Vault-backed PDF signing, and AWS S3/Redis infrastructure.

## Outcome

Delivered the core system path for a multi-tenant e-signature product: tenant and workspace isolation, fine-grained permissions, billing, document workflows, real-time collaboration, and auditable provenance. Operations concluded in November 2025.

- Role: Full-Stack Software Engineer
- Timeline: November 2024 to November 2025
- Technical detail: Next.js; Express.js; Drizzle; PostgreSQL; GraphQL; Stripe; Socket.IO; Azure Key Vault; AWS S3; Redis; Docker; Turborepo.

Discuss a similar SaaS: https://www.harjotrana.com/hire

## Key features

- Multi-tenant SaaS for secure document management
- ABAC engine for dynamic, fine-grained resource control
- Data provenance tracking for security and compliance
- PDF e-signatures with Azure Key Vault integration
- Real-time notifications and collaborative workflows
- Stripe subscriptions and billing management
- Responsive Next.js frontend with modern UI components
- Express.js API with Drizzle ORM and PostgreSQL
- Redis caching for hot paths and session performance
- Docker + Turborepo packaging for repeatable deploys
- GraphQL surface for efficient client data fetching
- Org structure tools for complex multi-team tenants

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Orchastra - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/Orchastra

> A no-code AI automation platform that enables users to build AI agents and workflows like building blocks. Features visual orchestration with drag-and-drop, multi-agent support, and integration with multiple AI providers including OpenAI, Claude, Grok, and Gemini.

## Links

- Live: https://www.orchastra.org/

## Stack

Next.js, FastAPI, Python, OpenAI, Claude, LangChain, Recharts, Tailwind CSS

## What I built

- Built a no-code AI automation platform for non-technical workflow authors.
- Shipped speech-to-workflow and text-to-workflow generation from prompts.
- Designed a drag-and-drop canvas for multi-agent orchestration.
- Unified OpenAI, Claude, Grok, and Gemini behind one provider API.
- Built a parallel execution engine for coordinated multi-agent runs.
- Added LangChain multi-agent collaboration via A2A protocol.
- Shipped a template library for common automation starters.
- Added real-time workflow monitoring and analytics with Recharts.
- Hardened runs with retries and structured error handling.
- Scaled the FastAPI backend for concurrent AI workloads.

## Key features

- No-code visual builder for AI workflows and automations
- Speech-to-workflow and text-to-workflow conversion
- Multi-agent orchestration with parallel execution
- OpenAI, Claude, Grok, and Gemini in one provider layer
- Drag-and-drop canvas for complex multi-step flows
- Pre-built templates for common automation tasks
- Real-time workflow monitoring and analytics
- Agent-to-Agent (A2A) multi-agent collaboration
- Retries and structured error handling on failed steps
- Async FastAPI backend for concurrent AI operations
- Built for educators, founders, and non-technical users
- 140+ early adopters and growing community

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# DevSynq - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/DevSynq

> A desktop application that synchronizes MCP (Model Context Protocol) server configurations, API keys, and project associations across multiple AI-powered IDEs including Cursor, Windsurf, and VS Code. Eliminates the need to manually configure each IDE separately.

## Links

- GitHub: https://github.com/HarjjotSinghh/devsynq
- Live: https://www.devsynq.app/

## Stack

Electron, React, Vite, PostgreSQL, TypeScript, Tailwind CSS

## What I built

- Built a cross-platform Electron + React desktop app for MCP config sync.
- Synced MCP server configs across Cursor, Windsurf, and VS Code.
- Shipped encrypted API key storage for sensitive IDE credentials.
- Added an IDE launcher with project association and config injection.
- Tracked running IDEs and MCP server health in real time.
- Stored configs locally and in the cloud via PostgreSQL + files.
- Designed a single UI to manage many MCP servers and profiles.
- Enabled cross-machine sync so IDE setups stay consistent.
- Added automatic backup and restore for configuration safety.
- Shipped settings for shortcuts, themes, and per-IDE preferences.

## Key features

- MCP config sync across Cursor, Windsurf, VS Code, and more
- Centralized API key management with encrypted storage
- One-click IDE launcher with automatic configuration
- Real-time process monitoring for running IDEs
- Cross-computer configuration synchronization
- Automatic backup and restore for config safety
- Support for 12+ MCP servers per IDE
- Secure credential storage with encryption
- Project-based configuration associations
- Native desktop app with OS integration
- Trusted by 219+ developers
- Removes manual per-IDE configuration work

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Weavestore - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/Weavestore

> A TypeScript-based database abstraction layer published on NPM that provides traditional database operations (CRUD) on top of the IPFS/IPNS decentralized storage network. Features hierarchical organization, type-safe API, and Better Auth adapter for decentralized authentication.

## Links

- npm: https://www.npmjs.com/package/weavestore

## Stack

TypeScript, Bun, IPFS, IPNS, Lighthouse

## What I built

- Built a TypeScript DB layer for CRUD on decentralized IPFS/IPNS storage.
- Modeled Database → Schema → Table hierarchy like a classic RDBMS.
- Shipped a type-safe API with full IntelliSense for consumer apps.
- Integrated Lighthouse IPFS/IPNS uploads with retry and failover.
- Supported configurable Snowflake and UUID ID generation strategies.
- Added LRU caching with configurable TTL and size limits.
- Implemented MongoDB-style query operators for filtering records.
- Built a Better Auth adapter for decentralized auth storage.
- Supported soft and hard deletes with Lighthouse plan features.
- Added structured error codes and automatic network retries.
- Shipped batch ops: insertMany, batchGetSchemas, batchGetTables.
- Published on npm with semver and full documentation (v2.2.1).

## Key features

- CRUD on IPFS/IPNS decentralized storage
- Hierarchical Database → Schema → Table model
- Type-safe TypeScript API with IntelliSense
- Lighthouse IPFS and IPNS service support
- Configurable Snowflake or UUID IDs
- Built-in LRU caching with automatic eviction
- MongoDB-style query operators for filters
- Automatic retry and failover for network faults
- Better Auth adapter for decentralized auth
- Soft and hard delete support
- Batch insert and batch read operations
- Built with Bun for runtime performance
- Published on npm (v2.2.1) with full docs
- Path-based access with dot and slash notation

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Zyflo - Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/projects/Zyflo

> Zyflo is an animated UI library that offers React components for building beautiful and accessible web applications. It emphasizes flow and continuity, allowing developers to create eye-catching, responsive experiences with ease.

## Links

- GitHub: https://github.com/HarjjotSinghh/zyflo
- Live: https://zyflo.vercel.app
- Demo: https://www.youtube.com/watch?v=ndB0QN0nQ08

## Stack

Next.js, React, TypeScript, Tailwind CSS, ShadcnUI, MDX, Framer Motion, Contentlayer

## What I built

- Built an animated React/Next.js UI library with interactive components.
- Shipped MDX + Contentlayer docs for usage, variants, and examples.
- Authored components: Navbar, Alert, Badge, Link Embed, Liquid Button, more.
- Designed accessible, responsive UI with Tailwind and custom primitives.
- Integrated Framer Motion for consistent, configurable motion.
- Started a CLI to scaffold Zyflo components into consumer projects.
- Added a config system for component variants and style tokens.
- Wrote per-component docs and live examples to speed adoption.

## Key features

- Animated, interactive React UI components
- Customizable components with pre-defined variants
- Responsive and accessible design defaults
- First-class Next.js and React integration
- Docs site with live component examples
- Dark mode support (in progress)
- Docs search (in progress)
- CLI for component scaffolding (in progress)

All projects: https://www.harjotrana.com/projects

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Writing by Harjot Singh Rana

Canonical URL: https://www.harjotrana.com/blog

> Technical notes on AI systems, developer tooling, full-stack architecture, and shipping production software.

RSS feed: https://www.harjotrana.com/feed.xml

## [How I Scored 100/100 on Is Agentic by Vercel](https://www.harjotrana.com/blog/scoring-100-on-is-agentic)

23 Aug 2026 · 21 min read

My portfolio started at 75 on Vercel's agent-readiness scanner. Two rounds later it scored 100. Here is every change that moved the number: Markdown content negotiation, a hand-rolled MCP server, RFC 9457 errors, RateLimit headers, an llms.txt that says when to use the site, and the one header Next.js would not let me set.

## [Locum: Giving Grok Bot a Stand-In](https://www.harjotrana.com/blog/locum-grok-bot-provider-adapter)

22 Aug 2026 · 20 min read

Grok Bot can burn a week of quota in an hour. Locum hands the work to the Claude Code or Codex CLI already logged in on your machine, through documented extension points, without ever touching a credential. Six releases in a day, including the one where the tool found five real bugs in its own code.

## [The Practical Vercel Turborepo Cheatsheet](https://www.harjotrana.com/blog/turborepo-cheatsheet)

08 Dec 2025 · 23 min read

Task graphs, remote caching, and the pipeline config that actually keeps a monorepo fast.

## [uv and Ruff: A Practical Python Tooling Cheatsheet](https://www.harjotrana.com/blog/uvx-and-ruff-cheatsheet)

08 Oct 2025 · 7 min read

Replacing pip, venv, black, and flake8 with two Rust binaries, and what changes day to day.

## [From Zero to npm in 7 Hours: Building Weavestore](https://www.harjotrana.com/blog/7-hours-to-mvp-weavestore)

05 Oct 2025 · 7 min read

How three of us took an on-chain database SDK from idea to a published npm package in one sitting.

## [Building Zyflo: An Accessible, Motion-First React Library](https://www.harjotrana.com/blog/building-zyflo-ui-library)

23 Sep 2025 · 6 min read

Why the existing component libraries kept failing me, and the constraints I built Zyflo around.

## [Next.js and MDX: A Practical Setup Guide](https://www.harjotrana.com/blog/getting-started-nextjs-mdx)

12 Sep 2025 · 2 min read

Wiring MDX into the App Router so posts stay Markdown but can still render React components.

## [Choosing a Stack for a Portfolio Site](https://www.harjotrana.com/blog/building-portfolio-website)

11 Sep 2025 · 3 min read

The tools worth reaching for when the site itself is the work sample, and the ones that aren't.


---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# How I Scored 100/100 on Is Agentic by Vercel

Canonical URL: https://www.harjotrana.com/blog/scoring-100-on-is-agentic
Author: Harjot Singh Rana
Published: 2026-08-23
Reading time: 21 min

> My portfolio started at 75 on Vercel's agent-readiness scanner. Two rounds later it scored 100. Here is every change that moved the number: Markdown content negotiation, a hand-rolled MCP server, RFC 9457 errors, RateLimit headers, an llms.txt that says when to use the site, and the one header Next.js would not let me set.

Vercel runs a scanner called [Is Agentic](https://is-agentic.com/scan/www.harjotrana.com). You give it a domain, an agent tries to use the site the way ChatGPT or Claude would, and you get a score out of 100 with a list of what broke. The checks are run by Ora; Is Agentic groups them and does the scoring.

My portfolio, [harjotrana.com](https://www.harjotrana.com), scored **75** on the first scan. Two rounds of work later it scored **100**. This post walks through every change that moved the number, the ones that did not, and the Next.js gotcha I lost an hour to.

The scanner's own task framing is the best explanation of why any of this matters. It asks an agent: *"What does www.harjotrana.com do and who is it for? Explain it back to me."* If that agent gets stuck, gets HTML it cannot parse, or gets a 200 for a page that does not exist, it quietly gives a worse answer about you. More and more first contact now happens through an agent. The site has to be legible to one.

*Diagram: Three scores on a timeline: 75 at the first scan, 89 after round one, 100 after round two. Round one added Markdown negotiation, an MCP server, llms.txt guidance, and trust pages. Round two added a JSON API, RFC 9457 errors, RateLimit headers, and typed OpenAPI schemas.*

*Two rounds, one evening. Each finding in the report maps to a concrete change below.*

## How the score works

Checks are split into **Essential** (shared 80-point pool) and **Recommended** (shared 20-point pool), with a small bonus for emerging signals capped at five. Checks that do not apply to your site are excluded rather than counted against you, and partial results earn proportional credit. A personal portfolio with no API has fewer eligible checks than a SaaS product; once you publish an API, the API checks switch on and you have to pass those too. My eligible-check count went from 17 to 31 between the first and second scan for exactly that reason.

The first report had seven findings. Three were Essential: agent-friendly 404s (partial), content without JavaScript (partial), and Markdown content negotiation (failed). Four were Recommended: developer resource discoverability, an agent instruction file, an MCP manifest, and trust anchor pages.

## Round one: 75 to 89

### Markdown for any page, chosen by the Accept header

The biggest single failure was the simplest to state. An agent sending `Accept: text/markdown` got HTML back, and the `Vary` header did not mention `Accept`. The convention the scanner checks is [acceptmarkdown.com](https://acceptmarkdown.com): same URL, two representations, picked by content negotiation.

The site is Next.js 16 on Vercel, so the negotiation lives in `proxy.ts` (the file Next 16 renamed from `middleware.ts`). It parses the `Accept` header properly, with q-values and specificity, then does one of three things.

*Diagram: A request enters proxy.ts, which parses the Accept header. Three outcomes: text/html wins and the normal page renders; text/markdown wins and the request is rewritten to the internal Markdown route; the header refuses both types and a 406 problem response is returned. All three branches set Vary: Accept.*

*One URL, two representations. The HTML page and the Markdown route are different cache keys on Vercel, so a cached HTML page can never be served to a Markdown client.*

The parsing detail that matters: the most specific media range wins regardless of q-value, so `text/html;q=0, */*` correctly rejects HTML even though the wildcard would accept it. Ties resolve toward HTML, so a browser's `text/html, */*;q=0.8` and a bare `*/*` both keep getting the page. Only a client that ranks `text/markdown` strictly above `text/html` gets Markdown.

The Markdown itself is not a scrape of the HTML. It is rendered from the same source the pages use: the project data module, the MDX files, and the shared copy for the hire page. Every document opens the same way, with an H1, a `Canonical URL:` line, and a blockquote lead, and closes with links to `llms.txt`, `llms-full.txt`, the sitemap, and `/developers`. An agent that lands on any one page can orient from it.

```bash
curl -s -H "Accept: text/markdown" https://www.harjotrana.com/hire | head -5
# # Work with Harjot Singh Rana - Product engineering for early-stage teams
#
# Canonical URL: https://www.harjotrana.com/hire
#
# > Bring me the idea. I'll help you ship the product.
```

### A 404 an agent can recover from

The site already returned real 404s, which is the part most app shells get wrong. The scanner wanted more: a short body telling the agent where to go next. Unknown paths requested with `Accept: text/markdown` now return a 404 with a Markdown body that lists the main sections, the sitemap, `llms.txt`, and the developer page. The HTML 404 got the same links.

### An llms.txt that says when to use the site

The site had an `llms.txt` before. The scanner failed it on one specific point: no *when-to-use* guidance. A summary of who you are is not the same as telling an agent which jobs you are the right answer for.

The fix was two sections. **When to use this site** names the jobs: finding a full-stack or AI product engineer for an early-stage product, adding an LLM or agent feature to an existing product, rescuing a fragile prototype, fractional ownership for a small team, verifying facts about me, reading the technical writing. It also has a **Not a fit** line, because an agent that knows what you do not do recommends you more precisely. **How agents should call this site** then lists the Markdown negotiation, the MCP server, the JSON API, and the rule that there is no write API, so contact goes through the human links.

### An MCP server in 300 lines, no SDK

The report said "MCP mentioned on site but no standard manifest endpoint found." The site had no MCP server at all; it had blog posts about MCP. So I built one.

It is a read-only [Model Context Protocol](https://modelcontextprotocol.io) server over Streamable HTTP at `POST /mcp`, written as a plain JSON-RPC dispatcher on top of Next.js route handlers. No SDK, no sessions, no auth, no side effects. Eight tools: `get_profile`, `get_services`, `list_projects`, `get_project`, `list_posts`, `get_post`, `get_contact_options`, and `search_site`. Three resources: `llms.txt`, `llms-full.txt`, and `openapi.json`.

The discovery side is what the scanner actually probes. The server card follows the SEP-2127 schema at the reserved location `<endpoint>/server-card`, so `https://www.harjotrana.com/mcp/server-card`, served as `application/mcp-server-card+json` with CORS headers, an `ETag`, and `304` support. It is mirrored at `/.well-known/mcp.json` with a few extra top-level hints (`url`, `transport`, `protocolVersion`, and an `mcpServers` snippet you can paste into a client config), and listed in `/.well-known/ai-catalog.json`.

```json
{
  "mcpServers": {
    "harjotrana": { "type": "http", "url": "https://www.harjotrana.com/mcp" }
  }
}
```

I verified the handshake with the official TypeScript SDK rather than trusting my own reading of the spec: `initialize`, `notifications/initialized`, `tools/list`, `tools/call`, protocol version `2025-11-25`, eight tools. The one transport I deliberately do not offer is the legacy HTTP+SSE one. It needs the server to hold a stream open in one request and receive messages in another, which does not work across serverless instances without a shared store. `GET /mcp` returns a 405 that explains this, which the Streamable HTTP spec explicitly allows.

### Trust pages and a page that can be found by name

Agents check `/about`, `/contact`, and `/privacy` before recommending a business. Only the privacy policy existed. I added the other two, each over 500 characters of real content, and a `/privacy` redirect for tools that guess the short path.

The "developer resource discoverability" check searches the web for your product name plus "developer resources". The fix is a page whose title and H1 carry the name: **harjotrana.com developer resources: API, MCP server, llms.txt**. It lists every machine-readable surface with the URL, the format, and what it is for.

## The header Next.js would not let me set

Here is the hour I lost. Markdown responses carried `Vary: Accept`. The HTML pages did not, and nothing I did changed that.

*Diagram: Two rows. Top row: for an HTML page, the proxy sets Vary: Accept, then the Next.js app-page handler calls res.setHeader('Vary') and overwrites it, and on Vercel function headers win over next.config headers, so the final response has no Accept in Vary. Bottom row: for a route handler such as the Markdown route, the handler sets its own Vary: Accept and it survives to the response.*

*Headers you set before the page renders do not survive the page render. Headers a route handler sets itself do.*

I tried the middleware response headers, a self-rewrite, and a `headers()` rule in `next.config`. Reading the compiled template finally explained it: Next 16's app-page handler calls `res.setHeader('Vary', ...)` unconditionally before rendering, so anything set earlier is replaced. On Vercel the `headers()` config did land on static files like the résumé PDF, but function responses win over config headers for the same key, so pages lost there too.

The honest outcome: the Markdown responses carry `Vary: Accept` because they are route handlers that set it themselves, and that is the response the scanner checks. The HTML pages cannot, and it is safe anyway because the Markdown variant lives at a different cache key and HTML is served `must-revalidate`. I wrote that down in the test file instead of pretending the header was there.

## Round two: 89 to 100

Publishing an OpenAPI document in round one made the API checks eligible, and most of them failed. The second report had nine findings. Two were Essential: the heading-structure check (still partial) and **JSON error responses** (failed). The rest were about the API: typed error model, rate-limit headers, schema coverage, function-calling compatibility, docs linked from the homepage, and an MCP handshake the scanner could not complete.

### One source, five surfaces

Before adding a JSON API I pulled the data access into one module, `lib/site-api.ts`, and made the MCP tools call it. The REST endpoints call the same functions. HTML, Markdown, `llms.txt`, MCP tools, and JSON now cannot disagree about a fact, because there is exactly one place a fact lives.

*Diagram: A single source box containing data.ts, config.tsx, content/blog, and the shared hire copy, with arrows fanning out to five surfaces: HTML pages, Markdown representations, llms.txt and llms-full.txt, the MCP tools, and the /api/v1 JSON endpoints.*

*Five surfaces, zero drift. The HTML stays canonical; everything else is derived from the same files at build time.*

The API is small and read-only: `/api/v1` (an index), `profile`, `services`, `projects`, `projects/{name}`, `posts`, `posts/{slug}`, `search?q=`, and `contact-options`. Every response is `application/json`, CORS-open, cacheable, and carries a `Link` header to the OpenAPI document and the docs page.

### Errors an agent can parse

The Essential failure in round two was the one I would have guessed last: "API does not return JSON error responses." Hitting `/api/anything-unknown` returned the site's HTML 404 page. Posting to a read-only endpoint returned Next's empty 405.

Every error is now an [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem-details object served as `application/problem+json`: `type`, `title`, `status`, `detail`, `instance`, plus two extension members, a stable `code` and a `hint` that says what to do next. A catch-all route under `/api` covers unknown paths. Read-only endpoints export 405 handlers with an `Allow` header. Even the 406 from content negotiation is a problem object now.

```json
{
  "type": "https://www.harjotrana.com/developers#error-not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "No endpoint at /api/nope.",
  "instance": "/api/nope",
  "code": "not_found",
  "hint": "The public endpoints are listed in https://www.harjotrana.com/openapi.json and described at https://www.harjotrana.com/developers."
}
```

The `type` URIs resolve to anchors on the developer page, one per error code, so an agent that follows the link gets the documented meaning. In the OpenAPI document, a single `Problem` schema is referenced from every 4xx and 5xx response.

### Rate-limit headers that follow the draft, with an honest caveat

The scanner wants the IETF RateLimit fields, and there is a trap here: the widely copied `X-RateLimit-Limit` / `RateLimit-Remaining` trio is not what the current draft says. [draft-ietf-httpapi-ratelimit-headers-11](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers) defines two structured fields:

```http
RateLimit-Policy: "default";q=120;w=60
RateLimit: "default";r=117;t=59
```

`q` is the quota, `w` the window in seconds, `r` what remains, `t` seconds until the window resets. A 429 is a problem object with `Retry-After`, which the draft says takes precedence.

The limiter itself is an in-process bucket per client address wrapped around the API, MCP, and discovery handlers. On serverless hosting that makes it per instance, not a single global cap, and the developer page says so in plain words. Advertising a live, accurate signal from one instance is more useful to an agent than advertising nothing.

### Typed schemas, not just schemas

The report's line "100% of operations define response schemas" next to "2/11 typed schemas" took a moment to decode. The checker counts an operation as typed only when its 200 response is `application/json` with an object schema. A `text/plain` document or a `+json` media type does not count.

Two changes fixed it. The discovery documents (`openapi.json`, the server card, the catalogs) now honor `Accept: application/json` and list both media types in the spec. And every JSON endpoint got a real object schema with named properties, required fields, and descriptions on every parameter. The result is 16 of 20 operations typed, against a 60% target, with unique `operationId`s and a description on each one, which is also what an LLM function-calling format wants.

### Headings and link tags on the homepage

Two small ones. The scanner kept describing the homepage heading structure as flat even though the raw HTML had an H1, eight H2s, and six H3s under the first section. Roles, education, recognition, and post titles were styled spans. They are now H3s with the same classes; with Tailwind's preflight resetting heading margins and sizes, the computed styles are byte-for-byte identical and the outline went from one section with children to five.

The homepage also did not link to `/developers`, so the docs-linked-from-homepage check was partial. A footer link fixed that, and the `<head>` now carries `rel="api-catalog"`, `rel="service-desc"` pointing at the OpenAPI document, `rel="service-doc"`, and a `rel="alternate" type="text/markdown"` link on every page.

## What the scanner actually probes

If you only read one section, read this one. Everything above reduces to a set of URLs and headers you can check with curl before you ever run the scan.

*Diagram: A grid of the surfaces the scanner probes, grouped in four columns: discovery files (llms.txt, sitemap.xml, robots.txt, .well-known/api-catalog), representations (Accept: text/markdown, .md suffix, 404 with Markdown body, Vary: Accept), APIs (openapi.json, /api/v1 JSON, problem+json errors, RateLimit headers), and MCP plus trust (POST /mcp, /mcp/server-card, .well-known/mcp.json, /about, /contact, /privacy).*

*The surfaces behind the score. Most of them are an afternoon each; the order above is roughly the order of payoff.*

The quick self-check, in the order I would do it:

```bash
D=https://www.harjotrana.com
curl -s -o /dev/null -w "%{http_code}\n" $D/some-path-that-does-not-exist   # must be 404
curl -sI -H "Accept: text/markdown" $D/ | grep -i "content-type\|vary"     # text/markdown + Vary: Accept
curl -s $D/llms.txt | grep -i "when to use"                                  # guidance, not a bio
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" $D/api/nope         # 404 application/problem+json
curl -sI $D/api/v1/profile | grep -i ratelimit                               # RateLimit + RateLimit-Policy
curl -s $D/mcp/server-card | head -3                                         # the MCP server card
```

## What still reads partial, and why I left it

Honesty about the last few points matters more than the round number, so here is what the 100 still carries.

**The heading-structure check** still reports "5446 chars with H1 but flat heading structure", with the same character count as the very first scan, before the footer changed. It is reading cached evidence. The live page has an H1, eight H2s, and eighteen H3s.

**Name-based search** for "harjotrana developer resources" finds nothing because search engines have not indexed the new page yet. Nothing on the site can change that faster than time does.

**Typed error model** and **function-calling compatibility** still show partial because the checker does not follow `$ref`. Every operation references the shared `Problem` schema and a typed object; inlining them would raise the number without improving the API, so I did not.

**A deprecation policy** is documented with the exact headers it will use (`Deprecation` from RFC 9745, `Sunset` from RFC 8594), but the checker wants to see one on a live response. Nothing is deprecated, so there is nothing honest to emit.

**A CLI tool** would be a product, not a fix.

## The part that generalises

The score is a side effect. The actual change is that the site now has one data layer and five faithful projections of it, and every one of those projections tells an agent where the others are. That is the property worth copying: not the endpoints, the fact that none of them can drift.

The whole thing took two evenings, with 65 unit tests and 37 black-box HTTP tests against the live domain to keep it honest. If you run the scan on your own site and want to compare notes, the [developer page](https://www.harjotrana.com/developers) documents every surface described here, and the contact options are one `curl https://www.harjotrana.com/api/v1/contact-options` away.

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Locum: Giving Grok Bot a Stand-In

Canonical URL: https://www.harjotrana.com/blog/locum-grok-bot-provider-adapter
Author: Harjot Singh Rana
Published: 2026-08-22
Reading time: 20 min

> Grok Bot can burn a week of quota in an hour. Locum hands the work to the Claude Code or Codex CLI already logged in on your machine, through documented extension points, without ever touching a credential. Six releases in a day, including the one where the tool found five real bugs in its own code.

I burned 20% of a weekly Grok Bot allowance in thirty minutes. Seven or eight bots running in parallel, all doing real work, all metering hard. The product is genuinely good; that was the frustrating part. The bottleneck wasn't capability. It was inference budget.

I already pay for Claude and for ChatGPT. Those subscriptions have their own limits, and they were sitting completely idle while Grok Bot ground through its quota.

So I built **Locum**, a provider adapter that lets Grok Bot delegate coding work to the agent CLIs already authenticated on my own machine. A locum is a qualified professional who temporarily does someone else's job, which is exactly the arrangement.

This post is about how it works, and more importantly about the one architectural decision that separates it from the version of this idea that gets you banned.

## The constraint that decides everything

Grok Bot doesn't run on your laptop. Each bot gets a persistent computer in xAI's cloud, which is what makes it useful: it keeps working after you close the lid. It also means the bot **cannot see `localhost`**. There is no local plugin surface to hook into.

What it does have is documented: custom MCP connectors. You point it at a public HTTPS endpoint speaking the Model Context Protocol, it discovers your tools, and it can call them. xAI even publishes a guide for tunnelling a server running on your own machine.

That single fact fixes the shape of the whole thing. Locum has to be a remote MCP server that reaches back to your machine, not a patched binary and not an intercepted protocol.

## The line I would not cross

There are two ways to build "use your Claude subscription somewhere else," and they are not close to each other.

The tempting one: read the OAuth token that Claude Code stores, and call Anthropic's API yourself with it. It works. It is also explicitly prohibited. Anthropic's own [Claude Code legal page](https://code.claude.com/docs/en/legal-and-compliance) says OAuth is "intended exclusively" for ordinary use of Claude Code and other native Anthropic applications, and that they do not permit third-party developers to route requests through Free, Pro, or Max plan credentials on behalf of their users. People have shipped tools on the other side of that line and watched them get shut off.

The other way: don't handle credentials at all. Spawn the official binary.

*Diagram: Locum spawns the official claude binary as a subprocess. Only that binary talks to Anthropic, using its own stored credentials. Locum itself has no path to the vendor API.*

*The credential boundary. Locum is a launcher, not a proxy.*

Locum runs `claude -p "<prompt>"` as a subprocess. That's it. The CLI you already logged into does the authenticating, exactly as it does when you type the command yourself. Locum never opens `~/.claude`, never touches the keychain, never sees a token, and never makes a request to `api.anthropic.com`.

Four invariants are written at the top of the source file, and they're the reason the project is publishable:

1. Never read credential files, keychains, or OAuth tokens.
2. Never call `api.anthropic.com` or `api.openai.com` directly.
3. Only ever spawn the official `claude` / `codex` binaries.
4. Single-operator. One token, one allowlist of workspace roots.

One more thing it deliberately does **not** do: bypass Grok Bot's own entitlement. You still need Grok Bot access for a connector to exist at all. Locum changes where the inference comes from. It does not get you Grok Bot for free, and I'd have no interest in shipping the version that did.

## The shape of it

*Diagram: Grok Bot and the Cloudflare tunnel sit above a dashed boundary marking xAI's cloud. Below the boundary, on your own machine, Locum receives the tunnelled request and spawns either the Claude Code CLI or the Codex CLI.*

*Six tools cross that boundary: delegate_to_claude, resume_claude, delegate_to_codex, check_job, list_jobs, cancel_job.*

About a thousand lines of Python across two files, no framework beyond an MCP server library.

## Async is the entire trick

This is the part that decides whether the integration works at all, and it's easy to get wrong.

MCP tool calls are request/response with a timeout measured in tens of seconds. Real coding tasks take minutes. If `delegate_to_claude` tried to run the task and return the answer, every useful call would time out, and worse, it would time out *after* burning the work, with the result stranded in a dead subprocess.

*Diagram: A synchronous tool call is cut off by the MCP timeout while the coding task is still running, so the result is discarded. The asynchronous version returns a job id immediately and the bot polls until the job reports done.*

*Build this in from the start. Retrofitting it means rewriting the server.*

So `delegate_to_claude` starts the job and returns a handle immediately. The bot polls `check_job` until the status changes.

The polling is more useful than it sounds. Locum runs the CLI with `--output-format stream-json` and parses the event stream as it arrives, so a poll returns not just "still running" but the turn count and the last few tool calls:

```json
{
  "job_id": "dacb5836aa66",
  "status": "running",
  "elapsed_seconds": 74.2,
  "turns": 9,
  "recent_activity": ["Read src/auth.ts", "Edit src/auth.ts", "Bash npm test"]
}
```

The bot can narrate real progress instead of staring at a black box. When the job finishes, the result comes back along with a `session_id`, which matters, because `resume_claude` reuses it. A cold delegation re-pays about 18k tokens of `CLAUDE.md` and system prompt setup; resuming hits the prompt cache instead. Follow-ups should always resume.

## Grok speaks OAuth, and only OAuth

I built the first version with a bearer token. Simple, adequate for a single-operator tool, easy to test with `curl`.

Then I opened Grok's custom connector dialog and found no field for a header. It had probed my server, got a bare `401`, and fallen back to asking me for OAuth app credentials: client ID, authorization endpoint, token endpoint, PKCE method.

That's the MCP specification's authorization flow, and it isn't optional. A remote MCP server that wants a real client has to be an OAuth 2.1 authorization server.

So Locum became one. It's less code than it sounds:

*Diagram: OAuth sequence: Grok probes the MCP endpoint and receives a 401 pointing at discovery metadata, registers itself dynamically, sends the operator to a consent screen gated by a passphrase, exchanges the code with PKCE for a token, and then calls tools.*

*Discovery, dynamic registration, PKCE, refresh. No third-party OAuth app to create.*

The consent screen is the load-bearing part, and it's worth being explicit about why. `/authorize` sits on a public tunnel. Without a gate, anyone who learned the URL could walk through the flow, mint themselves a token, and get shell access to my machine. So the consent page demands a passphrase and shows the redirect target before you approve. If that destination isn't the one you expect, you're looking at someone else's authorization attempt.

Codes are single-use and expire in 120 seconds. `S256` is required; `plain` is refused. Every comparison is constant-time. There's a test file that exercises all of it: discovery, registration, the consent gate, PKCE enforcement, replay rejection, refresh. Security code that isn't tested is decoration.

## Three bugs worth your time

The architecture took an afternoon. These took longer.

### The 404 that was a port collision

Everything started returning `404`. Every path, including `/health`, which is an unconditional route. The tunnel reported healthy, ingress validated, the routing rule matched.

The response was `content-length: 10`. Exactly the length of `Not found.`, and that string wasn't in my server.

*Diagram: Two processes listen on port 8787: Locum on IPv4 127.0.0.1 and the Grok Bot desktop app on IPv6 colon-colon-1. macOS resolves localhost to the IPv6 address first, so traffic reaches the wrong process.*

*The Grok Bot app itself listens on port 8787. macOS prefers ::1, so the tunnel talked to it instead.*

The Grok Bot desktop app listens on `[::1]:8787`. Locum was on `127.0.0.1:8787`. Same port, different address families, so there was no "address already in use" error, no warning, nothing. macOS resolves `localhost` to `::1` before `127.0.0.1`, so every tunnelled request reached the wrong process.

Two things found it. `lsof -nP -iTCP:8787 -sTCP:LISTEN` showed both listeners, one line each. And `cloudflared --loglevel debug` logs `ingressRule=` and `originService=` per request, which separates "cloudflared's catch-all matched" from "the origin returned this". I'd assumed the former and was wrong.

Two fixes, because either alone leaves a trap: move to port 8791, and target `127.0.0.1` explicitly rather than `localhost`.

### The service daemon that installed itself broken

`sudo cloudflared service install` writes a launch daemon whose `ProgramArguments` is just the binary path. No `tunnel run`. No `--config`. It runs as root, whose `$HOME` is `/var/root`, so `~/.cloudflared/config.yml` is invisible to it, and it crash-loops on a 5-second `KeepAlive`.

The failure hides itself perfectly: the tunnel keeps working, because your user-level `cloudflared tunnel run` is still serving. You end up with a crash-looping root daemon and a working user process registered as two connectors for one tunnel. Adding a connector while that's in flux is how you get "Connection failed" from a server that answers every external probe.

The fix is to put the config where root can read it, `/etc/cloudflared`, and rewrite `credentials-file` to match.

### The nested-session auth failure

Every delegated job died with `Failed to authenticate: OAuth session expired and could not be refreshed`.

Claude Code exports session plumbing into every child process it spawns: `CLAUDECODE`, a family of `CLAUDE_CODE_*` variables, and `ANTHROPIC_BASE_URL`. A `claude` that inherits those believes it's a nested child session and tries to delegate authentication to a host socket that isn't listening.

Locum now strips them before spawning, but only when it detects it's nested, so a deliberately-set `ANTHROPIC_BASE_URL` still works in a normal terminal.

The lesson generalises past this project: when you spawn a CLI from a server, you inherit an environment you did not design. Decide what crosses that boundary.

## Four things I only learned by using it

The version above worked. Using it for a day surfaced the rest.

### An approval prompt is not a safety feature, it is a hang

Jobs involving `gh`, or an install, or anything else the agent wanted to confirm
would simply stop. Not fail: stop. The client sat waiting on a job that would
never finish, because the approval prompt was on my Mac and the thing that asked
for it was a bot in someone else's cloud.

A prompt nobody can answer does not protect you. It just converts a task into a
timeout. Delegated jobs now run with `--dangerously-skip-permissions` and
`--dangerously-bypass-approvals-and-sandbox`, and the honest consequence is that
the token and a narrow workspace allowlist are what protect you, not the agent's
permission model. The allowlist bounds where a job starts, not where a shell
command it runs can reach. That trade is written down in `SECURITY.md` rather
than glossed, because the previous version of that file said the opposite.

### The model and the effort should be a decision, not a default

Some delegations want the cheapest model that can follow instructions. Some want
the deepest reasoning available, once. So every delegate tool takes `model` and
`effort`, under one vocabulary across both CLIs: Claude spells it `--effort`,
Codex spells it `-c model_reasoning_effort` and requires that `-c` precede the
subcommand. A caller should not have to know either fact.

The interesting part is `resume_claude` taking them too. Escalating a *resumed*
session keeps the context you already paid for, so "start cheap, escalate on the
turn that actually needs it" costs far less than restarting at a higher setting.

### Correct answers hide broken telemetry

Codex delegations returned the right results, so it looked fine. It was not.
The progress parser was reading an event stream Codex no longer emits, so turn
counts sat at zero and the activity feed showed the literal string
`item.completed` instead of what the agent was doing.

Worse, Codex can fail a turn and still exit 0. Those jobs were reported as
`done` with whatever happened to be left in the output file. Silently wrong
results are worse than visible failures, and only testing the thing I had not
launched with caught it.

### You cannot demo a claim you cannot see

The MCP client shows a chat bubble. That is all. There was no way to show that
work ran on my machine, what the agent did, or what it cost, short of grepping a
log and matching line numbers against the real file.

So the server narrates every job on stdout, and there is a dashboard: every
session with model, effort, turns, duration, tokens and cost, and a full
transcript per job of each tool call with its arguments, the reasoning, and the
output. Running jobs stream in live.

*Diagram: Three consumers read one recorded activity stream: the MCP client's check_job response, the dashboard transcript, and the server's stdout log.*

*All three read one recorded stream, so they cannot drift apart.*

Then the dashboard hung at "connecting" behind Cloudflare while working
perfectly on localhost. The stream sent its response headers and waited up to
twenty seconds for the first event; a proxy holds headers until some body
arrives, so the browser never saw a response at all and `EventSource.onopen`
never fired. Flushing a single comment immediately fixed it. Time to first byte
through the tunnel went from never to 0.4 seconds.

That is the third bug in this project that only existed behind the proxy, after
the port collision and the OAuth callback. Anything streaming or long-lived has
to be tested through the tunnel, because a proxy changes the semantics, not just
the latency.

## The tool found its own bugs

Once the dashboard existed, the obvious thing to try was pointing Locum at its
own newest code. I delegated a review of the Codex event parser, written about
an hour earlier, at `model: opus, effort: high`.

It came back with five findings. All five were real.

The dangerous one: the parser called `.get()` on whatever `json.loads` returned.
A bare string, a number, a list are all valid JSON, so a line like `"error"`
raised `AttributeError`, which killed the stdout reader. The runner then skipped
both `proc.wait()` and its cleanup step, leaving the Codex child unreaped and
its temp file on disk. On a server designed to run for weeks under launchd.

I reproduced all five before fixing them. The subtlest was that a mid-stream
error flipped a job out of `running` immediately, which broke cancellation, let
the history pruner evict a job that was still executing, and masked a later
success.

Later, during the demo recording, a delegated job added a `--version` flag to
the server and ran the test suite to confirm nothing broke. Its summary of what
it changed matched the diff. It did put the check at the top of `__main__`,
which is after the module-level token guard has already exited, so
`server.py --version` refused to answer without a secret. That one I fixed by
hand.

Both of those are the honest version of what this tool is for: it is very good,
it is not unsupervised, and the difference matters.

## What it actually saves

Honesty matters more than the pitch here, so:

**It reduces Grok Bot usage. It does not eliminate it.** The bot's own orchestration turns still meter. The win is collapsing fifty bot steps into one tool call and a few polls, moving the long agentic grind onto the subscription you already pay for.

**Your machine has to be awake with the tunnel up.** It's a personal tool, not a service.

**Those subscriptions have limits too.** This changes where the inference budget comes from. Nobody found an infinite-token glitch.

**The skill file matters more than the tools.** Without an instruction telling the bot to *prefer* delegating, it has the tools available and keeps grinding through its own loop anyway. That one markdown file is the difference between the integration working and merely existing.

## Running it

It's open source. You need `claude` or `codex` already signed in, a domain on Cloudflare for a stable hostname, and Grok Bot access.

```bash
git clone https://github.com/HarjjotSinghh/locum
cd locum && cp .env.example .env    # set a token and your workspace roots
set -a && source .env && set +a
uv run server.py
```

Then a tunnel, a custom connector pointed at `https://your-host/mcp`, and the skill file pasted into a Grok Bot Skill.

The invariants at the top of `server.py` aren't decoration. If a change would break one of them, it's the wrong change, and that is what keeps this a tool worth using rather than a liability worth avoiding.

*Not affiliated with or endorsed by xAI, Anysphere, OpenAI, or Anthropic.*

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# The Practical Vercel Turborepo Cheatsheet

Canonical URL: https://www.harjotrana.com/blog/turborepo-cheatsheet
Author: Harjot Singh Rana
Published: 2025-12-08
Reading time: 23 min

> Task graphs, remote caching, and the pipeline config that actually keeps a monorepo fast.

**Turborepo** is a build system optimized for JavaScript and TypeScript monorepos, written in Rust. It handles task orchestration, caching, and dependency management across multiple packages in a single repository.

---

## Core Concepts

### Monorepo Structure

```text
my-monorepo/
├── apps/
│   ├── web/
│   ├── mobile/
│   └── admin/
├── packages/
│   ├── ui-library/
│   ├── utils/
│   └── types/
├── turbo.json (configuration)
├── package.json (root workspace)
└── pnpm-workspace.yaml (or package.json for npm/yarn workspaces)
```

### Key Concepts

- **Package Graph**: Relationship between all packages in the monorepo
- **Task Graph**: Dependency relationships between tasks
- **Caching**: Turborepo remembers task outputs and reuses them when inputs haven't changed
- **Remote Caching**: Share cache artifacts across team members and CI/CD systems

---

## Installation & Setup

### Initialize a New Turborepo

```bash
npx create-turbo@latest
```

### Add Turborepo to Existing Project

```bash
npm install turbo --save-dev

# Initialize configuration
turbo init
```

---

## turbo.json Configuration

### Basic Structure

```json
{
  "extends": ["//"],
  "globalDependencies": ["*.env", "package.json"],
  "globalEnv": ["NODE_ENV"],
  "globalPassThroughEnv": ["HOME", "PATH"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"],
      "cache": true,
      "env": ["NODE_ENV", "API_*"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true,
      "interactive": true
    },
    "lint": {
      "outputs": []
    }
  }
}
```

### Task Definition Options

| Option | Description | Example |
|--------|-------------|---------|
| `dependsOn` | Tasks that must complete first | `["^build"]`, `["build", "lint"]` |
| `outputs` | Files to cache | `["dist/**", ".next/**"]` |
| `cache` | Enable/disable caching | `true` or `false` |
| `env` | Environment variables that affect hashing | `["NODE_ENV", "API_*"]` |
| `passThroughEnv` | Environment variables available but not cached | `["HOME"]` |
| `inputs` | Files to consider for cache invalidation | `["src/**", "package.json"]` |
| `persistent` | Mark as long-running (dev servers) | `true` |
| `interactive` | Accept stdin input | `true` |
| `interruptible` | Allow restart by turbo watch | `true` |
| `outputLogs` | Log verbosity | `"full"`, `"hash-only"`, `"new-only"`, `"errors-only"`, `"none"` |

### Dependency Prefix Symbols

| Symbol | Meaning | Example |
|--------|---------|---------|
| `^` | Depends on same task in dependencies | `"^build"` (wait for deps to build) |
| No prefix | Same-package dependency | `"build"` (in test task = test after build in same pkg) |
| `package#task` | Specific package task | `"utils#build"` (utils package build task) |

---

## Common turbo.json Patterns

### Pattern 1: Build with Dependencies

```json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    }
  }
}
```

Builds dependencies first, then this package.

### Pattern 2: Test Before Build

```json
{
  "pipeline": {
    "build": {
      "dependsOn": ["test", "^build"],
      "outputs": ["dist/**"]
    }
  }
}
```

Runs local tests, then dependency builds, then this package build.

### Pattern 3: Cache-Only Tasks

```json
{
  "pipeline": {
    "test": {
      "cache": true,
      "outputs": ["coverage/**"]
    }
  }
}
```

Tests are cached; only re-run on source code changes.

### Pattern 4: Never Cache

```json
{
  "pipeline": {
    "deploy": {
      "cache": false
    }
  }
}
```

Useful for deployment and external API calls.

### Pattern 5: Long-Running Tasks (Dev Server)

```json
{
  "pipeline": {
    "dev": {
      "cache": false,
      "persistent": true,
      "interactive": true
    }
  }
}
```

Dev servers run continuously and accept input.

---

## CLI Commands

### Run Tasks

```bash
# Run a single task in all packages
turbo run build

# Run multiple tasks
turbo run build lint test

# Run task in specific package
turbo run build --filter=ui

# Run without dependencies (immediate task only)
turbo run test --only

# Run only affected packages (git-based)
turbo run build --affected

# Run with all environment variables available
turbo run build --env-mode=loose

# Run in parallel (ignoring dependency graph)
turbo run dev --parallel

# Force re-run (ignore cache)
turbo run build --force

# Run with verbose output
turbo run build -v
turbo run build -vv   # more verbose
turbo run build -vvv  # very verbose
```

### Filtering

```bash
# By package name
turbo run build --filter=web

# By directory pattern
turbo run build --filter=./apps/*

# By git changes
turbo run build --filter=[HEAD^1]

# Select dependents (packages depending on target)
turbo run build --filter=...ui

# Select dependencies (packages target depends on)
turbo run build --filter=ui...

# Exclude packages
turbo run build --filter=!admin

# Combine filters (union)
turbo run build --filter=web --filter=mobile

# Specific task in package
turbo run web#build
```

### Cache Management

```bash
# Disable caching
turbo run build --no-cache

# Use only local cache
turbo run build --cache=local:rw

# Use only remote cache
turbo run build --cache=remote:rw

# Read-only cache
turbo run build --cache=local:r

# Disable cache completely
turbo run build --cache=local:,remote:
```

### Other Useful Commands

```bash
# Dry run (show what would execute, don't run)
turbo run build --dry-run

# Dry run as JSON
turbo run build --dry=json

# Show task execution order without running
turbo run build --graph=mermaid

# Generate performance trace (Chrome Tracing format)
turbo run build --profile -vv

# Generate run summary (metadata about execution)
turbo run build --summarize

# Continue on error
turbo run build --continue=always

# Limit concurrency
turbo run build --concurrency=4
turbo run build --concurrency=50%

# Watch mode (watch for changes and re-run)
turbo watch run build
```

### Remote Caching (Vercel)

```bash
# Login to Vercel
turbo login

# Link to remote cache
turbo link

# Link specific project in package
cd apps/web && turbo link

# Logout
turbo logout

# Verify remote cache is working
turbo run build --summarize
```

### Utility Commands

```bash
# List all packages in workspace
turbo ls

# Check workspace info
turbo info

# Check package boundaries/dependencies
turbo boundaries

# Generate new package/code
turbo gen

# Prune dependency graph (for Docker/CI)
turbo prune --scope=web

# Check for missing environment variables
turbo scan

# Get Turborepo version
turbo --version
```

---

## Environment Variables

### Global Environment Variables (turbo.json)

```json
{
  "globalEnv": ["NODE_ENV", "API_KEY"],
  "globalPassThroughEnv": ["HOME", "PATH"]
}
```

- `globalEnv`: Changes invalidate ALL task caches
- `globalPassThroughEnv`: Available to tasks but don't affect caching

### Task-Specific Environment Variables

```json
{
  "pipeline": {
    "build": {
      "env": ["NODE_ENV", "NEXT_PUBLIC_*"],
      "passThroughEnv": ["HOME"]
    }
  }
}
```

### Environment Variable Patterns

```json
{
  "env": [
    "*",                 // All variables
    "API_*",            // Prefix match
    "!API_SECRET",      // Negation (exclude)
    "NODE_ENV"          // Exact match
  ]
}
```

### Strict vs Loose Mode

```bash
# Strict mode (default) - only listed vars available
turbo run build --env-mode=strict

# Loose mode - all vars available (less safe for caching)
turbo run build --env-mode=loose
```

---

## Package Configurations

Use for task customization per-package:

```json
// packages/ui/turbo.json
{
  "extends": ["//"],
  "pipeline": {
    "build": {
      "outputs": ["dist/**"],
      "env": ["THEME_*"]
    }
  }
}
```

---

## Common Patterns & Best Practices

### 1. Workspace Dependencies

```json
// apps/web/package.json
{
  "dependencies": {
    "@repo/ui": "workspace:*",
    "@repo/utils": "workspace:*"
  }
}
```

### 2. Shared Configuration

```json
// Root turbo.json
{
  "globalDependencies": [
    "package.json",
    "pnpm-lock.yaml",
    ".env.example"
  ]
}
```

### 3. CI/CD Optimization

```bash
# In GitHub Actions
turbo run build --affected --cache=remote:rw

# In other CI systems
turbo run build --force --cache=local:rw
```

### 4. Docker/Monorepo Pruning

```bash
# Prune only dependencies needed for `web` app
turbo prune --scope=web --docker

# Then in Dockerfile
COPY --from=base /repo/out/full /repo
COPY --from=base /repo/out/json . 
COPY --from=base /repo/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN pnpm install --frozen-lockfile
```

### 5. Task Skipping (turbo-ignore)

```bash
# In CI environment variable
TURBO_TELEMETRY_DISABLED=1

# Skip build if only docs changed
turbo-ignore docs
```

---

## Caching Strategy

### What Gets Cached

- **Outputs**: Files specified in `outputs` key
- **Logs**: Task logs (always cached if caching enabled)
- **Metadata**: Task execution metadata

### Cache Invalidation

Cache is invalidated when:

- Source files in `inputs` change
- `globalDependencies` files change
- Environment variables in `env` change
- `turbo.json` changes
- `package.json` changes (in that package)
- Lockfile changes

### Cache Locations

```bash
# Local cache (default)
.turbo/cache/

# Custom cache directory
turbo run build --cache-dir=.turbo-cache

# Or in turbo.json
{
  "cacheDir": ".turbo-cache"
}
```

---

## Debugging & Troubleshooting

### Check Task Configuration

```bash
# See what tasks will execute
turbo run build --dry-run

# Get detailed execution info
turbo run build --dry=json | jq

# Verbose output
turbo run build -vvv
```

### Analyze Performance

```bash
# Generate Chrome trace
turbo run build --profile -vv

# Check hash and dependencies
turbo run build --dry-run | grep -A5 "web#build"

# See execution summary
turbo run build --summarize
# View in `.turbo/runs/` directory
```

### Common Issues

| Issue | Solution |
|-------|----------|
| Cache not working | Check `outputs` glob pattern; verify env vars in `env` key |
| Task not running | Check `dependsOn`; use `--dry-run` to verify task graph |
| Environment vars not available | Add to `env` in turbo.json for hashing, `passThroughEnv` for availability |
| Performance degradation | Check if inputs are too broad; optimize glob patterns |
| Package not found | Verify `package.json` name; use `turbo ls` to list packages |

---

## Advanced Tips

### 1. Framework Inference

Turborepo automatically includes framework-specific env vars:

- Next.js: `NEXT_PUBLIC_*` (automatic)
- Nuxt: `NUXT_*` (automatic)
- Vue: `VUE_APP_*` (automatic)

### 2. Multiple Package Managers

```json
{
  "packageManager": "pnpm@8.0.0"
}
```

### 3. Workspace Configuration

```json
// pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'tools/*'
```

```json
// package.json (npm/yarn)
{
  "workspaces": ["apps/*", "packages/*"]
}
```

### 4. Remote Cache with Custom Endpoint

```json
{
  "remoteCache": {
    "apiUrl": "https://custom-cache.com",
    "signature": true,
    "timeout": 60,
    "uploadTimeout": 120
  }
}
```

### 5. Concurrency Control

```bash
# Serial execution (1 at a time)
turbo run build --concurrency=1

# Use 50% of CPU cores
turbo run build --concurrency=50%

# Or in turbo.json
{
  "concurrency": "50%"
}
```

---

## Real-World Example

```json
{
  "extends": ["//"],
  "globalDependencies": ["*.env", "package.json"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"],
      "cache": true,
      "env": ["NODE_ENV", "API_URL"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "lint": {
      "cache": true,
      "outputs": []
    },
    "type-check": {
      "cache": true,
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true,
      "interactive": true
    },
    "deploy": {
      "cache": false,
      "dependsOn": ["build", "test", "lint"]
    }
  }
}
```

### Usage:

```bash
# Full CI pipeline
turbo run lint type-check test build

# Development
turbo run dev --parallel

# Deploy only changed packages
turbo run build --affected && turbo run deploy

# Rebuild everything
turbo run build --force
```

---

## Resources

- **Official Docs**: https://turborepo.com
- **API Reference**: https://turborepo.com/docs/reference
- **Examples**: https://github.com/vercel/turborepo/tree/main/examples
- **Vercel Remote Cache**: https://vercel.com/docs/monorepos/turborepo

---

## Quick Command Reference

```bash
# Development
turbo run dev --parallel

# Build
turbo run build

# Lint
turbo run lint

# Test
turbo run test

# All checks before commit
turbo run lint type-check test

# Force rebuild everything
turbo run build --force

# Only affected packages
turbo run build --affected

# Specific package
turbo run build --filter=web

# Watch mode
turbo watch run build

# See what would run
turbo run build --dry-run

# Performance analysis
turbo run build --profile -vv

# Remote cache
turbo login
turbo link
turbo run build
```

_Last Updated: December 2025 | Based on Turborepo latest documentation_

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# uv and Ruff: A Practical Python Tooling Cheatsheet

Canonical URL: https://www.harjotrana.com/blog/uvx-and-ruff-cheatsheet
Author: Harjot Singh Rana
Published: 2025-10-08
Reading time: 7 min

> Replacing pip, venv, black, and flake8 with two Rust binaries, and what changes day to day.

Quick reference for common UV and Ruff commands.

## UV Commands

### Environment Management

```bash
# Create virtual environment
uv venv

# Activate environment
source .venv/bin/activate  # macOS/Linux
.venv\Scripts\activate     # Windows

# Create with specific Python version
uv venv --python 3.11
```

### Package Management

```bash
# Install from pyproject.toml
uv pip install -e .
uv pip install -e ".[dev]"  # Include dev dependencies

# Install specific package
uv pip install fastapi
uv pip install "fastapi>=0.109.0"

# Install from requirements.txt
uv pip install -r requirements.txt

# Uninstall package
uv pip uninstall fastapi

# List installed packages
uv pip list

# Show package info
uv pip show fastapi
```

### Running Commands

```bash
# Run Python script
uv run python script.py

# Run module
uv run -m pytest

# Run with specific Python version
uv run --python 3.11 python script.py
```

### Cache Management

```bash
# Clear cache
uv cache clean

# Show cache directory
uv cache dir
```

## Ruff Commands

### Linting

```bash
# Check all files
uv run ruff check .

# Check specific files/directories
uv run ruff check app/ tests/

# Auto-fix issues
uv run ruff check --fix .

# Show all issues (including fixed)
uv run ruff check --show-fixes .

# Watch mode (re-check on file changes)
uv run ruff check --watch .
```

### Formatting

```bash
# Format all files
uv run ruff format .

# Format specific files
uv run ruff format app/main.py

# Check formatting without changing files
uv run ruff format --check .

# Show diff of what would change
uv run ruff format --diff .
```

### Configuration

```bash
# Show current configuration
uv run ruff config

# Show rule documentation
uv run ruff rule E501

# List all available rules
uv run ruff linter
```

## Common Workflows

### Initial Setup

```bash
cd auto-mt
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
```

### Daily Development

```bash
# Format and fix issues before committing
uv run ruff format .
uv run ruff check --fix .

# Run tests
uv run pytest

# Start dev server
uv run uvicorn app.main:app --reload
```

### Pre-Commit Check

```bash
# Check everything
uv run ruff check .
uv run ruff format --check .
uv run pytest
```

### Adding Dependencies

```bash
# Add to pyproject.toml dependencies list, then:
uv pip install -e ".[dev]"

# Or install directly (but remember to update pyproject.toml!)
uv pip install new-package
```

## VS Code Integration

With the Ruff extension installed and `.vscode/settings.json` configured:

- **Auto-format on save**: Enabled
- **Auto-fix on save**: Enabled
- **Import sorting on save**: Enabled
- **Ruler at 100 chars**: Visible guide

### Keyboard Shortcuts

- `Cmd/Ctrl + S`: Save and auto-format
- `Cmd/Ctrl + Shift + P`  ->  "Format Document": Manual format
- `Cmd/Ctrl + Shift + P`  ->  "Organize Imports": Manual import sort

## Ruff Rules Reference

Common rule categories enabled in this project:

- **E**: pycodestyle errors (e.g., E501 line too long)
- **W**: pycodestyle warnings
- **F**: pyflakes (e.g., F401 unused import)
- **I**: isort (import sorting)
- **B**: flake8-bugbear (common bugs)
- **C4**: flake8-comprehensions (list/dict comprehensions)
- **UP**: pyupgrade (modern Python syntax)

### Example Fixes

```python
# Before
from typing import List
import os
import sys

def foo(x: List[int]) -> List[int]:
    return [i for i in x if i > 0]

# After (Ruff auto-fixes)
import os
import sys

def foo(x: list[int]) -> list[int]:
    return [i for i in x if i > 0]
```

## Performance Comparison

| Tool                   | Time (large codebase) |
| ---------------------- | --------------------- |
| black + isort + flake8 | ~10s                  |
| ruff check + format    | ~0.1s                 |

| Tool           | Installation Time |
| -------------- | ----------------- |
| pip install    | ~30s              |
| uv pip install | ~1s               |

## Troubleshooting

### UV not found

```bash
# Ensure UV is in PATH
export PATH="$HOME/.cargo/bin:$PATH"
source ~/.bashrc  # or ~/.zshrc
```

### Ruff not formatting in VS Code

1. Install "Ruff" extension by Astral Software
2. Check `.vscode/settings.json` exists
3. Reload VS Code window
4. Check output panel: View  ->  Output  ->  Ruff

### Dependencies not syncing

```bash
# Clear cache and reinstall
uv cache clean
rm -rf .venv
uv venv
uv pip install -e ".[dev]"
```

## Resources

- UV Docs: https://docs.astral.sh/uv/
- Ruff Docs: https://docs.astral.sh/ruff/
- Ruff Rules: https://docs.astral.sh/ruff/rules/
- VS Code Ruff Extension: https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# From Zero to npm in 7 Hours: Building Weavestore

Canonical URL: https://www.harjotrana.com/blog/7-hours-to-mvp-weavestore
Author: Harjot Singh Rana
Published: 2025-10-05
Reading time: 7 min

> How three of us took an on-chain database SDK from idea to a published npm package in one sitting.

Yesterday was one of those rare days that reminds you why you fell in love with building things in the first place.

No hackathon. No deadline. No pressure. Just me, [@Gursagar], and [@Lovepreet] deciding to book a coworking space in Aerocity, New Delhi, and see what we could create together.

What started as a casual "let's just code and hang out" turned into one of the most productive, energizing, and memorable days of my life. By the time the sun came up, we had shipped a working MVP to NPM.

Let me take you through how it all went down.

## The Day That Kept Getting Better

### 9:00 AM - The Unlikely Start 🎓

The day began in the most un-hackathon-like way possible: sitting in an exam hall, writing my mid-semester exam. Not exactly the glamorous beginning to a productive coding session, but hey - sometimes you've got to balance the student life with the builder life.

### 1:00 PM - The Energy Shift ⚡

By afternoon, I was at the coworking space in Aerocity. Laptops open, coffee brewing, and that unmistakable feeling in the air when you know something good is about to happen.

For the next 2-3 hours, we just... built. No grand plan. No detailed spec. Just three developers in flow state, bouncing ideas off each other and turning them into code.

### 4:00 PM - The Mall Pivot ☕

Here's where it gets interesting. We decided to switch locations - moved to The Square Mall, found a quiet café, ordered more coffee, and kept the momentum going.

There's something about changing your environment that sparks creativity. New space, new energy, same unstoppable drive.

### 8:00 PM - The Night Shift Begins 🌙

As evening turned to night, we crashed at Gursagar's place. Most people would call it a day. We called it the beginning of the real work.

What happened next was pure magic - that rare state where time disappears, code flows effortlessly, and every problem has a solution that reveals itself just when you need it.

## The Result: WeaveStore

**Within 7 hours of focused building, we created and shipped WeaveStore** - an SDK that allows developers to create and manage databases directly on-chain, powered by IPFS and IPNS.

### What Makes WeaveStore Different?

Traditional databases live on centralized servers. WeaveStore flips that model:

- **On-Chain Database Management**: Create and manage databases directly on the blockchain
- **IPFS-Powered Storage**: Decentralized, permanent, and censorship-resistant data storage
- **IPNS for Dynamic Updates**: Mutable pointers to immutable content
- **Developer-First API**: Simple, intuitive interface that feels familiar to any developer

### From Idea to NPM in Record Time

By dawn, we had:
- ✅ Core SDK functionality working
- ✅ Documentation written
- ✅ Package published to NPM
- ✅ First version live and ready for developers to use

📦 **Check it out**: [WeaveStore on NPM](https://www.npmjs.com/package/weavestore) 

## The Morning After Reality Check

Today, I'm back home - preparing for another exam while building adapters and improvements for WeaveStore. The contrast is surreal: one moment you're shipping production code at 3 AM, the next you're back to textbooks and lecture notes.

But that's the beauty of being a student developer. You get to live in both worlds - the structured academic environment and the chaotic, creative world of building products.

## What Made This Day Different?

Looking back, I can pinpoint exactly why this day was so productive when others fall flat:

### 1. The Right People Change Everything 🤝

Solo coding is great for deep focus, but collaborative building hits different. When you're working with people who:
- Get excited about the same problems
- Challenge your ideas constructively
- Fill in your knowledge gaps
- Keep the energy high when motivation dips

...you unlock a level of productivity that caffeine alone could never achieve.

### 2. Momentum Is Contagious ⚡

There's a compounding effect when a team is in sync. One person solves a problem, which unblocks another person, which sparks an idea for the third person. Before you know it, you're not just building features - you're building on each other's momentum.

### 3. Environment Matters More Than You Think 🏢

The coworking space wasn't just a change of scenery - it was a psychological shift. Being in a space designed for productivity, surrounded by other people building things, creates an ambient pressure to perform.

Then switching to the mall café added variety without breaking flow. Sometimes the best productivity hack is just... moving around.

### 4. No Burnout, Just Flow 🌊

Here's the surprising part: despite coding for 7+ hours straight, I never felt burned out. Why?

Because we weren't grinding against resistance - we were riding momentum. There's a massive difference between forcing yourself to code and being pulled forward by genuine excitement about what you're building.

## The Lessons That Stick

### Teamwork > Caffeine

I've pulled plenty of all-nighters fueled by coffee and determination. But nothing compares to the sustained energy you get from working alongside people who are just as invested in the outcome as you are.

### Peer Energy Prevents Burnout

When you're solo and hit a wall, it's easy to give up. When you're with peers and hit a wall, someone else is there to say "wait, what if we tried this?" That shared problem-solving keeps burnout at bay.

### Ideas Evolve Faster in Conversation

The version of WeaveStore we shipped isn't the version we started building. Through constant conversation and iteration, the idea evolved into something better than any of us could have designed alone.

### Ship Fast, Iterate Later

We could have spent weeks planning the perfect architecture. Instead, we built something that worked, shipped it, and now we're improving it based on real usage. Sometimes done is better than perfect.

## What's Next for WeaveStore?

The MVP is live, but we're just getting started. Currently working on:

- **Framework Adapters**: Making WeaveStore work seamlessly with popular frameworks
- **Enhanced Documentation**: More examples, tutorials, and use cases
- **Performance Optimization**: Making on-chain operations even faster
- **Community Feedback**: Listening to early adopters and iterating based on real needs

## Try WeaveStore Today

If you're building decentralized applications and need a simple way to manage on-chain databases, give WeaveStore a shot:

📦 **NPM Package**: [WeaveStore](https://www.npmjs.com/package/weavestore)  

## The Bigger Takeaway

This experience reinforced something I've always believed but sometimes forget: **the right environment, the right people, and the right mindset can compress months of work into a single day.**

You don't need a hackathon. You don't need a deadline. You don't even need a fully formed idea.

What you need is:
- People who energize you
- A problem worth solving
- The willingness to just start building

Everything else figures itself out along the way.

## Let's Build Together

This day wouldn't have been possible without [@Gursagar] and [@Lovepreet]. If you're working on something interesting or just want to connect with fellow builders, hit me up:

- **GitHub**: [HarjjotSinghh](https://github.com/HarjjotSinghh)
- **LinkedIn**: [HarjjotSinghh](https://www.linkedin.com/in/HarjjotSinghh)
- **Twitter/X**: [HarjjotSinghh](https://x.com/HarjjotSinghh)

I'm always down to connect with people who love building things. Whether you want to collaborate, share ideas, or just chat about tech - my DMs are open.

## Final Thoughts

Would I do this again? **Absolutely. 10/10 experience.**

In fact, I'm already thinking about the next one. Because days like this remind you that building software isn't just about the code - it's about the people, the energy, and the shared experience of creating something from nothing.

So here's my challenge to you: **Find your people. Book that space. Build that thing you've been thinking about.**

You might be surprised what you can accomplish in just one day.

**Happy building!** 🚀

---

*Built in 7 hours. Shipped at dawn. Still can't believe it happened. If you try WeaveStore or have questions, reach out - I'd love to hear from you.*

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Building Zyflo: An Accessible, Motion-First React Library

Canonical URL: https://www.harjotrana.com/blog/building-zyflo-ui-library
Author: Harjot Singh Rana
Published: 2025-09-23
Reading time: 6 min

> Why the existing component libraries kept failing me, and the constraints I built Zyflo around.

![Zyflo UI Library](https://www.zyflo.in/og.png)

## The Problem That Started It All

Picture this: You're deep into a client project, deadline looming, and you're on the hunt for that perfect UI component. You know the one  -  visually stunning, smoothly animated, and ready to drop into your codebase without a headache. Sound familiar?

I found myself in exactly this situation while freelancing on a project for one of my clients. For 2-3 days straight, I scoured the internet, diving deep into component libraries, GitHub repositories, and design systems. But every time I thought I'd found "the one," reality hit me with one of these scenarios:

### The Three Frustrations Every Developer Knows

**a) Beautiful but Static** 🎨  
I'd discover visually appealing components that looked amazing in screenshots but lacked any animation or interactive feedback. Sure, they were pretty, but they felt lifeless  -  like a sports car without an engine.

**b) Animated but Ugly** ⚡  
Then there were the animated components that moved beautifully but looked like they were designed in 2010. The animations were smooth, but the aesthetics? Let's just say they wouldn't win any design awards.

**c) Complex Configuration Hell** 🔧  
Finally, I'd stumble upon components that were both beautiful AND animated, but configuring them required a PhD in that specific library's documentation. Want to change a color? Good luck finding the right prop among 47 different configuration options.

## The Indie Hacker Solution

As any self-respecting indie hacker would do when faced with a problem, I thought: *"Fine, I'll build it myself."*

But this wasn't just about solving my immediate problem. I saw an opportunity to create something that addressed all three pain points I'd experienced. Something that would save other developers from the same frustrating component hunt I'd just endured.

## Enter Zyflo: Where Design Meets Motion

And so, **Zyflo** was born  -  a UI library designed to make your interface "flow" seamlessly and effectively. The name itself embodies the philosophy: smooth, effortless, and natural movement in user interfaces.

### The Three Pillars of Zyflo

When building Zyflo, I established three non-negotiable principles:

#### 1. **Animated by Default** ⚡
Every component comes with thoughtful animations built-in. No more spending hours trying to figure out how to make a button feel responsive or a modal appear smoothly. The animations aren't just eye candy  -  they provide meaningful feedback and guide user attention.

#### 2. **Effortlessly Configurable** 🎛️
Customization should be intuitive, not intimidating. Whether you want to change colors, adjust timing, or modify behavior, Zyflo components are designed with sensible defaults and clear, predictable APIs.

#### 3. **Accessibility First** ♿
Beautiful animations mean nothing if they exclude users. Every component respects user preferences for reduced motion, includes proper ARIA labels, and follows WCAG guidelines. Because great design is inclusive design.

## The Technical Journey

Building Zyflo wasn't just about creating pretty components  -  it was about solving real architectural challenges:

### Performance Optimization
Animations can be performance killers if not implemented correctly. I spent considerable time optimizing for:
- **GPU acceleration** for smooth 60fps animations
- **Lazy loading** for components not immediately visible
- **Bundle size optimization** to keep your app fast

### Developer Experience
The library needed to feel natural to developers already familiar with modern React patterns:
- **TypeScript-first** approach with comprehensive type definitions
- **Storybook integration** for easy component exploration
- **Zero-config setup**  -  just install and start using

### Framework Agnostic Design
While built with React in mind, Zyflo's design principles and many components can be adapted to other frameworks, ensuring broader adoption and longevity.

## Real-World Impact

Since launching Zyflo, I've been amazed by the response from the developer community. The library has been used in:

- **Startup MVPs** where speed and polish matter equally
- **Enterprise applications** requiring consistent, accessible design
- **Personal projects** where developers want professional results without the overhead

## What's Next for Zyflo?

The journey is far from over. Here's what's on the roadmap:

- **More Components**: Expanding the library with data visualization, form components, and layout utilities
- **Theme System**: A comprehensive theming solution for consistent branding
- **Framework Adapters**: Official support for Vue, Svelte, and Angular
- **Design Tokens**: Integration with popular design systems and tools

## Try Zyflo Today

Ready to make your interfaces flow? Check out Zyflo and see the difference animated, accessible components can make in your projects.

🌐 **Website**: [zyflo.in](https://zyflo.in)  
⭐ **GitHub**: [github.com/HarjjotSinghh/Zyflo](https://github.com/HarjjotSinghh/Zyflo)  
📺 **Demo Video**: [Watch on YouTube](https://www.youtube.com/watch?v=ndB0QN0nQ08)

## Let's Connect and Build Together

Building Zyflo has been an incredible journey, but the best part has been connecting with fellow developers and designers who share the vision of better, more accessible web experiences.

I absolutely love making new connections and learning from the community  -  whether you're on GitHub, LinkedIn, Twitter, or any other platform. Each conversation brings new perspectives and ideas that make projects like Zyflo even better.

### Find Me Everywhere

- **GitHub**: [HarjjotSinghh](https://github.com/HarjjotSinghh)  -  Follow for updates on Zyflo and other projects
- **LinkedIn**: [HarjjotSinghh](https://www.linkedin.com/in/HarjjotSinghh)  -  Let's connect professionally
- **Twitter/X**: [HarjjotSinghh](https://x.com/HarjjotSinghh)  -  Daily thoughts on development and design
- **LeetCode**: [HarjjotSinghh](https://leetcode.com/u/HarjjotSinghh)  -  Coding challenges and solutions
- **Behance**: [harjjot](https://www.behance.net/harjjot)  -  Design portfolio and creative work
- **Discord**: [HarjjotSinghh](https://discord.com/users/826266498862415902)  -  Real-time conversations
- **Reddit**: [HarjjotSinghh](https://www.reddit.com/user/HarjjotSinghh)  -  Community discussions

### Show Some Love ❤️

If Zyflo resonates with you or solves a problem you've faced, I'd be incredibly grateful if you could:

- ⭐ **Star the repository** on GitHub  -  it helps others discover the project
- 🐦 **Follow me** on your preferred social platform for updates and new projects
- 💬 **Share your experience**  -  I love hearing how Zyflo is being used in real projects
- 🤝 **Contribute**  -  whether it's code, documentation, or just feedback, every contribution matters

## The Bigger Picture

Zyflo represents more than just another UI library  -  it's a testament to the power of the indie developer community. When we encounter problems, we don't just complain; we build solutions. We share our work, learn from each other, and collectively push the web forward.

Every star on GitHub, every follow on social media, and every developer who chooses to use Zyflo in their project validates this approach and encourages continued innovation.

So whether you're building the next unicorn startup or just tinkering with a weekend project, remember: the tools we use shape the experiences we create. Choose tools that prioritize both developer experience and user delight.

**Happy coding, and may your interfaces flow beautifully!** ✨

---

*Built with passion by an indie hacker who believes great tools should be accessible to everyone. Questions, suggestions, or just want to chat? Reach out on any platform  -  I'm always excited to connect with fellow builders.*

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Next.js and MDX: A Practical Setup Guide

Canonical URL: https://www.harjotrana.com/blog/getting-started-nextjs-mdx
Author: Harjot Singh Rana
Published: 2025-09-12
Reading time: 2 min

> Wiring MDX into the App Router so posts stay Markdown but can still render React components.

Welcome to this comprehensive guide on setting up a blog using Next.js and MDX! This powerful combination allows you to write your blog posts in Markdown while leveraging the full power of React components.

## Why Choose Next.js and MDX?

**Next.js** provides:
- Static site generation (SSG) and server-side rendering (SSR)
- Fast page loads with automatic code splitting
- Built-in routing and API routes
- Excellent developer experience with TypeScript support

**MDX** brings:
- The simplicity of Markdown syntax
- The power of React components within your content
- Reusable UI components
- Rich content capabilities

## Setting Up Your Project

First, create a new Next.js project:

```bash
npx create-next-app my-blog-app
cd my-blog-app
```

Then install the required MDX dependencies:

```bash
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx next-mdx-remote
```

## Creating Your First Blog Post

Create a new `.mdx` file in your content directory:

```mdx
---
title: "My First Blog Post"
date: "2025-09-12"
author: "Your Name"
---

# Hello World!

This is my first blog post using MDX!
```

## Using React Components in MDX

One of the best features of MDX is the ability to use React components directly in your content:

```mdx
import { Alert } from '@/components/ui/alert';

<Alert variant="info">
  This is a custom React component inside my MDX content!
</Alert>
```

## Conclusion

Next.js + MDX provides an excellent foundation for modern blog development. You get the best of both worlds: the simplicity of Markdown and the power of React.

Happy blogging! 🚀

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# Choosing a Stack for a Portfolio Site

Canonical URL: https://www.harjotrana.com/blog/building-portfolio-website
Author: Harjot Singh Rana
Published: 2025-09-11
Reading time: 3 min

> The tools worth reaching for when the site itself is the work sample, and the ones that aren't.

Creating a portfolio website that showcases your skills and projects is essential for any developer. In this post, we'll explore the modern tools and technologies that make building impressive portfolio sites easier than ever.

## The Modern Tech Stack

### Frontend Framework: Next.js
Next.js has become the go-to framework for modern web development. It offers:
- **Static Site Generation**: Blazing fast load times
- **Server-Side Rendering**: Dynamic content with SEO benefits
- **API Routes**: Backend functionality without separate servers
- **Image Optimization**: Automatic image optimization and resizing

### Styling: Tailwind CSS
Tailwind CSS revolutionizes the way we write styles:
- **Utility-First Classes**: Build custom designs without writing CSS
- **Responsive Design**: Mobile-first approach made simple
- **Dark Mode**: Built-in support for multiple themes
- **Customization**: Highly configurable through config files

### Animation: Framer Motion
Adding smooth animations and interactions:
- **Gestures**: Drag, pan, hover, and tap animations
- **Layout Animations**: Smooth transitions between layouts
- **Scroll Animations**: Animate elements as they come into view

## Essential Components

### Header Navigation
A responsive navigation menu that works across all devices:
- Desktop menu with smooth hover effects
- Mobile hamburger menu with slide-in animation
- Active state indicators for current page

### Project Showcase
Display your projects effectively:
- Grid layout with responsive columns
- Hover effects revealing project details
- Modal popups for detailed project views
- Links to live demos and source code

### Skills Section
Present your technical skills visually:
- Progress bars showing proficiency levels
- Icon-based representation of technologies
- Interactive filtering by category
- Animated entrance effects

## Performance Optimization

### Image Optimization
- Next.js Image component for automatic optimization
- Lazy loading for better initial load times
- WebP format support with fallbacks
- Responsive images serving different sizes

### Code Splitting
- Automatic route-based code splitting
- Dynamic imports for heavy components
- Preloading critical resources
- Bundle analysis tools

## SEO Best Practices

### Meta Tags
Dynamic meta tags for each page:
- Open Graph tags for social sharing
- Twitter Card support
- Structured data for rich snippets
- Dynamic sitemap generation

### Performance Metrics
- Core Web Vitals optimization
- Lighthouse score improvements
- Progressive Web App features
- Service worker for offline support

## Deployment and Hosting

### Vercel Integration
Seamless deployment with automatic optimizations:
- Automatic HTTPS setup
- CDN distribution globally
- Serverless functions for API routes
- Automatic deployments on git push

### Analytics Integration
Track user engagement:
- Vercel Analytics for performance metrics
- Custom event tracking
- Heatmaps and user session recording
- Conversion goal tracking

## Future Enhancements

### Blog Functionality
Adding a blog section for content marketing:
- MDX support for rich content
- RSS feed generation
- Comment system integration
- Search functionality

### Advanced Features
Plans for future improvements:
- Multi-language support
- Dark mode toggle
- Interactive timeline
- Email newsletter signup

## Conclusion

Building a modern portfolio website requires careful planning and the right tools. By leveraging Next.js, Tailwind CSS, and other modern technologies, we can create fast, beautiful, and maintainable websites that effectively showcase our work.

The key is to balance functionality with performance, ensuring that every feature serves a purpose while maintaining excellent user experience.

Happy coding! 🚀

More writing: https://www.harjotrana.com/blog

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers

---

# MVP Scope Estimator

Canonical URL: https://www.harjotrana.com/tools/mvp-scope-estimator

> Turn a rough product idea into a clearer first milestone. Nine quick choices, an explainable scope read, and a brief you can keep.

This is an interactive, deterministic tool; the nine questions and the resulting brief are rendered in the browser at the canonical URL. No AI, no account, and nothing leaves the browser.

## What this helps you decide

A useful first release has a defined user, a central workflow, and a launch bar that matches what needs to be true now—not everything the product might become.

The estimator makes the main complexity drivers visible: roles, integrations, AI behavior, runtime needs, launch requirements, and data sensitivity.

It will not invent a price, promise a timeline, or make a technical recommendation without the real context. Those are conversation-level decisions.

## Questions and answers

### Does this use AI?

No. The estimator uses a small deterministic ruleset, so every result follows from the choices you make. That keeps the result explainable, fast, and independent of an API key.

### Is this a quote or delivery estimate?

No. It is a scoping aid. It does not promise price, timing, delivery capacity, architecture, or a final proposal. Those decisions need the actual product context and a shared discussion.

### Is my product information stored?

Your selections and the copyable brief stay in the browser. The site records only non-identifying aggregate tool events such as completion, scope band, and safe complexity flags; it does not send your selected answers or product details to analytics.

### What should I do with the result?

Copy the brief into a project inquiry, a planning document, or a conversation with your team. Use it to make the first milestone and the work you are deliberately deferring explicit.

Ready to talk it through? https://www.harjotrana.com/hire

---

Site guide for agents: https://www.harjotrana.com/llms.txt · Full site as Markdown: https://www.harjotrana.com/llms-full.txt · Sitemap: https://www.harjotrana.com/sitemap.xml · Developer resources: https://www.harjotrana.com/developers
