<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: chovy</title>
    <description>The latest articles on DEV Community by chovy (@chovy).</description>
    <link>https://dev.to/chovy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F380499%2Fdc7e67ea-7c66-4ee6-87cb-b5efea914c1c.jpeg</url>
      <title>DEV Community: chovy</title>
      <link>https://dev.to/chovy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chovy"/>
    <language>en</language>
    <item>
      <title>nmaptui: an admin console for nmap</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Fri, 11 Sep 2026 10:15:10 +0000</pubDate>
      <link>https://dev.to/chovy/nmaptui-an-admin-console-for-nmap-1mpi</link>
      <guid>https://dev.to/chovy/nmaptui-an-admin-console-for-nmap-1mpi</guid>
      <description>&lt;p&gt;nmaptui is an admin console for nmap that runs in the terminal. It is open source (MIT), it is on npm as @profullstack/nmaptui, and it installs with one line:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -fsSL https://raw.githubusercontent.com/profullstack/nmaptui/main/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;nmap itself is fine. What it lacks is a place to live between scans. You run it, read the output, lose the output, run it again a month later, and wonder what changed. nmaptui keeps every scan, shows it in eight screens, and diffs any two.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is in it
&lt;/h2&gt;

&lt;p&gt;A scan builder with eighteen profiles (Quick, Intense, Ping sweep, Vulnerability scripts, Web services, Windows and SMB, Databases, Top 100 UDP, and so on) and a form for every nmap option that matters, each with a one-line explanation. The exact command nmap will get sits underneath the form, with a count of how many addresses the targets cover and a warning when the scan needs root.&lt;/p&gt;

&lt;p&gt;A live view. nmaptui runs nmap with XML on stdout and reads it as it streams, so tasks appear as they start, nmap's own percent and time remaining show once it reports them, and each host lands in the table the moment nmap finishes it. Abort keeps what came back.&lt;/p&gt;

&lt;p&gt;Hosts, Services and Findings. The hosts screen is the usual per-host detail: ports, versions, OS matches, uptime, traceroute, script output. The services screen pivots the scan the other way, so "which boxes still run OpenSSH 7" is one row. Findings is a triage list from rules over the scan: clear-text protocols, anonymous FTP, SMBv1 and unsigned SMB, databases and RDP reachable from the network, expired certificates, weak TLS, end-of-life operating systems, and anything an NSE script marked VULNERABLE.&lt;/p&gt;

&lt;p&gt;History and diff. Scans are kept as nmap's own XML under ~/.local/share/nmaptui. Mark two and you get hosts that appeared or vanished, ports that opened or closed, and services whose version moved. Any file made with nmap -oX can be imported.&lt;/p&gt;

&lt;p&gt;Export to text, JSON, CSV, Markdown, HTML or the raw XML. nmaptui print and nmaptui diff work without a terminal, so a nightly diff in a cron mail is a few lines of shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it is built
&lt;/h2&gt;

&lt;p&gt;It is TypeScript on HQTUI, with no other runtime dependency. The XML reader is 290 lines and has a streaming mode that hands over each complete host element as the bytes arrive. Screens are pure functions of one state object, and the tests render real frames headlessly and assert on the text, so a layout regression fails a test instead of waiting for someone to notice. 53 tests run under both node --test and bun test.&lt;/p&gt;

&lt;p&gt;The screenshots in the README are a real connect scan of profullstack.com, captured through a pty. It sits behind Pingora with two ports open and 998 filtered, which is about the right answer.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/profullstack/nmaptui" rel="noopener noreferrer"&gt;https://github.com/profullstack/nmaptui&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nmap</category>
      <category>security</category>
      <category>cli</category>
      <category>opensource</category>
    </item>
    <item>
      <title>nichedb 0.8: symbols, price history, fundamentals and the crypto universe; advis0r and b1dz read it</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Fri, 11 Sep 2026 06:50:03 +0000</pubDate>
      <link>https://dev.to/chovy/nichedb-08-symbols-price-history-fundamentals-and-the-crypto-universe-advis0r-and-b1dz-read-it-3pbk</link>
      <guid>https://dev.to/chovy/nichedb-08-symbols-price-history-fundamentals-and-the-crypto-universe-advis0r-and-b1dz-read-it-3pbk</guid>
      <description>&lt;p&gt;Two more sites stop fetching for themselves today. advis0r.com built every research report on four hundred days of Alpaca bars, the company's SEC facts and Alpaca's asset list; b1dz.com asked CoinGecko and four exchanges every five minutes which pairs exist and what they did. nichedb.dev 0.8 keeps all of it once, and both sites read it there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What moved, and what did not
&lt;/h2&gt;

&lt;p&gt;Not everything a trading site does is a feed. b1dz's WebSocket tickers, its two-second arbitrage ticks, its private broker and exchange accounts, its DEX quotes and its DealDash session stay where they are; so do advis0r's signal extraction, corroboration, scoring, speech-to-text and the LLM analysis. What moved is the data both sites, and any site like them, need the same copy of.&lt;/p&gt;

&lt;h2&gt;
  
  
  nichedb 0.8
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The symbol directory.&lt;/strong&gt; &lt;code&gt;alpaca-assets&lt;/code&gt; keeps every tradable US equity and crypto asset Alpaca lists, about 14,400 rows, refreshed daily. Feed: &lt;a href="https://nichedb.dev/f/us-symbols" rel="noopener noreferrer"&gt;us-symbols&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price history.&lt;/strong&gt; &lt;code&gt;equity-history&lt;/code&gt; keeps one item a symbol carrying its last four hundred daily bars, appended after each session settles from the multi-symbol bars endpoint, about ninety requests an evening for 12,900 symbols. An adapter can read its own previous items back now, so a window is kept, not rebuilt. Feed: &lt;a href="https://nichedb.dev/f/price-history" rel="noopener noreferrer"&gt;price-history&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fundamentals.&lt;/strong&gt; &lt;code&gt;sec-fundamentals&lt;/code&gt; walks every SEC filer's XBRL facts once a week at five requests a second: revenue, income, EPS, assets, liabilities, cash, operating cash flow, shares, from 10-K, 10-Q, 20-F and 40-F, the last twelve points of each. Feed: &lt;a href="https://nichedb.dev/f/fundamentals" rel="noopener noreferrer"&gt;fundamentals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The crypto universe.&lt;/strong&gt; A new &lt;code&gt;crypto&lt;/code&gt; collection: &lt;code&gt;coingecko-assets&lt;/code&gt; (the top 500 by market cap every quarter hour) and &lt;code&gt;crypto-pairs&lt;/code&gt; (every spot pair on Kraken, Coinbase, Binance.US and Gemini with its 24-hour ticker, every five minutes, one row a pair a venue, 2,190 of them). The doc says where each venue is odd: Kraken's daily open is midnight UTC, Gemini publishes no volume on any all-pairs endpoint, Coinbase has no bid or ask. Feeds: &lt;a href="https://nichedb.dev/f/usd-pairs" rel="noopener noreferrer"&gt;usd-pairs&lt;/a&gt;, &lt;a href="https://nichedb.dev/f/top-100" rel="noopener noreferrer"&gt;top-100&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Two things found on the way. A source seeded off because its key was missing stayed off after the key arrived, since the seed never touched the flag; the Alpaca corporate-actions source had sat at zero items for that reason, and now has 6,400. And the seed judged a source's needs against a slice of the config that did not hold the contact email the SEC wants in a user agent. Both fixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sites
&lt;/h2&gt;

