<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Cogignition</title>
    <subtitle>Notes from the workshop, off the radar.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.cogignition.cloud/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.cogignition.cloud"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-06-22T00:00:00+00:00</updated>
    <id>https://blog.cogignition.cloud/atom.xml</id>
    <entry xml:lang="en">
        <title>A game in the dormant slot.</title>
        <published>2026-06-22T00:00:00+00:00</published>
        <updated>2026-06-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.cogignition.cloud/a-game-in-the-dormant-slot/"/>
        <id>https://blog.cogignition.cloud/a-game-in-the-dormant-slot/</id>
        
        <content type="html" xml:base="https://blog.cogignition.cloud/a-game-in-the-dormant-slot/">&lt;p&gt;When I stood this blog up as a proof of concept, I left one thing deliberately
empty. The page template had a slot for a WebAssembly module, wired in and
shipping nothing, with a comment that said &lt;em&gt;drop a module here later.&lt;&#x2F;em&gt; It was a
promise to myself that the static-first rule had a pressure valve. This is later.&lt;&#x2F;p&gt;
&lt;p&gt;The ask was simple and a little nostalgic: a classic arcade game, Asteroids,
running entirely as WebAssembly, wearing the look of an old toy I&#x27;d built before,
and hosted right here on the same static site you&#x27;re reading. No new server, no
app store, no plugin. A link.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-webassembly-is-for-the-rest-of-us&quot;&gt;What WebAssembly is, for the rest of us&lt;&#x2F;h2&gt;
&lt;p&gt;Browsers have always run one programming language: JavaScript. WebAssembly, or
WASM, is the second thing they learned to run. It&#x27;s a compact binary format that
languages like Rust, Go, and C can compile down into, and the browser executes it
at close to the speed of a normal installed program, inside the same locked room
it runs everything else in. You don&#x27;t install it. It arrives as a file over the
same connection as the page, and it can&#x27;t reach any further into your machine than
the webpage already could.&lt;&#x2F;p&gt;
&lt;p&gt;For a game, that&#x27;s the whole pitch. The physics, the collisions, the rendering all
run as compiled code instead of interpreted script, and the entire thing is one
&lt;code&gt;.wasm&lt;&#x2F;code&gt; file sitting on disk next to an HTML file. That&#x27;s it. The browser fetches
it like an image and runs it like an app.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-boring-choices-made-on-purpose&quot;&gt;The boring choices, made on purpose&lt;&#x2F;h2&gt;
&lt;p&gt;I could have reached for a game engine. I didn&#x27;t. Asteroids is a few hundred lines
of vectors and arithmetic; talking to the browser&#x27;s drawing surface directly keeps
the dependency list nearly empty and the download tiny, about sixty kilobytes of
compiled code. That&#x27;s the same rule the rest of this system runs on. Boring is a
feature; every moving part has to argue for its place.&lt;&#x2F;p&gt;
&lt;p&gt;The look was the interesting part. The CRT shimmer I wanted, all scanlines and
digital rain bent around a curved glass tube, lived in that old toy as a tangle of
framework code driving a graphics shader. I kept the shader, the small program
that actually paints each pixel, and threw away everything around it, rewriting the
scaffolding so it runs straight from Rust. The part worth carrying forward was the
look, not the machinery that happened to be under it the first time.&lt;&#x2F;p&gt;
&lt;p&gt;Then I opened the first build on my phone, which has no arrow keys, and the game
was unplayable. So it grew thumbs before it grew almost anything else: on-screen
rotate, thrust, and fire pads that lay themselves out to fit whatever screen you&#x27;re
holding. Mobile wasn&#x27;t a port I did at the end. It was the second thing I built.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-ships-like-everything-else&quot;&gt;It ships like everything else&lt;&#x2F;h2&gt;
&lt;p&gt;The finished game is four files: the compiled code, a little loader, a page, and a
stylesheet. They sit in the blog&#x27;s static assets exactly like a photo would.
Publishing the blog copies them up untouched, and the thing that serves this site,
a plain file server pulling a git repository every thirty seconds, now hands out a
real-time arcade game with precisely zero new infrastructure. The compiler ran on
my laptop. Nothing built on a machine I don&#x27;t own.&lt;&#x2F;p&gt;
&lt;p&gt;The two real detours weren&#x27;t the game at all. My Rust install flatly could not
target the browser, and I spent an hour teaching the toolchain a trick it ought to
have known out of the box. And I managed to poison my own preview image by checking
its address one second before it existed, so a cache somewhere dutifully remembered
the absence for a day. Both are the kind of tax you pay once and only notice because
everything around it was quiet.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s the lesson hiding in a toy. Static-first was never static-only; the
discipline was leaving a labeled, empty slot and trusting myself to fill it on
purpose, a deliberate island in water I keep calm by default. The &lt;a href=&quot;https:&#x2F;&#x2F;blog.cogignition.cloud&#x2F;the-machine-as-code&#x2F;&quot;&gt;blog that proves
I don&#x27;t have to remember my infrastructure&lt;&#x2F;a&gt; now has
a game in it. Same repository, same thirty-second pull, one more file.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s in the nav, up top, under &lt;strong&gt;Arcade&lt;&#x2F;strong&gt;. Go fly it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Machine as Code</title>
        <published>2026-06-21T00:00:00+00:00</published>
        <updated>2026-06-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.cogignition.cloud/the-machine-as-code/"/>
        <id>https://blog.cogignition.cloud/the-machine-as-code/</id>
        
        <content type="html" xml:base="https://blog.cogignition.cloud/the-machine-as-code/">&lt;p&gt;Most of my infrastructure used to live in my memory. I knew which service ran
