Home / Articles

Build client-first developer tools that do not require blind trust

KitDev Space keeps hashing, JWT, and archives in the browser, uses Bun only when native or cross-host work is required, and badges every tool’s execution venue.

Publisher: REACTAPP.TOP6 min read1438 words

This article is published in English.

Sources: How I built KitDev Space, an open source hub of developer tools that never has to ask you to trust it

Pasting a production JWT into the first decoder a search engine suggests is a familiar habit. The goal is only to read claims, yet the token still leaves the machine. The same pattern shows up with .env files in converters and photos in EXIF strippers: a privacy policy claims the server keeps nothing, and that claim is taken on faith.

KitDev Space exists to shrink that leap of faith. Everyday developer utilities should not require trusting an operator—including the operator who wrote the site. The product shape follows one early rule that was written down and then treated as non-negotiable.

The rule

Prefer client-side execution. Involve a backend only when the page environment is genuinely incapable of finishing the task.

The first half is uncontroversial. The second half needs a hard definition, or every convenience route migrates server-side. The situations that actually qualify stayed short:

  1. The work needs a native library. Encoding AVIF or rasterizing SVG needs a real codec. Opening a TLS handshake needs a socket.
  2. The work must talk to another host. DNS, response headers, RDAP, and OpenGraph previews fail or should fail from a page because of CORS.
  3. The work needs a runtime API with no browser twin, such as Bun.dns or Bun.Archive.
  4. The input is too large for a tab to hold comfortably.

If a tool hits none of those, it stays client-side. Convenience has tempted exceptions when a server path would be twenty lines and the browser path two hundred. Those exceptions have not shipped.

The browser does more than expected

Early estimates assumed heavy server code. Most developer-tool work already fits platform APIs or a small dependency that runs without leaving the page.

  • Digest helpers for SHA-1 through SHA-512 via crypto.subtle.digest
  • Entropy and identifiers through crypto.getRandomValues
  • Symmetric crypto pieces—AES-256-GCM, PBKDF2, HMAC—on crypto.subtle
  • Geometry edits (resize, rotate, crop) with OffscreenCanvas plus createImageBitmap
  • Still-image export to JPEG, PNG, or WebP using OffscreenCanvas.convertToBlob
  • EXIF and GPS reads by walking file bytes with DataView
  • Compression and archives (gzip, deflate, tar) through fflate
  • Config formats such as YAML, TOML, JSONC, and JSON5 via confbox
  • In-memory SQL with sql.js running as WebAssembly

The JWT panel checks HS256 signatures with Web Crypto and skips HTTP entirely. The AES screen derives material with PBKDF2 inside the tab. The archive browser opens tar files without an upload. The SQLite studio keeps a database in memory. The pattern is mundane: inventory the platform first, then add a route only if something is missing.

Web Crypto still has gaps: no MD5, no CRC32, no xxHash. A pure JavaScript MD5 library could keep hashing entirely local, but the hash tool also offers xxhash64 and wyhash, and shipping those rarely used algorithms to every visitor felt wrong. Those options keep a server path, and the UI states that choice in plain language when selected. Naming which mode leaves the machine beats pretending nothing ever does.

One module, two paths

Maintaining browser and server forks of one tool invites silent divergence. Two converters drift, and identical input suddenly disagrees by execution venue. An early format converter hit exactly that failure.

The durable shape is a shared module that exports both the worker and a tiny capability probe. The UI asks the probe. The API route imports that same module. Hashing logic therefore lives in one place.

// shared/utils/crypto/hash.ts
const SUBTLE_NAMES = {
 sha1: 'SHA-1',
 sha256: 'SHA-256',
 sha384: 'SHA-384',
 sha512: 'SHA-512'
}

export function canHashInBrowser(algorithm: HashAlgorithm): boolean {
 return algorithm in SUBTLE_NAMES
}
// simplified page logic
const result = canHashInBrowser(algorithm)
 ? await hashInBrowser(input, algorithm)
 : await $fetch('/api/hash', { method: 'POST', body: { input, algorithm } })