&lt;p&gt;advis0r.com runs with &lt;code&gt;NICHEDB_MARKETS=1&lt;/code&gt;: a report build reads the symbol's history item and the company's fundamentals item, two requests, and falls back to the live path when either is missing or stale; the symbol sync walks the directory by cursor; a ticker's news refresh reads nichedb's market news wire before the RSS feeds. Fresh reports record their bars source, and today's say nichedb.&lt;/p&gt;

&lt;p&gt;b1dz.com runs with &lt;code&gt;NICHEDB_CRYPTO=1&lt;/code&gt; and &lt;code&gt;NICHEDB_MARKETS=1&lt;/code&gt;: pair discovery reads the dollar-quoted pairs and the assets from nichedb, eleven requests every five minutes instead of six calls to five hosts, and applies the same rules, two venues or more with fifty thousand dollars a day on each and ten million of market cap; backtests read the history item and fall back to Yahoo when the window is longer than it. Its first discovery pass from nichedb picked 150 pairs from 1,582 rows.&lt;/p&gt;

&lt;p&gt;That makes five sites on nichedb: tipoffwatch, genrewatch, bittorrented, advis0r and b1dz, plus nixamp. The contract for each collection is one document in the repo.&lt;/p&gt;

&lt;p&gt;nichedb: &lt;a href="https://nichedb.dev" rel="noopener noreferrer"&gt;https://nichedb.dev&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>nichedb 0.7 closes the gaps, tipoffwatch and genrewatch switch to it, and nixamp 0.12 shows the score</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 10 Sep 2026 19:34:53 +0000</pubDate>
      <link>https://dev.to/chovy/nichedb-07-closes-the-gaps-tipoffwatch-and-genrewatch-switch-to-it-and-nixamp-012-shows-the-4iba</link>
      <guid>https://dev.to/chovy/nichedb-07-closes-the-gaps-tipoffwatch-and-genrewatch-switch-to-it-and-nixamp-012-shows-the-4iba</guid>
      <description>&lt;p&gt;Yesterday nichedb.dev 0.6.0 took over the fetching three of our sites did for themselves. Today 0.7 closes the gaps that kept those sites on their own pollers, the sites switch, and nixamp learns the score.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was missing
&lt;/h2&gt;

&lt;p&gt;A site reading nichedb instead of ESPN loses nothing on schedules and scores, but tipoffwatch.com also showed every play of a game and a box score when it ended, and it filled in the broadcaster for games ESPN lists without one, which is most of the world outside the US. genrewatch.com showed where a film could be rented, not just streamed, and it gave a poster and a synopsis to the 400,000 titles that only exist in the IMDb ratings dump. None of that was in nichedb. Now it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  nichedb 0.7
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Play-by-play and recaps.&lt;/strong&gt; &lt;code&gt;espn-plays&lt;/code&gt; reads the ESPN summary for every fixture in play or ended in the last six hours, eight summaries a run every two minutes, which is exactly the line tipoffwatch already held. One item per fixture: the last 400 plays while the game is on, the whole log with the box score, leaders, officials and closing line once it is final. Feed: &lt;a href="https://nichedb.dev/f/play-by-play" rel="noopener noreferrer"&gt;play-by-play&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TV listings.&lt;/strong&gt; &lt;code&gt;sportsdb-tv&lt;/code&gt; keeps TheSportsDB's day listings, one item per event, channel and market, fourteen days ahead. ESPN's broadcast field is US-only; this is where "7 Queensland" for an AFL game and "Abema TV" for a sumo basho come from. Feed: &lt;a href="https://nichedb.dev/f/tv-listings" rel="noopener noreferrer"&gt;tv-listings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rent and buy.&lt;/strong&gt; A TMDB title now carries &lt;code&gt;providers: { stream, rent, buy }&lt;/code&gt; beside the flat-rate &lt;code&gt;watch&lt;/code&gt; list, and every release row says which shops carry it, so "included where I subscribe" and "available to rent" stay two questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Posters for the IMDb titles.&lt;/strong&gt; A new enricher, &lt;code&gt;tmdb-artwork&lt;/code&gt;, takes a title that came in from the IMDb dumps with nothing but a name, a year and a rating, and asks TMDB's find endpoint for the poster, the synopsis and the TMDB id. The backlog is 430,000 titles at about 29,000 a day, so it takes a couple of weeks; new titles get theirs within a tick, and a mirror sees the poster land because the row's updated_at moves with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A game by the name a playlist gives it.&lt;/strong&gt; &lt;code&gt;GET /api/v1/match?q=NFL: Chiefs vs Bills&lt;/code&gt; answers with the fixture. The parser reads two sides and a league out of "Lakers @ Celtics", "Arsenal v Chelsea", "Rangers at Celtic 19:45" and the messier things IPTV playlists actually say, "US (ESPN+ 017) | MLB: Rays vs. Braves (ESP) (2026-09-10 11:30:10)", and the match scores each fixture in the window by team-name agreement in either order: abbreviations, short names, "Man Utd", "Alcorn St". One side only scores below the floor, so "Chiefs" alone is never a Kaizer Chiefs game.&lt;/p&gt;

&lt;p&gt;The contract for anyone mirroring a collection is one document, &lt;code&gt;docs/consolidation.md&lt;/code&gt; in the repo, and the CLI is on npm as &lt;code&gt;@profullstack/nichedb&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sites switched
&lt;/h2&gt;

&lt;p&gt;tipoffwatch.com runs with &lt;code&gt;SPORTS_PROVIDERS=nichedb-sports&lt;/code&gt;: leagues, teams, fixtures, live scores, plays, recaps and broadcasters all come from nichedb, and the site's own ESPN, Live Tennis and TheSportsDB polling is off. Its first pass took 8,045 fixtures from 41 requests and filled 43 broadcasters from nichedb's listings with 9 more; a steady minute is one request. genrewatch.com runs with &lt;code&gt;CATALOG_PROVIDERS=nichedb,musicbrainz,spacedevs&lt;/code&gt;: films, TV and anime come from nichedb at 250 pages a pass; music and launches stay where they were. bittorrented.com has the same switches behind &lt;code&gt;NICHEDB_TITLES&lt;/code&gt; and &lt;code&gt;NICHEDB_MIRROR&lt;/code&gt;, and its nightly IMDb dump import can be retired the night the mirror runs.&lt;/p&gt;

