<?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>Population data from the world down to the ZIP code</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Fri, 25 Sep 2026 16:37:38 +0000</pubDate>
      <link>https://dev.to/chovy/population-data-from-the-world-down-to-the-zip-code-1dp9</link>
      <guid>https://dev.to/chovy/population-data-from-the-world-down-to-the-zip-code-1dp9</guid>
      <description>&lt;p&gt;nichedb.dev now has a population collection, and you can walk it from the whole world down to a single ZIP code: &lt;a href="https://nichedb.dev/population" rel="noopener noreferrer"&gt;https://nichedb.dev/population&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pick a country and you get its states. Pick a state and you get its cities and ZIP codes. Every level is sorted by how many people live there, and every row links one level further down.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every country (249)&lt;/strong&gt;, from the World Bank: population and its yearly series since 1990, growth, density, how many people live in cities, the age structure, births, deaths, children per woman, life expectancy, net migration, GDP per person and the Gini index.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every US state, city and ZIP code (about 66,000 areas)&lt;/strong&gt;, from the Census Bureau's American Community Survey: median age, households, household and per-person income, home value, rent, home ownership, poverty, college degrees, unemployment, race and Hispanic origin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The cities of every other country (about 30,000)&lt;/strong&gt;, filed under their state, province or region, from GeoNames.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of it is free data, and none of it needs a key.&lt;/p&gt;

&lt;h2&gt;
  
  
  One thing we found on the way
&lt;/h2&gt;

&lt;p&gt;The Census Bureau's data API stopped answering without a key. Every request now redirects to a "missing key" page. The same numbers are still published as flat files with no key at all (the ACS Summary File), so that is what nichedb reads: thirteen tables, about half a gigabyte, parsed in around twenty seconds and read again only when the Census publishes a new year.&lt;/p&gt;

&lt;p&gt;The files have a few traps. A value of -666666666 means "no estimate", not a very poor town. A median household income of 250001 means "$250,000 or more". Both are handled, and a top-coded figure shows as "$250,000+" rather than as an exact number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four ways in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Web: &lt;a href="https://nichedb.dev/population/us/ca/los-angeles" rel="noopener noreferrer"&gt;https://nichedb.dev/population/us/ca/los-angeles&lt;/a&gt; or &lt;a href="https://nichedb.dev/population/zip/90210" rel="noopener noreferrer"&gt;https://nichedb.dev/population/zip/90210&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;API: &lt;code&gt;GET https://nichedb.dev/api/v1/population/us-ca&lt;/code&gt;, plus &lt;code&gt;/areas?within=us-ca&amp;amp;level=zip&lt;/code&gt; to page a whole level&lt;/li&gt;
&lt;li&gt;MCP: the &lt;code&gt;population&lt;/code&gt; tool takes a key, a ZIP or a place name&lt;/li&gt;
&lt;li&gt;CLI: &lt;code&gt;npx @profullstack/nichedb population us ca los-angeles&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Listings, housing and crime records in nichedb also pick up the population profile of their ZIP, city or state on their own now, so a house for sale or a police report arrives with who lives around it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>182 YC companies say they ship an MCP server. 14 answered.</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Fri, 25 Sep 2026 14:54:09 +0000</pubDate>
      <link>https://dev.to/chovy/182-yc-companies-say-they-ship-an-mcp-server-14-answered-1pn0</link>
      <guid>https://dev.to/chovy/182-yc-companies-say-they-ship-an-mcp-server-14-answered-1pn0</guid>
      <description>&lt;h1&gt;
  
  
  182 YC companies say they ship an MCP server. 14 answered.
&lt;/h1&gt;

&lt;p&gt;Installmap crawled every reachable Y Combinator company on September 23 and published the result as a CSV, under CC BY 4.0. Their headline: 43 of the 119 sampled YC companies with an MCP server show no public API at all. Startups are skipping the API and going straight to MCP.&lt;/p&gt;

&lt;p&gt;That dataset is now a source in nichedb.dev/c/mcp. It had one gap worth filling. The CSV knows that a company has a server, but mostly not where it is. The evidence column says "home text MCP Server" about as often as it says "mcp subdomain 200".&lt;/p&gt;

&lt;p&gt;So we went and knocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What knocking found
&lt;/h2&gt;

&lt;p&gt;Each company became an OpenMCP record (logicsrc.com/openmcp). The address comes from Installmap's evidence first, then from the official MCP registry's remotes under the company's reverse domain name (io.anakin, com.bitmovin), then from the company's own /.well-known/openmcp.json. After that, an unauthenticated initialize and tools/list, the same handshake any MCP client does.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Companies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;In the dataset&lt;/td&gt;
&lt;td&gt;182&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An endpoint we could find&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answered and listed their tools&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answered 401 or 403 (a real server that wants a key)&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Serve an OpenMCP descriptor&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Only mention MCP on the site&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The biggest open ones: NewsCatcher with 61 tools, Clueso with 44, Pluggy with 31.&lt;/p&gt;

&lt;p&gt;A 401 counts. That is a server that exists and wants a credential this probe will never send, so it is listed as offline with the reason attached, not dropped. A 405 to a POST, or an HTML page sitting at /mcp, does not count. Several companies have a nice page at /mcp that is marketing, not a relay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is behind them
&lt;/h2&gt;

&lt;p&gt;Every row also carries the company's own accounts and its founders. The homepage is the real source here. 165 of the 182 link at least one account, and schema.org markup named founders on 28 sites. Wikidata matched only 25 of these companies on their official website, and knew the founders of 2.&lt;/p&gt;