where because I had set it up; the rest was a handful of scripts I had
half-forgotten. This is the first entry of a publication I run from a system built
so I do not have to remember, and it is a proof of concept for that system. What
follows is the whole of it, named part by part, because a proof you cannot inspect
is only a claim.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;one-repository-owns-the-machine&quot;&gt;One repository owns the machine&lt;&#x2F;h2&gt;
&lt;p&gt;I built a single monorepo, Sysop, and made it own three things at once: the
configuration of the host it runs on, the configuration of the cluster it operates,
and this publication. I reason about the whole repository now, not any one piece;
that is the point of putting them together.&lt;&#x2F;p&gt;
&lt;p&gt;The host is basestar, an M3 Ultra Mac Studio. It runs as a declarative nix-darwin
flake on the Lix interpreter, version 2.95.2, pinned in the configuration rather
than tracking whatever is newest. One file describes the machine: 54 Homebrew
formulae and 8 casks captured from the live system, three scheduled and background
jobs (a weekly baseline check, a weekly supply-chain watch, and a local language
model daemon that serves Gemma 4 26B on port 1235, whose launcher script I vendored
into the repo because it was tracked nowhere else), and TouchID for sudo. A second
file composes my shell and git configuration from typed options, so several tools
each contribute to one generated file instead of me hand-editing a template.&lt;&#x2F;p&gt;
&lt;p&gt;I build it with one verb, and the build changes nothing on its own; it produces an
artifact I diff against the running system before I commit to it. Then I activate,
and that step is now done. The flake is the source of truth for the host. One switch
took over Homebrew, the login daemons, and the system defaults, turned on TouchID for
sudo, and moved my shell, prompt, and terminal configuration off the old dotfile
manager and under the same repository as everything else. The activation is
idempotent, so re-running it converges the machine rather than drifting it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-took-the-cluster-over-while-it-ran&quot;&gt;I took the cluster over while it ran&lt;&#x2F;h2&gt;
&lt;p&gt;I already had a live cluster, k3d-pet, running k3s 1.31.5 inside OrbStack and doing
real work: a Cloudflare tunnel, a Prometheus and Grafana stack, an OpenTelemetry
collector, a Discord bot, and this blog. Flux now reconciles it from the repository
about once a minute, applying three ordered layers; infrastructure first, then the
secret stores, then the applications, each waiting on the one before it.&lt;&#x2F;p&gt;
&lt;p&gt;I did not rebuild anything. For each component I had installed by hand, cert-manager
1.19.2, the kube-prometheus-stack 80.2.0, the Grafana operator 5.20.0, and External
Secrets Operator 2.6.0, I wrote a release object pinned to the exact version already
deployed, fed it the values I had captured from the live install, and told it to
leave alone the custom resource definitions it already owned. The upgrade re-rendered
to the same manifests, so nothing changed and nothing restarted. I verified every
adoption the same boring way: I confirmed the pod restart counts and the stateful
identities did not move. The proof of a clean takeover is the absence of an event.&lt;&#x2F;p&gt;
&lt;p&gt;I also threw something away. An automation tool I no longer used was deleted cleanly,
after a backup, because in this repository removing a moving part rarely needs a
justification and keeping one always does.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-keep-no-secrets-in-the-repository&quot;&gt;I keep no secrets in the repository&lt;&#x2F;h2&gt;
&lt;p&gt;Nothing sensitive is in my git history. External Secrets Operator runs inside the
cluster and pulls credentials from a 1Password vault at runtime; the repository holds
only references, small objects that name an item and a field. There is exactly one
secret I inject by hand: a read-only service-account token, seeded into the cluster
once, with its trailing newline stripped because the integration rejects it
otherwise. From that single root, the operator fetches everything else.&lt;&#x2F;p&gt;
&lt;p&gt;I moved four secrets this way: the tunnel credentials, the blog&#x27;s deploy key, the
Discord bot&#x27;s tokens, and a database token for a Grafana datasource. Each migration
was verified before I trusted it. I synced the value from 1Password into a throwaway
secret, compared a sha256 hash against the value already live in the cluster, and only
then let the operator adopt the real one in place, with no gap in service. One secret
I deliberately left alone: Grafana&#x27;s admin credentials are generated and owned by its
own operator, and the secrets operator cannot take ownership of something another
controller already holds. The history is safe to read; it records where my secrets
go, never what they are.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;this-page-is-the-proof&quot;&gt;This page is the proof&lt;&#x2F;h2&gt;
&lt;p&gt;This publication is the smallest honest end-to-end test of the system, so let me
trace the exact path of the words you are reading. I wrote this entry as Markdown
with a small structured header. A version-pinned static generator, Zola, rendered it
through a rigid editorial template: numbered sections, the metadata table above, a
skeptic&#x27;s section, one concrete action, and the verification footer below. It carries
a single third-party browser dependency, htmx, vendored as one file and checked
against a fixed hash, never loaded from a content network. The page ships as plain
HTML and CSS; nothing dynamic runs unless I later decide it must.&lt;&#x2F;p&gt;
&lt;p&gt;The whole publish runs behind one verb. It renders the site, lints the frontmatter so
every entry carries its required sections, checks the internal links, and pushes the
output to a git repository over my own key. Inside the cluster, a different and
read-only key, the one the secrets operator manages, pulls that repository every
thirty seconds; an nginx container serves the static files; and the same Cloudflare
tunnel that fronts everything else maps blog.cogignition.cloud to it. The write path
and the read path use two separate keys that never overlap.&lt;&#x2F;p&gt;
&lt;p&gt;No build ran on a server I do not control. If this reached you at that address, every
part named above did its job. The next entries will test whether the discipline was
worth it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