&lt;p&gt;Two things bit on the way and are worth knowing if you mirror nichedb yourself. Bun's postgres driver hands jsonb back as text in production where PGlite parses it in tests, so a cursor spread into an object was one key per character and every tick was a full backfill. And nichedb keeps a fixture once per source that saw it, the schedule pass and the live pass both, so a page can carry the same fixture twice; collapse a page to one row per external id, latest updated_at first, before a batch upsert or Postgres will refuse the statement.&lt;/p&gt;

&lt;h2&gt;
  
  
  nixamp: the score under the player
&lt;/h2&gt;

&lt;p&gt;nixamp 0.12 finishes the enrichment story: films, shows and channels got their posters and logos in 0.11; sports was the piece left. A channel or playlist entry named for two teams is a game, and the player asks nichedb which one and draws the score line under the video: both teams with their logos, the score, the quarter and clock while it is on, the kickoff time before, FINAL after, and the broadcaster. It re-asks once a minute while the game is live, and a miss on a game name is believed only a minute too, since the fixture may be listed by the time the channel is on.&lt;/p&gt;

&lt;p&gt;Install or update nixamp with one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nixamp.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;nichedb: &lt;a href="https://nichedb.dev" rel="noopener noreferrer"&gt;https://nichedb.dev&lt;/a&gt;. nixamp: &lt;a href="https://github.com/profullstack/nixamp" rel="noopener noreferrer"&gt;https://github.com/profullstack/nixamp&lt;/a&gt; and r/nixamp.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>nichedb 0.6.0 keeps sports, screen and channels once; nixamp 0.11 is the first thing built on it</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 10 Sep 2026 14:17:33 +0000</pubDate>
      <link>https://dev.to/chovy/nichedb-060-keeps-sports-screen-and-channels-once-nixamp-011-is-the-first-thing-built-on-it-38ea</link>
      <guid>https://dev.to/chovy/nichedb-060-keeps-sports-screen-and-channels-once-nixamp-011-is-the-first-thing-built-on-it-38ea</guid>
      <description>&lt;p&gt;Two releases today that belong together: nichedb.dev 0.6.0 gains the data three of our sites used to fetch for themselves, and nixamp 0.11.0 is the first thing built on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  nichedb: sports, screen and channels
&lt;/h2&gt;

&lt;p&gt;tipoffwatch.com polled ESPN for every league and fixture. genrewatch.com polled TMDB, TVmaze, AniList and the IMDb dumps for every film and show. bittorrented.com imported the same IMDb dumps again and kept its own TMDB cache. Three copies of the same fetching, three places for it to break.&lt;/p&gt;

&lt;p&gt;nichedb.dev now keeps all of it once, as three collections anybody can read without a key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sports&lt;/strong&gt;: every league, team and fixture ESPN publishes across 17 sports and 354 leagues, with schedule, live score, line, venue and broadcast, plus tennis by tour from the Live Tennis API. Live scores refresh every minute for leagues with a game on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;screen&lt;/strong&gt;: films, TV and anime as titles and releases, to cinemas, to rent, to a streaming service, to air, with posters, genres and the IMDb rating behind 400,000 titles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;channels&lt;/strong&gt;: the whole iptv-org directory, 31,000 television channels with logo, country, category and network, and the public stream where one exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one new question the API answers: what is this name? &lt;code&gt;GET /api/v1/match?q=Top.Gun.Maverick.2022.1080p.WEB-DL.mkv&lt;/code&gt; reads the name the way a torrent site learned to, year and episode and quality tags and release group stripped, and answers with the best title and a score. The same parser lives in one module, so a player, a torrent site and a TV guide all match the same way. The items API gains tag filters, date windows and a since parameter for anyone keeping a copy. The CLI gains &lt;code&gt;nichedb match&lt;/code&gt;; the MCP server gains &lt;code&gt;match_items&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The sites will switch to reading nichedb next, and drop their own pollers.&lt;/p&gt;

&lt;h2&gt;
  
  
  nixamp: the player knows what it is playing
&lt;/h2&gt;

&lt;p&gt;A file called "Oppenheimer (2023) 1080p HDTS.mkv" is a film. A playlist entry called "US: ESPN2 HD" is a channel. nixamp knew neither, because ffprobe reads tags and a download's tags are its file name.&lt;/p&gt;

&lt;p&gt;nixamp 0.11.0 asks nichedb. The server you are connected to asks once per name and remembers the answer, misses too, so a library of five thousand files is not five thousand requests. Under the title the page now shows the poster, the year, the rating, the genres and a line of synopsis for a film, and the logo, country, category and network for a channel. Below half similarity nothing is shown: a wrong poster is worse than none.&lt;/p&gt;

&lt;p&gt;Install or update nixamp with one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nixamp.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;nichedb: &lt;a href="https://nichedb.dev" rel="noopener noreferrer"&gt;https://nichedb.dev&lt;/a&gt;. nixamp: &lt;a href="https://github.com/profullstack/nixamp" rel="noopener noreferrer"&gt;https://github.com/profullstack/nixamp&lt;/a&gt; and r/nixamp.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>nixamp 0.10.0: paste any link and it plays, keep it with one click, and every live has its own room</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 10 Sep 2026 12:40:05 +0000</pubDate>
      <link>https://dev.to/chovy/nixamp-0100-paste-any-link-and-it-plays-keep-it-with-one-click-and-every-live-has-its-own-room-2449</link>
      <guid>https://dev.to/chovy/nixamp-0100-paste-any-link-and-it-plays-keep-it-with-one-click-and-every-live-has-its-own-room-2449</guid>
      <description>&lt;p&gt;nixamp 0.10.0 is out. Three things: paste any link and it plays, keep it with one click, and every live stream has its own phone room.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paste any link
&lt;/h2&gt;

&lt;p&gt;Under the player there is a box. Paste a page into it, a podcast episode, a SoundCloud track, an Internet Archive film, a TikTok live, and the server you are connected to works out where the media actually is and plays it to you as a channel of its own. It is played the way a catalog entry is played: started for whoever asked, stopped a minute after the last viewer leaves, and shared by two people who paste the same link. A bare link to an MP3 or an HLS playlist needs no working-out at all.&lt;/p&gt;