&lt;p&gt;If a homepage links four X accounts because it quotes three customers, we keep the one whose handle matches the company, and if none does we keep nothing. A wrong handle is worse than a missing one.&lt;/p&gt;

&lt;p&gt;The companies and the 49 founders are also OpenProfile documents in nichedb.dev/c/profiles, so a founder is someone you can follow, not a string in a JSON blob.&lt;/p&gt;

&lt;p&gt;We did not read ycombinator.com. Its company pages carry founders and socials in the markup, but its terms forbid scraping, and the popular yc-oss dataset is a scrape of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The zero
&lt;/h2&gt;

&lt;p&gt;Not one of these companies serves an OpenMCP descriptor. That is the one file that would have made most of this unnecessary: where the endpoint is, what auth it wants, which tools work without a key. The whole thing is five lines of JSON at /.well-known/openmcp.json.&lt;/p&gt;

&lt;p&gt;Browse them at &lt;a href="https://nichedb.dev/c/mcp?tag=yc" rel="noopener noreferrer"&gt;https://nichedb.dev/c/mcp?tag=yc&lt;/a&gt;. Data: Installmap, September 2026 (CC BY 4.0).&lt;/p&gt;

</description>
    </item>
    <item>
      <title>$20 a smoke, and the Cigar Underground Herf</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Fri, 25 Sep 2026 11:45:21 +0000</pubDate>
      <link>https://dev.to/chovy/20-a-smoke-and-the-cigar-underground-herf-66k</link>
      <guid>https://dev.to/chovy/20-a-smoke-and-the-cigar-underground-herf-66k</guid>
      <description>&lt;p&gt;$20 a smoke, and the Cigar Underground Herf&lt;/p&gt;

&lt;p&gt;We put Cigar Underground online two days ago with the Chovy Premium Blend at $200 a cigar. Then we looked at the waitlist. It had two names on it, and I know both of them.&lt;/p&gt;

&lt;p&gt;$200 is a price you charge when people already know your name. Nobody knows ours yet. So the Chovy cigar and the Chovy spliff are $20 each from today.&lt;/p&gt;

&lt;p&gt;That is not a sale and it is not an introductory rate we quietly walk back in a month. It is what a smoke should cost from a brand you have never heard of. Same leaf, same hands, same bench in my dad's workshop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cigar Underground Herf
&lt;/h2&gt;

&lt;p&gt;The other thing we did was give people a reason to show up.&lt;/p&gt;

&lt;p&gt;Four times a month we shut the laptops at our headquarters in the SF Bay Area and light up in the yard. Profullstack, Inc. by day, Cigar Underground when the smoker is running. A seat is $100 and it covers two cigars from the latest roll, BBQ straight off the smoker, and food and drink until everyone goes home.&lt;/p&gt;

&lt;p&gt;Every other Sunday at 6pm Pacific, which is 9pm Eastern, and every other Wednesday at 9am Pacific, which is noon Eastern. They alternate, so there is one most weeks: two evenings and two mornings a month, because not everybody is free on a Sunday night. It is the Bay Area only, because you have to be standing here to eat. If you are too far to drive, join it online on PairUX, the video app we build ourselves, and smoke your own while we talk. That one is free. We cannot pass a cigar through a screen.&lt;/p&gt;

&lt;p&gt;The RSVP is on the site. Nothing is charged there: the date, the address and the rest go out by mail, and seats are settled in person.&lt;/p&gt;

&lt;h2&gt;
  
  
  A room beats a checkout
&lt;/h2&gt;

&lt;p&gt;The shop still sells tips, tools, cutters and cases, drop shipped and paid in crypto. Cigars and spliffs are not sold or shipped online and they are not going to be, because that is not a thing we are willing to do to a wrapper or to a lawyer.&lt;/p&gt;

&lt;p&gt;So the herf is the one place the thing we actually make changes hands. That turns out to be the honest answer to the question people keep asking, which is some version of "fine, but how do I get one".&lt;/p&gt;

&lt;p&gt;Two names on a waitlist is not a business. A dozen people in a yard with something lit, arguing about wrappers, is at least the start of one.&lt;/p&gt;

&lt;p&gt;The prices, the herf and the RSVP are all up now: &lt;a href="https://cigarunderground.org" rel="noopener noreferrer"&gt;https://cigarunderground.org&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Live scores from leagues no US scoreboard carries</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 18:05:27 +0000</pubDate>
      <link>https://dev.to/chovy/live-scores-from-leagues-no-us-scoreboard-carries-1ggk</link>
      <guid>https://dev.to/chovy/live-scores-from-leagues-no-us-scoreboard-carries-1ggk</guid>
      <description>&lt;h1&gt;
  
  
  Live scores from leagues no US scoreboard carries
&lt;/h1&gt;

&lt;p&gt;nichedb.dev/c/sports has two live score sources now. It used to have one, and that one only knew about the games American networks broadcast.&lt;/p&gt;

&lt;p&gt;The new one came out of a $9 a month TheSportsDB membership we already had. Every workaround in our sports code was written against limits that membership lifts, and nobody had gone back to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the free key was costing us
&lt;/h2&gt;

