<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
    <title>Atelium Journal</title>
    <link>https://atelium.ai/journal</link>
    <description>Crafted tools, not enterprise sludge.</description>
    <language>en-us</language>
    <atom:link href="https://atelium.ai/journal/feed" rel="self" type="application/rss+xml" />
        <item>
        <title>A Discord Server in Git</title>
        <link>https://atelium.ai/journal/a-discord-server-in-git</link>
        <guid isPermaLink="true">https://atelium.ai/journal/a-discord-server-in-git</guid>
        <pubDate>Mon, 08 Jun 26 00:00:00 +0000</pubDate>
        <description><![CDATA[We don&#039;t click our infrastructure together by hand. So we stopped clicking together our team&#039;s chat server, too.]]></description>
        <content:encoded><![CDATA[<p>There is a particular kind of software you are supposed to assemble by hand. A Discord server is the canonical example. You click &quot;create channel,&quot; you drag roles into an order, you toggle permission checkboxes one at a time, and when you are finished you have a configuration that exists nowhere but in the running service and your memory of having made it. Six months later someone asks why a channel is private, and nobody can say.</p>
<p>We don't run our infrastructure that way, so this week we stopped running our chat that way. Atelium's team server is now defined in a file, in git, and a small program reconciles the live server to match it.</p>
<h2>What &quot;as code&quot; means here</h2>
<p>The whole server is one declarative config: roles, categories, channels, who can see what. A provisioner reads the live server, compares it against the file, and applies only the difference. Point it at an empty server and it builds everything. Run it a second time and it says, simply, in sync.</p>
<p>That second run is the entire point. Discord has no &quot;desired state&quot; endpoint — every tool that manages it, ours included, has to read what exists, decide what should change, and make exactly those changes and nothing else. So the heart of the thing is a pure function with a deliberately boring signature:</p>
<pre><code class="language-ts">// Given the live server and the file, compute the difference.
function reconcile(current: ServerState, desired: Config): Action[]
</code></pre>
<p>Everything else — logging in, creating a channel, denying a permission — is plumbing around that function. Because it is pure, we can test the hard part (does it notice a renamed role? an extra channel? nothing at all?) without ever touching Discord. The plumbing we trust to a single live dry run that prints the plan before a thing happens.</p>
<h2>The decision that mattered</h2>
<p>The one choice worth keeping: every channel is hidden by default. The everyone role can see nothing; the team role can see everything. That sounds austere for a three-person server, and today it is. But it means the day we invite a client into a single project channel, that is one line — grant them their category — and there is no path for them to accidentally see the rest. The safe thing and the easy thing become the same thing, which is the only arrangement that survives a busy week.</p>
<p>There is a smaller lesson folded in, too. A bot cannot own the server it manages, and in Discord a role's position outranks even administrator rights — so a human still has to do two clicks no script is allowed to. Worth knowing before you assume automation can do all of it.</p>
<h2>Why bother, for a chat server</h2>
<p>Because it will drift. Someone makes a channel at 11pm, someone loosens a permission to unblock a call, and a month later the server is a place nobody designed. Code is how a shared space stays honest: the file is the intent, the dry run is the diff, and &quot;in sync&quot; is a real answer to the question &quot;is this still what we meant?&quot;</p>
<p>It is the same reason this website is flat files and the database is git. A room you can rebuild from a file is a room you actually understand. Ours rebuilds in about twenty seconds, and we will never again squint at a channel wondering why it is private.</p>
<p>If that is the kind of thing you like to read about, subscribe below. We don't email often, and when we do it will be worth opening.</p>
]]></content:encoded>
    </item>
        <item>
        <title>Hello from Atelium</title>
        <link>https://atelium.ai/journal/hello-from-atelium</link>
        <guid isPermaLink="true">https://atelium.ai/journal/hello-from-atelium</guid>
        <pubDate>Thu, 28 May 26 00:00:00 +0000</pubDate>
        <description><![CDATA[A small company, two products, and one conviction about how software should be made.]]></description>
        <content:encoded><![CDATA[<p>This is the first post, so it should say what this is.</p>
<p>Atelium is a small company with two products and one conviction: that software made with care is worth more than software made at scale. Workbench is our unified surface for working with AI. DocuSpot is our document automation platform for operations teams. Both are built the same way, by the same small team, with the same refusal to ship things that are not good enough.</p>
<h2>What this journal is</h2>
<p>This is where we write about the work. Not launch posts, not press releases. The actual decisions: why we chose flat files over a database in one place, why a feature did not make the cut in another, what we got wrong and then fixed. The thinking behind crafted tools, in public.</p>
<h2>Why bother</h2>
<p>Because the software we admire was made by people who wrote down how they thought. We learned the craft from their posts. This is us paying that forward while we build.</p>
<p>Expect code, too. When a decision comes down to a few lines, we will show the lines:</p>
<pre><code class="language-php">// The whole site is flat files. The &quot;database&quot; is git.
Route::statamic('journal', 'journal.index');
</code></pre>
<p>If that sounds worth your time, subscribe below. We will not email often, and when we do it will be worth opening.</p>
]]></content:encoded>
    </item>
    </channel>
</rss>