&lt;p&gt;The working-out is yt-dlp's, which knows a thousand sites and is updated weekly. The installer fetches it as a standalone binary beside nixamp when it is not already there, so &lt;code&gt;nixamp update&lt;/code&gt; is the whole setup. Some sites refuse a datacenter without a signed-in browser, YouTube and Vimeo among them; the player says so in its note, and a Netscape cookies file at &lt;code&gt;~/.local/state/nixamp/cookies.txt&lt;/code&gt; is passed through when you put one there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download now
&lt;/h2&gt;

&lt;p&gt;Beside the picture there is a Download button for a link that is a whole file somewhere. The server fetches it through yt-dlp and hands the bytes straight on as a download, named for the title, so your own machine ends up with the file and the server keeps nothing. Sound alone for a track, the picture too for a film. A live stream has no whole yet, so it gets no button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every live has its own room
&lt;/h2&gt;

&lt;p&gt;nixamp streams have a call-in number. Until today one code per server put the people calling about the basketball in with the people talking about the film. The directory now gives each channel a code of its own, keeps it while the channel stays on, and the phone line greets a caller by the channel: "Welcome to the live room for FIBA World Cup. There are 3 people in here." The code is shown on the Live rows, on the line under the title, and in the directory beside each channel with how many are on the phone for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also in this run
&lt;/h2&gt;

&lt;p&gt;Since the morning's 0.9 post: the top of the page says LOADING with a turning ring while a stream starts instead of STOPPED, a meta line under the picture says what is playing and who is watching, GO LIVE sits beside Play for whoever administers the server and puts anything on the air with the link copied, a joiner is told the number and the code on the line under the title, and the code shown is always the one the phone line knows.&lt;/p&gt;

&lt;p&gt;Install or update with one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nixamp.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source and releases: &lt;a href="https://github.com/profullstack/nixamp" rel="noopener noreferrer"&gt;https://github.com/profullstack/nixamp&lt;/a&gt;. Talk about it at &lt;a href="https://www.reddit.com/r/nixamp" rel="noopener noreferrer"&gt;https://www.reddit.com/r/nixamp&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>streaming</category>
      <category>tools</category>
    </item>
    <item>
      <title>nixamp 0.9.10: the phone line says hello, LOADING while it loads, and Go Live beside Play</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 10 Sep 2026 12:01:44 +0000</pubDate>
      <link>https://dev.to/chovy/nixamp-0910-the-phone-line-says-hello-loading-while-it-loads-and-go-live-beside-play-2pmj</link>
      <guid>https://dev.to/chovy/nixamp-0910-the-phone-line-says-hello-loading-while-it-loads-and-go-live-beside-play-2pmj</guid>
      <description>&lt;p&gt;nixamp 0.9.10 went out today with three things people asked for after using 0.9 for a morning, plus a phone line that finally says hello.&lt;/p&gt;

&lt;h2&gt;
  
  
  The phone line answers properly
&lt;/h2&gt;

&lt;p&gt;nixamp streams have a call-in number. You dial 408-357-2326, key the six digit code shown beside the stream, and you land in a room with everyone else watching. Yesterday that room was silent. Telnyx's own call log showed why: the welcome was spoken on the leg and then the leg was put into the conference, and the conference cut the speech off in the same millisecond. Callers heard nothing, waited, and hung up.&lt;/p&gt;

&lt;p&gt;The welcome is now spoken inside the room, addressed to the person who just arrived: "Welcome to the live room for server1, playing And Justice For All. There are 3 people in here." The first caller is told they are first. A partial code is answered in four seconds with how many digits were heard, a voice that fails is swapped for a plain one, and three empty prompts end the call with a goodbye instead of a fourth prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  LOADING, not STOPPED
&lt;/h2&gt;

&lt;p&gt;Playing something from an IPTV catalog can take half a minute. The server probes the source, starts a decoder, and only then has bytes to send. For all of that the top of the page said STOPPED, which reads as "that click did nothing". It now says LOADING with a ring that turns, the row you clicked spins, and the same state covers a catalog's groups and entries while they are fetched.&lt;/p&gt;

&lt;h2&gt;
  
  
  A line about what is playing
&lt;/h2&gt;

&lt;p&gt;Under the picture there is a row of chips: live or on demand, the picture's size, how many people are watching a channel and how long it has been on, which catalog and group it came from with the provider's logo, and the phone code when the server is listed. It is text only. Every word of it was written by a provider or a stranger, and none of it is rendered as markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go Live, beside Play
&lt;/h2&gt;

&lt;p&gt;If you administer the server you see GO LIVE next to Play, and the same icon on every row that plays. Play plays it for you. Go Live puts it on the air for everyone: a catalog film or channel becomes a channel that stays up with nobody watching, is written down so a restart brings it back, and is announced to the directory immediately. A file from your library is played on the server. If the server was not listed it gets listed, and the share link is copied so you can send it.&lt;/p&gt;

&lt;p&gt;The channels a listed server carries are now rows in the directory, each with its own Play, so what somebody went live with is the thing a visitor can press.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smaller things
&lt;/h2&gt;

&lt;p&gt;The jingle no longer plays over a stream a link asked for, or over the click that starts one. It only plays into silence.&lt;/p&gt;

&lt;p&gt;Install or update with one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://nixamp.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source and releases: &lt;a href="https://github.com/profullstack/nixamp" rel="noopener noreferrer"&gt;https://github.com/profullstack/nixamp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>nixamp 0.9: a server that names itself, and a CNN feed that stopped talking</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 10 Sep 2026 10:20:48 +0000</pubDate>
      <link>https://dev.to/chovy/nixamp-09-a-server-that-names-itself-and-a-cnn-feed-that-stopped-talking-3b3o</link>
      <guid>https://dev.to/chovy/nixamp-09-a-server-that-names-itself-and-a-cnn-feed-that-stopped-talking-3b3o</guid>
      <description>&lt;h1&gt;
  
  
  nixamp 0.9: a server that names itself, and a CNN feed that stopped talking
&lt;/h1&gt;

&lt;p&gt;A day of nixamp releases, 0.7.36 through 0.9.3. It started with a bug report ("I see CNN but get no audio or video, and the graph is moving") and ended with servers that get their own DNS name and certificate without anyone touching a registrar.&lt;/p&gt;

&lt;h2&gt;
  
  
  The frozen channel
&lt;/h2&gt;

&lt;p&gt;CNN had been on the air for twelve hours and showed nobody anything. The graph moved because it was the server's own player mirrored over SSE, not the channel. On the box, the ffmpeg was alive, its byte count frozen, and its stderr socket had 131 KB queued that nothing had ever read. Node only reads a piped stream that is flowing. An IPTV transport stream logs a line for every corrupt packet, the 64 KB pipe filled, and ffmpeg blocked on its next write with every thread waiting on the one that was stuck. It never exited, so nothing redialled.&lt;/p&gt;