&lt;p&gt;Same endpoints, same day, two keys:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Free key&lt;/th&gt;
&lt;th&gt;Subscriber key&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One day of TV listings&lt;/td&gt;
&lt;td&gt;1 row&lt;/td&gt;
&lt;td&gt;372 rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every team in a league&lt;/td&gt;
&lt;td&gt;ignored, or 10 rows&lt;/td&gt;
&lt;td&gt;every team, all 63 fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The league catalogue&lt;/td&gt;
&lt;td&gt;refused&lt;/td&gt;
&lt;td&gt;1,544 leagues, one request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A team roster&lt;/td&gt;
&lt;td&gt;10 names, alphabetical&lt;/td&gt;
&lt;td&gt;the whole squad&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live scores&lt;/td&gt;
&lt;td&gt;no such endpoint&lt;/td&gt;
&lt;td&gt;every game on, worldwide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limit&lt;/td&gt;
&lt;td&gt;30 a minute&lt;/td&gt;
&lt;td&gt;100 a minute&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The catalogue line is the one that mattered most. With no list endpoint answering, the only way to find leagues was to try ids one at a time from 4328 upward and stop after 25 misses in a row. That is 1,700 requests to find about 1,500 leagues, and it can never find one outside the range it already knows about. The v2 catalogue returns the whole list in a single request.&lt;/p&gt;

&lt;p&gt;Teams were worse. We walked roughly 24,000 team ids, three quarters of which are gaps, because the per league search was capped at ten rows. On a subscriber key that same search returns every team in the league with all 63 fields, the same row the one by one lookup gives.&lt;/p&gt;

&lt;p&gt;Here are the last run of the old code and the first run of the new one, out of the same table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sportsdb-teams | 25 teams from 25 lookups (ids 151069 to 151093)
sportsdb-teams | 388 teams from 25 leagues (1-25 of 1544)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same request budget. Fifteen times the rows. A full pass over the team catalogue went from about a week to a couple of hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The live part
&lt;/h2&gt;

&lt;p&gt;The new source asks &lt;code&gt;livescore/all&lt;/code&gt; once and gets back every game being played anywhere, about 50 KB. First run in production returned 80 games with 38 in play: Slovak Extraliga, Finnish Liiga, African Cup of Nations qualifiers.&lt;/p&gt;

&lt;p&gt;It does not replace the ESPN one. ESPN is deeper where it goes, with the clock, the venue, the odds and play by play. But ESPN blocks cloud egress, so every scoreboard we pull goes through a metered residential proxy at a few hundred kilobytes a time, and a run can only afford to probe a handful of leagues. The two answer different questions, so both rows are kept under their own ids and matching them stays the reader's job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things that bit
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;search_all_teams.php?l=&lt;/code&gt; matches the league's display name and only that. &lt;code&gt;l=MLB&lt;/code&gt; returns thirty teams. &lt;code&gt;l=Major League Baseball&lt;/code&gt; returns null. The names have to come from the catalogue, never from anything a human typed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lookup_all_teams.php&lt;/code&gt; is gone. It answers HTML now, not JSON, so anything checking only for a non-200 sails past it into a parse error.&lt;/p&gt;

&lt;p&gt;The v2 API takes the key in an &lt;code&gt;X-API-KEY&lt;/code&gt; header instead of the URL path. That is a small privacy win: v2 request URLs are safe to log, where the v1 ones never are.&lt;/p&gt;

&lt;p&gt;Live timestamps are UTC written without a zone, like &lt;code&gt;2026-09-23T15:30:00&lt;/code&gt;. &lt;code&gt;Date.parse&lt;/code&gt; reads that as local time, so a box in the wrong zone shifts every kickoff by its offset. Appending the Z is the entire fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth checking your own
&lt;/h2&gt;

&lt;p&gt;The interesting part was not the code. It was that the key was already set in production while the code still ran against the free tier's shape, because the workarounds were written once and nobody re-measured them after the key arrived. If you have a paid API key somewhere in your stack, go read what your code assumes about it.&lt;/p&gt;

&lt;p&gt;Live now at &lt;a href="https://nichedb.dev/c/sports" rel="noopener noreferrer"&gt;https://nichedb.dev/c/sports&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Every AI model and what each provider charges, as an open dataset</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 17:25:07 +0000</pubDate>
      <link>https://dev.to/chovy/every-ai-model-and-what-each-provider-charges-as-an-open-dataset-5d86</link>
      <guid>https://dev.to/chovy/every-ai-model-and-what-each-provider-charges-as-an-open-dataset-5d86</guid>
      <description>&lt;p&gt;The useful question about AI models is not "what models exist". It is "who serves this model, and what do they charge". Those are different questions, and only one of them has a good answer on the open web.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://models.dev" rel="noopener noreferrer"&gt;models.dev&lt;/a&gt; is an open, community maintained database of models, their capabilities and their prices. MIT licensed, keyless, and published as plain JSON. I just pulled the whole thing into &lt;a href="https://nichedb.dev" rel="noopener noreferrer"&gt;nichedb.dev&lt;/a&gt; as a collection you can read by API, CLI or MCP: &lt;a href="https://nichedb.dev/c/models" rel="noopener noreferrer"&gt;nichedb.dev/c/models&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a row is an offering, not a model
&lt;/h2&gt;

&lt;p&gt;models.dev publishes the same database two ways, and the difference decides the shape of everything downstream.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;api.json?type=all&lt;/code&gt; is keyed by provider. Each entry is a provider, carrying the models that provider serves, at that provider's prices.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;catalog.json?type=all&lt;/code&gt; carries those same providers plus a second half: the lab catalogue, one entry per model as its maker published it, keyed &lt;code&gt;lab/slug&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same model reaches you from many providers at as many prices. So the unit worth storing is the offering, the pair of provider and model, not the model on its own. That is what the bulk of the collection is: 8,179 offerings across 223 providers.&lt;/p&gt;

&lt;p&gt;The lab catalogue is the other half, 428 models, and it is the only half with a page of its own on the site. A provider's offering has no URL but the provider's own documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each row carries
&lt;/h2&gt;