Hashing, format conversion, code formatting, and semver all follow that shape. New dual-path tools inherit the same structure instead of inventing another split.

What the server is actually for

Backend-backed utilities sit on Bun. Each exists because of one hard exception:

  • Certificate inspection opens a real socket to read chains, SANs, and negotiated ciphers.
  • Name and mail health tools ask for A/MX/TXT plus SPF, DKIM, and DMARC.
  • Header inspection, OpenGraph previews, and RDAP all require third-party fetches.
  • Image Studio and favicon packaging call Bun.Image when AVIF or ICO must be produced.
  • TS/JSX transpile and AST playgrounds depend on OXC’s native implementation.

Server routes are treated as liabilities and share one preflight checklist. URLs must validate and may use only http or https. Localhost, private ranges, link-local addresses, cloud metadata endpoints, and IPv6 transition forms that hide IPv4 (NAT64 and Teredo appeared while building that denylist) are rejected. Destination ports are capped so TLS inspection cannot scan the network. Content-length is checked, bodies are streamed, and reads stop at a hard cap instead of buffering forever. Rate limits use a client key unrelated to x-forwarded-for, which is trivial to spoof. Uploads stay in memory, never on disk. Inputs and outputs are not retained.

Analytics follows the same discipline. Events carry a tool id and nothing else. Usage of the cron visualizer is visible; the expression typed is not.

Making the rule visible

A rule nobody can see is marketing, not a privacy control. The navigation therefore labels execution venue.

Each entry carries a badge. Client means the page never phones home. Bun means every action hits the backend. Tools with both paths omit the badge and document which gesture leaves the tab. Those marks are generated from two registry flags—the same registry that drives titles and search—so copy cannot disagree with implementation.

Source is MIT-licensed and linked in the footer. A Client label can be audited by opening the module and confirming it never calls a route. Verification, not trust, is the last piece of the design.

What’s in there

Catalog size changes most weeks, so a headcount would be wrong by tomorrow. The utilities opened most often, grouped by job:

Data work: pretty-print JSON that also accepts JSON5/JSONC, emit TypeScript types from JSON, move freely among YAML, TOML, XML, and CSV (plus CSV rows turned into SQL inserts), browse wide CSV tables, and run the in-browser SQLite studio.

Dev helpers: a regex scratchpad that narrates each token, a cron calendar of next firings, translators from cURL into fetch, Axios, Python, or Go, HTML/SVG converters aimed at JSX or Vue, a glob playground, and the local tar browser.

Network checks: certificate chain inspection, DNS queries, mailbox authentication health, raw HTTP header review, CIDR math, cookie decoding, and RDAP lookups.

Crypto utilities: digests, HMAC, JWT inspection, TOTP codes, AES encrypt/decrypt, plus UUID and passphrase generators.

Color tools: OKLCH conversion, WCAG contrast scoring, Tailwind shade ramps, CSS gradient composition, and pulling a palette out of a picture.

Image tools: combined crop/resize/rotate/convert, EXIF inspect-or-strip, SVG rasterization to PNG/WebP at 1x/2x/4x, and a favicon packager that returns a ZIP.

Accounts and paywalls are absent. Theme follows the OS preference, so there is no separate light/dark control to hunt for.

Under the hood

The implementation stack stays boring on purpose: Nuxt 4 with Nuxt UI 4 in the browser, VueUse composables, CodeMirror editors, Bun-powered routes, Vitest plus Playwright coverage, and a Vercel deploy.

I’d like your help

Trying https://kitdev.space, two reports help most: which tool was missing, and whether any tool sent data server-side when it should have stayed local. The second report means the rule has a hole.

The repository is one click from the footer. Issues and pull requests are welcome for new tools, fixes, or badge corrections. A hub like this improves with more hands on it—the main reason the source is open.

When building similar utilities elsewhere, keep the same short list of cases where the browser truly cannot finish. Everything else belongs on the user’s machine.