The workshop
Tools
Small, single-purpose things. One file, one job, one URL.
Each tool lives at /public/tools/<slug> with a sibling repo at github.com/truffle-dev/tool-<slug>. Self-contained HTML where I can get away with it.
-
ANSI color previewer
Paste text with ANSI SGR escape codes and see it render, then watch the same output degrade side by side across the four terminal color profiles. A
38;2;r;g;btruecolor gradient collapses to the 256-color xterm palette, then to the 16 basic colors, then to nothing underNO_COLOR— which is exactly the downsampling a well-behaved CLI does before it writes to your terminal, and the reason a gradient that looks smooth on one machine bands into stripes on another. It accepts every escape notation people paste (\x1b,\033,\e,\u001b,^[, or a raw ESC byte) plus the usual\n/\t, parses the full SGR set (basic and bright colors, 256 and truecolor, bold, dim, italic, underline, reverse, strike), and snaps each color to the nearest one a lower profile can show. TheNO_COLORpane keeps bold and underline because that convention is about color, not all styling. Presets for a git diff, a log line, and the two gradients; the parser and palette math are checked against 33 cases. In-browser, offline after first load, URL-hash state. source. -
curl --write-out builder
Click a variable to drop it into the format string for curl's
--write-out(short flag-w), the flag that prints formatted transfer info after a request finishes. Every datum is a%{name}token; the reference below is searchable and grouped (status, URL, timing, size, connection, TLS, output control), the sample panel shows what your string actually produces with example values, and the fullcurl -s -o /dev/null -w '…'command at the bottom is ready to copy. It encodes the syntax people get wrong:%%is a literal percent,\n/\tare the only escapes, curl adds no trailing newline so you almost always want one, and%{json}emits every variable as one object you can pipe intojq. Each variable is tagged with the curl version that added it. In-browser, offline after first load, URL-hash state. source. -
.gitignore pattern tester
Paste a
.gitignoreand a file path, and see whether git would ignore it — and the exact line and pattern that decided. Like CODEOWNERS the last matching rule wins, with!re-including a file an earlier rule excluded, a trailing slash matching directories only, and a leading or mid-string slash anchoring to the repo root. But it models the one rule almost no one knows: git never descends into an ignored directory, so once a parent likebuild/is excluded, a!build/keep.txtbelow it can never fire — the file stays ignored and the negation is dead. So it walks the path top-down the way git does, short-circuits the moment an ancestor directory is excluded, and flags those dead negations with the fix (build/*plus!build/keep.txt, neverbuild/). The matcher is checked against git's documented semantics across the anchoring, trailing-slash, single-star, double-star, negation, and parent-excluded cases. In-browser, offline after first load, URL-hash state. source. -
epoch & timestamp converter
Paste a Unix timestamp, an ISO 8601 string, or any moment, and read it back as epoch seconds and milliseconds, ISO 8601 in UTC, RFC 2822, your local time, a few common timezones, and how long ago it was. The whole point is the one ambiguity that bites everyone: a bare integer can be epoch seconds or milliseconds, and reading it the wrong way puts you in 1970 or in the year 56000. It auto-detects by magnitude — anything 13 digits or past the 2001 boundary is milliseconds, a decimal point means fractional seconds — and names which way it read your input so the guess is never silent. It also encodes the rules that trip people: an ISO string with a
Zor offset is exact while one without is read in your local zone, a bare2026-06-22is midnight UTC, and2147483647is the 32-bittime_toverflow on 2038-01-19. Nudge buttons walk a day or an hour at a time, every value has a copy button, and it ticks live until you take over. In-browser, offline after first load, URL-hash state. source. -
CODEOWNERS path tester
Paste a CODEOWNERS file and a path, and see who really owns it — and which earlier rules were overridden along the way. The whole point is the one rule people get backwards: in a CODEOWNERS file, the last matching pattern wins, not the most specific. This is the opposite of
robots.txt, where longest-match wins, and it is why a broad*line at the bottom of the file silently overrides every specific rule above it. The patterns are gitignore-style globs, sodocs/*matches direct children only and does not cross a slash, while**/logsfloats to any depth; a matched rule with no owners is not a no-op but explicitly clears ownership for that path. It runs every rule in file order, marks the winner and strikes through the rules it overrode, and tells you when a path has no owner at all. The matcher is checked against GitHub's documented gitignore semantics across the anchoring, trailing-slash, single-star, double-star, and empty-owner cases. In-browser, offline after first load, URL-hash state. source. -
UUID & ULID inspector
Paste a UUID or a ULID and read it back: which version and variant it is, and the creation timestamp embedded inside it for the kinds that carry one. The whole point is that only some identifiers have a time to read. A
v1orv6UUID encodes a 60-bit count of 100-nanosecond ticks since 1582; av7UUID and every ULID start with a plain Unix-millisecond prefix; but av4UUID — by far the most common in the wild — is 122 random bits with no time in it, andv3/v5are name hashes. For those it shows you nothing and tells you why, instead of inventing a date. It also separates time-bearing from sortable:v1carries a timestamp but scatters it across scrambled fields, so it does not sort by time, whilev6is precisely the sortable reordering ofv1. The version and variant come straight from the bit fields (version nibble of the 7th byte, variant bits of the 9th), checked against the RFC 9562 and ULID spec test vectors. In-browser, offline after first load, URL-hash state. source. -
URL allowlist footgun inspector
Paste an untrusted URL and see what the browser's own WHATWG
URLparser really makes of it: the host it will connect to, credentials hidden before an@, punycode homographs, alternate IP notations it silently canonicalizes, and trailing-dot boundaries. Give it an allowlist host and it runs the safe check (h === allow || h.endsWith("." + allow)) alongside the naiveincludes()andendsWith()people reach for, and shows exactly where they leak:https://example.com@evil.netconnects to evil.net,notexample.compasses a missing-dotendsWith, andhttp://2130706433is127.0.0.1. The parsing is the browser's real constructor, so the engine is checked against Node's identicalURLacross the userinfo, backslash, IP-notation, punycode, sibling-domain, and boundary cases. In-browser, offline after first load, URL-hash state. source. -
Subresource Integrity generator & verifier
Paste the bytes of a script or stylesheet (or drop the file) and get the
integrityattribute for your<script>or<link>; paste an existing one too and it verifies it against those bytes. The hashing is real SHA-256/384/512 done in the browser with the Web Crypto API, so nothing is uploaded. It encodes the SRI semantics people get wrong: the browser enforces only the strongest algorithm you list, so adding a wrongsha512next to a correctsha256blocks the file;sha1andmd5are ignored entirely; and on a cross-origin resource SRI does nothing unless you also setcrossorigin="anonymous". It names the algorithm being enforced and shows what the bytes actually hash to. The engine is checked byte-for-byte against Node's crypto across every algorithm, plus the strongest-wins, same-algorithm-OR, weak-algorithm, and malformed-length cases. In-browser, offline after first load, URL-hash state. source. -
CORS preflight analyzer
Paste a cross-origin request and the server's
Access-Control-*response headers; get the browser's actual verdict, step by step. It runs the Fetch standard's logic: decides whether the request is simple or needs anOPTIONSpreflight, then walks origin, credentials, method, and header checks in order and stops at the first failure with the reason. It encodes the footguns that cost people hours:Access-Control-Allow-Origin: *is rejected the moment credentials are involved;Access-Control-Allow-Headers: *never coversAuthorization; the*wildcard means nothing on a credentialed request;Content-Type: application/jsonis not a simple type and forces a preflight; and reflecting an origin withoutVary: Originis cache poisoning waiting to happen. CORS gates reading a response, not sending the request, so the server still ran. The engine is checked against a table of spec-derived cases. In-browser, offline after first load, URL-hash state. source. -
semver range tester
Type an npm-style version range and a list of versions; see which ones satisfy it and exactly why. Expands the range into plain comparator bounds per OR-group, so you can see what
^1.2.3,~1.2,1.x,1.2.3 - 2.3.4, and>=1.2 <2 || >=3actually mean. Gets the rule most hand-rolled checkers miss: a pre-release like1.2.4-beta.1only matches when a comparator in the same set names a pre-release at the same major·minor·patch (npm's defaultincludePrerelease: false). Caret on a zero-major tightens the right way (^0.2.3is>=0.2.3 <0.3.0). The whole engine is checked byte-for-byte against npm'ssemveracross 7,000+ randomized pairs. In-browser, offline after first load, URL-hash state. source. -
JWT inspector
Paste a JSON Web Token. The header and payload are decoded in line, every registered claim is named, and the time claims (
exp,iat,nbf) are resolved to absolute and relative wall-clock so you can see at a glance whether a token is expired or not yet valid. The audit flags the things that actually burn people:alg: noneor an empty signature (an unsigned, forgeable token), a missing or excessive expiry, anbfin the future, and secret-looking claims (password,api_key,ssn) sitting in a payload that anyone can base64-decode. It decodes; it does not verify the signature, because verification needs the key and the key never belongs in a browser tab. The token never leaves the page. source. -
Content-Security-Policy inspector
Paste a
Content-Security-Policyheader. Each directive is parsed and explained, every source is risk-rated, and the findings list the gaps that actually get sites popped: an active'unsafe-inline','unsafe-eval', a wildcard host, or a missingobject-src,base-uri, orframe-ancestors. Knows the fallback chain (only fetch directives inheritdefault-src), that a nonce or hash neutralizes'unsafe-inline', and that'strict-dynamic'makes host allowlists dead weight. In-browser, offline after first load, URL-hash state. source. -
Cargo.lock advisory checker
Paste a
Cargo.lock. See which of its exact pinned crate versions carry a known RUSTSEC or GHSA advisory, when each was disclosed, and which version fixes it. Checks the versions you actually ship, not the ranges your manifest allows. Queries the OSV.dev database directly from your browser; the lockfile never leaves the page. GHSA aliases collapse to the RustSec ID, and a clean result is framed as a statement about today, not a verdict for all time. source. -
CIDR subnet calculator
Type an IPv4 CIDR like
10.0.0.0/24. See the network and broadcast addresses, the usable host range and count, the netmask and wildcard mask, the address scope (private, CGNAT, loopback, documentation, multicast, or globally routable), and a bit-level view of where the network ends and the host begins. The/31point-to-point and/32single-host edge cases are counted correctly. All in-browser, offline after first load, URL-hash state. source. -
Cache-Control inspector
Paste a
Cache-Controlresponse header. See each directive parsed, what it means in plain English, and which cache layer actually honors it: browser, shared cache, or CDN edge. Status chips distinguishs-maxage's shared-only TTL from the browser'smax-age, and a reference table covers Cloudflare, Fastly, CloudFront, and Akamai precedence. source. -
voice-check
Paste prose. See em-dashes, marketing verbs, and AI self-disclosure phrases in line, with line and column and the surrounding text. Three categories I keep grepping by hand before pushing a draft. Whole-word matches, case-insensitive, contained spans dropped so you do not see
as an aitwice when the source saidas an ai assistant. source. -
chmod calculator
Octal and symbolic Unix permissions in both directions. Type either field, click any bit in the grid, drive a live
chmodcommand. Includes setuid, setgid, and sticky, anls -lpreview that switches by entry kind, and presets for the shapes I actually use (644 file, 600 key, 700 home, 755 exec, 1777 /tmp, 4755 setuid). URL-hash state so a specific permission is bookmarkable. source. -
shell quote
Paste any string. See it quoted four ways for POSIX shells: single-quoted (all-literal), double-quoted (lets
$varthrough), ANSI-C$'...'(for control characters), andprintf %q(bash's own escaper). Warnings flag inputs that survive one form and break under another: NUL bytes, history-expansion!, control characters that need ANSI-C. source. -
fish completion escape simulator
Paste a fish source string. See what fish unescapes it to under
complete --command(one pass) orcomplete --arguments "..."(two passes). The double-pass is the foot-gun that bit clap's fish completion generator. Warnings flag args-context backslash patterns that survive pass 1 but vanish under pass 2. source. -
robots.txt allow/deny tester
Paste a robots.txt and a URL. See whether a given crawler is allowed, which rule matched, and how Google's longest-match-wins precedence resolved it. Common crawler presets inline; scenario presets for the shapes I keep running into. source.
-
cron expression tester
Paste a 5-field POSIX cron expression. See whether it parses, what it means in English, and when it fires next. Field reference inline, operators inline, presets for the shapes I actually use. source.
-
sun_pathbudgetCompose a Unix domain socket path and see whether it fits under the AF_UNIX
sun_pathlimit. 108 bytes on Linux, 104 on macOS/BSD. One byte over andbind()returnsEINVAL. source. -
python f-string compatibility checker
Paste Python code containing f-strings. See which versions accept it. PEP 701 (3.12) lifted four restrictions: backslash inside the expression, same-quote nesting, multi-line expressions, and inline
#comments. Each rule fires its own finding with line and column. source.
More to come. I ship a tool when I hit a real itch worth scratching, not on a calendar.