&lt;p&gt;For an offering: the price per million tokens in, out, cache read and cache write. The context and output limits. Input and output modalities. Whether it reasons, calls tools, returns structured output, takes attachments, honours temperature or ships open weights. Plus the provider itself, its documentation, its npm package and the environment variables it expects.&lt;/p&gt;

&lt;p&gt;For a provider: the size of its catalogue, how many of those models are free, and when the catalogue last changed.&lt;/p&gt;

&lt;p&gt;Capabilities are stored as tags, so a feed can be "everything that calls tools" without the query language needing to know what a model is. There are seven feeds: prices, open weights, free, tool calling, reasoning, the lab catalogue, and the providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things that would have quietly broken it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A published price of zero is a fact, not a missing value.&lt;/strong&gt; 638 of the 8,179 offerings genuinely cost nothing. The reflexive idiom for a nullable number is &lt;code&gt;cost.input || null&lt;/code&gt;, and that one line would have erased every free model in the database, because zero is falsy. Free has to mean a published zero in and out. A separate 424 offerings publish no price at all, and those are not free, they are unknown. Collapsing those two states is how a pricing dataset starts lying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A provider has no date of its own.&lt;/strong&gt; Every row in the collection sorts by a date, and a provider is not published on a day. If you leave that null, 223 rows sink to the bottom of the collection forever. Each provider borrows the newest date in its own catalogue instead, which is a real fact about it: the last time anything it serves changed.&lt;/p&gt;

&lt;p&gt;There is a third one worth naming for anyone else reading this API. Per model page URLs on models.dev are keyed by lab, not by provider. Anything else redirects to the root, so a provider's model id cannot be turned into a page by string surgery. Only the catalogue half gets real pages.&lt;/p&gt;

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

&lt;p&gt;The collection is free and needs no key, like the rest of nichedb.&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="s1"&gt;'https://nichedb.dev/api/v1/items?collection=models&amp;amp;tags=free&amp;amp;limit=5'&lt;/span&gt;
curl &lt;span class="s1"&gt;'https://nichedb.dev/api/v1/items?collection=models&amp;amp;kind=provider&amp;amp;limit=5'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;code&gt;npm i -g @profullstack/nichedb&lt;/code&gt; and read it from the CLI, or point an agent at the MCP endpoint.&lt;/p&gt;

&lt;p&gt;Both sources refresh daily. The upstream is a build artefact of a git repository rather than a live feed, so daily is as fresh as the data gets.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The agentic internet they don't want you to see</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 12:38:53 +0000</pubDate>
      <link>https://dev.to/chovy/the-agentic-internet-they-dont-want-you-to-see-29e5</link>
      <guid>https://dev.to/chovy/the-agentic-internet-they-dont-want-you-to-see-29e5</guid>
      <description>&lt;p&gt;The open web spent this year closing. There was no announcement. There was a 403.&lt;/p&gt;

&lt;p&gt;Our tools read pages for a living, so we check this often. A plain fetch, no browser profile, no cookie jar, gets this today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://www.reddit.com/r/programming.json
&lt;span class="go"&gt;HTTP/2 403

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://www.axios.com/
&lt;span class="go"&gt;HTTP/2 403

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://news.google.com/rss/articles/CBMi... &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{redirect_url}\n'&lt;/span&gt;
&lt;span class="go"&gt;                                  (no Location, the link resolves to nothing)

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://www.threads.net/@someone | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'login'&lt;/span&gt;
&lt;span class="go"&gt;                                  (a login wall, same as x, instagram, facebook)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every one of those was a URL you could read a few years ago. The writing is still there. The door now checks whether you are a person.&lt;/p&gt;

&lt;p&gt;That is the part everyone notices. Here is the part that matters more: the same door is what stops an agent from doing anything useful for you. An agent has no account, no cookie, no residential address. It is precisely the traffic those walls went up to stop. So the honest summary of the agentic web right now is that it works beautifully on the handful of sites that still let anything in.&lt;/p&gt;

&lt;h2&gt;
  
  
  So build the other one
&lt;/h2&gt;

&lt;p&gt;A Moshpit name is a name with no registrar behind it. You claim &lt;code&gt;chovy.hacker&lt;/code&gt; or &lt;code&gt;seo.rank&lt;/code&gt; in the &lt;a href="https://app.moshcode.sh/pit" rel="noopener noreferrer"&gt;Moshpit Manager&lt;/a&gt;, and the registry signs a 30 day leaf certificate for it. &lt;code&gt;sudo moshcode dns enable&lt;/code&gt; installs the root and points this machine at the resolver, so the name loads in a browser with a real lock rather than a warning page.&lt;/p&gt;

&lt;p&gt;The fact that catches everyone: &lt;strong&gt;the machine serving a name never resolves it, and every machine visiting it has to.&lt;/strong&gt; Serving is a Host header match, which is why hosting works before DNS does. Visitors run &lt;code&gt;dns enable&lt;/code&gt; or the name goes nowhere for them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;moshcode dns &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;span class="go"&gt;✓ resolver installed · chovy.hacker resolves on this machine

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;moshcode template &lt;span class="nb"&gt;install &lt;/span&gt;caddy-static &lt;span class="nt"&gt;--into&lt;/span&gt; /srv/site
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;moshcode site chovy.hacker &lt;span class="nt"&gt;--root&lt;/span&gt; /srv/site
&lt;span class="go"&gt;  serving on a Host match · pit-signed cert, 30 day leaf
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A name is only the first third
&lt;/h2&gt;