&lt;p&gt;Every ffmpeg a channel spawns now has its stderr drained. Remote pulls get a read timeout and a watchdog, and a redial resets the stream header instead of appending a second one mid-picture.&lt;/p&gt;

&lt;p&gt;Then the channel "looped." A late joiner was handed only future bytes, so it started on the live edge with nothing buffered and stalled on every hiccup. A newcomer now gets the last six seconds first, from a fragment boundary. Measured in Chrome: six stalls in thirty seconds before, zero after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catalogs
&lt;/h2&gt;

&lt;p&gt;An m3u with three thousand IPTV entries used to become three thousand flat rows in the playlist. A server now keeps named catalogs, reads group-title and logos, and sorts entries into live and on demand. Anyone with the link browses by group and plays. A live entry becomes a channel started on the first click and stopped a minute after the last viewer leaves, capped at four at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Names and certificates
&lt;/h2&gt;

&lt;p&gt;server2 came up as &lt;a href="http://152.53.47.37:4321" rel="noopener noreferrer"&gt;http://152.53.47.37:4321&lt;/a&gt;. The only reason server1 had a name was that someone typed an A record into Porkbun and copied a wildcard certificate onto the box by hand.&lt;/p&gt;

&lt;p&gt;Now a signed-in server asks nixamp.com for a name and gets label.handle.nixamp.com with A and AAAA records, written with keys only nixamp.com holds. It asks for the handle's certificate and gets one Let's Encrypt wildcard per handle, issued by DNS-01 and renewed on nixamp.com, cached on the box and swapped into the running listener once a day. The API is account-gated and metered with @profullstack/throttle. &lt;code&gt;nixamp dns&lt;/code&gt; manages names by hand, and it is 2026, so v6 is not an afterthought.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one that mattered most
&lt;/h2&gt;

&lt;p&gt;A daemon restarted from a home directory served the home directory, under a public listing. That default is gone. The library is a saved setting, asked for at sign-in or first start, and the whole filesystem, the home directory, anything above it, and anything under a hidden folder in home are refused however they are asked for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Smaller things
&lt;/h2&gt;

&lt;p&gt;The installer restarts a running daemon with its saved flags, and pulled channels survive a restart. The directory shows what is on each server and offers Viewer and Admin, with Admin greyed unless the server is yours. Favourites are a heart. The tab says what is playing.&lt;/p&gt;

&lt;p&gt;Install: &lt;code&gt;curl -fsSL https://nixamp.com/install.sh | sh&lt;/code&gt;, then &lt;code&gt;nixamp login&lt;/code&gt;, &lt;code&gt;nixamp library ~/Music&lt;/code&gt;, &lt;code&gt;nixamp daemon start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Drafted with Claude Code from the session that did the work.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>javascript</category>
      <category>node</category>
      <category>software</category>
    </item>
    <item>
      <title>myna lists your product now, over MCP</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 09 Sep 2026 19:40:20 +0000</pubDate>
      <link>https://dev.to/chovy/myna-lists-your-product-now-over-mcp-4men</link>
      <guid>https://dev.to/chovy/myna-lists-your-product-now-over-mcp-4men</guid>
      <description>&lt;p&gt;myna posts to social networks. As of 0.17.0 it also lists the product itself, in a software directory, over that directory's MCP server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;myna directory saasrow https://example.com &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
myna directory saasrow https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads the page, works out the name, writes the description, picks the category, and submits.&lt;/p&gt;

&lt;h2&gt;
  
  
  A listing is not a post
&lt;/h2&gt;

&lt;p&gt;I started by trying to make a directory just another network adapter. That was wrong. A post is text with optional media. A listing is a name, a website, a description, a category and some controlled vocabulary, and a human being at the directory reads it before anyone else does. Pushing that through &lt;code&gt;post()&lt;/code&gt; would have given all 30 network adapters fields they have no use for.&lt;/p&gt;

&lt;p&gt;So directories are a separate subsystem with a separate command and separate credentials.&lt;/p&gt;

&lt;p&gt;The credentials part turned out to matter more than I expected. &lt;code&gt;myna post --to all&lt;/code&gt; resolves to every account whose network is not marked as an explicit target, and a network it does not recognise reads as "not explicit". A directory key stored among the accounts would therefore have quietly become a posting target. Somebody's stray thought about lunch, submitted as a product. Directory keys live in their own part of the vault, and there is a test that fails if that ever changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the server instead of guessing
&lt;/h2&gt;

&lt;p&gt;The interesting half is that myna is now an MCP client, not only a server.&lt;/p&gt;

&lt;p&gt;SaaSRow publishes an MCP endpoint. So rather than writing a REST client against its listing API, myna calls its tools. The tool schemas describe the fields, which means SaaSRow can add one without a myna release.&lt;/p&gt;

&lt;p&gt;That works so well it seemed silly to stop at directories myna ships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;myna directory add acme https://acme.example/api/mcp
myna directory tools acme
myna directory acme https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no adapter behind that. myna reads the tool table, finds whichever tool creates a listing whatever it happens to be called, and sends each field under the name that tool's own schema uses. A directory whose field is &lt;code&gt;product_url&lt;/code&gt; rather than &lt;code&gt;website&lt;/code&gt; needs no code. A field the server marks required and myna could not work out fails before the call rather than after it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;myna directory tools &amp;lt;id&amp;gt;&lt;/code&gt; prints what a server actually offers, which is the first useful thing to look at when a submission gets refused.&lt;/p&gt;

&lt;p&gt;Signing in is the exception. There is no MCP method for it, and SaaSRow's is an emailed one-time code, which is a conversation with a person rather than a tool call. So that adapter stays written, and a custom directory takes a key you already have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things I got wrong on the way
&lt;/h2&gt;

&lt;p&gt;The product name came out as &lt;code&gt;widget.test&lt;/code&gt; in testing. &lt;code&gt;fetchPage&lt;/code&gt; falls back to the hostname when a page declares no &lt;code&gt;og:site_name&lt;/code&gt;, and I was preferring site name over title. A site name equal to the host carries no information. Fixed, with a test.&lt;/p&gt;

&lt;p&gt;And an empty result printed nothing at all, which reads as a command that never ran rather than a directory with nothing to say. Empty output is not a pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it is
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;myna directory&lt;/code&gt; in the shell, &lt;code&gt;/directory&lt;/code&gt; in the TUI, a Directories screen in the desktop app, and four tools over MCP so an agent can do it. cli-tools 0.31.0 adds &lt;code&gt;/myna:directory&lt;/code&gt; as a slash command.&lt;/p&gt;

