Shellsharks Blogroll - BlogFlock2026-07-09T00:13:11.663ZBlogFlockAdepts of 0xCC, destructured, fLaMEd, Aaron Parecki, Trail of Bits Blog, Westenberg, James' Coffee Blog, gynvael.coldwind//vx.log (pl), joelchrono, Evan Boehs, cool-as-heck, Kev Quirk, Posts feed, Sophie Koonin, cmdr-nova@internet:~$, <span>Songs</span> on the Security of Networks, Johnny.Decimal, Werd I/O, Robb Knight, Molly White, Hey, it's Jason!, Terence Eden’s BlogRE: Screens ≠ Books - Joel's Log Fileshttps://joelchrono.xyz/blog/re-screens-books2026-07-08T21:45:00.000Z<p>Since <a href="https://mas.to/@janerationx">JanerationX</a> started a new blog on <a href="https://pika.page/">Pika</a> a couple months ago, I have been enamoured by it. She is not afraid to express her thoughts and is refreshing to see.</p>
<p>I’ve been meaning to add her to my blogroll because of how cool she is, and I have wanted to comment on some of the posts, such as the one on <a href="https://janerationx.me/posts/the-human-touch/">the human houch</a>, or another one about how <a href="https://janerationx.me/posts/yeah-social-media-is-dead">social media is dead</a>. So yeah, go read those! Can’t help but appreciate how raw her writing is and how unapologetic her opinions are.</p>
<p>To continue contributing to <a href="https://robertbirming.com/julyreply/">July Reply</a>, I thought that <a href="https://janerationx.me/posts/screens-books">this latest post</a> would make for a nice reply from me. I’ll be trying to speak my mind about things as well in something that I love: Reading books, ereaders, and analog things!</p>
<p>First, it is absolutely true that the feeling of a book is a wonderful pleasure. A book that is well loved, or a book that is brand new, each has its charms and traits, and no piece of technology will ever equate to the analog sensation it provides.</p>
<p>After all, there’s a reason why I have a growing interest on writing by hand or listening to music offline, and I often still buy at least one physical book every couple of months.</p>
<p>The one thing that kind of irked me about the post, is not that books aren’t great, but that the screens of an e-reader and the screen of any other device are <em>extremely</em> different, and much less damaging than your average IPS/LED screen.</p>
<p>I felt like there were a few misconceptions, and I wanted to gently correct them, if I may do so in this little corner of the web I call my blog, I’m sorry, I couldn’t help myself, I had to share some good news!</p>
<p>First, e-ink screens do not need a backlight to function! I am unable to explain how the technology actually works, but it’s pretty much just a panel that prints information on itself and displays it in the same way a sheet of paper would. Think of it like an improved version of a calculator or a cheap digital watch display, they don’t really have a light—other than an LED in the corner that you can turn on for a second to see the time—that’s why the battery lasts so long!</p>
<p>Most ereaders do come with a backlight of course, but it’s just a useful bonus feature, and only really needed at night, otherwise, an e-ink screen can be lit up by any external light source just like a sheet of paper in a book needs light to be seen.</p>
<p>Also, when you do use the backlight, most have a blue light filter setting to avoid eye strain. Another funny thing is that you don’t need power to keep e-ink screens working, it can display the same thing indefinitely, you just need energy to change its state.</p>
<p>Of course, there’s other points and negatives to Kindles. Such as a false sense of ownership, with Amazon messing up and even modifying purchased ebooks from people without consent, altering personal libraries or even banning users, unable to access books they paid for!</p>
<p>However, it is also true that you can acquire digital books without any DRM, books which you can put in a hard drive or USB or any medium you personally own, and transfer to your device without a problem!</p>
<p>Physical books should always have a place, and it’s a shame to see some publishers cheapening out as they prefer to earn more selling on digital form, with proprietary platforms more often than not.</p>
<p>I guess I just wanted to say that e-ink screens and book pages have a lot more in common than one may think. As do digital and physical copies, as long as they can be owned by the individuals, or why not, through libraries thanks to services like Libby or Overdrive!</p>
<p>The real enemy is being unable to access the books you own, just because of the greed of some companies who don’t respect what should be our rights as a costumer. I think digital and physical ownership are of equal value, and books (or any media) you buy should belong to the person who paid for it!</p>
<p>In the end, screens aren’t equal to books, but e-ink isn’t too bad I’d say :3</p>
<p>This is day 92 of <a href="https://100daystooffload.com">#100DaysToOffload</a>.</p>
<p>
<a href="mailto:me@joelchrono.xyz?subject=RE: Screens ≠ Books">Reply to this post via email</a> |
<a href="https://fosstodon.org/@joel/116886574230674361">Reply on Fediverse</a>
</p>Startups track business metrics. Newsrooms should learn from them. - Werd I/O6a4e57db19899d00015d87f12026-07-08T13:59:55.000Z<p>Link: <a href="https://news.crunchbase.com/saas/metrics-unit-economics-questions-sagie/?ref=werd.io"><em>Your SaaS Metrics Are A Result, Not A Strategy, by Itay Sagie in Crunchbase News</em></a></p><p>I still subscribe to sites like Crunchbase News from my time in startup-land; although it’s been a while since I’ve run the financial side of a business, I’m interested, and I know that I’ll run one again. I see stories like this and wonder: what would it look like for a newsroom to think this way? In startups, these metrics are known top to bottom, but I’ve rarely heard business teams talk about <a href="https://executiveeducation.wharton.upenn.edu/thought-leadership/wharton-online-insights/why-customer-lifetime-value-matters/?ref=werd.io">LTV</a> (customer Life Time Value), <a href="https://www.paddle.com/resources/customer-acquisition-cost?ref=werd.io">CAC</a> (Customer Acquisition Cost), or even <a href="https://www.salesforce.com/sales/revenue-lifecycle-management/annual-recurring-revenue-arr/?ref=werd.io">ARR</a> (Annual Recurring Revenue). </p><p>This may be happening in finance and fundraising teams, but the culture of talking about customers / donors in teams more widely often simply isn’t there: metrics aren't communicated, dashboards aren't made available, the concepts of the metrics themselves are not explained. Not everyone should be thinking about this all the time – the firewall between business and editorial is important to maintain – but in order to make sharp prioritization and experimentation decisions, the business side should be much more customer / donor focused than they often are.</p><p>Beyond that, this piece points out, rightly, that metrics are not strategy: they’re the measurable outcome of your strategy. They’re important tools to help you figure out cause and effect and improve your revenue efficiency, but they are not the underlying mechanism.</p><p>Interesting provocation here from the author:</p><blockquote>“The Rule of 4 adds a simple durability check: ARR growth divided by annual customer churn should be above four. If it is low, growth may be hiding a leaking bucket.<br><br>[The board should ask:] are we growing on top of a loyal customer base, or replacing customers we should have kept?”</blockquote><p>Growth in annual recurring revenue — the portion of your revenue that is from recurring customers like subscribers or monthly / annual donors — is expressed as a percentage. So is churn: what percentage of customers (paid subscribers, members, recurring donors) cancel their commitments and don’t return?</p><p>How many newsrooms have those numbers handy? What would it take to measure them? Which systems are missing that would let you do that?</p><p>There is <em>so much</em> that newsrooms — including non-profit publications — can learn from for-profit startups and other businesses. There’s a lot to be gained by sharing knowledge from those other domains. Figuring out which metrics successful businesses track and mapping the data gaps inside a newsroom is a good place to start.</p>A bug which only affected left-handed users - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=724972026-07-08T11:34:43.000Z<blockquote><p>Verily, some of our brethren (and sistren) be afflicted with a sinister disposition. While the righteous scroll using the thumb of their right hand - as is good and proper - an accurs'd minority swing the other way.</p></blockquote>
<p>Look, you try writing an interesting bug report without sounding like a clanker, OK!</p>
<p>I try to optimise my blog as much as possible. It may not look like much, but it has got it where it counts. I've made a lot of special modifications myself to the base WordPress install.</p>
<p>One of those modifications is reducing the amount of JavaScript in use to the bare minimum. Everything functions without it, but there are a few places where it helps - the most notable being comments.</p>
<p>That's why I was distressed when a loyal reader wrote in saying there was a bug on my site. When they were scrolling the page a comment box would suddenly appear and interrupt their browsing.</p>
<p>I scroll my own site a lot (probably more than is healthy) so why hadn't I noticed this bug?</p>
<p>Because I scroll with my right thumb and the bug reporter uses their left. The "reply" link which was being triggered is on the left side of the page. A bug which won't be triggered by righteous people but infuriating to those who will surely be left behind after The Rapture™.</p>
<p>To be fair, <a href="https://core.trac.wordpress.org/ticket/46713">this bug was reported seven years ago</a> - but I guess the WordPress team have been too busy cleaning up after their mad God-Emperor to take a look at it.</p>
<p>Back in 2017, a developer <a href="https://github.com/WordPress/wordpress-develop/commit/14dd35e62e1325ca1c6caef0a8623ce3f02d7da9">added a <code>touchstart</code> listener to link clicks</a>. I don't really understand why. At one point in history, browsers couldn't be sure if a touch event was the start of a <code>click</code> or a double-tap to zoom. So firing an even when a touch occurred on a link sort of made sense to avoid a 300ms delay.</p>
<p>But that hadn't been the case for several years - <a href="https://developer.chrome.com/blog/300ms-tap-delay-gone-away">as this 2013 blog post makes clear</a>. Even in 2015 <a href="https://github.com/ftlabs/fastclick">it was no longer an issue</a>.</p>
<p>So why was this lefty-baiting code added? Not a clue.</p>
<p>Anyway, <strong>seven years</strong> after the bug was reported <a href="https://github.com/WordPress/wordpress-develop/pull/12168">I committed a fix</a>. It isn't the most sophisticated change to WordPress - merely deleting a couple of lines. But hopefully it will stop those strange and unusual mutants from complaining that their unnatural thumb-usage is accidentally triggering unwanted events on my website.</p>
<p>Sadly, there is as yet no way to prevent the corrupt from using our blessed sites. The WHAT-WG haven't seen fit to take on board my suggestion of <code><meta handed="right"></code> to keep out the unwanted and polluted. So, hopefully, this change will at least prevent them complaining.</p>
<p>Why, yes officer, I have had a glass or two of tonic wine. Why do you ask?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=72497&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager">Mutation testing comes to DAML - Trail of Bits Bloghttps://blog.trailofbits.com/2026/07/08/mutation-testing-comes-to-daml/2026-07-08T11:00:00.000Z<p>In April we released <a href="https://blog.trailofbits.com/2026/04/01/mutation-testing-for-the-agentic-era/">Mewt</a>, our open-source mutation-testing engine that finds the gaps in your test suite. Today we’re expanding it with support for DAML, the language Canton Network applications are written in. Mewt now reads DAML, generates several classes of mutants (including two built for DAML&rsquo;s authorization primitives), and runs them through your existing test suite to count how many mutants survive. If you want to try it, simply install Mewt from the <a href="https://github.com/trailofbits/mewt">repository</a>, point a <code>mewt.toml</code> at your project and its test command, and use <code>mewt run</code>.</p>
<p>For a team shipping DAML to production, that count is what a passing test run is actually worth: it puts a number on how much your suite checks, whereas a green run on its own does not.</p>
<h2 id="why-damls-coverage-reports-lie">Why DAML’s coverage reports lie</h2>
<p>Test coverage is the most reassuring lie in smart-contract development. Hitting 100% line coverage tells you the test runner walked the code; it does not tell you whether any test would fail if that code stopped doing what it is supposed to. We have been grading test harnesses by how many mutants they kill since at least <a href="https://blog.trailofbits.com/2019/01/23/fuzzing-an-api-with-deepstate-part-2/">2019</a>, and <a href="https://blog.trailofbits.com/2025/09/18/use-mutation-testing-to-find-the-bugs-your-tests-dont-catch/">our primer on finding the bugs your tests don&rsquo;t catch</a> shows how a green suite can still miss the bug that matters.</p>
<p>DAML&rsquo;s built-in coverage measures execution at the template and choice level: which templates were created and which choices were exercised over the test run. It reports whether each choice was exercised, not what happened inside it. A test that exercises a choice once and asserts nothing about the result reports that choice as covered. The report prints the same green percentage whether the test verifies the outcome or discards it.</p>
<h2 id="how-mutation-testing-works">How mutation testing works</h2>
<p>Instead of asking whether your tests reached the code, mutation testing grades your tests by sabotaging that code. The engine generates mutants, copies of the code that each carry one small deliberate change: a flipped comparison, a removed branch, a dropped party. It then runs your test suite against each one. A mutant that makes the suite fail is caught; a mutant that passes every test survives. Every survivor is a change your tests let through, and each one is either harmless or a potential bug. The harmless ones are equivalent code no test could distinguish or a branch no execution reaches, and you can set those aside. The rest are a to-do list: each one is a specific test you are missing, a case your suite should check but does not, occasionally with a real bug sitting behind the gap. The primer above describes a real audit where a mutation campaign surfaced a high-severity bug that the project&rsquo;s tests had missed.</p>
<h2 id="mutation-testing-forces-the-unhappy-path">Mutation testing forces the unhappy path</h2>
<p>A DAML contract encodes rights and obligations between named parties: who holds what, who owes what to whom, and who must authorize each step. A party is not an anonymous address. It represents a real organization or person, and the contract is the rulebook for how those parties interact, including which of them can take which action, what each is allowed to see, and what stays private between them.</p>
<p>Authorization is how that rulebook is enforced: who may take which action. It is also easy to get wrong in ordinary ways, such as a typo in a controller clause, a missing party, an extra one left over from a refactor. Every combination type-checks, so nothing rejects it before it ships. A static analyzer can flag suspicious patterns, but it has no way to know which party should hold which authority on your contract. That knowledge lives in your specification, and for most projects, the only executable form of the specification is the test suite. Happy-path tests supply every signature the contract asks for and confirm the transaction succeeds. They never try the negative case—removing a required signature and checking that the ledger rejects the transaction—so they never actually test whether that signature was required at all. If the tests don&rsquo;t encode that rule, nothing downstream can recover it. Mutation testing is what tells you whether they do.</p>
<p>A green test run tells you your tests passed today. Mutation testing asks the harder question: would your tests catch a mistake, now or after the next code change? Where the answer is no, you have found a test case worth writing.</p>
<h2 id="what-mewt-adds-for-daml">What Mewt adds for DAML</h2>
<p>Mewt parses every language it supports with a tree-sitter grammar. As of mid-2026, there is no maintained tree-sitter grammar for DAML, so we reused the upstream <code>tree-sitter-haskell</code> grammar. DAML is Haskell-shaped, but its contract constructs (<code>template</code>, <code>choice</code>, <code>controller</code>, and <code>signatory</code>) are not Haskell, and the grammar parses them as error-recovered subtrees. That matters less than it sounds. The common mutations still work on DAML&rsquo;s ordinary expressions, so Mewt swaps arithmetic and comparison operators, flips Booleans, and removes branches just as it does in any other language, with only small adjustments where DAML&rsquo;s surface syntax differs (DAML writes <code>/=</code> where most languages write <code>!=</code>). We got most of the value of a from-scratch grammar without building one.</p>
<p>The new engineering went into DAML&rsquo;s authorization primitives, where the authorization bugs from the previous section live. Mewt adds two DAML-specific mutations:</p>
<ul>
<li>
<p><strong>Controller party swap</strong> (CPS in Mewt&rsquo;s output): replace one party in a <code>controller</code> clause with another party that is in scope at that site.</p>
</li>
<li>
<p><strong>Controller party removal</strong> (CPR): drop one party from a multi-party controller list.</p>
</li>
</ul>
<p>Both target the same question: if the set of parties allowed to exercise this choice silently changed, would any test fail? They are a deliberately small starting set aimed at the bug class above, and more DAML-specific mutations are in the pipeline.</p>
<p>Driving a campaign needs no new harness. A short <code>mewt.toml</code> names the files to mutate and the test command (<code>dpm test</code> for a Daml 3 project), and <code>mewt run</code> does the rest, reporting each mutant as caught or surviving. The setup is deliberately small: trying it on your own project costs minutes, and we encourage exactly that.</p>
<h2 id="what-a-surviving-mutant-looks-like">What a surviving mutant looks like</h2>
<p>Picture a conditional payment between a buyer and a seller: the buyer sets money aside for the goods, and paying it out to the seller requires both parties to sign off. The buyer&rsquo;s signature is the delivery confirmation. In DAML, that policy is one line: the <code>controller</code> line on the <code>Release</code> choice.</p>
<figure class="highlight">
<pre tabindex="0"><code class="language-" data-lang=""
>template ConditionalPayment
with
buyer : Party
seller : Party
amount : Decimal
where
signatory buyer
observer seller
choice Release : ()
with
paid : Decimal
controller buyer, seller
do
assert (paid == amount)</code></pre>
<figcaption><span>Figure 1: A payment that requires both the buyer and the seller to approve its release</span></figcaption>
</figure>
<p>A typical happy-path test creates the payment and has both parties approve the release. The <code>actAs buyer &lt;&gt; actAs seller</code> line submits the command with both parties&rsquo; authority:</p>
<figure class="highlight">
<pre tabindex="0"><code class="language-" data-lang=""
>testHappyPath : Script ()
testHappyPath = script do
buyer &lt;- allocateParty &#34;Buyer&#34;
seller &lt;- allocateParty &#34;Seller&#34;
payment &lt;- submit buyer do
createCmd ConditionalPayment with
buyer
seller
amount = 100.0
submit (actAs buyer &lt;&gt; actAs seller) do
exerciseCmd payment Release with paid = 100.0
pure ()</code></pre>
<figcaption><span>Figure 2: The happy-path test. It passes, and coverage reports 100%.</span></figcaption>
</figure>
<p>The test passes, and by the usual measure the suite looks complete: running <code>dpm test</code> with coverage reporting enabled shows full coverage.</p>
<figure class="highlight">
<pre tabindex="0"><code class="language-" data-lang=""
>$ dpm test --show-coverage --coverage-ignore-choice Archive
testHappyPath: ok, 0 active contracts, 2 transactions.
- Internal templates: 1 defined, 1 (100.0%) created
- Internal template choices: 1 defined, 1 (100.0%) exercised</code></pre>
<figcaption><span>Figure 3: The coverage report for the happy-path test. Every template is created and every choice is exercised, for 100% coverage.</span></figcaption>
</figure>
<p>The <code>--coverage-ignore-choice Archive</code> flag deserves a word. Every DAML template automatically gets an implicit <code>Archive</code> choice. It is not part of the business logic under test, so we exclude it for simplicity. With it included, this one-choice template would report 50% even though the test exercises everything we wrote.</p>
<p>Run Mewt on the project and it generates seven mutants. The test suite catches three of them. Four survive. Here is one of the survivors, shown as the diff Mewt reports:</p>
<figure class="highlight">
<pre tabindex="0"><code class="language-" data-lang=""
> choice Release : ()
with
paid : Decimal
- controller buyer, seller
&#43; controller seller
do
assert (paid == amount)</code></pre>
<figcaption><span>Figure 4: The controller-removal mutant that survives the test suite</span></figcaption>
</figure>
<p>Re-run the test suite against this mutant. It still passes, and coverage still reports 100%. The contract claims releasing the buyer&rsquo;s money requires both parties. The mutant lets the seller release it to themselves without the buyer ever confirming delivery. The tests report green either way. Only a test that tries the <em>forbidden</em> path, the seller acting alone, expecting the ledger to reject it, can tell the two contracts apart. No such test exists, and the mutation score says so. (The other three survivors tell the same story from different angles: the buyer-alone twin of this mutant, and two mutants that weaken the <code>paid == amount</code> check to <code>&lt;=</code> and <code>&gt;=</code>, which survive because the test only ever pays the exact amount.)</p>
<p>Step back, and this is the whole point of the exercise. Your tests are the executable specification of your code. Here the implementation changed, one required approval instead of two, and the specification did not react. That means the expected behavior was underspecified all along: whether both the buyer and the seller have to sign off, or just one of them, was never actually written down anywhere a machine could check. Every controller combination type-checks, and coverage reports 100% for all of them. The only place &ldquo;both must sign&rdquo; can exist in checkable form is a test that expects the weakened contract to fail, and writing that test is exactly what the surviving mutant tells you to do.</p>
<h2 id="limitations-and-what-comes-next">Limitations and what comes next</h2>
<p>Mewt is not magic. Two limits are worth knowing before you run your first campaign: not every survivor is a real gap, and a campaign costs time. The roadmap that follows them is where we are taking the work next.</p>
<p>Equivalent mutants exist: some survivors turn out to be semantically identical to the original program, so no test could ever catch them. Few public DAML codebases on GitHub come with a full test suite, so we are glad OpenZeppelin open-sourced its <code>canton-stablecoin</code> reference implementation. Mewt generated hundreds of mutants for it. We ran the highest-priority ones through the existing test suite, and seven of those survived. Three were equivalent mutants or sat behind a guard that no path reaches, and the other four were genuine missing test cases. None of the survivors we reviewed pointed to a bug. Such a clean result is what you want when you run Mewt on your own code, and triaging them took minutes.</p>
<p>One of those equivalent mutants shows what that means concretely. A helper computed accrued debt:</p>
<figure class="highlight">
<pre tabindex="0"><code class="language-" data-lang=""
>accrueDebt currentDebt lastAccrual now annualRate =
if currentDebt == 0.0 || annualRate == 0.0 then currentDebt
else
let elapsedYears = ... -- elapsed time as a fraction of a year
in currentDebt * (1.0 &#43; annualRate * elapsedYears)</code></pre>
<figcaption><span>Figure 5: The accrueDebt helper. Its first-line guard is a shortcut that returns the same value the calculation already produces.</span></figcaption>
</figure>
<p>Mewt forced the <code>if</code> to always take the <code>else</code> branch. No test failed, and none ever could: when the debt is zero, the formula multiplies by zero and returns zero, and when the rate is zero, it multiplies the debt by one and returns it unchanged. The guard is a shortcut that returns the value the formula already produces, so removing it changes nothing. Mewt suppresses the equivalent mutants it can detect. The rest need a reviewer&rsquo;s judgment to dismiss.</p>
<p>Campaigns cost time in two places. The machine part: Mewt runs your test suite once per mutant, so the wall-clock cost is roughly the number of mutants times how long one test run takes, plus a rebuild if your project needs one. That is minutes on a small codebase and hours on a large one or a slow suite, so the cadence that works is nightly or weekly rather than per-commit. The human part: someone has to look at the survivors. We are working on that front from several directions at Trail of Bits, including our <a href="https://github.com/trailofbits/skills/tree/main/plugins/mutation-testing">mutation-testing skill</a> that helps configure campaigns for your project, and <a href="https://blog.trailofbits.com/2026/04/23/trailmark-turns-code-into-graphs/">Trailmark</a> with its <code>genotoxic</code> triage skill. None of these understand DAML yet, but the direction is clear: given the right harness and tools, the time-consuming parts of a campaign can be handed to AI agents. The effort is modest and the payoff is concrete: each genuine survivor is a specific test you can write, and every test you add makes your suite enforce one more guarantee your contracts are supposed to make.</p>
<p>Also on the roadmap: choice-consumption mutations (<code>consuming</code> vs <code>nonconsuming</code>) sit cleanly on top of the controller-mutation scaffolding and target a bug class Mewt does not yet reach.</p>
<h2 id="dive-in">Dive in</h2>
<p>Install Mewt from the <a href="https://github.com/trailofbits/mewt">repository</a>, point a <code>mewt.toml</code> at your project and its test command, and <code>mewt run</code>. The quickstart in the README covers the rest. DAML works out of the box. Everything here ran on Daml 3.4 with <code>dpm</code>, but Mewt just drives whatever test command you configure, so Daml 2 projects using the <code>daml</code> assistant work the same way.</p>
<p>Mutation testing complements the rest of your security stack, the type checkers, linters, and property tests you already run, rather than replacing any of it.</p>
<p>If you&rsquo;re building on Canton, we help teams with security reviews of DAML applications and with the way the code gets built: working directly with your engineers on the development process itself. <a href="https://www.trailofbits.com/contact/">Contact us</a>.</p>RE: On Form Factor - Joel's Log Fileshttps://joelchrono.xyz/blog/on-form-factor2026-07-07T21:25:00.000Z<p>Josh, a fellow member of the TWG server, wrote an awesome piece about <a href="https://espressotonic.beehiiv.com/p/on-form-factor">the form factor</a> of the devices and stuff he carries and how that affects the way he uses them! Since we are celebrating <a href="https://robertbirming.com/julyreply/">July Reply</a>, this post seemed like the perfect way for me to reply to!.</p>
<p>In a way, what the premise boils down to is what properties should an object have in order to be used <em>with intent</em> and not ignored?</p>
<p>The way we can afford to spend time intentionally, playing videogames, reading a book or listening to music, is heavily dependant in the context of our lives.</p>
<p>Right now the most time I get to spend doing such activities is on my <a href="/blog/the-time-commuting-to-work">commutes to work</a>, but going to the gym, arriving early to work, or simply being home, come with their own set of situations.</p>
<h2 id="on-commutes">On commutes</h2>
<p>During my commute, I have plenty of advantages. I have free time, I have nothing else to do, and I can carry a backpack with fun trinkets inside.</p>
<p>This means that I can bring my Nintendo Switch and play it the whole trip without any problem. Or any other device for that matter.</p>
<p>However, not all commutes are the same, bringing the Switch is only worth it if my commutes are long and if I really just go from home to work and viceversa.</p>
<p>Sometimes, my commute takes an hour—perfect for some Switch gaming!—but other times, it only takes about 25 minutes, and that makes things a bit more tight. Those shorter sessions are excellent and they were plenty of time for me to complete something like <a href="/blog/metal-gear-solid-peace-walker/"><em>Metal Gear Solid: Peace Walker</em></a> or <em>Fire Emblem Awakening</em>. However, I was lucky enough to have longer commutes when I played <a href="/blog/metroid-prime-remastered/"><em>Metroid Prime Remastered</em></a>.</p>
<p>Ocassionaly, I will get off the work bus on my way back home, and take the public transport, this happens when I’m heading to the gym, or to pick-up a delivery. In such a situation, I don’t dare to bring out my Nintendo Switch in public—I don’t want it stolen—and I won’t take out my PSP either, because the buses here are wild and if I drop it, the battery and its cover popping out who knows where.</p>
<p>Something like my <a href="/blog/the-gba-experience-i-wanted/">Anbernic RG35XX SP</a> is often ideal, much cheaper and smaller, and while not sturdier than the PSP overall, the battery doesn’t pop out—although the cheap plastic will definitely chip.</p>
<p>Of course, this is assuming I can sit. There are seasons where buses will be full of people all the time, and it’s in those moments where my <em>Miyoo Mini Flip</em> comes to the rescue! I actually haven’t used it that much, but when I know I’ll probably have to stand up among lots of people, using it to play one-handed-friendly games is awesome.</p>
<p>However, in that case, I may prefer to read some pages on my <a href="/blog/early-days-with-xteink-x4/">XTEINK X4</a>. And yes, I am one of those weirdos that can actually read while on the move, even on a bus where I keep swaying in every direction at every stop.</p>
<p>Another reason I wouldn’t carry my Switch is because of the added space taken by it. A huge deterrent for me to go to the gym is carrying my extra clothing. I also don’t feel comfortable leaving such a device alone, even when there are locker rooms.</p>
<h2 id="at-the-gym">At the gym</h2>
<p>I don’t use gaming handhelds at the gym. I probably shouldn’t care of what others may think if I did, but I do. Besides, it’s much easier for me to get distracted from the excercise by it, or slowing down my walking pace because of the game. Even if I feel justified when others spend that time on their phones resting for five whole minutes.</p>
<p>I simply stick to a podcast when at the gym, put my earphones and ignore everything else. My body focuses on the excercise and my mind is entertained by an episode of <em>Wolf 359</em>.</p>
<p>However, every once in a while I’ve been bold enough to take out my Kobo reader and read a whole chapter of <em>The Expanse</em> while using the threadmill. I have done the same thing with my XTEINK without much issues.</p>
<p>This is only really useful while on a walk or stationary bike, as most machinery lacks a decent place for me to place the ereader in a good position for me to read. Plus, getting stuck staring at a page I already completed because of an excercise would probably drive me mad.</p>
<p>Indeed, music and podcasts are best here, though the XTEINK’s form factor is nice to carry without feeling uncomfortable on my pocket for the rest of my routine. If it’s my Kobo, I’d have to bring it on hand, leave it on the floor or some other random surfaces, or store it in the locker after the warm-up.</p>
<h2 id="at-work">At work</h2>
<p>To be clear I <em>do not</em> play with my handheld consoles during work hours.</p>
<p>Fine, fine… maybe a casual session of <em>Slice & Dice</em> on my phone every once in a while.</p>
<p>Still, for the longest time the fact that the Kobo looks like paper allowed me to just have it by my side and read a few pages here and there. The form factor was ideal to pass off as a notebook—or maybe people knew sometimes but nobody cared.</p>
<p>Eventually, when the XTEINK came about, things got a lot easier! Although I still use both interchangeably, as it seems not many people mind about telling me to <em>not</em> read books at work…</p>
<p>Oh, right, outside of work hours, I do play with my handhelds sometimes, whenever my commute is short and I arrive early at my workplace! I only do this with smaller form factors, latest of which is my <a href="/blog/nintendo-3ds/">New Nintendo 3DS XL</a>, that has been the largest of the bunch, but still slim enough for me to use before more people start showing up. The Nintendo Switch is way too big and I don’t need to bring attention to it, honestly.</p>
<h2 id="at-home">At home</h2>
<p>Tthe only thing that stops me from gaming, reading, or doing whatever I want, is the fact that I don’t live by myself!</p>
<p>Now, it’s not that I can’t do something I want, I am simply reminded of my responsibilities and aware of the fact that everyone in the house has to fulfill them for the sake of peace, harmony and love!</p>
<p>Because of this, I mostly stick to devices that allow me to have quick sessions, or some leisure time on the TV with my Nintendo Switch, when every duty of mine is taken care of—or when there’s nobody at home to stop me.</p>
<p>I rarely read at home using my Kobo or my XTEINK, if I’m honest, as I would rather spend my time doing things like playing longer games with a narrative that may end up too disjointed when split into short bursts.</p>
<p>This is also the one moment where I actually don’t mind reading a physical book as well, something different from my sci-fi novels or Manga. It’s where I get to read or study the bible, rather than entertainment.</p>
<p>Podcasts also win a lot of ground especially during chores here, great background noise when doing dishes or mopping the floor.</p>
<p>All in all, the form factor matters less when there’s no real limit to what I can do, I guess.</p>
<h2 id="finishing-thoughts">Finishing thoughts</h2>
<p>Josh’s final paragraphs focused on the intentionality of choosing a specific device for a specific moment, for a specific use. Every gadget having a certain meaning, a form factor that makes it ideal for a time, a place, or a state of mind.</p>
<p>Especially so when so much out there is trying to convinced of how encompassing everything into a single point—the smartphone—is the ideal. In the end, it’s all too overwhelming, and overdependence on it only ends up hurting us, being a distraction to what matters in the moment.</p>
<p>That was some pretty good stuff, and I feel I sort of wandered off into what use every device of mine has in many situations. There’s a couple of other things I could have talked about, such as going out shopping or similar, but it’s clear that the tiniest devices are often great when they are meant to fill up empty spaces rather than being the focus.</p>
<p>At last, this is enough for a post, I’d say! I am also going to continue playing some <em>Fire Emblem Awakening</em> on my 3DS after I’m done with this.</p>
<p>This is day 92 of <a href="https://100daystooffload.com">#100DaysToOffload</a>.</p>
<p>
<a href="mailto:me@joelchrono.xyz?subject=RE: On Form Factor">Reply to this post via email</a> |
<a href="https://fosstodon.org/@joel/116880830558317275">Reply on Fediverse</a>
</p>Implementing standard.site on a nonstandard site - Posts feedhttps://www.coryd.dev/posts/2026/implementing-standardsite-on-a-nonstandard-site2026-07-07T21:13:00.000Z<div class="e-content"><p>Now that <a href="https://www.coryd.dev/colophon">this site is written in Go</a><sup id="fnref:1" class="footnote-ref">1</sup>, I've turned to restoring and adding more features connecting it to the open web. While I maintain a healthy skepticism of Bluesky the company, I'm enamored with open protocols and <a href="https://en.wikipedia.org/wiki/AT_Protocol">ATProto</a> falls under that umbrella.</p>
<p>ATProto exists apart from Bluesky and Bluesky is built on top of ATProto.</p>
<p><a href="https://en.wikipedia.org/wiki/AT_Protocol">Wikipedia</a></p>
<blockquote>
<p>The AT Protocol (Authenticated Transfer Protocol, pronounced "at protocol", commonly shortened to ATproto or "ATP") is a protocol and set of open standards for decentralized publishing and distribution of self-authenticating data within the social web. It serves as the technical foundation of the Bluesky social network, originally developed as a reference implementation for the protocol, as well as an ecosystem of interoperable social applications and services collectively referred to as the ATmosphere.</p>
</blockquote>
<p>After reluctantly joining <a href="https://www.coryd.dev/posts/2026/lifting-mastodon-rate-limits">Bluesky</a>, I saw a host of folks implementing <a href="https://standard.site">standard.site</a>.</p>
<blockquote>
<p>Standard.site provides shared lexicons for long-form publishing on AT Protocol. Making content easier to discover, index, and move across the ATmosphere.</p>
</blockquote>
<p>It's another protocol to support, another network, another implementation, another maintenance burden and all that, but it gets you a fancy button and avatar attached to posts. How could I resist?</p>
<p>My implementation is hand-rolled against Bluesky's endpoints since that's where my PDS lives.<sup id="fnref:2" class="footnote-ref">2</sup></p>
<aside>
<p><a href="https://en.wikipedia.org/wiki/AT_Protocol#Personal_Data_Servers">Wikipedia, again:</a> Personal Data Servers (PDSes) host user repositories and their associated media. They also serve as the network access point for users, facilitating repository updates, backups, data queries, and user requests.</p>
</aside>
<article class="entry-card h-entry"><aside class="meta"><time class="dt-published" datetime="2026-07-07T17:21:00Z"><a href="https://www.coryd.dev/links/publishing-on-the-atmosphere-with-standardsite" class="u-url">July 7, 2026</a></time> </aside><h3><a href="https://piccalil.li/blog/publishing-on-the-atmosphere-with-standardsite/?ref=main-rss-feed" title="Publishing on the Atmosphere with Standard.site" class="p-name u-bookmark-of">Publishing on the Atmosphere with Standard.site</a> via <a href="https://vale.rocks">Declan Chidlow</a></h3><blockquote><p>Standard.site provides a set of lexicons for publishing long-form content on the internet using the same protocol used under the hood by Bluesky.</p>
</blockquote><div class="badges"><a href="https://www.coryd.dev/tags/tech" class="badge p-category">tech</a><a href="https://www.coryd.dev/tags/atproto" class="badge p-category">atproto</a><a href="https://www.coryd.dev/tags/bluesky" class="badge p-category">bluesky</a></div></article><h2 id="getting-things-working">Getting things working</h2>
<p>There are two record types that are required as part of this implementation: <code>site.standard.publication</code> which describes the site — name, description, url, theme and icon. <code>site.standard.document</code> is a record required for each piece of content, each pointing back to my site (the publication).</p>
<p>When I post a link the <a href="https://en.wikipedia.org/wiki/AT_Protocol#AppViews">AppView</a> crawls the page looking for two tags: <code><link rel="site.standard.document"></code> and <code><link rel="site.standard.publication"></code>. These tags have <code>href</code> attributes that are resolved against my PDS, verifying ownership by also fetching my <a href="https://www.coryd.dev/.well-known/site.standard.publication">/.well-known/site.standard.publication</a> endpoint.</p>
<p>I use a handful of <a href="https://riverqueue.com">River</a> jobs to make this work. Records are authored as plain <code>map[string]any</code> objects that are marshalled to JSON and <code>PUT</code> via <code>com.atproto.repo.putRecord</code>.</p>
<p>I post a bunch of different content types on this site, many of which are syndicated to Mastodon and Bluesky and each type that is syndicated is now published as part of the standard.site implementation. The shape of syndicated content is typically: title, description, hashtags and a link to the content. Only movies, books and concerts I've written something about are syndicated, while all posts are syndicated. The query to fetch movie data from my <a href="https://www.postgresql.org">PostgreSQL</a> database looks like this:</p>
<pre class="language-sql"><code class="language-sql"><span class="token keyword">SELECT</span> title<span class="token punctuation">,</span> COALESCE<span class="token punctuation">(</span>description<span class="token punctuation">,</span><span class="token string">''</span><span class="token punctuation">),</span> url<span class="token punctuation">,</span> last_watched<span class="token punctuation">,</span> COALESCE<span class="token punctuation">(</span>tags<span class="token punctuation">,</span><span class="token string">'{}'</span><span class="token punctuation">)</span> <span class="token keyword">FROM</span> optimized_movies <span class="token keyword">WHERE</span> review <span class="token keyword">IS</span> <span class="token keyword">NOT</span> <span class="token keyword">NULL</span> <span class="token keyword">AND</span> review <span class="token operator">!=</span> <span class="token string">''</span> <span class="token keyword">AND</span> last_watched <span class="token keyword">IS</span> <span class="token keyword">NOT</span> <span class="token keyword">NULL</span> <span class="token keyword">ORDER</span> <span class="token keyword">BY</span> last_watched <span class="token keyword">DESC</span>
</code></pre>
<p>The constructed document record looks like this:</p>
<pre class="language-go"><code class="language-go">record <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"$type"</span><span class="token punctuation">:</span> <span class="token string">"site.standard.document"</span><span class="token punctuation">,</span>
<span class="token string">"site"</span><span class="token punctuation">:</span> publicationURI<span class="token punctuation">,</span> <span class="token comment">// AT-URI back to the publication</span>
<span class="token string">"title"</span><span class="token punctuation">:</span> item<span class="token punctuation">.</span>Title<span class="token punctuation">,</span>
<span class="token string">"publishedAt"</span><span class="token punctuation">:</span> item<span class="token punctuation">.</span>Date<span class="token punctuation">.</span><span class="token function">UTC</span><span class="token punctuation">().</span><span class="token function">Format</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>RFC3339<span class="token punctuation">),</span>
<span class="token string">"path"</span><span class="token punctuation">:</span> item<span class="token punctuation">.</span>URL<span class="token punctuation">,</span> <span class="token comment">// also the rkey seed</span>
<span class="token punctuation">}</span>
</code></pre>
<p>The publication record looks like this:</p>
<pre class="language-go"><code class="language-go"><span class="token operator">...</span>
record <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"$type"</span><span class="token punctuation">:</span> <span class="token string">"site.standard.publication"</span><span class="token punctuation">,</span>
<span class="token string">"name"</span><span class="token punctuation">:</span> name<span class="token punctuation">,</span>
<span class="token string">"url"</span><span class="token punctuation">:</span> siteURL<span class="token punctuation">,</span>
<span class="token string">"preferences"</span><span class="token punctuation">:</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"showInDiscover"</span><span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
<span class="token punctuation">},</span>
<span class="token string">"basicTheme"</span><span class="token punctuation">:</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"$type"</span><span class="token punctuation">:</span> <span class="token string">"site.standard.theme.basic"</span><span class="token punctuation">,</span>
<span class="token string">"background"</span><span class="token punctuation">:</span> <span class="token function">rgbColor</span><span class="token punctuation">(</span><span class="token number">239</span><span class="token punctuation">,</span> <span class="token number">242</span><span class="token punctuation">,</span> <span class="token number">246</span><span class="token punctuation">),</span>
<span class="token string">"foreground"</span><span class="token punctuation">:</span> <span class="token function">rgbColor</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">14</span><span class="token punctuation">,</span> <span class="token number">19</span><span class="token punctuation">),</span>
<span class="token string">"accent"</span><span class="token punctuation">:</span> <span class="token function">rgbColor</span><span class="token punctuation">(</span><span class="token number">78</span><span class="token punctuation">,</span> <span class="token number">83</span><span class="token punctuation">,</span> <span class="token number">89</span><span class="token punctuation">),</span>
<span class="token string">"accentForeground"</span><span class="token punctuation">:</span> <span class="token function">rgbColor</span><span class="token punctuation">(</span><span class="token number">239</span><span class="token punctuation">,</span> <span class="token number">242</span><span class="token punctuation">,</span> <span class="token number">246</span><span class="token punctuation">),</span>
<span class="token punctuation">},</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> description <span class="token operator">!=</span> <span class="token string">""</span> <span class="token punctuation">{</span>
record<span class="token punctuation">[</span><span class="token string">"description"</span><span class="token punctuation">]</span> <span class="token punctuation">=</span> description
<span class="token punctuation">}</span>
<span class="token keyword">if</span> icon <span class="token operator">!=</span> <span class="token keyword">nil</span> <span class="token punctuation">{</span>
record<span class="token punctuation">[</span><span class="token string">"icon"</span><span class="token punctuation">]</span> <span class="token punctuation">=</span> icon
<span class="token punctuation">}</span>
<span class="token keyword">return</span> record
<span class="token operator">...</span>
</code></pre>
<aside>
<p>The icon referenced here isn't a URL. <code>atproto</code> records reference blobs, so I fetched my existing OG image and uploaded it to the PDS first, then dropped the returned blob ref into the record. I derived the theme colors from the canonical <code>vars.css</code> used to style my site.</p>
</aside>
<p>I created a pair of standard.site specific commands in the CMS for this site. One to sync my site record to my PDS and another to sync my backlog of posts. I ran both, verified the records, tried a test post and nothing happened. I hoped it was a caching or propagation issue, waited and still nothing. I hit a few walls building this but, being unreasonably stubborn, I continued on.</p>
<h3 id="bug-1-apex-vs-www">Bug 1: apex vs. www</h3>
<p>As best I can tell, the <code>AppView</code> verifies my <code>.well-known</code> endpoint using a non-JavaScript call. My username on Bluesky is my apex domain but that redirects to <code>www</code>. I set the site record to <code>www.coryd.dev</code> and allowed the <code>.well-known</code> path to resolve at the apex in my Caddy config.</p>
<h3 id="bug-2-set-a-stable-record-key">Bug 2: set a stable record key</h3>
<p>For this to work, the record key in Bluesky <em>must</em> match the one embedded in the head of each document. However, a number of my content types have a published date that can shift over time — books when I read them again and movies when I watch them again. I store a record for each read and watch but the published date is updated to the most recent each time. I chose to derive the <code>rkey</code> from the URL for each content type alone.</p>
<aside>
<p>Every record on a PDS lives in a collection at a record key. Its address is <code>at://{my-did}/site.standard.document/{rkey}</code>. I get to choose the <code>rkey</code> which, naturally, is how I managed to break things.</p>
</aside>
<pre class="language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">ATProtoTID</span><span class="token punctuation">(</span>urlPath <span class="token class-name">string</span><span class="token punctuation">)</span> <span class="token class-name">string</span> <span class="token punctuation">{</span>
h <span class="token operator">:=</span> fnv<span class="token punctuation">.</span><span class="token function">New64a</span><span class="token punctuation">()</span>
_<span class="token punctuation">,</span> _ <span class="token punctuation">=</span> h<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">([]</span><span class="token class-name">byte</span><span class="token punctuation">(</span>urlPath<span class="token punctuation">))</span>
n <span class="token operator">:=</span> h<span class="token punctuation">.</span><span class="token function">Sum64</span><span class="token punctuation">()</span> <span class="token operator">&</span> <span class="token number">0x7FFFFFFFFFFFFFFF</span>
<span class="token comment">// base32-encode to 13 chars</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Each URL is unique, stored with the record and can be used to derive a stable <code>rkey</code>.</p>
<h3 id="bug-3-theme-object-validation">Bug 3: theme object validation</h3>
<p>A simple, but fun one: failing to set <code>$type</code> on the theme object will cause the publication block to be dropped by the Bluesky parser.</p>
<p>I solved this one by comparing the structure of my publication record to Mat Maquis', line by line, after reading his <a href="https://wil.to/posts/implementing-standard-site/">implementation post</a>.</p>
<h2 id="stapling-things-together">Stapling things together</h2>
<p>When I syndicate something to Bluesky, the external embed carries a pair of references pointing at the document and publication:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">if</span> refs <span class="token operator">:=</span> w<span class="token punctuation">.</span><span class="token function">standardSiteRefs</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> session<span class="token punctuation">,</span> content<span class="token punctuation">);</span> <span class="token class-name">len</span><span class="token punctuation">(</span>refs<span class="token punctuation">)</span> <span class="token punctuation">></span> <span class="token number">0</span> <span class="token punctuation">{</span>
external<span class="token punctuation">[</span><span class="token string">"associatedRefs"</span><span class="token punctuation">]</span> <span class="token punctuation">=</span> refs
<span class="token punctuation">}</span>
record<span class="token punctuation">[</span><span class="token string">"embed"</span><span class="token punctuation">]</span> <span class="token punctuation">=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"$type"</span><span class="token punctuation">:</span> <span class="token string">"app.bsky.embed.external"</span><span class="token punctuation">,</span>
<span class="token string">"external"</span><span class="token punctuation">:</span> external<span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre>
<p>To properly derive these, the syndicated document has to be on the PDS before Bluesky indexes the post:</p>
<pre class="language-go"><code class="language-go"><span class="token comment">// Bluesky is delayed so the standard.site document record lands on the PDS</span>
<span class="token comment">// before Bluesky indexes the post</span>
<span class="token punctuation">{</span>jobs<span class="token punctuation">.</span>SyncSingleStandardSiteDocumentArgs<span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">},</span> <span class="token operator">&</span>river<span class="token punctuation">.</span>InsertOpts<span class="token punctuation">{</span>UniqueOpts<span class="token punctuation">:</span> dedupeInFlight<span class="token punctuation">}},</span>
<span class="token punctuation">{</span>jobs<span class="token punctuation">.</span>SyndicateToBlueskyArgs<span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">},</span> <span class="token operator">&</span>river<span class="token punctuation">.</span>InsertOpts<span class="token punctuation">{</span>
ScheduledAt<span class="token punctuation">:</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">().</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">30</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">),</span> UniqueOpts<span class="token punctuation">:</span> dedupeInFlight<span class="token punctuation">}},</span>
</code></pre>
<aside>
<p>Each ref is a <code>strongRef</code>: a record's <code>AT-URI</code> paired with a hash of its exact contents. This allows the embed to tell Bluesky precisely which document and publication this link maps to, rather than making it recrawl the page.</p>
</aside>
<p>The other fun part of this all is that these failures (inasmuch as Bluesky was concerned) were silent. My implementation passed the available validators I tried and the published records passed the eye test, but they failed Bluesky's validation.</p>
<article class="entry-card h-entry"><div class="status-card-body"><aside class="meta"><time class="dt-published" datetime="2026-06-13T03:24:00Z"><a href="https://www.coryd.dev/status/this-standardsite-2026-06-13-03-24-25" class="u-url">June 13, 2026</a></time> <span aria-hidden="true">•</span> <a href="https://www.coryd.dev/status">Status</a></aside><div class="e-content"><p>This standard.site stuff is super confusing</p>
</div><a class="u-url visually-hidden" href="https://www.coryd.dev/status/this-standardsite-2026-06-13-03-24-25" aria-hidden="true" tabindex="-1">/status/this-standardsite-2026-06-13-03-24-25</a></div></article><h3 id="actually-publishing">Actually publishing</h3>
<p>Every job authenticates when run using an app password against <code>com.atproto.server.createSession</code> which returns a short-lived bearer token (<code>accessJwt</code>) and confirms my <code>DID</code>:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">atprotoCreateSession</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> identifier<span class="token punctuation">,</span> appPassword <span class="token class-name">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token operator">*</span>blueskySession<span class="token punctuation">,</span> <span class="token class-name">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
body<span class="token punctuation">,</span> _ <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">string</span><span class="token punctuation">{</span>
<span class="token string">"identifier"</span><span class="token punctuation">:</span> identifier<span class="token punctuation">,</span>
<span class="token string">"password"</span><span class="token punctuation">:</span> appPassword<span class="token punctuation">,</span>
<span class="token punctuation">})</span>
<span class="token comment">// POST to /xrpc/com.atproto.server.createSession → { accessJwt, did }</span>
<span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Once I have the token, I can write the document record:</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">atprotoPutRecord</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> session <span class="token operator">*</span>blueskySession<span class="token punctuation">,</span> collection<span class="token punctuation">,</span> rkey <span class="token class-name">string</span><span class="token punctuation">,</span> record <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">)</span> <span class="token class-name">error</span> <span class="token punctuation">{</span>
body<span class="token punctuation">,</span> _ <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"repo"</span><span class="token punctuation">:</span> session<span class="token punctuation">.</span>DID<span class="token punctuation">,</span>
<span class="token string">"collection"</span><span class="token punctuation">:</span> collection<span class="token punctuation">,</span>
<span class="token string">"rkey"</span><span class="token punctuation">:</span> rkey<span class="token punctuation">,</span>
<span class="token string">"record"</span><span class="token punctuation">:</span> record<span class="token punctuation">,</span>
<span class="token punctuation">})</span>
<span class="token comment">// POST /xrpc/com.atproto.repo.putRecord with Authorization: Bearer <accessJwt></span>
<span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre>
<p><code>putRecord</code> creates the record if the <code>rkey</code> is unique and updates it if not. I can re-run my backlog sync safely knowing that it will update old records and add new ones but not destroy anything.</p>
<p>Syndicating to Bluesky itself is a separate and straightforward process. The record looks like this:</p>
<pre class="language-go"><code class="language-go">record <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"$type"</span><span class="token punctuation">:</span> <span class="token string">"app.bsky.feed.post"</span><span class="token punctuation">,</span>
<span class="token string">"text"</span><span class="token punctuation">:</span> text<span class="token punctuation">,</span>
<span class="token string">"createdAt"</span><span class="token punctuation">:</span> time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">().</span><span class="token function">UTC</span><span class="token punctuation">().</span><span class="token function">Format</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>RFC3339<span class="token punctuation">),</span>
<span class="token string">"langs"</span><span class="token punctuation">:</span> <span class="token punctuation">[]</span><span class="token class-name">string</span><span class="token punctuation">{</span><span class="token string">"en"</span><span class="token punctuation">},</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Unlike Mastodon, however, you can't simply send text. For tags to be active rather than plain text, you have to attach them as <code>facets</code>:</p>
<pre class="language-go"><code class="language-go">hashRE <span class="token operator">:=</span> regexp<span class="token punctuation">.</span><span class="token function">MustCompile</span><span class="token punctuation">(</span><span class="token string">`#([a-zA-Z][a-zA-Z0-9_]*)`</span><span class="token punctuation">)</span>
<span class="token keyword">for</span> _<span class="token punctuation">,</span> m <span class="token operator">:=</span> <span class="token keyword">range</span> hashRE<span class="token punctuation">.</span><span class="token function">FindAllStringIndex</span><span class="token punctuation">(</span>text<span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
tag <span class="token operator">:=</span> text<span class="token punctuation">[</span>m<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token operator">+</span><span class="token number">1</span> <span class="token punctuation">:</span> m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]]</span>
facets <span class="token punctuation">=</span> <span class="token class-name">append</span><span class="token punctuation">(</span>facets<span class="token punctuation">,</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{</span>
<span class="token string">"index"</span><span class="token punctuation">:</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">int</span><span class="token punctuation">{</span><span class="token string">"byteStart"</span><span class="token punctuation">:</span> m<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">],</span> <span class="token string">"byteEnd"</span><span class="token punctuation">:</span> m<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]},</span>
<span class="token string">"features"</span><span class="token punctuation">:</span> <span class="token punctuation">[]</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token class-name">string</span><span class="token punctuation">]</span><span class="token class-name">any</span><span class="token punctuation">{{</span><span class="token string">"$type"</span><span class="token punctuation">:</span> <span class="token string">"app.bsky.richtext.facet#tag"</span><span class="token punctuation">,</span> <span class="token string">"tag"</span><span class="token punctuation">:</span> tag<span class="token punctuation">}},</span>
<span class="token punctuation">})</span>
<span class="token punctuation">}</span>
</code></pre>
<p>You have to do the same for URLs (<code>facet#link</code>) and handles (<code>facet#mention</code> — this also requires resolving the handle to a DID, not providing the readable name). It's also worth noting that Bluesky uses <code>createRecord</code> rather than <code>putRecord</code> here which creates its own fresh <code>rkey</code>:</p>
<pre class="language-go"><code class="language-go">req<span class="token punctuation">,</span> _ <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">NewRequestWithContext</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> http<span class="token punctuation">.</span>MethodPost<span class="token punctuation">,</span>
blueskyPDSURL<span class="token operator">+</span><span class="token string">"/xrpc/com.atproto.repo.createRecord"</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">)</span>
</code></pre>
<p>I want a new post each time (hence the new <code>rkey</code>), but also need to guard against duplicate posts (this is called in every syndication dispatch):</p>
<pre class="language-go"><code class="language-go"><span class="token keyword">var</span> dedupeInFlight <span class="token punctuation">=</span> river<span class="token punctuation">.</span>UniqueOpts<span class="token punctuation">{</span>
ByArgs<span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
ByState<span class="token punctuation">:</span> <span class="token punctuation">[]</span>rivertype<span class="token punctuation">.</span>JobState<span class="token punctuation">{</span>
rivertype<span class="token punctuation">.</span>JobStatePending<span class="token punctuation">,</span>
rivertype<span class="token punctuation">.</span>JobStateScheduled<span class="token punctuation">,</span>
rivertype<span class="token punctuation">.</span>JobStateAvailable<span class="token punctuation">,</span>
rivertype<span class="token punctuation">.</span>JobStateRunning<span class="token punctuation">,</span>
rivertype<span class="token punctuation">.</span>JobStateRetryable<span class="token punctuation">,</span>
<span class="token punctuation">},</span>
<span class="token punctuation">}</span>
</code></pre>
<p><code>ByArgs</code> ensures uniqueness by checking the job's args, <code>ByState</code> monitors the queue state and discards duplicate syndication attempts from the UI. Retryable is in the list to prevent a second insert while a job is waiting to auto-retry after a transient failure.</p>
<aside>
<p>These guards aren't unique to Bluesky but are worth mentioning.</p>
</aside>
<hr/>
<p>With all of that in place — the manual testing, random debug posts, reading of documentation, re-reading of documentation, cycles of frustration and endless deployments — I now sport a shiny publication bar for shared posts on Bluesky.</p>
<div class="footnotes" role="doc-endnotes"><hr/><ol><li id="fn:1"><p>More on that, bit by bit, later. ↩</p>
</li><li id="fn:2"><p>I'll probably move to a self-hosted PDS eventually, but the complexity of this implementation scared me away from doing so for now. ↩</p>
</li></ol></div></div>📝 2026-07-07 18:43: Guinea fowl keets are doing great too. All 5 are loving life with their foster... - Kev Quirkhttps://kevquirk.com/2026-07-07-18432026-07-07T17:43:00.000Z<p>Guinea fowl keets are doing great too. All 5 are loving life with their foster mums.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-07-1843/1000010240.webp" alt="1000010240" /></p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-07-1843/1000010242.webp" alt="1000010242" /></p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-07-1843/1000010243.webp" alt="1000010243" /></p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-07-1843/1000010246.webp" alt="1000010246" /></p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=%F0%9F%93%9D%202026-07-07%2018%3A43">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-07-07-1843#comments">leave a comment</a>.</p>
</div>📝 2026-07-07 18:34: We now have 3 chicks - two white and one black. - Kev Quirkhttps://kevquirk.com/2026-07-07-18342026-07-07T17:34:00.000Z<p>We now have 3 chicks - two white and one black.</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-07-1834/1000010239.webp" alt="1000010239" /></p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=%F0%9F%93%9D%202026-07-07%2018%3A34">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-07-07-1834#comments">leave a comment</a>.</p>
</div>CNN and CNBC promote gambling to make a cheap buck - Werd I/O6a4d3079ffd4800001dcb3dd2026-07-07T16:59:37.000Z<p>Link: <a href="https://www.publicnotice.co/p/kalshi-cnn-cnbc?ref=werd.io"><em>How Kalshi infects the news, by Aaron Rupar and Judd Legum in Public Notice and Popular Information</em></a></p><p>Kalshi’s deals with newsrooms seem to be paying dividends for the company:</p><blockquote>“Since December CNBC has published 58 articles that do little more than advertise the existence of a Kalshi market related to a news event. […] Since April, CNBC has employed a <a href="https://www.cnbc.com/davis-giangiulio/?ref=werd.io">dedicated reporter</a> to produce these articles. CNBC also maintains a <a href="https://www.cnbc.com/markets/prediction-markets/?ref=werd.io">page</a> on its website featuring Kalshi prediction markets selected by CNBC editors, along with its web coverage. […] In at least 22 cases, CNBC has written about Kalshi and not disclosed its financial conflict.”</blockquote><p><a href="https://www.axios.com/2025/12/02/cnn-kalshi-prediction-market-data?ref=werd.io">CNN doesn’t pay for access</a>, and instead is <a href="https://www.newyorker.com/news/the-lede/americas-betting-craze-has-spread-to-its-news-networks?_sp=bc5180fe-4e8b-4c96-a107-44eca35ac553.1783442134229&ref=werd.io">paid to exclusively promote Kalshi</a>. <a href="https://www.publicnotice.co/p/kalshi-cnn-cnbc?ref=werd.io">CNBC reporting carries a disclosure</a> which states that its relationship goes further: “CNBC and Kalshi have a commercial relationship that includes customer acquisition and a minority investment.” CNBC will gain financially if its coverage leads to more signups or a growth in Kalshi’s valuation. CNN’s is a simpler paid placement, but both deals are aggressive ways for Kalshi to compete with Polymarket, which has been making similar deals with newsrooms like Yahoo Finance.</p><p>This is even happening when markets are not significant enough to be newsworthy. <a href="https://www.newyorker.com/news/the-lede/americas-betting-craze-has-spread-to-its-news-networks?_sp=bc5180fe-4e8b-4c96-a107-44eca35ac553.1783442134229&ref=werd.io">As the New Yorker noted in December</a>:</p><blockquote>“When Enten lauded the benefits of analyzing betting odds, on air the other day, he failed to mention that only several hundred thousand dollars had been bet on that particular market. Kalshi’s odds provided good fodder for television, but, statistically speaking, they didn’t say much.”</blockquote><p>It reminds me of the deals Twitter made with newsrooms relatively early in its life. Suddenly, almost out of nowhere, anchors read out tweets on the news, and shows promoted their official Twitter accounts over their websites. This didn’t happen organically: Twitter partnerships teams made deals behind the scenes to ensure their product was showcased well. It was one of the first times that a web startup impactfully executed on a media strategy, and startups have built on that pattern ever since.</p><p>Here, rather than serving a social network, money is changing hands for newsrooms to promote gambling markets — and in CNBC’s case, they will make more money if more people gamble. It’s obviously weirder, and the incentives here would pull at traditional newsroom ethics in an uncomfortable way even if the adequate disclosures were published. This comes at an unfortunate time when trust in news is falling quickly, and newsrooms like CNN are increasingly seen as serving their owners rather than bastions of trustworthy reporting. These Kalshi deals are weird, and an obvious conflict of interest that will likely drive people to trust the news even less than they do today.</p><p>The Reuters Institute’s 2026 Digital News Report found that <a href="https://reutersinstitute.politics.ox.ac.uk/digital-news-report/2026?ref=werd.io">70% of respondents think media owners and corporate parents exert undue influence on the news</a>. As more of these sorts of deals are made, and as trust in news continues to decline, newsrooms are going to need to more overtly state that their coverage is free from this sort of sponsored content. Stronger, more transparent ethics statements, and louder conversations about how reporting decisions are made, will help some newsrooms to explain how they stand apart from these dynamics. In the meantime, CNN and CNBC are helping to drive trust in media into the gutter.</p>How I lost my phone - Joel's Log Fileshttps://joelchrono.xyz/blog/lost-phone2026-07-07T15:20:00.000Z<p>Yesterday after work I got in the bus back home and proceeded to happily play on my <a href="/blog/nintendo-3ds/">Nintendo 3DS</a> for the whole trip. I was about to finish a stage of <em>Fire Emblem Awakening</em>, leveling up a lot of characters, dodging attacks and surviving hits, all nice and well!</p>
<p>Then the ride was over, I noticed I was the last person left in the vehicle, I rushed to the exit and stepped out to the street. Ten seconds later, I realized I did not have my phone with me.</p>
<p>My workplace provides us with free transportation, so there are contacts and people in charge who may be able to help me out, I just didn’t know who they are.</p>
<p>Thankfully, I didn’t lose my work phone. I started messaging people from there, a coworker who may know someone, or another peer who uses the same bus.</p>
<p>I manage to join the WhatsApp group of the bus route, where I can finally contact the transport manager, but I got no reply back.</p>
<p>I decide to let my boss know, to see if he could help me find someone else to ask around, he told me he would look into it as well. I was stressed out of my mind.</p>
<p>Running back home like a madman, I dash to my room and my laptop and check to see if Google’s <em>Find My Device</em> works—I remembered my Google account was never logged in. Sometimes you wonder if a degoogled life is even worth it. LOL</p>
<p>Both Signal and WhatsApp can work without having the device online nowadays, so I was able to message a bunch of people and let them know I could be offline for a while, I may even have to change phone numbers because of this, but I wasn’t fully giving up yet.</p>
<p>Another advantage I had is that I still keep my older Xiaomi phone, and even though the power button was acting up, it decided to behave for once and turn on after a charge. I was able to log-in to my bank apps from there and keep an eye on them. That also meant that not every single picture was lost.</p>
<p>Oh, how many things I would have lost… My chat history, all the photos I’ve taken on my Nothing 3(a), my AntennaPod stats, one of my banking apps. I’d have to redownload all my music too, which is annoying.</p>
<p>Eventually, my boss tells me he gave my contact info to someone in HR, and few more minutes later I’m sent photos of the lost device. That brings me relief, as I’m told the transport supervisor has it and that I can check with him tomorrow—which is today now.</p>
<p>My phone is safe and sound, and now it’s back in my hands, peace of mind returns.</p>
<p>Clearly, it’s a bad idea not to check where your belongings are before getting out of a bus. It’s also not ideal to get too distracted by a videogame or to rush things—definitely double-checking from now on. This was honestly one of the most ideal scenarios ever, there are a lot of things that could have gone very very wrong.</p>
<p>I could have left my work phone there as well, my boss could have ignored me completely, I could have no access to Signal/WhatsApp on my laptop, or lack a secondary phone to check my bank accounts, I could have taken a public bus with no ties to my workplace, the bus driver could have kept the device for himself, or the bus manager, a simple lie is all it takes. Thank God it’s all over now, and nothing was lost.</p>
<p>Later I realized I <em>do</em> have <em>Find My Device</em>, I was using a blank Google account untied from anything but that specific phone, so I forgot. I did have the login info backed up, so in the future I can make use of this. Although it would probably have been stressful to see my phone somewhere without being able to do much about it.</p>
<p>Make your backups guys! And also, if you know some good way to track your device without using Google services, let me know!</p>
<p>At the very least, I guess that being able to not be glued to my phone for ten seconds is a good thing, so… <em>win?</em></p>
<p>This is day 91 of <a href="https://100daystooffload.com">#100DaysToOffload</a></p>
<p>
<a href="mailto:me@joelchrono.xyz?subject=How I lost my phone">Reply to this post via email</a> |
<a href="https://fosstodon.org/@joel/116879394050333528">Reply on Fediverse</a>
</p>📝 2026-07-07 07:06: First two chicks! - Kev Quirkhttps://kevquirk.com/2026-07-07-07062026-07-07T06:06:00.000Z<p>First two chicks!</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-07-0706/1000010230.webp" alt="1000010230" /></p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=%F0%9F%93%9D%202026-07-07%2007%3A06">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-07-07-0706#comments">leave a comment</a>.</p>
</div>Finished reading Wasteland Warlords 1 - Molly White's activity feed6a4c7ae291f30f1ebeded41f2026-07-07T04:04:50.000Z<article class="entry h-entry hentry"><header><div class="description">Finished reading: </div></header><div class="content e-content"><div class="book h-entry hentry"><a class="book-cover-link" href="https://www.mollywhite.net/reading/books?search=Wasteland%20Warlords%201"><img class="u-photo book-cover" src="https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1698673897i/201044505.jpg" alt="Cover image of Wasteland Warlords 1" style="max-width: 300px;"/></a><div class="book-details"><div class="top"><div class="series-info"><i>Wasteland Warlords</i> series, book <span class="series-number">1</span>. </div><div class="title-and-byline"><div class="title"><i class="p-name">Wasteland Warlords 1</i> </div><div class="byline">by <span class="p-author h-card">James A. Hunter</span> and <span class="p-author h-card">Eden Hudson</span>. </div></div><div class="book-info">Published <time class="dt-published published" datetime="2023">2023</time>. 150 pages. </div></div><div class="bottom"><div class="reading-info"><div class="reading-dates"> Started <time class="dt-accessed accessed" datetime="2026-07-06">July 6, 2026</time>; completed July 6, 2026. </div></div></div></div></div><img src="https://www.mollywhite.net/assets/images/placeholder_social.png" alt="Illustration of Molly White sitting and typing on a laptop, on a purple background with 'Molly White' in white serif." style="display: none;"/></div><footer class="footer"><div class="flex-row post-meta"><div class="timestamp">Posted: <time class="dt-published" datetime="2026-07-07T04:04:50+00:00" title="July 7, 2026 at 4:04 AM UTC">July 7, 2026 at 4:04 AM UTC</time>. </div></div><div class="bottomRow"><div class="tags">Tagged: <a class="tag p-category" href="https://www.mollywhite.net/reading/books?tags=fantasy" title="See all books tagged "fantasy"" rel="category tag">fantasy</a>, <a class="tag p-category" href="https://www.mollywhite.net/reading/books?tags=litrpg" title="See all books tagged "litRPG"" rel="category tag">litRPG</a>, <a class="tag p-category" href="https://www.mollywhite.net/reading/books?tags=post_apocalyptic" title="See all books tagged "post-apocalyptic"" rel="category tag">post-apocalyptic</a>. </div></div></footer></article>Purpose, mission, values - Johnny.Decimalhttps://johnnydecimal.com/blog/0229-purpose-mission-values/2026-07-07T02:00:00.000Z<p>After <a href="https://johnnydecimal.com/blog/0228/">discussing this at the last small business meeting</a>, we've thought about and written down our purpose, mission, and values. I'll put a copy here, and add them to their proper home which is our <a href="https://johnnydecimal.com/support/about-legal/policies">policies page</a>. If it's much later than July 2026 when you read this, you should check there for the latest version. This post won't change.</p>
<p>With thanks to <a href="https://oldstructures.nyc">Don</a> and <a href="https://lovettsundries.com">Jeff</a> for the interesting discussion. Don kindly shared a section of his company manual with us, which really helped.</p>
<hr>
<h2 id="purpose">Purpose</h2>
<blockquote>
<p><em>Why do we exist?</em></p>
</blockquote>
<p>In the 1980s, it was possible to live your life without using a computer. You might have made furniture by hand and driven it to a store that sold it for you. You paid the road toll with coins and received a paper receipt, your invoice was hand-typed and recorded in a ledger, and your tax return was filled in with a pen. This generated a small handful of physical documentation which was easily managed using a concertina folder: a basic skill you had been taught at school.</p>
<p>The volume of information was manageable. <strong>You could find anything, quickly and easily</strong>.</p>
<p>Today, that same carpenter – whose job has not changed – needs online accounts for her eToll account and the invoicing software that interfaces with the furniture store and her accountant. Both of these send important documents to her Gmail, which is full of transactional email from the 142 other organisations that she routinely deals with, and the social media accounts that she needs to maintain. Parking her 1982 Land Cruiser outside the furniture store now requires an app.</p>
<p>To help her manage this, her computer provides tools no more sophisticated than <code>Create New Folder</code>. It allows her to save anything anywhere she wants. It positively encourages the duplication of information. At no point was she given any training in these systems, which change constantly and without warning. The people who understand them charge hundreds of dollars an hour.</p>
<p>The volume of information is unmanageable. <strong>Nobody can find anything any more</strong>. This is stressful and inefficient. Computers were meant to make our lives easier. In this regard, they have done the opposite.</p>
<h2 id="mission">Mission</h2>
<blockquote>
<p><em>What are we going to do about it?</em></p>
</blockquote>
<p>We help people manage their information. This information is mostly, but not exclusively, on a computer.</p>
<p>We do this using the Johnny.Decimal system. It's a simple way to organise any information. It provides:</p>
<ol>
<li>A simple structure that uses numbers instead of the alphabet.</li>
<li>An index of your content that you manage.</li>
<li>Training, software, and support.</li>
</ol>
<p>We will be the training you never got: we'll show you how to store things so that you can find them again.</p>
<p>We can't make it so that our carpenter doesn't have to use a computer. And that's not our goal: computers <em>are</em> more efficient. Filing your taxes online is better than doing them with a pen.<sup><a href="#user-content-fn-parking-meter" id="user-content-fnref-parking-meter" data-footnote-ref="" aria-describedby="footnote-label" class="footnote">1</a></sup></p>
<p>Our mission is to make it so that when she sits down in front of her computer, it is with a sense of calm, not dread. We want her to be able to find her stuff with more confidence, and less stress.</p>
<h2 id="values">Values</h2>
<blockquote>
<p><em>How will we behave while we do it?</em></p>
</blockquote>
<p>Johnny.Decimal is two humans: Johnny and his partner Lucy. We have no desire to grow. Becoming a big corporation wouldn't make us happy. We quit our jobs to do this <em>because</em> we love it; if we stop loving it, then what was the point?</p>
<p>This makes us a deeply 'Personal Business'.<sup><a href="#user-content-fn-personal" id="user-content-fnref-personal" data-footnote-ref="" aria-describedby="footnote-label" class="footnote">2</a></sup> You're dealing <em>with us</em>, and we with you.</p>
<p>We love the satisfaction that comes from helping an individual to be more organised. We love our community. We love the sense of worth that comes from creating something good using our own brains.</p>
<p>We don't seek to be incredibly good 'at business'. We don't follow the same old (dark) patterns, the same old (sleazy) techniques. Surely this means we earn less than we could, that we remain smaller than we might otherwise have grown. We're okay with that.</p>
<p>Because we, personally, seek something simple: we'd like to be able to buy a house and have chickens in the back yard. We think we can achieve that by making good things that people like, and selling them for an honest price. Let us know how we're doing.</p>
<h3 id="your-values">Your values</h3>
<blockquote>
<p><em>How would we like you to behave towards us?</em></p>
</blockquote>
<p>Company values statements typically don't turn it around to face the customer. But this is our company so we'll do whatever we like. ;-)</p>
<p>I spoke above about how we're proud to be a 'Personal Business'. But please remember that we are <em>still a business</em>. This is how we earn our living.</p>
<p>There is a curious behaviour that I have observed over the years.</p>
<ol>
<li>Everyone seeks to escape 'the rat race'. This is idolised as the highest form of being: to quit one's job, to 'go indie'.</li>
<li>So one quits one's job and 'goes indie', forfeiting the steady wage.</li>
<li>Now 'a creator', one needs to sell something in order to pay the rent and eat. (Capitalism! Who knew?)</li>
<li>People react to this 'sell-out'. How dare you sell the product of your mind? Information should be free!</li>
</ol>
<p>My ask of you is this: notice this reaction in your own mind. <strong>Be charitable in your assumptions</strong>.<sup><a href="#user-content-fn-hanlon" id="user-content-fnref-hanlon" data-footnote-ref="" aria-describedby="footnote-label" class="footnote">3</a></sup> If in doubt, ask the individual before posting about it on social media. We all need to earn money. You might do it by going to work. We do it by selling stuff online.</p>
<div data-footnotes="" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-parking-meter">
<p>We're yet to be sold on the utility of an app over coins when paying for parking. <a href="#user-content-fnref-parking-meter" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
<li id="user-content-fn-personal">
<p><a href="https://www.are.na/editorial/personal-business">Are.na / Personal Business by Charles Broskoski</a>. It's a great article, please read it. <a href="#user-content-fnref-personal" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
<li id="user-content-fn-hanlon">
<p><a href="https://en.wikipedia.org/wiki/Hanlon's_razor">Hanlon's razor</a> providing a useful heuristic: "Never attribute to malice that which is adequately explained by stupidity." <a href="#user-content-fnref-hanlon" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
</ol>
</div>Finished reading Kiosk Kingdom - Molly White's activity feed6a4c56646337b1f9eb5113992026-07-07T01:28:47.000Z<article class="entry h-entry hentry"><header><div class="description">Finished reading: </div></header><div class="content e-content"><div class="book h-entry hentry"><a class="book-cover-link" href="https://www.mollywhite.net/reading/books?search=Kiosk%20Kingdom"><img class="u-photo book-cover" src="https://m.media-amazon.com/images/S/compressed.photo.goodreads.com/books/1751370683i/237735252.jpg" alt="Cover image of Kiosk Kingdom" style="max-width: 300px;"/></a><div class="book-details"><div class="top"><div class="series-info"><i>Discount Dan's Backroom Bargains</i> series, book <span class="series-number">3</span>. </div><div class="title-and-byline"><div class="title"><i class="p-name">Kiosk Kingdom</i> </div><div class="byline">by <span class="p-author h-card">James A. Hunter</span>. </div></div><div class="book-info">Published <time class="dt-published published" datetime="2026-03-31">March 31, 2026</time>. 680 pages. </div></div><div class="bottom"><div class="reading-info"><div class="reading-dates"> Started <time class="dt-accessed accessed" datetime="2026-06-25">June 25, 2026</time>; completed July 6, 2026. </div></div></div></div></div><img src="https://www.mollywhite.net/assets/images/placeholder_social.png" alt="Illustration of Molly White sitting and typing on a laptop, on a purple background with 'Molly White' in white serif." style="display: none;"/></div><footer class="footer"><div class="flex-row post-meta"><div class="timestamp">Posted: <time class="dt-published" datetime="2026-07-07T01:28:47+00:00" title="July 7, 2026 at 1:28 AM UTC">July 7, 2026 at 1:28 AM UTC</time>. </div></div><div class="bottomRow"><div class="tags">Tagged: <a class="tag p-category" href="https://www.mollywhite.net/reading/books?tags=fantasy" title="See all books tagged "fantasy"" rel="category tag">fantasy</a>, <a class="tag p-category" href="https://www.mollywhite.net/reading/books?tags=humor" title="See all books tagged "humor"" rel="category tag">humor</a>, <a class="tag p-category" href="https://www.mollywhite.net/reading/books?tags=litrpg" title="See all books tagged "litRPG"" rel="category tag">litRPG</a>. </div></div></footer></article>Once again, we failed to focus - Johnny.Decimalhttps://johnnydecimal.com/blog/0226-we-failed-to-focus/2026-07-07T01:00:00.000Z<blockquote>
<p><strong>Editorial note:</strong> This one was drafted a while back, and things have changed. I'll publish it mostly as originally written; see the adjacent posts for more context.</p>
</blockquote>
<p>We've done it again. And we're sorry – Lucy in particular <em>hates</em> unfinished business – but we started a thing that we shouldn't have, so we're going to cancel it. <del>Two things! Those</del> That thing being our 'Everyday Obsidian' <del>and 'Online security essentials'</del> mini-courses.</p>
<blockquote>
<p><strong>Update:</strong> We're keeping the security thing for now. I have ideas how it can be an important part of how we help small business. I leave it here to show how these decisions aren't easy.</p>
</blockquote>
<p>Once again this is a textbook case of not focusing on what's important. As a <em>very</em> small business, we need to guard our time. We started these things without really thinking them through. At the time they were a welcome distraction: we'd just come out of a months-long zone of focus, <a href="https://johnnydecimal.com/blog/0208-this-week-2026-05-25#what-we-did-last-week">merging the two websites</a>. <del>And I really would love to do that online security thing one day. But in hindsight, they were wrong for now.</del></p>
<h2 id="whats-our-purpose">What's our purpose?</h2>
<p>We mock 'mission statements', but their value just became apparent to me. We've never written down exactly what it is we're here for: <strong>why do we exist?</strong></p>
<p>We still haven't written that down, but if I think out loud it's something like: <em>people need help organising stuff, and we have an interesting method of doing that, so we should try to help them</em>.</p>
<blockquote>
<p><strong>Update:</strong> I drafted this post weeks ago. Since then we've written down our <a href="https://johnnydecimal.com/blog/0229/">purpose, mission, and values</a>.</p>
</blockquote>
<p>Do <em>explaining Obsidian's features</em> or <em>helping you not get hacked online</em> fit into that? The former, kinda, maybe. But I'm not even that good at Obsidian! You want Obsidian advice there are tens of thousands of hours of that already on YouTube. The latter – having thought on it, I see a place where that fits into what we do.</p>
<p>When we're back at work later this week this is one of the first things we'll be doing: writing down short, practical, human answers to the following questions.</p>
<ol>
<li>Why do we exist? (Purpose)</li>
<li>What are we going to do about it? (Mission)</li>
<li>How do we behave while we do it? (Values)</li>
</ol>
<h2 id="use-this-as-a-filter">Use this as a filter</h2>
<p>I think the reason we mock large corporations' mission statements is that they feel like platitudes that someone's had engraved on a plate and then never read again. Certainly that's how I felt at basically every job I had.<sup><a href="#user-content-fn-think-different" id="user-content-fnref-think-different" data-footnote-ref="" aria-describedby="footnote-label" class="footnote">1</a></sup></p>
<p>So how do we make sure that these statements, once codified, serve their purpose? I haven't implemented this yet, but it feels simple enough.</p>
<ol>
<li>Create a 'new <a href="https://johnnydecimal.com/blog/0225/">project</a>' template in our JDex.</li>
<li>This template has a checkbox that asks you to consider the project in the context of this just-documented purpose, mission, and values. Does it align with them?</li>
</ol>
<p>Of course you need to actually give some thought to this whenever you create a new project. Boxes like this can be something you check without thought once it becomes routine. This is something that the two of us should actively discuss together.</p>
<p>This is a work-in-progress. I'll report back.</p>
<div data-footnotes="" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-think-different">
<p>Most such 'mission statements' can be summed up as: <em>think different and act bravely</em>. Well I tried that and let me tell you, that is <em>not</em> what your corporate middle-manager wants. <a href="#user-content-fnref-think-different" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref footnoteBackLink">↩</a></p>
</li>
</ol>
</div>Wonders of Web Weaving, Episode 9 - James' Coffee Bloghttps://jamesg.blog/2026/07/07/www-92026-07-07T00:00:00.000Z
<p><a href="https://web-weaving.jamesg.blog/9" rel="noreferrer">The ninth episode of Wonders of Web Weaving is out</a>:</p><blockquote>In Episode 9, I chat with Rachel, the author of <a href="https://kwon.nyc">kwon.nyc</a> and curator of "<a href="https://projects.kwon.nyc/internet-is-fun/">The internet used to be* fun *(It still is, but it used to be, too)</a>". We talk about, among other things, metaphors for the web, creative mediums on the web, physical creative spaces, and more.</blockquote><p>I hope you enjoy the episode!</p><p><a href="https://web-weaving.jamesg.blog/subscribe/" rel="noreferrer"><em>Wonders of Web Weaving also has an RSS feed</em></a><em> you can use to follow along from wherever you get your podcasts.</em></p><script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a177ebca7c208b9c',t:'MTc4MzQzODU0Nw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
<a class="tag" href="https://kwon.nyc">kwon.nyc</a>
<a class="tag" href="https://projects.kwon.nyc/internet-is-fun/">The internet used to be* fun *(It still is, but it used to be, too)</a>
<a class="tag" href="https://web-weaving.jamesg.blog/9">The ninth episode of Wonders of Web Weaving is out</a>
<a class="tag" href="https://web-weaving.jamesg.blog/subscribe/">Wonders of Web Weaving also has an RSS feed</a>
June-boree! - Hey, it's Jason!https://grepjason.sh/2026/june-boree-trophees2026-07-07T00:00:00.000ZIt's that time of year! Behold! June-boree!📝 2026-07-06 21:45: Two of the eggs are starting to hatch and we can hear multiple chicks cheeping... - Kev Quirkhttps://kevquirk.com/2026-07-06-21452026-07-06T20:45:00.000Z<p>Two of the eggs are starting to hatch and we can hear multiple chicks cheeping away. Exciting! 🐣</p>
<p><img loading="lazy" src="https://kevquirk.com/content/images/2026-07-06-2145/1000010211.webp" alt="1000010211" /></p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=%F0%9F%93%9D%202026-07-06%2021%3A45">reply to this post by email</a>, or <a href="https://kevquirk.com/2026-07-06-2145#comments">leave a comment</a>.</p>
</div>Return to the gym, 3DS theming, Mexico lost - W27 - Joel's Log Fileshttps://joelchrono.xyz/blog/w272026-07-06T17:30:00.000Z<p>Mexico was knocked out of the World Cup by England. You have no idea how much hope I had for this squad, the quality and resiliance Mexico had shown. Unfortunately we were unable to take advantage of the locality and it only took a couple of mistakes for England to put us in a very bad situation, two goals in just a couple minutes, and a third penalty due to one of the only errors our goalkeeper ever did. Quiñones helped us stay afloat, and a killer penalty kick from Jimenez too, but the game devolved into a battle of trying to get the ball in the area, when it was all filled by the brittish. The weirdest decision for me was taking out the creativity of Mora and the power of Quiñones, and relying on the same play.</p>
<p>Even with that, Mexico fought hard, so many attempts, many of them would have gone in, but the goalkeeper did his job very well. And now we lose, but it’s not the same as always for me. I believe in the new generation. Mora has a long way to go, I would have wanted to see more of la Hormiga, the inheritor of Chicharito’s 14. Rafa Marquez is the manager now, I’m hopeful that the Kaiser will do great for the future.</p>
<p>Oh right, week notes. Let’s see.</p>
<ul>
<li>
<p>💪 I have returned to the gym! It was a long hiatus full of laziness and overeating, I went up two kilos since the last time I checked, so I decided to go back and try again to take better care of myself. I continue to be annoyed by the need to go at all, it just kinda feels like a trend for me and a bit of a timewaste. I really need to get that bycicle commute going, it has the potential to really change things for the bettter for me.</p>
</li>
<li>
<p>🎮 The <em>Nintendo 2DS XL</em> arrived and it was a bit dissapointing, if I’m honest. While the design is fun, the plastic feels much cheaper and the structural integrity feels lesser than the New 3DS XL. I packaged it up and requested to return it, I only turned it on once and didn’t even try any games on it.</p>
</li>
<li>
<p>🍿 Invited my friend to another movie! This time we arrived earlier and went window shopping for a bit, and also played some card games! We watched <em>Supergirl</em>, and it was honestly kind of weird. All in all, the point was to spend some time together and it was alright.</p>
</li>
<li>🚲 This weekend’s bike ride was a very functional one! I went a bit later than my usual morning because I wanted to visit a couple of places. I actually carried a backpack this time too!
<ul>
<li>I headed to a delivery spot to return the <em>Nintendo 2DS XL</em>, I picked up a couple packages as well, then</li>
<li>I went to the local game shop to look for any 3DS games, I had no carts and I wanted at least one! I chose the <em>Ocarina of Time</em> remake.</li>
<li>I was supposed to go to the bike shop to buy fenders and a rack, but it was late already so it didn’t happen.</li>
</ul>
</li>
<li>
<p>🍜 Went to a Japanese restaurant and had some ramen, this was a night out with a few friends, it was really nice! We pretty much only go here when these specific friends are in town, so I always enjoy it.</p>
</li>
<li>
<p>🎨 Spent a couple hours figuring out how to create themes for the Nintendo 3DS, and ended up making two! Of course they are based on some epic locations from <em>Chrono Trigger</em>, I will write a blogpost detailing the process and have already uploaded them to <a href="https://themeplaza.art">Theme Plaza</a>, waiting for approval!</p>
</li>
<li>📱 I rarely use mainstream social media, but during the World Cup I keep visiting it to see the memes and the euphoria, and now the sadness of losing—and memes asking Haaland to avenge us. Until next time, México.</li>
</ul>
<p><img src="/assets/img/blogs/2026-07-06-week.webp" alt="Collage of the Week" /></p>
<h2 id="gaming">Gaming</h2>
<ul>
<li>
<p><strong>Fire Emblem Awakening</strong> continues to be at the top for my gaming time, the story has picked up quite a bit, but the mechanics alone, and the challenge of each chapter have been formidable. I am currently on chapter 13, and some revelations have been made that caught my eye. I also love my party quite a bit now! I’ve managed to create a pretty decent team but I’m also trying to level up some weaker characters and also level up the support between characters, I love the conversations that happen between them. Looking forward to what this game has next!</p>
</li>
<li>
<p><strong>Fire Emblem The Sacred Stones</strong> was played for a second. I remembered that I have a lot of handhelds, and my Miyoo Mini Flip had this one loaded up, I wanted to try a battle but didn’t even get far enough for that yet. I opened the game, saw the opening sequence, and got busy with something else.</p>
</li>
<li>
<p><strong>Super Smash Bros Ultimate</strong> saw some action! and the best player of my friend group has started to struggle with the younglings, we are getting old, RIP.</p>
</li>
<li>
<p><strong>Worms W.M.D</strong> - We did one battle of this as well, it was a fun time! We kinda forgot how to play Worms but picked things up in the end.</p>
</li>
</ul>
<h2 id="watching">Watching</h2>
<ul>
<li>
<p><strong>Avatar The Last Airbender (Live Action Season 2)</strong> - We are in the Earth Kingdom and they are actually pulling off a couple neat moments in great ways! Like, ways that could have worked on the original series rather well. It still has some very awkward things and bad pacing, but I’m invested a bit.</p>
</li>
<li>
<p><strong>Supergirl (2026)</strong> - Went to the movies with my friend and family to see this one. It was good, but it wasn’t great. It really has a lot to say about society and stuff, but it felt mixed, and the music selection wasn’t that great. Too convoluted, and nobody in my group resonated with it that much. I really really wanted to like it, and I do, it just didn’t quite stick the landing for me.</p>
</li>
<li>
<p><strong>Superman (2025)</strong> - Back at home, I thought I’d give a shot to the first movie in James Gunn’s DC universe. I enjoyed this one! It really questioned the nature of Superman and how it affects the politics of the world. Some moments felt over the top and had that characteristic style James Gunn has. I appreciate that the universe already exists and it doesn’t feel the need to hold our hand. However, the plot was predictable and with almost no surprises. Mr. Terrific was truly terrific though, great guy!</p>
</li>
<li>
<p><strong>Yes Day</strong> - I watched this movie ages ago and only recognized Electra, watching it again, I also recognized Jenna Ortega. What a weird family movie. Entertaining enough but there’s better ways to spend your time.</p>
</li>
</ul>
<h2 id="reading">Reading</h2>
<p><strong>Shikimori’s Not Just Cute</strong> is now complete! What a journey, a tiny bit rushed in the ending but still very nice! All in all this manga was a cozy read that had some great moments and hilarious situations. It was not life-changing or anything, but I’m glad it tied things nicely. I actually grew to love all the characters beyond the main couple quite a bit, which is something that got a bit overwhelming with <em>Komi Can’t Communicate</em>. The cast here was smaller but the roles were well defined and they all had a time to shine if only a bit. I will actually miss some of them! I ended up a little melancholic about it due to Mexico’s loss, and I am running out of romance manga to read too, which is a bit sad. Leave recommendations.</p>
<p>I didn’t read any of <em>The Expanse</em> at all because of the gaming I did.</p>
<h2 id="around-the-web">Around the Web</h2>
<h3 id="blog-posts">Blog posts</h3>
<ul>
<li><a href="https://gabz.blog/posts/a-new-nintendo-3ds-xl">A New Nintendo 3DS XL</a> - Gabz also has a 3DS! Who would have thought I’d inspire him to return to it? I am happy to have caused that in him and a few other people too, very cool.</li>
<li><a href="https://bell.bz/im-playing-the-zelda-games-in-timeline-order/">I’m playing the Zelda games in timeline order</a> - Oh look! Andy is playing Zelda, that’s cool. I should try playing through a whole franchise sometime, I guess I’m kind of close with <em>Resident Evil</em>…</li>
<li><a href="https://neilzone.co.uk/2026/07/being-excited-still-about-technology-in-2026/">Being excited still about technology in 2026</a> - Neil continues to remind us that technology still has some good things going on. It’s not all doom and gloom!</li>
<li><a href="https://pawelgrzybek.com/my-blog-is-not-on-the-at-protocol-standard-site-is-not-for-me">My blog is not on the AT Protocol, standard.site is not for me</a> - This. AT Protocol, to me, is not where it’s at, I added <a href="https://microformats.org/">microformats</a> and that feels much simple and universal to me, even then it feels a bit much, but it’s setup and forgotten and it’s okay!</li>
</ul>
<h3 id="youtube">YouTube</h3>
<ul>
<li><a href="https://youtu.be/dvAczAth9ms">How To Make Your Own Custom 3DS Themes / Kame-Editor</a> - This is the tutorial I followed to make my theme, although I had to figure out some things it didn’t explain especially for the image placement on static images (the tutorial is only about scrolling backgrounds).</li>
<li><a href="https://youtu.be/qeKJqnlF3UQ">The Reusable Water Bottle Trend is DEAD</a> - Making good purchasing decisions hasn’t been my forte this year, but at least I know I won’t fall for energetic drinks any time soon.</li>
<li><a href="https://youtu.be/QiEtHwCTPb0">Why Mexico v. England is already historic</a> - This article from the <em>Search Party</em> channel shared a lot of awesome details on the history of the Azteca Stadium! I recommend giving it a watch as it had a lot of details I didn’t know about at all.</li>
<li><a href="https://youtu.be/bue-lSUJr-g">For Real this Time!</a> - Someone seems to be working on a port of <em>Twilight Princess</em> for the Nintendo 3DS. I hope more GameCube games get ported to the handheld some day.</li>
<li><a href="https://youtu.be/QLkc_b1L9Ow">Dear Sony: You messed up</a> - It seems like physical discs for Play Station will stop being a thing. This is disaster, but kind of expected. I hope things change some day.</li>
</ul>
<p>This is day 90 of <a href="https://100daystooffload.com">#100DaysToOffload</a></p>
<p>
<a href="mailto:me@joelchrono.xyz?subject=Return to the gym, 3DS theming, Mexico lost - W27">Reply to this post via email</a> |
<a href="https://fosstodon.org/@joel/116874409962412108">Reply on Fediverse</a>
</p>Governance can prevent AI from being used to undermine democracy. But only if it has teeth. - Werd I/O6a4baf9b1f0a8b0001fa58532026-07-06T13:37:31.000Z<p>Link: <a href="https://www.techpolicy.press/in-geneva-the-world-can-anchor-ai-governance-in-free-expression/?ref=werd.io"><em>In Geneva, the World Can Anchor AI Governance in Free Expression, by Isabelle Anzabi in Tech Policy Press</em></a></p><p>I like that this is happening, will appreciate the recommendations that arise from it, and know with complete certainty that no major AI vendor will adhere to them unless they are forced:</p><blockquote>“The United Nations is convening its <a href="https://www.un.org/global-dialogue-ai-governance/en?ref=werd.io">Global Dialogue on Artificial Intelligence Governance</a> in Geneva July 6-7, and the stakes are high: what rights will anchor the future of how the world governs AI? Generative AI systems mediate, filter, and generate the information we encounter every day. They are, at their core, a technology of expression and access to information. How we govern them will have downstream effects on what people can say, seek, and know. Geneva is where we can get that right.”</blockquote><p>A previous UNESCO meeting recommended governance that “bars AI systems from being used for social scoring or mass surveillance, and  requires member states to ensure that AI actors respect rights in the AI lifecycle”. As we all know, that has <em>definitely</em> happened. Both AI vendors and the two countries that predominantly house them, the United States and China, are famously against surveillance and social scoring, and in favor of maintaining rights. Certainly none of them have, for example, used AI with <a href="https://www.npr.org/2026/01/23/nx-s1-5684185/doge-data-social-security-privacy?ref=werd.io">the most sensitive personal information government has access to</a>, or worked with the private sector to <a href="https://theconversation.com/us-government-ramps-up-mass-surveillance-with-help-of-ai-tech-data-brokers-and-your-apps-and-devices-277440?ref=werd.io">create a surveillance apparatus</a> for policing <a href="https://www.404media.co/elite-the-palantir-app-ice-uses-to-find-neighborhoods-to-raid/?ref=werd.io">that includes the broad detainment of immigrants</a>. Neither of them is famous for social scoring — in China by government or in the United States <a href="https://hai.stanford.edu/news/how-flawed-data-aggravates-inequality-credit?ref=werd.io">through private enterprise</a>. Job done!</p><p>Sarcasm aside, it’s important to have these meetings, and it’s important to continue to add pressure to protect rights and prevent abuses of this technology. But we should be clear-eyed about the fact that any recommendations almost certainly won’t actually be followed unless major nations that represent real dollars to these vendors enshrine them in law and hold the line when countries like the US apply pressure to undermine them.</p><p>It can’t just come down to <em>individual</em> governments. The linked article argues that we should also protect freedom of expression, in particular from government (although I’d argue we should also worry about the influence of wealthy private entities). Because these are black box systems, it’s vital that we prevent governments (or anyone with power) from opaquely tweaking their answers and outputs to benefit their agendas. They should not be allowed to be opaque systems; full transparency and auditability should be requirements. The danger is that, in the wrong hands, AI can be used to cement centralized, undemocratic power. To prevent this, ideally, organizations like the UN should apply real sanctions to nations that don’t obey transparency rules and tweak AI systems in service of undemocratic goals — but the UN’s history of doing this effectively is not strong.</p><p>Part of the problem is the “AI is the information technology of the future” framing used here. If you believe that AI is the future of information systems, you are also more likely to believe that your nation will miss out if you don’t embrace it completely. (That’s been the marketing: if you don’t jump into AI, you will be left behind.) But the reality is, of course, far more nuanced. AI will absolutely change industries like software engineering, and has already made an impact there, although it’s far from an existential transformation. I’m less receptive to the idea that it will transform entire nations. AI vendors need global markets more than global markets need AI. Governments should understand the power they have, and use it.</p>