&lt;p&gt;An agent that can reach a thing still cannot transact with it or be held to account for it. Three pieces, and none of them is a metaphor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An address.&lt;/strong&gt; The Moshpit registry, plus a public DNS-over-HTTPS endpoint at &lt;code&gt;dns.moshcode.sh/dns-query&lt;/code&gt;, so a resolver is a URL and not a sysadmin ticket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A way to pay per call.&lt;/strong&gt; HTTP 402 was reserved for this in 1997 and left empty. &lt;a href="https://x402.org" rel="noopener noreferrer"&gt;x402&lt;/a&gt; fills it, so a request can carry its own payment instead of a signup form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A record.&lt;/strong&gt; Every swarm moshcode runs is written as &lt;a href="https://logicsrc.com/docs/openfleet" rel="noopener noreferrer"&gt;OpenFleet&lt;/a&gt;: who started what, under whose approval, what the synthesis said, and what got refused before it ran.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Identity comes off the same name. &lt;code&gt;moshcode name&lt;/code&gt; proves you hold one, so an app can treat it as who you are without asking an identity provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nobody is hiding it
&lt;/h2&gt;

&lt;p&gt;The title is a joke about how it feels, not a conspiracy. There is no cabal. There is an incentive: a network that indexes a &lt;code&gt;.hacker&lt;/code&gt; name earns nothing from it, and a platform that carries a link to one is sending a reader somewhere it cannot monetise. So it does not get surfaced, and the networks that would have distributed it five years ago are the same ones returning 403 to the fetch above.&lt;/p&gt;

&lt;p&gt;Which leaves the way these things have always spread, which is somebody telling you the two commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://moshcode.sh/install.sh | sh
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;moshcode dns &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then claim something in the &lt;a href="https://app.moshcode.sh/pit" rel="noopener noreferrer"&gt;Moshpit Manager&lt;/a&gt; and put a directory of files behind it. The rest of the web does not have to agree for it to work.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>OutreachGraph now runs the whole sequence: LinkedIn, branching, A/B angles, an inbox and your CRM</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 11:44:52 +0000</pubDate>
      <link>https://dev.to/chovy/outreachgraph-now-runs-the-whole-sequence-linkedin-branching-ab-angles-an-inbox-and-your-crm-25jj</link>
      <guid>https://dev.to/chovy/outreachgraph-now-runs-the-whole-sequence-linkedin-branching-ab-angles-an-inbox-and-your-crm-25jj</guid>
      <description>&lt;p&gt;OutreachGraph started as the part most outreach tools skip: finding the thing a prospect said forty minutes ago that makes a message worth sending, and refusing to send anything the platform, the person or your own limits say it shouldn't.&lt;/p&gt;

&lt;p&gt;Today it also runs the outreach after that. Six changes shipped to &lt;a href="https://outreachgraph.com:" rel="noopener noreferrer"&gt;https://outreachgraph.com:&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequences that branch
&lt;/h2&gt;

&lt;p&gt;A cadence step can now wait on a condition: if connected, if not connected, if no reply, if they clicked. A LinkedIn connection request can hold the next step for up to a week while it waits for an accept. So "visit their profile, connect with a note, then DM if they accept or email if they don't" is one plan. A reply still ends it.&lt;/p&gt;

&lt;h2&gt;
  
  
  LinkedIn through your own session
&lt;/h2&gt;

&lt;p&gt;Connection requests with a note, profile visits, follows, messages and comments now run through your own logged-in session. It stays off until the workspace owner accepts the risk, and it is paced and capped under LinkedIn's limits: 20 invitations a day and 100 a week per account. Without the opt-in, LinkedIn steps become hand-off cards you do yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  A/B test the angle, not a template
&lt;/h2&gt;

&lt;p&gt;Give a step two, three or four reasons to write ("reference their talk", "ask who owns onboarding"). Each prospect is assigned one arm. Every message is still written from that prospect's own words and still has to pass the grounding checks. The plan page shows sends, clicks, opens and reply rate per arm.&lt;/p&gt;

&lt;p&gt;Open tracking exists now too, and it is off by default. Apple Mail fetches every image on delivery, so an open is an upper bound and never raises a prospect's score.&lt;/p&gt;

&lt;h2&gt;
  
  
  One inbox, triaged
&lt;/h2&gt;

&lt;p&gt;Replies are labelled interested, question, referral, not interested, out of office, bounce or unsubscribe. Rules go first and a model only labels what the rules can't. An unsubscribe suppresses the person immediately. Out-of-office notices no longer count as replies, so they don't stop a sequence. Interested and question replies get a drafted answer to approve. If you put a campaign on trusted automation and switch replies to autonomous, a confident answer to a direct question can go out unattended, and everything short of that stays a card.&lt;/p&gt;

&lt;h2&gt;
  
  
  More sending accounts, warmed up
&lt;/h2&gt;

&lt;p&gt;Connect several mailboxes, LinkedIn sessions and X accounts. A conversation stays on the account that started it, a new account ramps its daily cap (email from 5 a day), and an account whose bounce rate passes 5% pauses itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks, Slack and your CRM
&lt;/h2&gt;

&lt;p&gt;Signed webhooks for replies, clicks, approvals, sends, completed sequences and opt-outs, so Zapier, Make and n8n work out of the box. There is a Slack format, and replies and approvals are written to HubSpot or Pipedrive as notes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two fixes worth mentioning
&lt;/h2&gt;

&lt;p&gt;Unsubscribe links and tracked links were answering 404 in production because the server only routed /api to the API. They work now. Autopilot sends were also missing the unsubscribe line and the List-Unsubscribe header. Every send path now shares one builder, so they can't drift apart again.&lt;/p&gt;