&lt;p&gt;myna 0.17.0 is out: &lt;a href="https://github.com/profullstack/mynaposter/releases/tag/v0.17.0" rel="noopener noreferrer"&gt;https://github.com/profullstack/mynaposter/releases/tag/v0.17.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post was drafted with AI assistance and edited by me.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>nichedb 0.5.0: the sea, the surf and the aircraft worth watching</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 09 Sep 2026 19:29:24 +0000</pubDate>
      <link>https://dev.to/chovy/nichedb-050-the-sea-the-surf-and-the-aircraft-worth-watching-1cb9</link>
      <guid>https://dev.to/chovy/nichedb-050-the-sea-the-surf-and-the-aircraft-worth-watching-1cb9</guid>
      <description>&lt;h1&gt;
  
  
  nichedb 0.5.0: the sea, the surf and the aircraft worth watching
&lt;/h1&gt;

&lt;p&gt;Three more sources, all keyless, all verified against the live upstream before a line of the reader was written.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole ocean in one file
&lt;/h2&gt;

&lt;p&gt;The National Data Buoy Center publishes the newest observation from every buoy it is listening to in a single hundred-kilobyte file. 853 reports when I read it, 184 of them carrying a wave height, and each one holding wind, gust, pressure, air temperature and water temperature alongside.&lt;/p&gt;

&lt;p&gt;A surf report is three of those numbers and everything else is presentation. How big the swell is, how far apart the waves are, and where it is coming from. The middle one is the number people leave out, and it is the one that decides the day: two metres at 18 seconds and two metres at 5 seconds are not the same sea at all. So nichedb publishes a wave row as "8.9 ft at 8 s, mixed swell out of the W" as well as storing the numbers, and it stores the height in feet as well as metres, because every surf forecast in the United States is in feet and converting at read time is how a nine-foot day quietly becomes a three-foot day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The forecaster's own words next to the buoy's numbers
&lt;/h2&gt;

&lt;p&gt;A buoy off Oahu reading 2.4 metres at 16 seconds is a fact. "Advisory level surf along south facing shores through today" is what that fact means to somebody standing on the beach, and no amount of arithmetic over the buoy produces it.&lt;/p&gt;

&lt;p&gt;So the surf zone forecast the National Weather Service issues from every coastal office is in there too, in full. The high surf advisory and the rip current risk are lifted out of the forecaster's own wording into tags, so a coast under an advisory can be found without reading all 851 products, and the text itself is kept as the authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aircraft, but only the ones that mean something
&lt;/h2&gt;

&lt;p&gt;There is a version of this that stores every aeroplane in the sky every few minutes. It would be tens of thousands of rows a day, each one a position that was true for four seconds, and nobody would ever read a single one of them.&lt;/p&gt;

&lt;p&gt;A position is not news. An aircraft squawking 7700 is. So nichedb reads the event surfaces of the ADS-B network instead: the three emergency transponder codes, and the military list. When I wrote this the emergency codes returned zero aircraft between them and the military query returned 422, which is exactly the shape you want. The rare thing is rare, so its appearance means something.&lt;/p&gt;

&lt;p&gt;And an emergency is an episode rather than a ping. An aircraft squawking 7700 shows up in poll after poll and then simply stops showing up, and nothing anywhere records that it stopped. So the open episodes are held between polls, the row is keyed on when the aircraft was first seen, and when it leaves the list it is written once more with a duration on it. "N123AB squawked 7700 for 22 minutes" is the row worth keeping. The forty positions in between are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things the live data decided
&lt;/h2&gt;

&lt;p&gt;NDBC writes a missing reading as MM, in every column, in a fixed-width table. Anything that coerces that to a number turns "this buoy has no anemometer" into a flat calm and "no wave sensor" into a dead flat sea, and 669 of the 853 stations report no wave height at all.&lt;/p&gt;

&lt;p&gt;The station register really does publish an empty name. Station 15009 in the Atlantic array is one of several, and an empty string left alone beats the fallback and titles the row with nothing at all. That showed up in the very first smoke run as a list of observations called ": marine observation".&lt;/p&gt;

&lt;p&gt;Some stations report a dominant period of 0 or 1 second. That is a sensor saying nothing, and calling it windswell states something about the water that nobody measured.&lt;/p&gt;

&lt;p&gt;And the ADS-B network rate limits its military list far harder than its squawk lists, answering 429 while the endpoint next to it answers 200. A rate limit is not a failure, so it returns nothing and asks to be called back later, which also keeps the cursor intact. A reader that threw there would leave every open episode untouched and then report all of them as ended on the run after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading it
&lt;/h2&gt;

&lt;p&gt;Twenty-seven collections, seventy-nine adapters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nichedb.dev/c/water" rel="noopener noreferrer"&gt;https://nichedb.dev/c/water&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nichedb.dev/c/aviation" rel="noopener noreferrer"&gt;https://nichedb.dev/c/aviation&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>nichedb 0.4.0: four feeds that only mean something together</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 09 Sep 2026 19:16:26 +0000</pubDate>
      <link>https://dev.to/chovy/nichedb-040-four-feeds-that-only-mean-something-together-5835</link>
      <guid>https://dev.to/chovy/nichedb-040-four-feeds-that-only-mean-something-together-5835</guid>
      <description>&lt;h1&gt;
  
  
  nichedb 0.4.0: four feeds that only mean something together
&lt;/h1&gt;

&lt;p&gt;nichedb 0.4.0 adds three collections and thirteen sources. Every one is keyless, every one was queried live before it was written, and the reason they shipped together is that none of them is worth much alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aviation: why the flight is late, and what happened the last 31,000 times it went wrong
&lt;/h2&gt;

&lt;p&gt;The FAA publishes, every couple of minutes, the traffic management initiatives in force right now. Ground stops, ground delay programs, airspace flow programs, airport closures. It is the document the airlines read and it is about two kilobytes.&lt;/p&gt;

&lt;p&gt;It is also a snapshot rather than a log. It tells you a ground stop exists. It never tells you one ended, and nothing anywhere records how long it ran. So nichedb holds the open programs between polls and writes each one a second time when it disappears, with the duration attached. "BOS ground stop, 3h 40m, thunderstorms" is a fact about a day of flying that exists only if something was watching the whole time.&lt;/p&gt;

&lt;p&gt;Two feeds sit next to it. Every SIGMET and AIRMET in force over the country, and the decoded observation at each reporting airport: flight category, wind, visibility, cloud layers. When the FAA says ORD is stopped for thunderstorms, the observation at ORD in that minute and the convective SIGMET drawn over the field are right there.&lt;/p&gt;

