# Vinayak Kulkarni > GIS Engineer & Co-Founder building geospatial infrastructure — MapLibre, Planetiler, PMTiles, and Vue.js. Open-source cartographer shipping maps, tiling pipelines, and mapping tooling. Vinayak Kulkarni is a GIS engineer and co-founder at Invarya Technologies, where he builds maps.guru and geospatial tooling. He works across the modern web-mapping stack — MapLibre GL for rendering, Planetiler and PMTiles for vector-tile generation and delivery, and Vue.js on the frontend — and maintains open-source Vue mapping packages including v-maplibre and maplibre-gl-starfield. ## Pages - [About](https://vinayakkulkarni.dev/about): Who Vinayak is — a GIS engineer and co-founder focused on geospatial infrastructure, web cartography, and open-source mapping. - [Projects](https://vinayakkulkarni.dev/projects): Selected work across web mapping, vector tiling, and geospatial platforms, including maps.guru. - [Open Source](https://vinayakkulkarni.dev/open-source): Vue and MapLibre open-source packages Vinayak maintains, such as v-maplibre and maplibre-gl-starfield. - [Articles](https://vinayakkulkarni.dev/articles): Writing on GIS, MapLibre, vector tiles, PMTiles, and building with Vue.js. ## Articles - [100% coverage, three live bugs: porting a plugin to OpenCode 2](https://vinayakkulkarni.dev/articles/porting-a-plugin-to-opencode-2): I ported my OpenCode memory plugin to the OpenCode 2 API by the book: failing test first, 429 tests, 100% coverage. Then I pointed a real host at it and found the daemon had not run for 29 hours. Every bug that mattered lived in the contract with the host, where no unit test could reach. - [One caret sign, 194 silent 500s: the outage that only broke on the second request](https://vinayakkulkarni.dev/articles/one-caret-sign-194-silent-500s): A reader told me my docs site had rendering problems and refreshing led to a 404. My own check returned a clean 200, because I asked once and they asked twice. The cause was two copies of Vue, and 194 server errors had been printing in every build log for two weeks. - [TIL: Cloudflare was prepending Disallow rules above my robots.txt](https://vinayakkulkarni.dev/articles/til-cloudflare-prepends-managed-robots-txt): I authored a robots.txt that explicitly invited AI crawlers. Production served a Cloudflare-managed block above it that disallowed eight of them. The repo never showed it, no API can turn it off, and RFC 9309 group-merging is the only thing standing between your allowlist and invisibility. - [Making a Nuxt site agent-ready: from Level 1 to Level 4 on isitagentready.com](https://vinayakkulkarni.dev/articles/making-a-nuxt-site-agent-ready): AI agents are starting to operate websites, not just cite them. What it took to move a production Nuxt site on Cloudflare Workers from a score of 21 to Level 4 on the isitagentready scanner: markdown negotiation, API catalogs, agent-skills indexes, WebMCP, and the traps nobody documents. - [Workers Cache on an authenticated tile API: the split-entrypoint pattern](https://vinayakkulkarni.dev/articles/workers-cache-on-an-authenticated-tile-api): How to enable Cloudflare Workers Cache on an API that must authenticate and meter every request, using an uncached gateway entrypoint in front of a cached origin entrypoint. Warm viewport hit rate went from 10% to 100% and median warm tile latency dropped from 874ms to under 100ms. - [Encoding elevation into pixels: DEM terrain tiles in Rust](https://vinayakkulkarni.dev/articles/encoding-elevation-into-pixels): How tileserver-rs turns raw GDAL elevation rasters into Terrarium and Mapbox-RGB terrain tiles that MapLibre renders as 3D terrain. The math, the classic 1-LSB encoding bug, and why the alpha channel will betray you. - [How I cut a $35 Supabase bill to $0 with Cloudflare D1](https://vinayakkulkarni.dev/articles/cutting-a-supabase-bill-to-zero-with-cloudflare-d1): Moving a live multi-tenant SaaS off Supabase Postgres onto Cloudflare D1 with zero downtime. The schema rewrite was the easy part. The date-coercion bug, the PostGIS audit, and the break-even math were not. - [TIL: Shiki multi-theme highlighting does not switch the code-block background](https://vinayakkulkarni.dev/articles/til-shiki-multitheme-code-block-background): Shiki multi-theme mode swaps token colours per colour scheme but leaves the pre background variable empty, so light-theme text can land on a dark block. You have to theme the container yourself. - [Generating dynamic Open Graph images on Cloudflare Workers](https://vinayakkulkarni.dev/articles/dynamic-og-images-on-cloudflare-workers): Rendering per-page and per-article social cards at the edge with Satori and @cf-wasm/og: how the route works, what Satori refuses to render, and the cold-versus-warm numbers. - [Migrating a Nuxt site from Cloudflare Pages to Workers](https://vinayakkulkarni.dev/articles/migrating-a-nuxt-site-from-cloudflare-pages-to-workers): The full path from the cloudflare-pages preset to a cloudflare_module Worker: the config changes, the custom-domain cutover that no deploy step handles for you, and the gotchas that only show up in CI. - [TIL: Tailwind v4 mask utilities need the webkit prefix in plain CSS](https://vinayakkulkarni.dev/articles/til-tailwind-v4-webkit-mask-prefix): Porting a Tailwind mask-composite utility to scoped CSS quietly breaks in Chrome and Safari unless you also write the -webkit-mask-* properties, because Tailwind was emitting them for you. - [TIL: Enabling Workers Cache through Nitro without a native option](https://vinayakkulkarni.dev/articles/til-workers-cache-nitro-passthrough): Nitro forwards arbitrary keys into the generated wrangler config, so you can turn on Cloudflare Workers Cache with a single line even though there is no dedicated Nitro option for it. - [Build & Publish Vue 3 Components using Rollup.js](https://vinayakkulkarni.dev/articles/build-and-publish-vue-3-components-using-rollup): Let's build a reusable Vue 3 component using rollup ## Optional - [Full content index](https://vinayakkulkarni.dev/llms-full.txt): Plaintext of the most important pages for single-round-trip ingestion.