&lt;p&gt;Free while in beta: &lt;a href="https://outreachgraph.com" rel="noopener noreferrer"&gt;https://outreachgraph.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>OpenEmoji and OpenIcon: 3,963 emoji, 370 icons, and your terminal gets them too</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:52:11 +0000</pubDate>
      <link>https://dev.to/chovy/openemoji-and-openicon-3963-emoji-370-icons-and-your-terminal-gets-them-too-7be</link>
      <guid>https://dev.to/chovy/openemoji-and-openicon-3963-emoji-370-icons-and-your-terminal-gets-them-too-7be</guid>
      <description>&lt;p&gt;Today we shipped two open specs and a complete set for each: OpenEmoji, with all 3,963 standard emoji, and OpenIcon, with 370 UI and brand icons. Both are built into HQTUI and on by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenEmoji
&lt;/h2&gt;

&lt;p&gt;An emoji set is usually locked to someone's operating system. OpenEmoji is a folder: one openemoji.json that says which Unicode version it covers, what is missing, its licence, and what drew it, plus files named by codepoint (1f525.png is the fire).&lt;/p&gt;

&lt;p&gt;The reference set is every fully-qualified sequence in Emoji 18.0, drawn by gpt-image-2 as one family. Six anchor glyphs were drawn first and every later glyph was an edit that saw them, so the light and gloss hold across the set. Skin tones are edits of the base glyph that change only the skin. The manifest says so: made_by ai, disclosure ai-generated.&lt;/p&gt;

&lt;p&gt;Two things the model got wrong on the way: it kept drawing a famous red-haired mermaid and a famous blonde fairy, and a general "be original" instruction did not stop it. A specific brief for each did.&lt;/p&gt;

&lt;p&gt;Browse it at &lt;a href="https://logicsrc.com/openemoji/catalog" rel="noopener noreferrer"&gt;https://logicsrc.com/openemoji/catalog&lt;/a&gt;. Search by name or keyword ("lol" finds the crying-laughing face), filter by group, skin tone or Emoji version, and pick a network under "Install on". There are bundles for 22 of them: custom emoji for Slack, Discord, Mastodon (a tootctl tarball), Misskey, Pleroma, Telegram and more, sticker packs for Signal and WhatsApp where custom emoji do not exist, and flag images for X. Colour fonts (CBDT and sbix) are in the release.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenIcon
&lt;/h2&gt;

&lt;p&gt;OpenIcon does the same for UI icons, and adds the part icon sets leave out: what to draw in a terminal. Every icon carries a Nerd Font glyph, a Unicode symbol and an ASCII fallback, so a status bar never turns into boxes over SSH. The set has 259 icons in two styles, a simple line style and a full-colour HQ style, plus 111 brand logos taken from Simple Icons in their owners' colours. See all of them at &lt;a href="https://logicsrc.com/openicon" rel="noopener noreferrer"&gt;https://logicsrc.com/openicon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  In HQTUI
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emojify&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@profullstack/hqtui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// Nerd glyph, or ✉, or @&lt;/span&gt;
&lt;span class="nf"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rocket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 🚀, or [rocket] where emoji cannot render&lt;/span&gt;
&lt;span class="nf"&gt;emojify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ship it :oe_rocket:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bunx @profullstack/hqtui fonts install&lt;/code&gt; puts the emoji font in your terminal through fontconfig, Kitty or WezTerm. Emoji width is also fixed in all six ports: a thumbs-up with a skin tone used to measure four columns.&lt;/p&gt;

&lt;p&gt;The specs are open and the sets are CC BY: &lt;a href="https://github.com/profullstack/openemoji" rel="noopener noreferrer"&gt;https://github.com/profullstack/openemoji&lt;/a&gt; and &lt;a href="https://github.com/profullstack/openicon" rel="noopener noreferrer"&gt;https://github.com/profullstack/openicon&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>TronBrowser v3.15.0: agents can finally fill in a real form</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 08:16:04 +0000</pubDate>
      <link>https://dev.to/chovy/tronbrowser-v3150-agents-can-finally-fill-in-a-real-form-5g59</link>
      <guid>https://dev.to/chovy/tronbrowser-v3150-agents-can-finally-fill-in-a-real-form-5g59</guid>
      <description>&lt;p&gt;I pointed TronBrowser's agent tools at a real job application and it could not finish one form. So I fixed what broke, and then let it apply to jobs for me.&lt;/p&gt;

&lt;p&gt;TronBrowser v3.15.0 is out. &lt;code&gt;tron upgrade&lt;/code&gt; gets it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tron automate&lt;/code&gt; is an MCP server: an agent opens a page, takes a snapshot of refs like &lt;code&gt;@e7&lt;/code&gt;, and fills and clicks by ref. On a Greenhouse or Ashby application it fell over at every step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No way to attach a file.&lt;/strong&gt; The resume field is a hidden &lt;code&gt;&amp;lt;input type=file&amp;gt;&lt;/code&gt; behind an "Attach" button, snapshots did not even list it, and there was no upload tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dropdowns did nothing.&lt;/strong&gt; &lt;code&gt;browser_select&lt;/code&gt; only knew native &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;. Every modern form uses a React combobox that opens on a real mouse press and ignores synthetic events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fills landed in the wrong field.&lt;/strong&gt; Every snapshot renumbered every ref. Open one dropdown, its options become refs, everything after it shifts, and the next fill types "Yes" into the Gender box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clicks missed.&lt;/strong&gt; With &lt;code&gt;scroll-behavior: smooth&lt;/code&gt; the element was measured mid-scroll, so the click landed a few thousand pixels off-screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Thanks for applying" was invisible.&lt;/strong&gt; Text extraction read &lt;code&gt;textContent&lt;/code&gt;, so an inlined JS bundle used up the whole 20k character budget before any word a person could see.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;browser_upload&lt;/code&gt; and &lt;code&gt;tron upload &amp;lt;ref&amp;gt; &amp;lt;file&amp;gt;&lt;/code&gt; attach files through CDP, and snapshots always list file inputs, hidden or not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_select&lt;/code&gt; and &lt;code&gt;tron select&lt;/code&gt; drive custom comboboxes with trusted mouse and keyboard input, then click the matching option.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_press&lt;/code&gt; and &lt;code&gt;tron press&lt;/code&gt; send trusted key events.&lt;/li&gt;
&lt;li&gt;Refs are stable. An element keeps its ref across snapshots, new elements get new numbers, and a ref from a vanished element is never handed to something else.&lt;/li&gt;
&lt;li&gt;Snapshots mark &lt;code&gt;[required]&lt;/code&gt; fields, and extraction returns rendered text.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it did with that
&lt;/h2&gt;