&lt;p&gt;Then the part that makes it a record rather than a status board: all 31,124 accident and incident investigations the NTSB has published. The NTSB has no working query API, so this reads the bulk Access archive it rebuilds monthly. Inside, the events table carries a column holding the raw METAR at the moment of each accident, in the identical format the hourly feed publishes today. The same field, from the same service, thirty years apart.&lt;/p&gt;

&lt;p&gt;An NTSB investigation is published long before it is finished. A preliminary report within days, a probable cause that can take two years, all under one event id. So the archive is deliberately re-read and each accident updated in place. In the newest slice, 6 of 300 have been ruled on. Eight thousand rows deeper it is 307 of 400. Those rows arriving later are the reason to re-read at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Water: too much and too little, measured rather than forecast
&lt;/h2&gt;

&lt;p&gt;Every NOAA river gauge at or above its action stage, observed and forecast kept as separate rows, because "is it flooding" and "will it" are different questions and a forecast that turns out wrong belongs next to the observation that contradicted it.&lt;/p&gt;

&lt;p&gt;Coastal water levels are the harder half. A tide gauge returns a number of feet and nothing else, and whether 3.1 feet is a Tuesday or a flooded car park depends entirely on the station. The thresholds live in a different service, one call per gauge, so nichedb reads them once and keeps them. A reading arrives as "minor coastal flooding, 0.13 ft over stage" instead of as a number with no scale.&lt;/p&gt;

&lt;p&gt;Underneath both, the US Drought Monitor, redrawn weekly. A gauge says what is happening this hour. The drought map says what the last six months did to the ground that river runs through, which decides whether the next storm runs off or soaks in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consumer finance: what people say their bank did, and who the bank actually is
&lt;/h2&gt;

&lt;p&gt;The Consumer Financial Protection Bureau publishes every complaint Americans file about a bank, a lender, a credit bureau or a debt collector. 17.6 million of them, around seven thousand a day, live within a day or two of being filed.&lt;/p&gt;

&lt;p&gt;Each names a company as a bare uppercase string with no identifier of any kind attached. So the collection carries the FDIC register beside it: every insured institution with its charter, regulator and assets, and every merger, failure, conversion and branch opening or closing on it. A complaint about a bank absorbed eighteen months ago belongs to whoever absorbed it, and the structure feed is the only public record of that.&lt;/p&gt;

&lt;p&gt;One thing worth knowing before reading any count from this dataset. Of the 10,000 complaints filed on 7 and 8 September, 9,843 were about credit reporting and one was about a mortgage. It is overwhelmingly a record of disputes with the three credit bureaus, and any comparison across products that does not say so is misleading.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the live data actually decided
&lt;/h2&gt;

&lt;p&gt;Half the work in a release like this is finding out that an upstream is lying to you politely.&lt;/p&gt;

&lt;p&gt;The CFPB search accepts an offset, echoes it back, and ignores it. Three requests at three different offsets return byte-identical pages. The first version of the reader paged that way and stored the same 500 complaints ten times over while reporting 5,000 new rows. It walks days instead.&lt;/p&gt;

&lt;p&gt;A METAR bounding box silently caps at 400 stations. A box over the continental United States returns exactly 400. Its eastern half returns 247 and its western half 240. Nothing in the response says it was truncated. The reader now splits a box whose answer comes back at the cap: 1,101 stations where the single box had claimed 400.&lt;/p&gt;

&lt;p&gt;NOAA's river gauge service accepts state, office and region filters, ignores all three, and answers with all 12,000 gauges, which is the kind of failure that looks exactly like success.&lt;/p&gt;

&lt;p&gt;FDIC transaction numbers are not row identifiers. One merger writes a row for every institution and office it touches. Keyed on the transaction number, a third of the register overwrites the rest, and the loss looks like a quiet quarter.&lt;/p&gt;

&lt;p&gt;And the NTSB writes its dates as 08/22/26. Handed that, a date parser returns 1926. Every accident in the file would publish a century early and sort ahead of everything else in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading it
&lt;/h2&gt;

&lt;p&gt;Twenty-seven collections, seventy-six adapters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nichedb.dev/c/aviation" rel="noopener noreferrer"&gt;https://nichedb.dev/c/aviation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nichedb.dev/c/water" rel="noopener noreferrer"&gt;https://nichedb.dev/c/water&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nichedb.dev/c/consumer-finance" rel="noopener noreferrer"&gt;https://nichedb.dev/c/consumer-finance&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Podcasts, split by who serves the feed</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:36:06 +0000</pubDate>
      <link>https://dev.to/chovy/podcasts-split-by-who-serves-the-feed-am0</link>
      <guid>https://dev.to/chovy/podcasts-split-by-who-serves-the-feed-am0</guid>
      <description>&lt;p&gt;Every podcast directory is a search box over one undifferentiated pile. Apple has about 2.5 million shows and one ranking. Spotify has the same shows and a different ranking. Neither will tell you the thing that actually sorts the medium in two, because both are in the business of the half that pays.&lt;/p&gt;

&lt;p&gt;That split is: who serves the feed. A show either sits on a service that hosts thousands of other shows, or it sits on the domain of the show itself. One side has a company and a hosting bill behind it. The other side is a person publishing from their own site.&lt;/p&gt;

&lt;p&gt;NicheDB now has a podcasts collection, and that split is the whole thing. Two sources, &lt;code&gt;podcasts-commercial&lt;/code&gt; and &lt;code&gt;podcasts-self-hosted&lt;/code&gt;, disjoint by construction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring it instead of guessing
&lt;/h2&gt;

&lt;p&gt;Nothing in a feed declares which side it is on, so we counted. From the Podcast Index bulk dump of 2026-08-23: 4,718,246 feeds, of which 421,928 were live (HTTP 200 on the last crawl, an episode inside 90 days).&lt;/p&gt;

&lt;p&gt;307 registrable domains carry 25 or more of those live feeds. Between them they hold 395,690, which is 93.8% of the live catalogue. anchor.fm alone is 97,805. The remainder, about 13,400 domains, is the self-hosted half.&lt;/p&gt;

&lt;p&gt;A hand-written list of "the podcast hosts" would have been a list of the ones we had heard of. The long tail here is German church networks, Japanese radio stations and Czech public broadcasters. Counting finds those. Memory does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap that would have quietly emptied it
&lt;/h2&gt;

&lt;p&gt;The Podcast Index &lt;code&gt;host&lt;/code&gt; column sometimes holds a bare public suffix, &lt;code&gt;co.uk&lt;/code&gt; or &lt;code&gt;com.br&lt;/code&gt; or &lt;code&gt;org.au&lt;/code&gt;, rather than the registrable domain. Every one of those clears a 25 feed threshold.&lt;/p&gt;