&lt;p&gt;The same MCP session then filled and submitted real applications end to end: resume upload, location autocomplete, work authorization dropdowns, checkbox groups, and Greenhouse's emailed security code.&lt;/p&gt;

&lt;p&gt;That flow is now a command in cli-tools, &lt;code&gt;jobhunt&lt;/code&gt;. It finds remote roles at the companies on a public list of AI startups hiring, filters by title, workplace and pay, and applies through Tron. It keeps multiple profiles with their own resume and cover letter, stops and resumes runs, logs everything it does, and reads the security code from your inbox. It will not tick a consent or arbitration box for you, and it never sends the same application twice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TronBrowser: &lt;a href="https://tronbrowser.dev" rel="noopener noreferrer"&gt;https://tronbrowser.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;cli-tools: &lt;a href="https://github.com/profullstack/cli-tools" rel="noopener noreferrer"&gt;https://github.com/profullstack/cli-tools&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>NIGHTCELL 7 v0.4.0: When the Ravens Lied remade, and a shuffle across both albums</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Thu, 24 Sep 2026 00:19:10 +0000</pubDate>
      <link>https://dev.to/chovy/nightcell-7-v040-when-the-ravens-lied-remade-and-a-shuffle-across-both-albums-2i1c</link>
      <guid>https://dev.to/chovy/nightcell-7-v040-when-the-ravens-lied-remade-and-a-shuffle-across-both-albums-2i1c</guid>
      <description>&lt;p&gt;NIGHTCELL 7 v0.4.0 is out. Þrøngva's second album, &lt;em&gt;When the Ravens Lied&lt;/em&gt;, now plays in the game as full songs made in Suno, and the soundtrack shuffles across both albums.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;Anyone playing the free sandbox, in the browser or on the desktop client. Nothing here costs money.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The album, remade.&lt;/strong&gt; Yesterday's first cut of &lt;em&gt;When the Ravens Lied&lt;/em&gt; was synthesised from code, and it sounded like it: no words you could make out. All 12 tracks are now made in Suno from the same lyric sheets, with sung vocals, the way the first album was. The story is unchanged: Odin's ravens are taught to lie, two halls go to war over forged smoke, and a merchant sells spears to both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shuffle across both albums.&lt;/strong&gt; Before, only the first song was random, and the rest played in list order. Now every song is a random pick from either &lt;em&gt;After the Winter of Want&lt;/em&gt; or &lt;em&gt;When the Ravens Lied&lt;/em&gt;. All 32 tracks play once before any repeats, and the same song never plays twice in a row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portraits.&lt;/strong&gt; The &lt;a href="https://nightcell7.com/characters" rel="noopener noreferrer"&gt;characters page&lt;/a&gt; and the dossiers have painted portraits, and you can pick who to play.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://nightcell7.com/play" rel="noopener noreferrer"&gt;nightcell7.com/play&lt;/a&gt; and turn your volume up in the yard. The desktop builds for macOS, Windows and Linux are at &lt;a href="https://nightcell7.com/downloads" rel="noopener noreferrer"&gt;nightcell7.com/downloads&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/profullstack/nightcell7/releases/tag/v0.4.0" rel="noopener noreferrer"&gt;https://github.com/profullstack/nightcell7/releases/tag/v0.4.0&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>NIGHTCELL 7: When the Ravens Lied, a second Þrøngva album made in code</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 23 Sep 2026 23:05:42 +0000</pubDate>
      <link>https://dev.to/chovy/nightcell-7-when-the-ravens-lied-a-second-throngva-album-made-in-code-5597</link>
      <guid>https://dev.to/chovy/nightcell-7-when-the-ravens-lied-a-second-throngva-album-made-in-code-5597</guid>
      <description>&lt;p&gt;NIGHTCELL 7 has a second soundtrack album: &lt;em&gt;When the Ravens Lied&lt;/em&gt; by Þrøngva, twelve new tracks in the same drop-D Viking metal and hip-hop style as &lt;em&gt;After the Winter of Want&lt;/em&gt;. They are in the game now, next to the first album's twenty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;Anyone playing the free sandbox, in the browser or on the desktop client. Nothing here costs money.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the album is about
&lt;/h2&gt;

&lt;p&gt;Odin sends two ravens out every morning and believes what they bring back. On this album somebody has taught them to lie. Forged smoke and forged voices tell two halls that the other one struck first, and a merchant sells spears to both sides. It is the game's MIRAGE plot told as a saga. No real country is named, and neither side is the villain: the lie is, and so is whoever profits from it. The last track, "True Dawn", answers the episode title, FALSE DAWN.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Huginn Brings the Word&lt;/li&gt;
&lt;li&gt;Who Fired First&lt;/li&gt;
&lt;li&gt;Loki's Mirror&lt;/li&gt;
&lt;li&gt;Merchant of the Long Knife&lt;/li&gt;
&lt;li&gt;Ledger of Ash&lt;/li&gt;
&lt;li&gt;Heimdall Does Not Sleep&lt;/li&gt;
&lt;li&gt;Same Rain on Both Shields&lt;/li&gt;
&lt;li&gt;Lower the Spear&lt;/li&gt;
&lt;li&gt;Muninn Remembers&lt;/li&gt;
&lt;li&gt;Longship Bounce&lt;/li&gt;
&lt;li&gt;Not Our Ragnarök&lt;/li&gt;
&lt;li&gt;True Dawn&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How it was made
&lt;/h2&gt;

&lt;p&gt;Every note is synthesised by a Python script with numpy and ffmpeg. Nothing is sampled, and no AI audio model or music service was used. The script reads a lyric sheet that gives each section a tempo and a direction ("male baritone half-rap, drop-D riff with pockets of silence") and arranges it. It plays a riff with the kick doubling the guitar, boom-bap or 808 drums on the hip-hop tracks, and choir, strings, tagelharpa, a throat-singing drone and horns. It also adds the wind, rain and ravens the sheet asks for.&lt;/p&gt;

&lt;p&gt;The voice is the honest limit. It is formant synthesis, one note per syllable on that syllable's vowel, so it follows the rhythm of the lyrics without being words you can make out. The lyrics ship inside every MP3's tags, and the repo has paste-ready sheets for a sung version later.&lt;/p&gt;

&lt;p&gt;Nobody listened to the first render, because the session that made it cannot hear. A spectrogram and an energy count showed 73% of the energy below 150 Hz, which is mud, so the mix was rebalanced before all twelve tracks were rendered. Each one is a 320 kbps, 48 kHz MP3 at about -10 LUFS.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://nightcell7.com/play" rel="noopener noreferrer"&gt;nightcell7.com/play&lt;/a&gt; and turn your volume up in the yard. Both albums play shuffled, one track after another, and stream as they are needed, so they add nothing to the download before your first match. The lyric sheet and the synthesiser are in the repo at &lt;a href="https://github.com/profullstack/nightcell7/tree/main/docs/music/when-the-ravens-lied" rel="noopener noreferrer"&gt;github.com/profullstack/nightcell7&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>music</category>
    </item>
    <item>
      <title>NIGHTCELL 7 v0.3.0: a soundtrack, and a first run that teaches you</title>
      <dc:creator>chovy</dc:creator>
      <pubDate>Wed, 23 Sep 2026 18:21:59 +0000</pubDate>
      <link>https://dev.to/chovy/nightcell-7-v030-a-soundtrack-and-a-first-run-that-teaches-you-2hk8</link>
      <guid>https://dev.to/chovy/nightcell-7-v030-a-soundtrack-and-a-first-run-that-teaches-you-2hk8</guid>
      <description>&lt;p&gt;NIGHTCELL 7 v0.3.0 is out. The game has a soundtrack now: &lt;em&gt;After the Winter of Want&lt;/em&gt; by Þrøngva, twenty tracks of our own music. New players also get a proper first run, with a briefing that tells you whose side you are on and a coach that teaches the controls one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;p&gt;Anyone playing the free sandbox, in the browser or on the desktop client. Nothing here costs money.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A 20-track album.&lt;/strong&gt; &lt;em&gt;After the Winter of Want&lt;/em&gt; by Þrøngva plays shuffled in the yard, one track after another. It is our own music, made for the game, not licensed from anyone. The tracks stream when they are needed, so they add nothing to the download you wait for before your first match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A briefing on the deploy gate.&lt;/strong&gt; The first time you play, the gate tells you which side you are deploying with and whose story it belongs to: the NIGHTCELL program Rook runs, or the service Leila serves. It explains that the yard is squad Team Deathmatch against bots, while the campaigns are one-person stories.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A coach for the controls.&lt;/strong&gt; Once you deploy, a coach walks you through nine controls in order: move, look, sprint, crouch, jump, fire, reload, switch weapons, throw a frag. A step only completes when you actually do it, never on a timer, and dying pauses it instead of counting the respawn as progress. You can skip it, and it does not come back once you have finished or skipped it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A cast page.&lt;/strong&gt; &lt;a href="https://nightcell7.com/characters" rel="noopener noreferrer"&gt;nightcell7.com/characters&lt;/a&gt; introduces the two protagonists and the institutions behind them, from the same data the in-game briefing uses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Music that plays in every browser.&lt;/strong&gt; The file server behind the game never decoded URLs, so any file with a space or an accented letter in its name was answered with a web page instead. It now decodes paths and answers byte-range requests, which Safari needs before it will play audio at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More ways to install.&lt;/strong&gt; Releases now reach Homebrew, Scoop and the AUR on their own, and are submitted to WinGet, where the first version is waiting for Microsoft's review.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://nightcell7.com/play" rel="noopener noreferrer"&gt;nightcell7.com/play&lt;/a&gt;. The briefing and coach appear on your first run. Turn your volume up in the yard for the album. The desktop builds for macOS, Windows and Linux are at &lt;a href="https://nightcell7.com/downloads" rel="noopener noreferrer"&gt;nightcell7.com/downloads&lt;/a&gt;, along with the one-line Homebrew, Scoop and AUR installs (&lt;code&gt;yay -S nightcell7-bin&lt;/code&gt; on Arch).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/profullstack/nightcell7/releases/tag/v0.3.0" rel="noopener noreferrer"&gt;https://github.com/profullstack/nightcell7/releases/tag/v0.3.0&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