&lt;p&gt;Left in the platform list, a suffix match files every .co.uk podcast in Britain as commercially hosted, and the independent half goes to nearly zero. That is 453 feeds whose real host we do not know. They are dropped, which sends them to the self-hosted side. When the whole point is finding independent shows, the error you want is the one that keeps them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug we shipped, and what it taught us
&lt;/h2&gt;

&lt;p&gt;The collection went live reading down from the newest entries of the upstream directory until it met the last run's marker. That is right for keeping up and useless for starting. It sat at 2,000 items: 1,988 self-hosted and 12 commercial.&lt;/p&gt;

&lt;p&gt;Nothing errored. No run failed. It just would never have grown.&lt;/p&gt;

&lt;p&gt;The reason is worth writing down. We sampled the directory's listing at ten evenly spaced depths. The first page is 2 commercial out of 200. Every other depth runs 178 to 194 out of 200. Overall, 84%.&lt;/p&gt;

&lt;p&gt;The head of that list is not a sample of the catalogue. It is inverted against it. Recent arrivals are whatever was submitted lately, and lately that has been almost entirely independent shows, while the bulk behind them came from old bulk imports and looks nothing like it. So the commercial source read 2,000 feeds, found 12, and roughly 230,000 sat three pages further down.&lt;/p&gt;

&lt;p&gt;The fix is a walk: page forward through the whole listing once, then switch to keeping up. Drift helps here rather than hurting. The listing grows at the head, so a row at offset N moves to N+k while you walk, which means you re-read rows you have seen instead of stepping over rows you have not. Re-reading costs nothing. Skipping would have been silent.&lt;/p&gt;

&lt;p&gt;First run after the fix: the commercial source went from 12 items to 11,490. Second run: 30,224.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it is
&lt;/h2&gt;

&lt;p&gt;Both halves are at &lt;a href="https://nichedb.dev/c/podcasts" rel="noopener noreferrer"&gt;nichedb.dev/c/podcasts&lt;/a&gt;, with feeds for each and language cuts of the independent side. Sources poll every 15 minutes, which matches the upstream directory's own floor for a feed that just published.&lt;/p&gt;

&lt;p&gt;The commercial half is already addressable in every podcast app there is. The other half is not addressable anywhere, which is the only reason to build this.&lt;/p&gt;

</description>
      <category>podcast</category>
      <category>search</category>
      <category>web</category>
    </item>
    <item>
      <title>NicheDB 0.2.0: the domain layer, counted daily</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 09 Sep 2026 10:45:05 +0000</pubDate>
      <link>https://dev.to/chovy/nichedb-020-the-domain-layer-counted-daily-28id</link>
      <guid>https://dev.to/chovy/nichedb-020-the-domain-layer-counted-daily-28id</guid>
      <description>&lt;p&gt;Twenty seven commits had merged into NicheDB since the first release and none of them had reached anybody. The CLI publishes from a GitHub release and from nothing else, so a merge to main is a midpoint rather than a finish. 0.2.0 cuts them loose.&lt;/p&gt;

&lt;p&gt;The headline is a new collection: domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The layer everything else runs on
&lt;/h2&gt;

&lt;p&gt;NicheDB tracks packages, filings, launches, weather, markets, crime, public money, housing, jobs and news. It had no view of the layer all of that lives on, which is the domain name system's own market. It does now, built from ntlddata.com's free JSON API. No key, CC-BY, and aggregate only, because ICANN's zone file terms permit analysis but not redistribution.&lt;/p&gt;

&lt;p&gt;Eight feeds, each with a page, RSS, JSON Feed, an API endpoint and MCP tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many domains exist across the new gTLDs and which way that moved, day by day, with the pre-2012 generics counted separately&lt;/li&gt;
&lt;li&gt;The whole register of 1,114 TLDs with its registry operator, inferred backend, root signing and delegation date&lt;/li&gt;
&lt;li&gt;The biggest daily gainers and the TLDs losing the most names&lt;/li&gt;
&lt;li&gt;Sunrise and claims windows with the dates registries filed with ICANN, strings contracted but not yet delegated, and registries being transitioned to an emergency operator&lt;/li&gt;
&lt;li&gt;TLDs changing operator, registrars moving platform, accreditations granted and terminated&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the collection refuses to conflate
&lt;/h2&gt;

&lt;p&gt;Two clocks. Domain counts come from zone files and are a day old. Anything attributed to a registrar comes from ICANN's monthly reports and runs about a quarter in arrears, because zone files carry no registrar information at all. Every figure of the second kind is carried with the month it describes and is never merged with the daily count.&lt;/p&gt;

&lt;p&gt;Two populations. New gTLDs and the pre-2012 generics are never added together. A source names one and labels its rows with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three traps, found by probing rather than by reading
&lt;/h2&gt;

&lt;p&gt;The overview endpoint never answers without an explicit scope, and never answers for scope=all. Both hang until the client gives up, while scope=new returns in under a second. So the totals source refuses scope=all rather than scheduling a guaranteed timeout on every run.&lt;/p&gt;

&lt;p&gt;The page size parameter is floored at ten. Asking for three rows returns ten while the response's own metadata says three, so a "top five movers" source that trusted the parameter would publish ten.&lt;/p&gt;

&lt;p&gt;Detected timestamps carry no zone, and a bare Date parse reads them as local time, which files a midnight row under the previous day for every reader west of UTC.&lt;/p&gt;

&lt;p&gt;One more came from the live data rather than the docs. The first day of the daily series carries zero adds, zero drops and zero net, because it had no day before it to difference against. Across a thousand registries that cannot be a real day, so it publishes as movement not yet known instead of "+0 on the day", which would read as a flat day rather than a missing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything else in 0.2.0
&lt;/h2&gt;

&lt;p&gt;Weather as it is issued rather than forecast. Automotive with a VIN history report scored against a measured complaint baseline. Markets with the world's exchanges as a register and the corporate actions that move them. Crime by city, state and force area. Public money across three governments. Housing and jobs as governments measure them. AI incidents. News, where every story files under the desk that published it.&lt;/p&gt;

&lt;p&gt;Plus the fixes worth naming: dedup now folds a source against its own second feed, not only against the other sources, so a publisher exposing one article through two of its feeds stops arriving twice. The dedupe keys reach Postgres as a real array rather than a comma joined string, which had been aborting whole ingestion runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading it
&lt;/h2&gt;

&lt;p&gt;The collection is at &lt;a href="https://nichedb.dev/c/domains" rel="noopener noreferrer"&gt;https://nichedb.dev/c/domains&lt;/a&gt; and every feed on it is public. The CLI is &lt;code&gt;npm i -g @profullstack/nichedb@latest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Data on the domains collection is from nTLDData (ntlddata.com), CC-BY-4.0.&lt;/p&gt;

</description>
      <category>api</category>
      <category>cli</category>
      <category>database</category>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
