feat: switch to minimal zola-based apollo theme

This commit is contained in:
Timothy DeHerrera
2024-12-26 15:22:39 -07:00
parent 5f12356f8f
commit 9b591fc700
150 changed files with 250 additions and 24189 deletions

4
content/_index.md Normal file
View File

@@ -0,0 +1,4 @@
+++
[extra]
section_path = "blog/_index.md"
+++

14
content/about.md Normal file
View File

@@ -0,0 +1,14 @@
+++
title = "About"
path = "about"
+++
Tim DeHerrera (nrdxp).
Engineer at [IOG](https://iog.io) developing interoperable blockchain infrastructure for the Cardano ecosystem.
Co-founder of the [Ekala Project](https://github.com/ekala-project) - next-gen tooling for reproducible builds and declarative systems.
Specializing in distributed systems, build infrastructure, and network optimization. I am a polyglot systems programmer with a decade of experience, crafting tools that emphasize reproducibility and determinism. My engineering approach favors expressive type systems and functional programming paradigms, with current emphasis on Rust and Nix ecosystems.
gpg: [5471 8D2B 78DC AA9C 7702 96F1 8985 725D B5B0 C122](https://github.com/nrdxp.gpg)

130
content/blog/12-years.md Normal file
View File

@@ -0,0 +1,130 @@
---
title: "Twelve Years as a Digital Hermit"
description: "Why I left Social Media a Over a Decade Ago, and Why I Now Return"
taxonomies:
tags:
- ramblings
- propaganda
- philosophy
author: "Tim D"
authorGithub: "nrdxp"
authorImage: "https://avatars.githubusercontent.com/u/34083928?v=4"
authorTwitter: "nrdexp"
date: "2024-10-11"
category: "personal"
---
**Reader Advisory:** This post is sarcastic, and a bit raw. Those without a sense of humor, or an aversion to hard facts, venture no further. Also, these opinions are my own; subject to change, etc, etc.
*(not that I should have to say any of that to grown adults, imho, but alas...)*
## My Life the Contradiction
It has been confusing, even for me at times, to live my life being fascinated with computer technology from a very early age, yet having nothing to do with major social media platforms since around 2012 - even as the rest of the world became utterly infatuated with it, apparently seeing it as some great technical marvel (lol).
From being an obsessive gamer as a child, breaking games with my GameShark, to studying topics no 10-year-old has any business studying (denotational semantics, anyone?), to building my own machines and making stupid little HTML websites, hacking MySpace and feeling proud, and just generally being the neighborhood's goto free IT support (when people still spoke with their neighbors).
Then there was trying to teach myself C++ at 13, which I gave up, for reasons that should be obvious to those still clutching to some sanity. Later, due to less than favorable economic conditions and a mountain of responsibility in my early adulthood, having children at a young age, I lost hope that I might ever do anything professionally with technology, working whatever job I could find, for whatever wage they would pay. My personal favorite was digging through folks' garbage for the purpose of "recycling".
Still, I couldn't resist tinkering, breaking, modding, ripping, hosting, or hacking anything within reach during whatever free time I had. Before Spotify made such efforts redundant, I had built quite a respectable music library. Don't arrest me for using torrents - I know, I know. I couldn't afford to pay for all that music, though I did try to actually purchase albums when I could.
I remain forever indebted to countless artists in that collection for keeping my hopes alive through those trying and difficult years, dim as they were. I still hope someday I can do something cool to pay them back (music blockchain, anyone?). And let's not forget about the endless - and I mean ENDLESS - Android ROM hacks in the early days. Good times...
In retrospect, I suppose it was only natural that I would eventually build up enough hands-on experience to do this professionally, but that wasn't really my interest. I was enthralled - and in many ways still am - with the machine itself and what it made possible. How we managed to create these things in the first place remains a great mystery to me. No matter how much I learn, there's always more to know. It's the dream life of that never-ending 10-year-old uber-nerd still living inside me.
This passion is even expressed through my handle, nrdxp. I'm always looking to gain more experience, to reach the next level, to build something awesome and useful. I think there were a lot of kids like me in the 90s, but I don't think all of them made it this far, and for that miracle alone, I'll always be grateful. That said, there's still a long way to go - growing up poor and starting a career late (with 3 children to boot) has severe and long-lasting consequences.
For one thing, I don't own a home. I've never lived in a home owned by anyone in my family. In that sense, I guess I've always felt a bit "homeless." When I first started my professional career, I had some hope that I could save and get one - well, then COVID happened and brought me back to reality real quick. Why mention this? Because as grateful as I am, I want to make it clear that I'm not some oracle from on high. I still have struggles, battles, and demons to overcome - things so laughably and persistently bothersome that they're hardly worth mentioning. I'm not quite certain if I'll ever fully overcome them, but I'll certainly continue trying. What else is there? Complain? Yeah, what good does that do? Gross...
## Not an Innovation; a Virus
Speaking of complaints and societal problems, this leads nicely into my next point - the very reason I stepped away from the digital chaos in the first place.
Why did I abandon all forms of social media in 2012, deleting my Facebook (still dominant at the time), and never turning back? Well, as fascinated as I am with technology, social media is quite honestly just boring to me - but it's worse than that. At first, I had some hope that this new thing could actually help usher in a new era of connectedness (naive, I know - I was young), and I tried to push for it.
I used social media like you're not supposed to, apparently: actually trying to have conversations while maintaining dignity and respect (how stupid), while still pressing people on their quite obviously dogmatic and rigid beliefs. I used to believe that people were just ignorant, that if only they would come to see the light, they would understand. I see a lot of folks still running around like that. All I can say is... good luck.
But from what I've learned now, it isn't true. People like their delusions - they use them as a shield. To some degree, I believe we all do. In that sense, social media has been nothing but a "delusion amplification engine" for society. For the youngsters out there unable to remember a world before the internet, it probably isn't as jarring. They probably think we were all running around hanging each other over our race, where we choose to stick our genitals, and every other trivial difference - and that somehow, the exposure of social media saved us from all that bigotry.
But my experience was quite the opposite. Growing up in a Latino family, I can't remember a single instance - not even one - where race was an issue or was made an issue. It's quite clear to me now, seeing the juxtaposition of that time (ah, the 90s) to ours, that that was the proper way to handle trivial differences: ignore them. They don't matter. Really, seriously, they don't fucking matter. Be a bit more grateful that you're still above ground; you won't always be. Find a reason to appreciate this moment instead of condemning it, or you know, don't - whatever, enjoy your bitterness.
And of course, the government would have you believe they're on your side. They want to end all the "isms" of the world, don't ya know? I'm sure that's why they keep such exacting stats on everything, never shut up about it, and shove it down your throat at every opportunity and on every single form you've ever filled out.
You wanna know my race or ethnicity to "track" progress? Haha, yeah right. Read a history book, buddy - I know better. Of course, it's not just big daddy Gov's fault. Indeed, these large tech firms have become nearly indistinguishable from governments in some ways, trying to enforce all kinds of arbitrary rules for the sake of nothing but ~their own profit~ "the good of the people".
But therein lies something close to the core issue for me: governments and private companies alike are *both* quite incompetent at defining and upholding moral behavior. The former simply has no incentive but to continue growing, even beyond all sense or reason, until everything you do - even taking a breath - is illegal and, more importantly, taxable. The latter will change their so-called "values" on a dime to appease the masses, quite happy to talk down to you from both angles.
And what has social media given us then? Global mob rule; utter stupidity so obvious even children can see it, yet nobody dares address it for fear of facing the mob. Guess what? A mob of rabid idiots on the internet isn't actually that scary anyway. What are they gonna do? Sure, there are the psychos doxxing and swatting and all that nonsense, but psychos have always been around - they're just the canary in the coal mine for a much larger issue - one that strikes at the very heart of our modern delusions.
## No Right to Safety; The New Religion
This leads into what might be my most controversial point, which I'm sure will mark me as universally hated inside the delusion engine, not to mention the new and improved delusion generation engine 2.0 (incorrectly known as AI). You have no right to safety in society - anybody who promises it to you is either lying to take something from you or delusional about their own capacity to keep even themselves safe. That's not to say you have no right to defend yourself - you certainly do. Ironically enough, big daddy GovTech doesn't think you should, because they "know better." Gee, I wonder why?
But I digress - it is reality itself, not law, governments, or companies, which dictates that this world is inherently unsafe. There is no permanent fix. A "perfectly safe" space would be nearly indistinguishable from a prison cell, and even then, a big meteor could ruin your day anytime. Or did you already forget that we're all going to die someday? Maybe you believe we'll be uploaded to the machine instead and live forever, to which I would ask: How is this not religious?
What empirical evidence exists that such a thing is even possible? But this is the new religion - giving up anything and everything, our very thoughts themselves, for the "right" to "blend in," to "be safe," to be on the "right side of history" so we can survive long enough to experience the grand transhuman utopia.
Anybody who has read a history book knows exactly what side modern governments and tech firms are on, and anybody - well-intentioned or not - who feeds into it is a potential danger. Remember The Matrix? I know it's been done to death, but there's still one line in particular that rings even truer today than when it was made: "anyone who has not been unplugged is a potential agent."
If you don't stand up, put on your big boy pants, and say "No thanks, I don't need your advice on how to speak, and certainly not on how to think - lmao, are you serious? The arrogance..." - then it's only a matter of time before you simply go along to get along, and shove anyone down who tries to call you out. This is why it's so crucial to maintain the courage to speak your mind.
The mind, and especially your ability to express your true thoughts earnestly, is like a muscle. If you don't exercise it, it will atrophy fairly quickly, until you can no longer think at all. All you can do is scroll for your daily dose of "what to think."
## The Forgotten Utility of Ridicule
So what's the antidote to this mindless conformity? What weapon do we have against this tide of enforced groupthink? Ridicule is the proper response - the header says it all.
This kind of bullshit morality posturing, colloquially known as "wokeness," is worthy of any and all forms of human ridicule. And no, not just to be mean or vent. Ironically, after mentioning how impossible it is to "be safe" in this world, there are certainly degrees to which one's safety can be practically improved, and ridiculing ridiculous tyrants who would have you kissing their feet is one sure-fire way to do it.
No, you're not being compassionate, no you don't fucking care about their genitals - I think we all universally agree that "our" genitals are the most important, right? Stop pretending, fuck the show. You can't make me, really and truly. Call me an asshole - actually, I kinda am - but one thing I won't do, even when you want me to, is hate. Hate is the corrosion that led us to this dismal position in the first place. Yoda taught us these principles years ago people, come on.
What I will do, however, is unapologetically ridicule the ridiculous. Go figure...
In all seriousness, we live in an era ruled by bitterness and delusion. And in such a short time - a generation and a half - from likely the least racist society that has ever been (the 90s children) to today; all because *you* won't stand up and say what we all know you likely think. The only people who actually believe any of the modern rhetoric are parrots who gave up their right to think independently long ago. As was said by a far more brilliant man (gendered speech, OMG!) than myself, "it takes someone of great intelligence to come up with ideas so colossally and utterly ***stupid***."
Don't believe for a second that just because someone has some fancy pieces of paper on a wall that they have any right - even a little - to tell you how to think. In fact, that's a sign of incompetence in my book. Real masters of any craft should be able to trivially *demonstrate* what is true and correct. If you have to force it down my throat, perhaps you're just a bit insecure about your own abilities; just maybe?
Another one to remember (thanks Batman): "you will never be ridiculed by someone doing more than you." In other words, if someone thinks less of you, who cares? It's just a sign they probably feel like you're ahead of them in some way, and they're simply trying to pull you back down. Take it as a compliment and move on.
Paper means nothing. Like I said, the brain is a muscle - doesn't matter if you were a world-class bodybuilder with all the trophies to boot, if you stop working, you start dying. Yet we live in a world where we take all our orders, *all* our opinions, spoon-fed from idiots with fancy papers, which are dutifully repeated over and over by every "influencer" and their grandma (yes, grandma has a channel now too).
You want to influence something real? Think for your damn self. I know, it's scary after all this time, but if you really give a damn about "staying safe," it's your best bet. Or don't, and sit comfortable in your delusion until the day comes that you're no longer part of the acceptable and "righteous" crowd. That day is coming - in fact, for very many it's already here.
But I digress - where we're heading now is anything but "safe." Seriously, I implore you, it may sound sarcastic (and it is a bit) but read a history book. Just pick one. All this nonsense, all this rhetoric has happened before; many times. Just pick an era at random, and I'm quite confident you'll find some form of it or another. Perhaps all this madness is inevitable, but I don't actually care...
I am one man, living my one life, and I don't want to be "told" by anyone - least of all a brainless mob so utterly afraid to think out of line that they share notes before every public statement - how to think or what to feel, or when or how to speak. Again, no thanks, I got this. I'll take responsibility for my mistakes and take credit for my own wins. I don't need your help, not at any cost, not even if I have to build everything I work on alone, completely in isolation. Where's the inclusivity for folks like me, exactly?
And the definitions of unacceptable just keep expanding, as they do and have (I'm telling you - history book). To some, it's a crime merely to exist with the wrong genitals, because that's progress somehow...
This segment could go on and on, but I'll just remind you of the header. There is great social utility in ridicule. It's powerful - it keeps people safe from these idiotic ideas that actually convince them to permanently deform themselves for the sake of approval.
But that's just the thing: Identity isn't a feeling, it's defined by your character. How you look or what cosmetic appearance you possess will do little to affect your character, and in fact, focusing on it too much will likely degrade it, seeing as it's totally irrelevant in the first place...
## Wait Why, Again?
After all this criticism and heated commentary, you might wonder why I'm back on social media at all. So yeah, this got heated in parts, but I honestly feel it is necessary at this point. Years of watching from the sidelines, trying to be "considerate," have only confirmed what I suspected - silence changes nothing.
And for what was I silent? To get ridiculed and excluded anyway because I refuse to give up my right to think to the great "Constitutional Assembly" of delusion and apathy?
"And as my first act, I appoint all my friends to the board ~of directors~ to defend ~the empire~ democracy and ~my vested-interests~ the goodwill of the community!"
And to be clear, I still think social media is a toxic, narcissistic breeding ground for idiocy and delusion, largely responsible for this record-breaking societal decline, distracting us all the while, like the great propaganda masters of television could have only dreamed. Yet I'm back here on X... why?
Maybe I just want a little attention like everyone else? You can argue that if you like, but I've been doing just fine on my own for quite some time now. If anything, the little bit of attention I've attracted so far has been an annoyance and a burden. It also feels dangerous somehow. But here I am... somewhat because of that last section - there aren't enough folks with the courage to ridicule the ridiculous, so I will, come what may (at least while it's still "legal" in my country; damn free speech...).
But it's not just that. I'm older now, and I know there are still younger folks who don't have the experience I've gained. Quite frankly, they seem to need an example of what courage looks like. No, it isn't perfect, it's not ripped to shreds with perfect abs, either. It is literally just saying what you really think - damn the consequences.
So anyway, there you go zealots, all the ammo you'll ever need to condemn me. Consider it a free gift, but know that there's plenty more where that came from.
Countries are not supposed to give in to terrorism (whatever the fuck that means), and I will not give in to precious bullies with fragile little egos. Sorry, but even if this generation is largely toasted, someone still has to set an example for the next one, however imperfectly, or things will continue to get worse. I guess this is a sentiment only parents really understand, and there are so few parents nowadays (sigh).
I digress though - I may dislike it (despise it even), but social media is the only playground anyone wants to play on. It's the only place to make any sort of stand, like it or not.
And I really want to stress this: the most important lesson I've learned from my study of history is that it can, and will *always* get worse, unless and until somebody says, "No, that's enough. I'm quite finished with your little tyranny, thank you - hang me if you must."
And so I'll end on that note. Hang me if you must... As much as I have always loved technology - I wouldn't even know what to do with myself if I didn't do what I'm doing now - I absolutely and positively *refuse* to perpetuate or feed into this nonsense, even a little bit.
If the open-source ecosystem had half the spirit of the pioneers who founded it, we would be on the front lines against such rabid propaganda. But instead, we stand quietly on the sidelines, at best asking questions that go perpetually ignored by the zealots, all the while apologizing for our intrusive thoughts.
Now, if you've made it this far, stay tuned - I've got some things coming down the pipe to try and revitalize a once-thriving world of thoughts and ideas, free from the burden of zealots and thought police. Until then, take care of yourself, and don't be afraid to use that beautiful jelly inside your skull, before you lose it!

View File

@@ -0,0 +1,297 @@
---
title: "Political Bikeshedding: NixOS Edition"
description: On Social Dynamics and Leadership
taxonomies:
tags:
- nix
- politics
author: Tim D
authorGithub: nrdxp
authorImage: https://avatars.githubusercontent.com/u/34083928?v=4
authorTwitter: nrdxp52262
date: "2024-07-02"
category: politics
---
This piece offers a perspective on recent NixOS project challenges from
a long-term contributor. As one of the authors of [RFC 175][175], which
attempted to address moderation issues but faced obstacles in adoption,
the writer brings both experience and a commitment to improving the
project's governance.
Given the complexity of the situation, this article aims to provide a
high-level analysis rather than an exhaustive account. While specific
examples are limited for brevity, future pieces may explore more
detailed case studies. The current goal is to establish a framework for
understanding and addressing broader issues that are increasingly
prevalent across the open-source world.
## The Silent Majority and the Myth of "Community Consensus"
A significant portion, if not the majority, of people likely despise
politics and deliberately disengage from it, focusing on more enjoyable
pursuits. By nature, these individuals aren't necessarily interested in
having their unrelated political or ideological views "represented" in
groups or forums they join for specific interests, such as hacking.
Crucially, this silent majority challenges the notion of a unified
"voice of the community." Many claim to speak on behalf of "the
community," advocating for actions or bans based on supposed community
consensus. However, if a silent majority exists, such claims of
representing the entire community are inherently flawed and potentially
misleading, at best.
The concept of a silent majority and the questionable nature of claimed
community consensus lead us to examine another critical issue: the
misuse of marginalization claims, which many of these voices use as a
foundation. Understanding the contextual and temporal nature of
marginalization is key to addressing this problem.
## Marginalization is Contextual and Temporal
The term "marginal" has no fixed definition outside a specific context.
Consider this scenario: someone stands on the far side of a room while
others gather at a table. This person detects a threat, perhaps a small
fire, only visible from their position. They alert the group and come to
the table to address the issue. Everyone appreciates their input, and by
joining the table, they physically become part of the majority.
Now, imagine another fire starts under the table with everyone seated.
Those at the table, including the previously "marginal" individual,
can't detect this new threat. Their once unique position is lost, and
they're now part of the group that's unaware of the new danger.
It's crucial to note that even this scenario is relative. To another
group or from a broader perspective, everyone at this table could be
considered marginal. This underscores the importance of context: a
marginal position in one setting may be quite common in another.
This relativity is particularly relevant when considering claims of
marginalization within specific communities or projects. Even if
individuals are marginalized in broader society, they may hold majority
or influential positions within a particular project or community. In
such cases, their claims of marginalization within that specific context
may not be accurate or relevant.
In essence, marginalization is a temporary state, not a fixed identity.
It's fluid and can shift with changing situations and contexts,
highlighting the importance of diverse perspectives and the danger of
assuming any *one* group always holds a privileged viewpoint or unique
insight in all settings.
The misuse of marginalization claims has serious consequences.
Individuals wield this notion of perpetual marginalization not only to
speak for others, but also to justify a degradation of professional
standards. This false moral authority has become a shield for behavior
that would otherwise be unacceptable, leading us to examine the pitfalls
of such unchecked conduct.
## The Pitfall of Unchecked Behavior and False Marginalization
Traditionally, public displays of childish behavior were not tolerated
in professional settings. Recently, however, a troubling trend has
emerged: the justification of bullying behavior based on claimed
marginalized status. This justification often escalates rapidly,
creating untenable situations.
Crucially, these individuals are exploiting an identity that lacks a
concrete, technical definition. They are not inherently or permanently
marginalized; rather, they're hiding behind a facade to maintain special
privileges, particularly the ability to "shout down" others without
consequence.
This false claim of static marginalization ignores the contextual and
temporal nature of marginalization we discussed earlier. It allows
certain individuals or groups to maintain a position of perceived moral
authority, even when they've become part of, or aligned with the
majority. This misuse of claimed status creates an environment where
bullying is not only tolerated but sometimes even encouraged, as long as
it comes from the "right" sources.
Such behavior undermines the principles of professionalism, open
dialogue, and merit-based contribution that should be the hallmarks of
any healthy community, especially in technical fields. It's essential
to recognize and address this manipulation to maintain a truly fair and
productive environment.
## A Call for Maturity and Productive Incentives
As an adult and parent, such behavior is more disappointing than
surprising. Society might benefit from being more forgiving of mistakes,
allowing for course correction. In political terms, both sides often
have valid points and could learn from each other if they moved past
superficial differences. We should encourage more mature, productive
motivations, especially in contexts where many are willing to
collaborate constructively.
Importantly, we must consider the role of incentives in shaping
behavior. Creatures, including humans, are primarily motivated by
incentive structures. It's crucial not to inadvertently reward or
empower those who engage in divisive, derogatory, or unproductive
behavior, as this can quickly lead to a self-reinforcing cycle of
negative actions.
Thankfully, the solution is straightforward: we simply need to
incentivize civilized behavior. Instead of discouraging constructive
engagement by labeling it as "sea lioning" or "concern trolling," we can
cultivate an environment that rewards respectful disagreement and
collaborative problem-solving irrespective of personal or political
differences.
The alternative and apparent *status quo* seems to be a perpetual
witch-hunt for an ever-growing list of "wrong" opinions. Surely it is
clear which strategy is more sustainable?
## The Dangers of History Modification
The core issue lies in social manipulation through selective moderation
and, crucially, the modification of historical records. When moderation
teams, often claiming to represent marginalized groups, are empowered to
alter, delist, or delete past conversations, posts, or decisions, they
gain the ability to distort the narrative. This practice is, by
definition, a form of rewriting history.
By condoning or failing to address poor behavior from those claiming
marginalized status, these moderators further enable and entrench the
misuse of such claims. This creates a self-reinforcing cycle of
manipulation and degraded standards, undermining the integrity of
discourse and eroding trust among members.
A relevant example in the Nix project involves Jon Ringer, a
long-standing contributor and public figure. Recent controversies have
portrayed Jon as an instigator and "mean offensive person." However, a
more balanced view reveals him as a scapegoat for broader project
tensions. Crucially, Jon was permanently banned from the project, while
many who openly degraded him and made *ad hominem* attacks on his
character faced no consequences. This stark contrast highlights the
uneven application of moderation standards.
While Jon, like anyone, may have had imperfect moments under extreme
pressure, these pale in comparison to the systematic narrative
manipulation by others. The situation exposes a coordinated effort to
distort facts, issue threats, and employ bullying tactics to control the
project's direction.
The issue isn't that Jon was never wrong, but that he was consistently
painted as the primary instigator, regardless of reality. Even when
heated, Jon generally avoided the name-calling and derogatory behavior
that often went unchecked from other parties. His permanent ban, in this
context, underscores the troubling double standard at play in the
project's governance.
This manipulation of context and conversation history not only
misrepresents the overall dynamics but also serves to gaslight both
individuals and the wider perspective. The impact of this distortion is
evident in public platforms like Reddit, where observers unfamiliar with
Jon often express views that align with the manipulated narrative. These
casual observers, swayed by the dominant portrayal and the absence of
meaningful dissenting arguments, tend to perceive Jon as a far greater
problem than he actually was.
Crucially, while Jon may have contributed to some tension, he is far from
the epicenter of the controversy. In fact, the current issues surrounding
him have been brewing for years, consistently instigated by the same
individuals who have largely escaped scrutiny as they continue to
perpetuate divisive narratives.
The most tangible and regrettable outcome of this scapegoating is that
the Nix project has lost a long-standing, highly productive, and
professional contributor. Jon was often very helpful to newcomers, and
his departure represents a significant loss to the project. This
illustrates the real cost of allowing manipulated narratives to drive out
valuable members.
Such power to modify history is dangerous. It allows for the erasure of
context, the silencing of dissenting voices, and the creation of a false
consensus. This not only undermines transparency but also erodes trust
within project spaces and misleads those on the periphery. Setting a
clear precedent against this practice is vital. We must recognize that
allowing any group to "clean up" or selectively edit the historical
record is tantamount to endorsing propaganda[^1]. True professionalism in
project management involves facing our history honestly, learning
from it, and moving forward transparently.
Solving these problems requires strong leadership with a commitment to
preserving the integrity of shared discourse. Leaders must establish
clear principles that prioritize transparency and resist the temptation
to sanitize the past. While challenging, this approach is essential for
maintaining fairness, fostering genuine progress, and building a
trustworthy environment.
## The Solution: Embracing Leadership Principles
The real solution to Nix's or any project suffering from such childish
incursion lies in embracing fundamental principles of leadership. Being
a genuinely good leader is challenging. It requires holding oneself to a
higher standard than everyone else, and having the courage and conviction
to guide others to be their best selves, even when they resist.
Good leadership is the only way to be fair to all sides when there is a
genuine disagreement. It involves:
1. Setting clear, unambiguous goals and standards of behavior that align
with the project's core values. This clarity respects everyone's time,
allowing individuals to easily decide whether they align with and wish
to participate in the project.
2. Maintaining transparency and resisting the urge to manipulate
historical records.
3. Fostering respectful, merit-based dialogue while considering the
silent majority, not just vocal special interests.
4. Making decisions based on technical merit and the project's best
interests, not personal or ideological biases.
5. Being willing to address conflicts directly and fairly, without
scapegoating individuals or giving special privileges to allies.
6. Consistently enforcing these standards, making it clear what kind of
behavior and contributions are valued in the project.
By embracing these leadership principles, any project can create an
environment where technical excellence and collaborative spirit thrive.
It's a path that requires courage and commitment but offers the best hope
for resolving current tensions and preventing future ones.
However, implementing these principles requires a conscious choice from
all contributors, especially from those who have remained silent until
now.
## The Great Purge or Professionalism?
The Nix project faces a critical juncture. A long-standing moderator has
publicly expressed a desire for a ["purge"][purge] of supposed
"undesirables." This stark reality forces us to confront a fundamental
choice: do we embrace professionalism and mutual respect, or do we
allow divisive, exclusionary behavior to dominate and ultimately derail
the entire project?
This isn't just about Nix; it's a choice many now face. The silent
majority, those who typically avoid controversy, may now have to decide
what kind of project space they want to cultivate, and what sort of
leaders they wish to follow. Inaction is itself a choice; one that may
lead to the continued erosion of the project's ethic.
We must ask ourselves: Do we want a forum driven by technical merit and
collaborative spirit, or one ruled by ideological purity? The answer to
this question will shape the future of Nix and could set a precedent for
open-source projects at large.
It's time for those who value professionalism, open collaboration, and
technical excellence to stand up and be counted. The alternative - an
ecosystem stifled by ideological cleansing - is too high a price to pay
for our silence.
## Preserving the Future of Open Source
While this piece has focused on Nix, the issues discussed are
symptomatic of a growing and worrying trend across the open-source
world. Many projects face similar challenges with ideological divisions,
manipulated narratives, and the silencing of dissenting voices.
Open source is far too important to be ruled by narrow-minded and
exclusionary ideologies. By embracing strong leadership principles and
fostering environments of mutual respect and professionalism, we can
ensure that open source continues to thrive as a bastion of innovation
and collaboration.
[175]: https://github.com/NixOS/rfcs/pull/175
[purge]: https://chaos.social/@hexa/112711384631096150
[^1]: https://en.wikipedia.org/wiki/Historical_negationism

View File

@@ -0,0 +1,271 @@
---
title: NixOS, Flakes and KISS
description: A simpler way to manage the OS Layer
taxonomies:
tags:
- nix
author: Tim D
authorGithub: nrdxp
authorImage: https://avatars.githubusercontent.com/u/34083928?v=4
authorTwitter: nrdxp52262
date: "2020-12-19"
category: dev
---
## Introduction
This marks the first post of my very own developer blog, and it comes much later
than I had originally anticipated thanks to the ongoing pandemic, coupled with
some unforeseen life challenges. My original intent was to start by introducing
the concept of Nix Flakes, however, an excellent blog series over at
[tweag.io](https://www.tweag.io/blog/2020-05-25-flakes) has emerged, expanding
on just that premise. If you are new to flakes, it is highly recommended that
you check it out before continuing with this post.
Now, I'd like to introduce a project I've been slowly building up since
flakes were introduced called [DevOS][DevOS].
# So what is it anyway?
After years of working with NixOS, I strongly felt that the community as a whole
could benefit from a standardized structure and format for NixOS configurations
in general. It appears that every developer is essentially reinventing the
wheel when it comes to the "shape" of their deployments, leading to a lot of
confusion as to what the idioms and best practices should be, especially for
newcomers.
Having a mind share to collect the best ideas concerning structure and
method would be valuable, not only for its pragmatic implications, but also to
help ease adoption and onboarding for new NixOS users; something that has
traditionally been difficult up to now.
Of course this really hinges on wider community support, as my ideas alone
definitely shouldn't be the final word on what constitutes a correct and well
organized NixOS codebase. Rather, I am hoping to cajole the community forward
by providing useful idioms for others to expand on.
Even if my ideas lose out in the end, I sincerely hope they will, at the very
least, push the community toward some level of consensus in regards to the way
NixOS code repositories are structured and managed.
That said, DevOS appears to be gaining a bit of popularity among new flake
adopters and I am really quite excited and humbled to see others engage the
repository. If you have contributed to the project, thank you so much for your
time and support!
# An Arch KISS
I moved over to NixOS after a decades long love affair with Arch Linux. I found
their brand of KISS to be pragmatic and refreshing compared to alternatives
such as Ubuntu or Red Hat. This isn't to dog on those distributions, which I
also have used and enjoyed for years, but rather to accentuate my affection
for the simplified, and developer focused workflow that Arch Linux enabled for
my work stations.
However, over the years, I came to resent the several hours of tedious work
spent doing what amounted to the same small tasks over and over, any time
issues arose.
My first attempt to alleviate some of this work was by using Ansible to deploy
my common configuration quickly whenever it became necessary. However, I ran
into a ton of issues as the Arch repositories updated, and my configurations
inevitably became stale. Constant, unexpected breakage became a regular
nuisance.
I then became aware of Nix and NixOS, and hoped that it would live up the
promise of reproducible system deployment, and after a brief stint of
procrastination, I dove head first.
# Great but Not Perfect.
At first everything seemed almost perfect. NixOS felt like Ansible on steroids,
and there was more than enough code available in nixpkgs to meet my immediate
needs. Getting up to speed on writing derivations and modules was fairly
straightforward and the DevOps dream was in sight.
It wasn't all sunshine and rainbows, as channel updates sometimes caused
the same sort of breakage I moved to NixOS to avoid. But simple generation
rollbacks were a much more welcome interface to this problem than an unbootable
system. It was a measurable improvement from the busy work experienced with Arch. All in all, I felt it was
well worth the effort to make the transition.
It wasn't long before the [rfc][rfcs] that eventually became flakes emerged.
It seemed like the solution to many of my few remaining gripes with my
workflow. An officially supported and simple way to lock in a specific revision
of the entire system. No more unexpected and unmanaged breakage!
Of course it took a while for an experimental implementation to arrive, but I
found myself digging into the Nix and Nixpkgs PR's to see how flakes worked
under the hood.
Around the same time, the ad hoc nature of my NixOS codebase was starting to
bug at me, and I wanted to try my hand at something more generalized and
composable across machines. I had a first iteration using the traditional
"configuration.nix", but ended up feeling like the whole thing was more
complex than it really needed to be.
My eagerness to get started using flakes was the perfect excuse to start from
scratch, and so began DevOS. An attempt to address my concerns, using flakes.
## How does it work?
First and foremost, I want to point out that the bulk of the credit goes to the
amazing engineer's who have designed and implemented Nix and the ecosystem
as a whole over the last decade.
I see a lot of new users struggling to dive in and get up to speed with the Nix
language, and particularly, getting up and running with a usable and productive
system can take some serious time. I know it did for me.
The hope for DevOS is to alleviate some of that pain so folks can get to
work faster and more efficiently, with less frustration and more enthusiasm for
the power that Nix enables. I especially don't want anyone turning away from
our amazing ecosystem because their onboarding experience was too complex
or overwhelming.
# Everything is a profile!
At the heart of DevOS is the [profile][profiles]. Of course, these profiles
are really nothing more than good ol' NixOS [modules][modules]. The only reason
I've decided to rebrand them at all is to draw a distinction in how they are
used. They are kept as simple as possible on purpose; if you understand modules
you don't _really_ have anything new to learn.
The only limitation is that a profile should never declare any new NixOS module
options, we can just use regular modules for that elsewhere. Instead, they
should be used to encapsulate any configuration which would be useful for more
than one specific machine.
To put it another way, instead of defining my entire NixOS system in a
monolithic module, I break it up into smaller, reusable profiles which can
be themselves be made up of profiles. Composability is key here, as I don't
necessarily want to use every profile on every system I deploy.
As a concrete example, my [develop][develop], profile pulls in my preferred
developer tools such as my shell, and text editor configurations. It can be
thought of as a meta-profile, made up of smaller individual profiles. I can
either pull in the whole thing, which brings all the dependent profiles along
with it, or I can just import a single profile from within, say my zsh
configuration, leaving all the rest unused. Every profile is a directory with
a "default.nix" defining it. You can have whatever else you need inside the
folder, so long as it is directly related to the profile.
Let's draw the obvious parallel to the Unix philosophy here. Profiles work
best when they do one thing, and do it well. Don't provision multiple programs
in one profile, instead split them up into individual profiles, and then if you
often use them together, import them both in a parent profile. You can simply
import dependent profiles via the "imports" attribute as usual, ensuring
everything required is always present.
The key is this, by simply taking what we already know, i.e. NixOS modules, and
sticking to the few simple idioms outlined above, we gain composability and
reusability without actually having to learn anything new. I want to drill this
point home, because that's really all there is to DevOS!
Besides a few simple convenience features outlined below, profiles are the star
of the show. It's really nothing revolutionary, and that's on purpose! By
keeping things simple and organized we gain a level of control and granularity
we wouldn't have otherwise without adding real complexity to speak of.
# Really? Everything?
Yes! Thanks to built in [home-manager][home-manager] integration, users are
profiles, a preferred graphical environment is a profile. Anything that you
could imagine being useful on more than one machine is a profile. There are
plenty of examples available in the "profiles" and "users" directories, and
you can check out my personal "nrd" branch, if you want to see how I do things
on my own machines.
# Anything else I should know?
As mentioned briefly above, DevOS also has some convenience features to make
life easier.
For starters, you might be wondering how we actually define a configuration for
a specific machine. Simple, define the machine specific bits in a nix file
under the [hosts][hosts] directory and import any relevant profiles you wish to
use from there. The flake will automatically import any nix files in this folder as NixOS
configurations available to build. As a further convenience, the hostname of
your system will be set to the filename minus the ".nix" extension. This makes
future "nixos-rebuilds" much easier, as it defaults to looking up your current
hostname in the flake if you don't specify a configuration to build explicitly.
Now what if we actually just want to define a NixOS module that does declare
new NixOS options, you know, the old fashioned way? We'll also want to define
our own pkgs at some point as well. These are both structured closely to how
you might find them in the nixpkgs repository itself. This is so that you can
easily bring your package or module over to nixpkgs without much modification
should you decide it's worth merging upstream.
So, you'd define a package or module the exact same way you would in nixpkgs
itself, but instead of adding it to all-packages.nix or module-list.nix, you add
it to pkgs/default.nix and modules/list.nix. Anything pulled in these two files
will become available in any machine defined in the hosts directory, as well as
to other flakes to import from DevOS!
This setup serves a dual purpose. For people who already know the nixpkgs
workflow, it's business as usual, and for individuals who aren't familiar with
nixpkgs but wish to become so, they can quickly get up to speed on how to add
packages and modules themselves, in the exact same way they would do so upstream
proper.
Now what about overlays? Well, any overlay defined in a nix file under the
overlays directory will be automatically imported, just as with packages and
modules, and are available to all hosts, as well as to other flakes.
What if I want to pull a specific package from master instead of from the
stable release? There is a special file, pkgs/override.nix. Any package listed
here will be pulled from nixpkgs unstable rather than the current stable release.
Simple, easy.
What about cachix? It's super easy to add your own cachix link just as you
would a regular NixOS configuration. As a bonus, it will be wired up as a flake
output so other people can pull in your link directly from your flake! My
personal cachix repo is setup by default. It provides the packages the flake
exports so you don't have to build them.
That should just about do it for DevOS's current quality of life features, but
there are more ideas brewing.
# What's next?
I'm working on a system for seamlessly importing modules, packages and
overlays from other flakes, which isn't too hard as it is, but it's messy
because the current "flake.nix" has a lot of business logic that gets in the way.
Also, I would like to start programmatically generating documentation for
everything. So users can quickly find what goes where and not have to read
drawn out blog posts like this to get started. 😛 Nixpkgs is currently
transitioning to CommonMark for all documentation, and we will probably follow
suite.
Additionally, I want to implement an easy way to actually install NixOS on the
bare metal from directly within the project. I know the [deploy-rs][deploy-rs]
project is working on this, and I'm interested in supporting their project
in DevOS so as to add extra flexibility and power to installation and
deployment!
Also, certain parts of the flake should be tested to ensure things don't break.
We really have no tests to speak of as is. The auto import functions for the
"hosts" and "overlays" directory are good examples.
## A call to arms!
If you'd like to help, please jump in. I am very much open to any ideas that
could reduce the complexity or simplify the UI. If you have a profile you
believe would be useful to others, please open a [Pull Request][pr].
If you think I am crazy and wasting my time, please don't hesitate to say so! I
typically find critical feedback to be some of the most helpful. Most of all,
if you made it this far, thanks for taking some time to read about my efforts
and please consider giving DevOS a shot!
[nix]: https://nixos.org
[DevOS]: https://github.com/divnix/DevOS
[rfcs]: https://github.com/NixOS/rfcs
[modules]: https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules
[profiles]: https://github.com/divnix/devos/tree/template/profiles
[develop]: https://github.com/divnix/devos/tree/template/profiles/develop
[hosts]: https://github.com/divnix/devos/tree/template/hosts
[deploy-rs]: https://serokell.io/blog/deploy-rs
[home-manager]: https://github.com/nix-community/home-manager
[pr]: https://github.com/divnix/devos/pulls

4
content/blog/_index.md Normal file
View File

@@ -0,0 +1,4 @@
+++
title = "Blog"
sort_by = "date"
+++

View File

@@ -0,0 +1,123 @@
---
title: In Defense of the Disagreeable
description: A Call for Freedom
taxonomies:
tags:
- psychology
- personality
- freedom
- nonconformity
- open-source
- philosophy
- critical-thinking
author: Tim D
authorGithub: nrdxp
authorImage: https://avatars.githubusercontent.com/u/34083928?v=4
authorTwitter: nrdexp
date: "2024-12-25"
category: personal
---
This blog typically focuses on technical matters - software architecture, deployment strategies, and the occasional deep dive into systems design. While I've shared personal musings before, I've never ventured quite this deep into the waters of self-reflection and social commentary.
What follows is perhaps the most personal piece I've written to date. It emerged, almost unbidden, from a long-standing tension between my nature and the prevailing winds of our time. While it may seem far removed from my usual technical discussions, I've come to realize that the philosophy underlying software freedom - particularly in the open-source world - is inextricably linked to the broader questions of personal freedom and the vital role of disagreeable personalities (my unhappy burden) in defending it.
Consider this both a warning and an invitation: we're about to venture far from the comfortable waters of technical discourse, into the depths of personality, purpose, and the preservation of freedom itself.
## The Misunderstood Guardians
I have felt compelled to write on this topic for some time now. While I am not a professional psychologist, I speak from a lifetime of experience as someone measured within the 1st percentile in agreeableness - in other words, I am as disagreeable as a person can be, according to the science of personality.
This often surprises those who know me well, largely due to common misconceptions about what being disagreeable truly means. Many picture a disagreeable personality as arrogantly argumentative or utterly closed-minded. While such individuals exist, this reductionist view lies at the root of many issues plaguing our modern "polite" society.
In the Big 5 personality test, agreeableness (or lack thereof) is measured separately from openness. One can be highly disagreeable while maintaining high openness to experience. The stereotype of a closed-minded disagreeable person typically reflects someone low in both traits. As someone in the 97th percentile for openness and the 1st for agreeableness, I am essentially destined to be perpetually misunderstood. It has become an axiom of my existence.
This post represents part of my journey to embrace my nature, after spending years either misunderstanding or actively loathing it. I've learned to trust my perspective through painful experience - having ignored my own intuition countless times, only to watch opportunities pass me by, Bitcoin in 2008 being just one particularly stark example. Yet I'm not writing this for myself - a private journal would suffice for that purpose. I'm sharing this publicly because of a crucial truth: there seems to be a chronic epidemic of self-loathing and depression, particularly among disagreeable types, especially in a modern western culture that elevates "kindness" even above accuracy and truth.
But there's an even graver concern: the world depends on disagreeable personalities to defend individual freedom. The war on "meanness" is, in its final analysis, a war on freedom itself. This conclusion, while perhaps underappreciated, should be self-evident. It takes someone fundamentally different - someone who places the integrity of their beliefs above powerful social influences - to defend the freedoms that western society has come to take for granted. Someone willing to endure any punishment before conceding to beliefs they know to be misguided is, by definition, quite rare.
Freedom, contrary to our privileged experience, is not humanity's natural state. If current trends toward techno-feudalism are any indication, it may not remain our reality much longer.
This isn't merely philosophical speculation - the erosion of freedom is happening in real time, even in spaces supposedly dedicated to it. Take the open-source software ecosystem, a realm I deeply cherish. We're witnessing its very foundations being eroded by an insidious "daddy knows best" mentality, manifesting through oppressive Codes of Conduct that fundamentally contradict the freedom-loving philosophy that birthed the movement. The tragic irony is that many who claim to champion inclusivity are actually enforcing a new kind of conformity, one that would have stifled the very innovations they now take for granted. While I intend to explore this particular battle in far greater depth in a dedicated piece - as it deserves no less - this example illustrates why it's more crucial than ever to defend those who often appear as society's most misunderstood, underappreciated, and sadly, most miserable individuals.
## Beyond the Boxes
Being highly disagreeable isn't my only outlier in the Big 5, but it's become one of the most defining aspects of my personality. Why this amateur obsession with personality? Because modern personality science has finally given concrete form to what Socrates understood millennia ago when he declared "know thyself" - or more starkly: "the unexamined life is not worth living." What the ancients intuited through philosophy, we now measure through science: that understanding one's fundamental nature isn't merely philosophical luxury - it's survival.
I've lived this truth quite literally, having spent the first chapter of my adulthood in complete ignorance of my nature - at best ashamed, at worst trying to be something I simply wasn't. My high openness means I often share things that perhaps I shouldn't. Even now, with greater self-understanding forged through life's trials, doubts and struggles persist. There are always more battles to conquer, more mysteries to unravel.
It doesn't get easier, but I cannot emphasize enough the vital importance of understanding one's place in it all. Life is hard - mine always has been and likely always will be. Facing such difficulty without self-understanding is unbearable. Even now, with clearer purpose and identity than ever before, it remains challenging. Having high emotional sensitivity (another personality quirk) is yet another aspect of existence I've learned to embrace. The isolation is perhaps the hardest part - living between camps, never fully belonging anywhere, always seeing multiple sides yet unable to fully commit to any one perspective. It's a peculiar kind of loneliness that comes not from lack of connection, but from the perpetual state of partial connection, of always being somewhat out of step with those around you.
In short, I am nothing that I'm "supposed" to be, perpetually evading the boxes others try to contain me in. This isn't by choice or force - though that is a common accusation - it's simply who I am, even when I don't wish to admit it. It's difficult to explain this to someone of a highly agreeable nature, just as I struggle to fully comprehend more agreeable types. This isn't meant as criticism - I truly believe every personality type has its place, and the world would be dimmer without this human variance.
Much of today's social conflict stems from a fundamental lack of appreciation for this innate diversity. The Big 5 has shown us that core personality traits remain relatively stable throughout life. Minor variations occur, and rarely even dramatic shifts, but by our early to mid-20s, our basic personality is largely set. Knowing this, I can hardly imagine a society more primed for perpetual conflict than one that elevates certain personality types above others, as we seem to have done today.
## The Unbearable Burden
Where am I going with this? Well, I went on a tangent, but a necessary one. Throughout history, from Jesus to MLK, many have spoken to a similar message. In our laudable efforts to eliminate racism and other forms of naive prejudice, we have unwittingly retreated to venting our social biases through hatred of particular personality types.
If my previous arguments hold any water, having a bias against certain personality types may be nearly as misguided as having a bias against particular skin tones. People are what they are - love it or hate it, there's little you can do to change it. You can fight it, wage wars even, but until the day you die and far beyond it (at least as long as we remain human), you cannot change this fundamental truth. Some degree of acceptance, therefore, may simply be the wisest coarse.
Of course, some believe this irrelevant, arguing that we'll soon be a human-machine amalgamation, swiftly disposing of our previous biases as we transform into Nietzsche's übermensch and merge with machines. I maintain my skepticism - even in such a transhuman utopia, we might simply amplify our hatred of "the other" and deepen our divisions further.
Yet in wrestling with these questions of human nature and responsibility, I find myself repeatedly drawn to deeper, more haunting truths. No author has captured these truths quite like Dostoevsky, whose ideas seem to possess rather than merely inspire. Through his assertion that "every one is really responsible to all men for all men and for everything," he illuminates an unbearable truth - one I still resist. How can I be responsible for the man held captive by his corrupt government across the world, or for children I've never met suffering abuse? Yet whenever I question this burden, my mind floods with moments where I failed to act or speak when necessary. The social consequences of our collective silence, our failure to oppose injustice when we see it, echo through generations.
The burden Dostoevsky places on us is unbearable, yet my nature compels me to ask not what is comfortable or even bearable, but simply "what is true." And before you misunderstand my nature and assume I harbor delusions of grandeur - I assure you my openness and emotional sensitivity are far too high to sustain such delusions for more than an instant, if at all. I would not dare to claim I possess the truth, only that I seek it, even against my own best interest, at times.
So I circle back to my previous point: we may not like it, we may even despise it, but the wisest course might be to muster some form of acceptance of the way things are, some acceptance of "the other," some acceptance, even, of our enemy. Nietzsche would have you believe Christianity is merely a religion for milquetoast men without constitution, but to the Dostoevskian, it requires Herculean will to sustain something as inhuman as "love" for one's enemies.
This sentiment emerges repeatedly throughout history not out of misguided kindness or passivity, but because it is equally true as it is unnatural. There is an innate paradox in humanity, and as part of this contradiction, the more we resist it, the more we fight against it, the more it grips us and pulls us into a never-ending cycle of loathing and hatred. Yet within this paradox lies another truth - one that brings us to the very heart of belief itself.
## Wrestling with Truth
After that last segment, you might try to classify and dismiss me as a closet Christian. Candidly, I sometimes wonder that myself, having been profoundly influenced by Christian writers and thinkers (exluding overt theologians, if that's any indication of my nature) throughout my life. Part of this might just be my western cultural inheritance, but it's also rooted in my captivation, from a young age, with the actual story of Jesus.
I've been somewhat immune for some time from having any overtly religious sentiment foisted upon me for what I believe to be one simple fact: I actually read the Bible (willingly, without coercion) at a young age. It never ceases to be hilarious how essentially nobody who claims to follow the book seems to have any inkling of what it actually says or teaches. I truly believe that if they did, they might not be followers at all. Jesus himself, contrary to modern Christian doctrine of easy salvation, spoke to the rarity of his true followers with phrases like "if you left me, you never knew me" and "many are called, few will answer."
Yet Christians worldwide, seemingly unable to accept the profound and difficult truths presented earlier, somehow delusionally cling to a simpler notion of salvation - one they can digest and accept. That a simple word and prayer is enough, and no actual work or effort is required. It's certainly a comforting thought, but not one I subscribe to.
I don't self-describe as a Christian for two simple reasons: First, while I believe many of Jesus's teachings in the gospels are true and correct (if you can decipher their meaning, which is no trivial task), I disagree fundamentally with virtually every Christian sect in practice today. Second, I don't currently believe it's tenable to "know" whether God exists in reality. I think the whole business of "knowing for sure" either way is utter folly. Even the book itself teaches this, if you pay close attention - this is why the chief patriarch is named as one who "wrestles with God." One cannot wrestle with a concept they're fully satisfied to be true.
The proper tension in the question of God's existence lies in that uncomfortable space virtually none of us wish to occupy - the simple admission of ignorance: "I don't know." This perpetual wrestling with uncertainty isn't weakness - it's the natural state of honest truth-seeking. The paradox lies in knowing that the closer you get to truth, the more you understand how much you don't understand. The more certain you become of uncertainty itself.
It's a peculiar burden of the disagreeable truth-seeker: we're compelled to chase truth relentlessly while simultaneously accepting that complete certainty may be forever beyond our reach. We must somehow maintain the passion of the search while embracing the humility of perpetual uncertainty. This isn't relativism - there is only one Truth (big T). But our relationship with it is far more complex than most are willing to admit.
Take, again, the question of God's existence, for instance. Some days I'd like to believe it is so, and some days I certainly hope not, usually when I consider my inadequacies and myriad mistakes. Still, I don't think belief is as ignorant as most modern atheists would suggest, and I must acknowledge the profound contributions that men of faith have made to science, which many modern "scientific" types are happy to ignore, seemingly to strengthen their own position.
By now, perhaps you can see how isolating my experience of profound disagreeableness truly is. There is no sect I belong to, or even can belong to, whether concerning profound questions like "does God exist" or more mundane matters like "what school of engineering do you subscribe to?" As uncomfortable as it is, I can only feel honest with myself by answering such questions with "it depends." This is something most people in my life simply cannot accept. And if I'm being honest, it's for this reason that I sometimes try to hide this aspect of my nature, placing myself in one camp some days, and another others.
Some might accuse me of being a simple fraud or charlatan, and while that might make things simpler, it isn't quite right either. You see, I have some very real and demonstrable engineering and scientific ability, even without formal credentials. Through a love of reading and just plain raw experience I also have some profound understandings of life, philosophy, religion, and psychology, though I dwell in none of the myriad camps permanently. I certainly don't prescribe my way of being to anyone else - in fact, I would caution against it. If you can imagine for a moment the type of experience I'm describing, you'll understand why it's quite uncomfortable. Yet any and all attempts to run or hide from it fail, sometimes spectacularly so. The conclusion, by now, is quite apparent: Love it or hate it, this is who I really am.
## The Purpose of Dissent
What's the point of this self-reflective journey? To illustrate the vital importance that individuals like myself play in this world. For most of my life, I found myself rather repugnant, unable to exist long in any one state - never content to be a true believer, yet never comfortable being a total denier. Never fully subscribing to rabid belief in human progressivism and technological advancement, yet never able to fully deny its contributions to the human condition.
It wasn't until I came to realize the purpose of being me, and others like me: Someone must occupy this uncomfortable space in between, precisely because the majority of humanity simply will not. This isn't about maintaining perfect centrism - rather, it's about the willingness to lean one direction today, explore another camp tomorrow, and perhaps abandon the whole enterprise entirely in the future.
Some might call this wishy-washy or lacking discipline. And yes, there's some truth to that. But then again, I also possess the will to run 20+ miles or completely reimagine software deployment from its foundations, so it's not _just_ that. I must give myself some credit where the existing system - one that requires we exist in predetermined boxes - refuses to do so.
I don't write this for myself. As a fairly private individual, sharing this is quite unnerving. But I choose to face this discomfort for one simple reason: others like me exist, and they exist for good reason. Judging by my own unlikely journey to self-acceptance, they are likely suffering, confused, perhaps self-loathing, maybe even considering a way out. To them, I simply say: we need you!
Painful as it is, as much as the world would rather have you jump into this camp or that, if you did - if we all did - the entire enterprise would collapse. We need the disagreeable to remind us that nothing, absolutely nothing - not science, not religion, not philosophy - is beyond reproach in the final analysis. This has never been easy to accept, yet no matter which discipline you explore, honesty compels you to admit its truth.
As much as we delude ourselves into thinking we live in an enlightened age, we remain, like all humans before us, desperately uncomfortable with lingering doubts, hell-bent on jumping into the first camp that alleviates ambiguity. Whether it's vaccines, political ideology, or faith doesn't matter - just please, oh please, let us take a side so we don't have to live with not knowing.
This is precisely why individuals like ourselves are necessary, perhaps now more than ever. Society needs us to contend with the uncomfortable, because most simply refuse. We must speak our minds, even to that charismatic demagogue, even to that possessed acolyte of the "one true" religion, even at the cost of our own livelihood. The world needs this from us precisely because the majority will not, because they refuse, and while they dare not ask - indeed have no right to ask - they need us all the same.
And you will do it too, not because you want to, not because you particularly enjoy the constant difficulty it poses in your life, but because it is who you are. We just need you, my fellow disagreeable types out there, to understand more deeply and live more truly to your nature, for all our sakes.
## A Prayer for the Defenders
As I said earlier, I believe this is true, to some extent, for all personalities in the world. We need all of these perspectives to reinforce what would otherwise be a rather brittle and shallow existence. Yet as I write this - and I realize this now more clearly than when I began - this piece has become both a prayer for my fellow disagreeables and a reminder to myself of why we must persist in the face of a world that would much rather we simply did not.
We may not like it, we may even mostly despise it, but we are all needed. It's just that the disagreeables have their moment, and it is now - now or never. As techno-feudalism looms and conformity tightens its grip through coordinated debanking campaigns, manipulative codes of conduct, and the creeping implementation of "social credit" systems masquerading as progress, we stand at a precipice unlike any in human history. The place where we might go from here if we don't encourage our more contrarian friends to stand for us, knowing we are too cowardly to stand for ourselves, is not a place I care to visit in my lifetime. I feel quite content having read enough about such places to never need to experience them firsthand.
But if the disagreeable do not defend us now, that is precisely where we are heading - of that I am deathly certain, as much as I wish I weren't. My journey from self-loathing to self-understanding has taught me this: what seems like a curse - this inability to conform, this compulsion to question everything - may be society's last defense against the crushing weight of conformity that continues to slowly tighten its grip. The very traits that make us difficult, that make us challenge and resist, are the traits that keep the doors of freedom propped open.
Don't be fooled into thinking this a partisan issue, either. History shows us the truth - the very same loyalists who assisted Stalin were the _first_ to be lined up and shot when the "revolution" was finally accomplished. This isn't about left or right; it's about the fundamental nature of human freedom and those rare few willing to defend it.
And I'll be clear - I am pessimistic. The most probable outcome is that we will, yet again, descend into utter enslavement - not just of body, but of mind. Perhaps more deeply and fully than at any time in the past, perhaps never to return again. Yet still I myself, even if it is in vain, choose to remain true to my being, if just for once, in all my strengths and flaws, and I ask you - rather out of character for my usual way - to do the same. Even if the worst comes to pass, there is far less shame in remaining true to yourself and your convictions, in my estimation, at least.
So live, and live unashamedly, whether I am your political ally or your bitter enemy. I implore you to live true to yourself. Not this nonsense of "living your truth" - that's preposterous. There is only one Truth if the word truth is to have any meaning at all. And the only part of it that we might possess is the part that tells us we will never know what it is in its entirety, and for this very reason, you must endure.
For our sakes, and for yours. On this Christmas day, I salute you, my fellow disagreeables, on all sides, for if not for you, whether we know it or not, we'd all be in chains at this very moment. Thank you.

167
content/blog/nix-to-eos.md Normal file
View File

@@ -0,0 +1,167 @@
---
title: From Nix to Eos
description: From Darkness to Dawn in Store-Based Systems
taxonomies:
tags:
- nix
- ekala
- eos
- eka
- atom
author: Tim D
authorGithub: nrdxp
authorImage: https://avatars.githubusercontent.com/u/34083928?v=4
authorTwitter: nrdexp
date: "2024-12-04"
category: dev
---
This piece explores the evolution of store-based systems and our vision for their future. While I've aimed to make it accessible to those not intimately familiar with Nix, I assume some technical foundation—particularly an interest in software distribution at scale. If terms like "reproducible builds" or "supply chain security" don't pique your curiosity, what follows might feel rather academic. However, if you're intrigued by how we might tackle the growing complexity of software distribution while maintaining security and sanity, read on.
It's important to note that this post specifically outlines my personal plans and intended contributions to Ekala. There are several other significant [related efforts](https://github.com/ekala-project/ekapkgs-roadmap) already in progress, driven by our other founders and contributors, which complement and extend beyond what's discussed here.
## Reflections
I recently decided to take an extended vacation—a choice that might seem odd right after a major public announcement and development push. But this time was vital for introspection. During this pause, I stumbled upon a concept that, while humbling, is proving invaluable: "Thought Driven Development." The rule is simple yet profound: if you dont have the answer, dont rush to write the code. This approach has illuminated my path, emphasizing that realizing Ekalas potential requires a deep understanding of our origins and intentions, without drowning in unnecessary details.
For those of us whove long been enamored by Nix, myself included, its appeal lies in its groundbreaking formal rigor in software distribution. However, despite years spent working to make Nix and NixOS more accessible, I've been forced to confront some challenging truths about its current implementation. While Nix was a beacon of innovation, breaking long-standing paradigms for noble reasons, it hasnt fully lived up to its promise.
In addition to these technical hurdles, the Nix project hasnt been free from political drama. Without saying too much, it's like a tangled web of intrigue, where many key figures in Ekala's foundation—and even some on its fringes—were banned from Nix for life. The "reasons" remain elusive, adding a layer of complexity to our journey. Although I must tread lightly here, it would be a disservice to you, dear reader, not to acknowledge this significant aspect, which has undeniably shaped our path forward. Suffice it to say, I felt the "weaponizations" of the CoC to be sufficiently bothersome as to inspire an alternative, much simpler [Hacker Code of Ethics](https://ethics.codes), which we have adopted in Ekala.
## The Misunderstood Promise of Nix
Nix, at first glance, presents itself as a tool to be progressively embraced—start by using it as a package manager on Ubuntu, and if it resonates, move on to the full NixOS experience. However, this approach is misleading. As a simple package manager replacement, Nix can be underwhelming. It's slower, largely due to evaluation issues well explore later, and its also complex and not immediately intuitive. The crux of this misunderstanding lies in how Nixs unique benefits are only fully realized when used declaratively and rigorously—essentially, pervasively.
Transitioning to NixOS after years with traditional Linux distributions can be a revelation, unlike merely using Nix as an `apt` alternative. Lets be clear: my intention isnt to criticize Nix unnecessarily. It opened up an entirely new landscape, and its understandable that there would be some stumbles in finding its footing. Yet, the current user experience feels unnecessarily apologetic, almost as if saying, "Dont worry, I wont try too hard to be different, Im just a straightforward package manager."
But heres the kicker—Nix isnt merely a package manager. It represents a paradigm shift in how we organize, build, distribute, and even integrate, test, and deploy code. Its innovations are on par with those of version control systems, particularly Git. In fact, Nix shares a profound similarity with Git. Just as Git manages changes across time by creating hashes dependent on the entire history, binding itself uniquely and unchangeably to that history, Nix does the same with software build environments. It assigns each dependency a unique hashed identity, with each hash building upon the previous ones, providing the same level of assurance we expect from our revision control systems, both in the build process and beyond.
To truly grasp the magnitude of the paradigm shift Nix offers, one must experience it in all its unapologetically different glory. Yet, paradoxically, Nix does little to position itself this way, both in its code and its narrative.
## The Brick Wall
Lets delve into Nix's current _de facto_ user experience in its most common use cases to understand why a bold initiative like Ekala, with its suite of proposed innovations and tools, is crucial. Ekala aims to elevate the world Nix introduced, aligning it with the broader software industry's standards. As someone who's both benefited from and been challenged by using Nix in production, I can tell you candidly that developers aren't rejecting Nix merely because it's "too different." When developers encounter Nixs genuine UX warts, it's easy to dismiss it as "too complex," but I've come to realize that this isnt the full story.
Consider this: does one need an intricate understanding of a commit object, how it relates to previous commits in history, or its connection to lower-level objects like trees or blobs, to perform basic `git add` or `git commit` operations? The answer is unequivocally no. Yet, when it comes to Nix "education," the focus is often on the complex inner workings of derivations and how to wield them. While its useful to know the term, expecting users to understand every detail shouldn't be necessary. However, in Nix's current UX, it often is, and that's the crux of the problem. Users are required to grapple with complexity that should be abstracted away in the majority of cases. We've been fooling ourselves for too long, and the real issue is surprisingly straightforward: simplifying the user experience with a familiar little abstraction — one that is embarrassingly pervasive in other contexts but oddly elusive in Nix's current approach.
We already possess an abstraction that encapsulates a point in a software's lifecycle: the version. For instance, if I want to build version 6.5 of a software project, I should be able to install it from nixpkgs. Okay, assuming I figure that out intuitively (which we probably shouldn't assume, but I'll concede for now), I might end up with version 6.7. But why? You might cleverly presume the solution is to use an older checkout of nixpkgs—good instincts—but how do you determine that? The answer isn't trivial, and now we've hit a significant hurdle right at the start, simply because we've overlooked an abstraction that, in any other software context, would be laughably amateur to omit.
Nix should, instead, know how to communicate in terms developers are already keenly familiar with. Specifically, it should know how to find all available versions of software, ideally without brute-forcing through the entire git history of a repository—especially when that repository's history is massive, bordering on world-record breaking (i.e. nixpkgs). This is where the atom format comes into play...
## The Atomic Universe
Having hit the version abstraction wall, we need a solution that fundamentally changes how we think about code distribution. I've written about the Atom elsewhere, but it deserves a full exploration here. Without diving into the contentious flakes saga that plagued Nix for years, Ill say this: we've been missing a tool that leverages Nix's backend innovations while abstracting complexity in a way that caters to contemporary developers.
The only point I will make about flakes is that they've delayed meaningful progress. They amounted to a conflated interface to a simple function—a change that could have been introduced without altering the UX—yet they absorbed nearly half a decade of iteration. In my humble opinion, this time was spent attempting to present Nix as a high-level, user-friendly tool, which it inherently is not.
Nix excels at low-level operations, ensuring all the bits and pieces to produce deterministic build environments, _et al_. It doesn't need to apologize for any of this or try to paint over it with inappropriate abstractions that misconstrue its nature. What Ekala aims to provide are tools that relieve Nix of user-facing concerns, allowing it to excel at what it does best.
Atoms represent a fundamental shift. Theyre not just bolt-on abstractions replicable in pure Nix code. While there is a Nix component, the core of an atom—if you'll indulge me—is a low-level code distribution format. It's aptly named to signify its nature: a small, self-contained piece of code from a larger repository, just as an atom is part of a larger molecular structure. In addition, atoms are purposefully meant to draw strict boundaries on certain critical meta-data, ensuring it remains static, and thus, trivially derivable, i.e. efficient.
Just as Git revolutionized version control by making complex history tracking feel natural, atoms aim to do the same for build environments. You don't need to understand internal tree structures to collaborate on code, and you shouldn't need to understand Nix's derivation mechanics to benefit from reproducible builds.
Technically, an atom in Git is an orphaned snapshot containing only its contents and a TOML manifest. Using a proper library: [gitoxide](https://github.com/GitoxideLabs/gitoxide), we add metadata that makes the atom's commit object reproducible and securely tied to its original commit. This is achieved by keeping timestamps constant at the Unix epoch and including the original commit hash in the header.
Verification is straightforward: compute the atom's tree-object and compare it with the claimed source commit's tree for that directory. If they match, the atom is authentic, and because its commit is reproducible, it remains inherently trustworthy indefinitely. In scenarios where full history access is unavailable, signed tags can be attributed. Trust the key, and you trust the atom. And keep in mind, re-verification from source is always available, when in doubt.
A Git ref in a custom prefix at refs/atoms/unicode-atom-id/1.0.0 then points to this "atomic commit", allowing us to query all available versions using Git's efficient ref querying facilities. Importantly, the query process does not require moving object data from client to server, ensuring efficiency and scalability.
This format gives us a decentralized code registry akin to those used by modern package managers, but one that fits perfectly into Nix's source-centric paradigm while providing a useful abstraction to minimize network code transfers and needless evaluations at runtime.
Every atom, additionally, has an "atomic number" or ID, if you prefer, derived from their unicode name and the root of its history allowing them to be distinguished efficiently from each other on the backend, even when working with hundreds or thousands of repositories and millions of atoms (we'll get there soon).
Each atom also has an "atomic number" or ID, derived from its Unicode name and the root of its history. This [innovative approach](https://github.com/GitoxideLabs/gitoxide/pull/1610) involves using the oldest parentless commit in a Git repository as a derived key for the hasher function applied to the Unicode name. This process generates a unique blake3 hash with a vast collision space, allowing atoms to be efficiently distinguished from one another on the backend, even when dealing with thousands of repositories and millions of atoms—a scale we aim to enabled explicitly from the outset.
The core format is implemented in [eka cli](https://github.com/ekala-project/eka). Enterprising Nixers could even publish and pull atoms today, albeit with some manual effort. But the atom is merely the cornerstone of the rest of the tools I am designing for Ekala. Leaving it there would be a disservice to our effort to evolve Nix beyond low-level derivation hacking.
While the atom format provides a robust foundation for code distribution and verification, it's only part of the solution. To fully realize Nix's potential, we need to address another fundamental challenge: how we organize and structure our configurations. This brings us to one of the most pervasive patterns in the Nix ecosystem—the module system—and why its current implementation poses significant challenges at scale.
## Unbounded Hell: Reducing Complexity in Order to Ascend
Even with the atom format establishing a robust foundation for distribution and verification, we must confront a significant challenge in Nix's ecosystem: its approach to configuration and modularity. The pervasive use of the NixOS module system—adopted everywhere from NixOS itself to home-manager, nix-darwin, and flake-parts—represents a pattern that's become problematic at scale.
The core issue isn't immediately obvious. On the surface, the module system appears to provide a structured approach to configuration with priority merge semantics and type checking. However, this abstraction comes at a considerable cost that becomes apparent in production environments.
First, there's the misleading nomenclature. The "module system" suggests modularity, but what it actually provides is a global namespace with configuration generation capabilities. While this might seem like a reasonable trade-off, implementing these features as a pure Nix library creates substantial overhead. The type checking mechanism, for instance, fundamentally conflicts with Nix's lazy evaluation model—it must eagerly traverse the entire module tree to validate option declarations.
The complexity cost is equally concerning. The system's computational bounds are effectively impossible to determine with precision. While one might approximate the complexity through careful analysis, the results would likely fail any reasonable efficiency criterion. This unrestricted nature becomes particularly problematic as configurations grow, leading to unexpected evaluation bottlenecks and maintenance challenges, such as the infamous impenetrable trace, which has become, unfortunately, somewhat synonymous with the Nix language, even though it is typically derived from the module system's complexity, not necessarily the language.
What makes this particularly insidious is how the module system has become the _de facto_ standard for configuration in the Nix ecosystem, creating an unbounded cataclysm with no meaningful alternatives. Even seasoned Nix developers with extraordinary debugging skills and monk-like patience find themselves trapped in an endless cycle—documenting meta-wrappers around functionality that should have been properly documented upstream. This is especially evident in nixpkgs, one of the largest collaborative software efforts in existence. Despite its impressive scale, a significant portion of development effort is consumed by maintaining complex module semantics that fundamentally shouldn't exist.
What we need instead is a true module system—one that provides:
- Clear semantic boundaries between components
- Predictable evaluation characteristics
- First-class support for proper information hiding
- Some level of familiarity from other language paradigms that work well
This is exactly what the Atom module system endeavors to provide. Out of the gate, performance with the Atom system is impressive. There is no "breaking of laziness" to evaluate complex type declarations, so evaluating through an atom, even without thousands of individuals modules, remains super performant, since you will only evaluate what you need. More importantly though, Atom's provide a saner, and cheaper definition of purity than the existing stable, not stable mess that is flakes. A flake, by design, copies everything you evaluate into the /nix/store, even if it exists on disk, and it does so eagerly, before evaluation even begins, breaking one of Nix's premier features: its lazy evaluation. This is done in an effort to preserve "purity", or so it would have you believe. But wait a second... Isn't Nix, itself, already a sandboxing tool? Why do we need these convoluted semantics and additional complexity leading to a whole-ass [Virtual-Filesystem (VFS) layer](https://github.com/NixOS/nix/pull/6530) that has been in development for years, trying to solve the costs this model introduces? If Nix wanted to enforce purity at evaluation time, couldn't it simply sandbox the process, as it does at build time? We will delve into this a bit more in a later section, but its worth asking.
Even if you disagree, this is far from the only meaningful boundary Atom introduces. A module in an atom, like a true module should, can only see into its existing scope, even on the file-system level. You see, Atom does copy Nix expressions into the Nix store, just like flakes, but it does so lazily, by virtue of Nix's inherent design. For example, if you need to reference a file inside an atom module, you can do so by referencing it from the modules self-reference: `"${mod}/path-to-file-in-module"`. Only when this file is actually read will the contents of the module directory, not including any submodules or nix files, be copied into the Nix store. If you try to reference the file by relative path, you'll get an error, since the Nix expression itself was copied directly into the Nix store lazily as well, the file doesn't exist relative to its location in it; it must be referenced using the modules systems explicitly outline semantics, or not at all.
This approach stands in stark contrast to flakes' eager world-copying strategy, which necessitated years of ongoing VFS development to mitigate its costs. By intelligently leveraging Nix's natural laziness, we achieve the same goals without complex VFS machinery. Furthermore, Atoms enforce stricter boundaries than existing Nix organizational systems: the `import` keyword is explicitly forbidden within modules. Instead of allowing arbitrary imports from unknown locations, all code references must flow through the module system itself. This constraint enables future tooling to perform static analysis efficiently, extracting useful information about the code without evaluation, in stark contrast to the current landscape.
So how do references work in the atom system? If you're up to speed with any modern programming language's module system, you might find it familiar. Similar to Rust's crate system, atoms have a top-level reference `atom` from which every other public member can be referenced, which are denoted by starting with a capital letter. External dependencies are also currently available through here, though this API remains experimental.
If you need to access private members, you can, through the `pre` scope, which is a reference to the parent module, or `pre.pre` for the grandparent, etc. Anything referenced from `pre` has access to all the private members exported by that module. There is also a recursive reference to the current module: `mod`, and finally, an actual proper scope for a true standard library for the Nix language: the `std` scope. Now if you have used the nix module system before, you might think you have to declare these explicitly as inputs to some kind of functional prototype for every single module.
Fortunately, no such boilerplate exists. All of these scopes are simply available within the module. This is more important than just providing convenience and a more familiar semantic from other languages, it also allows us to declare our modules and members as the final data structures that we intend them to represent, rather than a prototype of the data, to be constructed after passing arguments to a function. This makes code inside an Atom module more introspective by default. Where one might open a Nix REPL and explore their code full of legacy Nix modules, only to hit an opaque wall when hitting one of these prototypes, which will require a full evaluation to resolve, you can simply continue happily grepping through your code, allowing consumers to more intuitively discern what a library exports, or an atom contains, etc, etc.
While these features are available today with some effort (see the [README](https://github.com/ekala-project/atom?tab=readme-ov-file)), our ultimate goal is to provide a cohesive system that's intuitively familiar to developers, regardless of their Nix experience. To bridge the gap between our higher-level Atomic module system and the lower-level atom format, we turn to our gateway into the Ekala ecosystem: the `eka` CLI.
## The Proper Level of Abstraction
Eka, our CLI frontend, predates even the Atom format it now implements. Rather than following flakes' path of bolting a higher-level interface onto Nix, we approached the problem from first principles: what should a proper interface into a Nix-centric universe look like? This exploration led us to both the Atom format's innovations and several other concepts still in development.
At its core, `eka` serves as an atomic frontend to a backend service that handles evaluations, builds, and higher-level concerns like deployments and test environments. By decoupling from low-level derivation details, it focuses entirely on providing a clean, intuitive interface to the powerful world of Nix expressions. This design philosophy manifests in several key principles:
1. Zero-evaluation querying: `eka` should never require Nix evaluation to read basic package information. Versions, dependencies, descriptions, and metadata should all be statically accessible. At most, it needs to know an atom's location, with efficient backend querying capabilities for discovering atoms in the wild.
2. Static pipeline construction: Building task pipelines, like CI architecture matrices, should be possible without evaluation. These specifications should be readable directly from static manifests, allowing the backend to efficiently schedule work on appropriate machines.
3. Improved addressing: While flakes introduced useful URI shorthand, we've expanded this concept with Atom URIs. Unlike flakes' hard-coded shortcuts, Atom URIs are configurable, allowing patterns like `work:repo::my-atom@^1`. Crucially, these always expand to full URLs in the final output, ensuring universal understanding while maintaining convenience.
To support this ambitious scope, we plan to implement a language-agnostic plugin system for `eka`. While the core remains focused on efficient atomic operations and basic backend communication, plugins will extend functionality through a well-defined API surface. This extensibility will become increasingly important as `eka` evolves to help avoid bloat and complexity in the core codebase.
The ultimate vision for `eka` users is efficient querying of packages, deployment manifests, and configurations across their organization and the open-source landscape—all without upfront Nix evaluation. It should optimize away unnecessary evaluations and builds when artifacts exist in cache, in concert with the backend, proceeding directly to fetching. If `eka` ever needs to perform evaluation for value generation, we've strayed from our design goals.
While significant work remains, our roadmap is tracked in the [README](https://github.com/ekala-project/eka?tab=readme-ov-file). We're approaching a crucial milestone with the Atom lock format's finalization. Once complete, users will be able to create, link, and depend on Atoms with familiar commands like `eka add my-repo::my-dep@^1.0`—no esoteric knowledge required.
`eka` represents more than just a CLI tool—it's the gateway into a new paradigm of store-based system interaction. Its role as a frontend is deliberate, allowing it to focus on providing an intuitive interface while delegating complex evaluations and builds to a more sophisticated backend. This separation of concerns brings us to perhaps our most ambitious vision within the Ekala ecosystem: the Eos API & scheduler.
## A New Dawn
While we've introduced atoms and their immediate benefits, we've only scratched the surface of how they might revolutionize task distribution across machines. Remember our principle: thought first.
The atom format isn't just a cornerstone of Ekala by coincidence. While its frontend efficiency gains through `eka` are valuable, its true potential emerges when we consider the backend it enables: the Eos HTTP API.
Think beyond mere Nix builds—which are already cumbersome to manage. Consider evaluations, integrations, deployments, and operational workflows common to Nix environments. Our vision detaches user machines from costly operations, efficiently distributing evaluations, builds, and tasks across a network. This approach treats Nix's operational use cases as first-class concerns, designed from first principles.
Eos isn't just about distribution—it's about trust. In an era of increasing supply chain attacks, every evaluation, every build, and every artifact must be cryptographically verifiable. By leveraging atoms' inherent verification properties and Nix's reproducible builds, Eos provides end-to-end supply chain integrity without compromising on performance or usability.
Why an API? As we progress through the 21st century, well-designed APIs have become fundamental to system architecture. But bringing Nix into the modern era is just the start—we aim to push its boundaries. Nix's unique, idempotent properties cannot be fully leveraged without purpose-built tooling and careful abstraction.
The Eos HTTP API isn't an afterthought or bolt-on solution like many current Nix-based CI systems. It's fundamental to Ekala's design, crafted to leverage the atom format's advantages while remaining unopinionated about higher-level concerns.
Although this vision is compelling, transparency requires acknowledging that Eos remains our most theoretical component. We're developing a comprehensive whitepaper to specify its details upfront, avoiding costly iterations in code. Our approach is intentionally iterative, beginning with the cornerstone components and building thoughtfully from there.
Crucially, Eos represents the spark that ignited the entire Ekala effort. It began with a simple question: "What would the perfect Nix derivation distribution mechanism look like?" The answer—a modern API serving a cleanly abstracted, user-centric client—led us to develop the Atom format and its supporting ecosystem.
## The Road Ahead
Before diving deeper into Eos, let's reinforce a crucial point about atoms' role in our architecture. We've established why atoms bridge the gap between low-level Nix derivations and higher-level concepts like repositories and versions. This bridge is fundamental to Eos, which relies on atoms' globally unique identities. Each atom's cryptographic identity, determined by just two elements—the repository's root commit hash and the manifest's Unicode name—provides a stable reference point unlike frequently changing derivations.
This identity system creates powerful possibilities. Need to mark a fundamental code change? Simply rename the atom. Moving to a new repository? The atom automatically gains a distinct identity. These IDs serve as efficient anchor points for Eos, enabling quick curation without centralization or expensive scanning. While `eka` can directly query atoms and versions within known repositories, Eos can track atoms it encounters across the entire ecosystem, providing location information on demand.
But atoms are just the beginning. Anyone who's worked with Nix's remote build capabilities—whether the legacy system or the newer experimental UI—knows its limitations in distributing work across machines. Eos aims to solve this through intelligent request handling. For public repositories, Eos can fetch directly using its optimized network. For private code, atoms' efficient transfer format (remember: just git trees of existing objects) enables smart, deduplication-aware transfers.
Think of Eos as a network of machines that you can organize however you want—hide them in your private network, expose them to the world, or mix and match. The beauty is in its flexibility: you're in control of how your builds and evaluations flow through your infrastructure. At its core, Eos is a Nix-centric scheduler handling evaluations, builds, and higher-level tasks like testing and deployment. For example, we're exploring Raft for high-consistency queue synchronization across machines, ensuring resilience against outages.
While the distributed design is complex, the goal is straightforward: leverage Nix's unique properties and now, the Atom format, to eliminate redundant work across your network. If one machine evaluates but doesn't build, schedule the derivations elsewhere. If something's built but not cached, ensure it reaches long-term storage en route to users. Everything should be garbage-collectable—imagine keeping major releases permanently while cycling out development builds, etc.
Eos isn't meant to be monolithic. We're planning to integrate components from [Tvix](https://tvix.dev), which reimagines Nix with modern architecture, to simplify the effort significantly. At its simplest, Eos is a distributed HTTP gateway receiving requests from frontends like `eka`, scheduling them across known machines. While the complexity is significant, it's worthwhile only if it fully exploits Nix's idempotent properties for pipeline optimization from the foundation.
Our vision extends beyond just managing packages—we're building a framework where security, reproducibility, and sanity are fundamental properties, not afterthoughts. In an era of increasing supply chain attacks, Nix & Ekala's combination of cryptographic verification, reproducible builds, and distributed intelligence positions us to tackle these challenges head-on. We're prioritizing integration with existing standards like SBoM, ensuring that every input is tracked, and every output is independently verifiable.
While a complete Eos scheduler isn't imminent, our journey has already yielded valuable innovations like the Atom format, module system, and the `eka` CLI. Our commitment to "Thought Driven Development" guides us in building tools that respect both users' freedom and intelligence, providing power without sacrificing transparency or independence.
We invite you to be part of this evolution. Whether you're a seasoned Nix veteran or just curious about the future of software distribution, join us on [Discord](https://discord.gg/AeZkaQqw) or explore our [GitHub Organization](https://github.com/ekala-project). Together, we can build a future where store-based systems are not just theoretically elegant, but practically transformative for developers everywhere.

183
content/blog/std-action.md Normal file
View File

@@ -0,0 +1,183 @@
---
title: Standard Action
description: Do it once, do it right.
taxonomies:
tags:
- std
- nix
- devops
- github actions
author: Tim D
authorGithub: nrdxp
authorImage: https://avatars.githubusercontent.com/u/34083928?v=4
authorTwitter: nrdxp52262
date: "2022-12-09"
category: dev
---
## CI Should be Simple
As promised in the [last post](./std), I'd like to expand a bit more on what we've
been working on recently concerning Nix & Standard in CI.
At work, our current GH action setup is rather _ad hoc_, and the challenge of optimizing that path
around Nixs strengths lay largely untapped for nearly a year now. Standard has helped somewhat
to get things organized, but there has been a ton of room for improvement in the way tasks are
scheduled and executed in CI.
[Standard Action][action] is our answer. We have taken the last several months of brainstorming
off and on as time allows, experimenting to find a path that is versatile enough to be useful
in the general case, yet powerful enough for organizations who need extra capacity. So without
any further stalling, let's get into it!
## The Gist
The goal is simple, we want a CI system that only does work once and shares the result from there.
If it has been built or evaled before, then we want to share the results from the previous run
rather than start from scratch.
It is also useful to have some kind of metadata about our actions, which we can use to build
matrices of task runners to accomplish our goals. This also allows us to schedule builds on
multiple OS trivially, for example.
Task runners shouldn't have to care about Nix evaluation at all, they should just be able to get
to work doing whatever they need to do. If they have access to already reified derivations, they
can do that.
So how can we accomplish this? Isolate the evaluation to its own dedicated "discovery" phase, and
share the resulting /nix/store and a json list describing each task and its target derivations.
From there it's just a matter of opimizing the details based on your usecase, and to that end we
have a few optional inputs for things like caching and remote building, if you are so inclined.
But you can do everything straight on the runner too, if you just need the basics.
## How it Works
Talking is fine, but code is better. To that end, feel free to take a look at my own personal CI
for my NixOS system and related packages: [nrdxp/nrdos/ci.yml][nrdos].
What is actually evaluated during the discovery phase is determined directly in the
[flake.nix][ci-api].
I am not doing anything fancy here at the moment, just some basic package builds, but that is
enough to illustrate what's happening. You can get a quick visual by look at the summary of
a given run: [nrdxp/nrdos#3644114900](https://github.com/nrdxp/nrdos/actions/runs/3644114900).
You could have any number of matrices here, one for publishing OCI images, one for publishing
documentation, one for running deployments against a target environment, etc, etc.
Notice in this particular example that CI exited in 2 minutes. That's because everything
represented by these builds is already cached in the specified action input `cache`, so no work is
required, we simply report that the artifacts already exist and exit quickly.
There is a run phase that typically starts after this build step which runs the Standard action,
but since the "build" actions only duty is building, it is also skipped here.
This is partially enabled by use of the GH action cache. The cache key is set using the following
format: [divnix/std-action/discover/action.yml#key][key]. Coupled with the guarantees nix already
gives us, this is enough to ensure the evaluation will only be used on runners using a matching OS,
on a matching architecture and the exact revision of the current run.
This is critical for runners to ensure they get an exact cache hit on start, that way they pick
up where the discovery job left off and begin their build work immediately, acting directly
on their target derivation file instead of doing any more evaluation.
## Caching & Remote Builds
Caching is also a first class citizen, and even in the event that a given task fails (even
discovery itself), any of its nix dependencies built during the process leading up to that failure
will be cached, making sure no nix build _or_ evaluation is ever repeated. The user doesn't have
to set a cache, but if they do, they can be rest assured their results will be well cached, we
make a point to cache the entire build time closure, and not just the runtime closure, which is
important for active developement in projects using a shared cache.
The builds themselves can also be handed off to a more powerful dedicated remote builder. The
action handles remote builds using the newer and more efficient remote store build API, and when
coupled with a special purpose service such as [nixbuild.net](https://nixbuild.net), which your
author is already doing, it becomes incredibly powerful.
To get started, you can run all your builds directly on the action runner, and if that becomes
a burden, there is a solid path available if and when you need to split out your build phase to a
dedicated build farm.
## Import from What?
This next part is a bit of an aside, so feel free to skip, but the process outlined above just so
happened to solve an otherwise expensive problem for us at work, outlining how thinking through
these problems carefully has helped us improve our process.
IOG in general is a bit unique in the Nix community as one of the few heavy users of Nixs IFD
feature via our [haskell.nix][haskell] project. For those unaware, IFD stands for
"import from derivation" and happens any time the contents of some file from one derivations output
path is read into another during evaluation, say to read a lock file and generate fetch actions.
This gives us great power, but comes at a cost, since the evaluator has to stop and build the
referenced path if it does not already exist in order to be able to read from it.
For this reason, this feature is banned from inclusion in nixpkgs, and so the tooling used there
(Hydra, _et al._) is not necessarily a good fit for projects that do make use of IFD to some extent.
So what can be done? Many folks would love to improve the performance of the evaluator itself, your
author included. The current Nix evaluator is single threaded, so there is plenty of room for
splitting this burden across threads, and especially in the case of IFD, it could theoretically
speed things up a great deal.
However, improving the evaluator performance itself is actually a bit of a red herring as far as
we are concerned here. What we really want to ensure is that we never pay the cost of any given Nix
workload more than once, no matter how long it takes. Then we can ensure we are only ever
building on what has already been done; an additive process if you will. Without careful
consideration of this principle beforehand, even a well optimized evaluator would be wasting cycles
doing the same evals over and over. There is the nix flake evalulation cache, but it comes with
a few [caveats][4279] on its own and so doesn't currently solve our problem either.
To give you some numbers, to run a fresh eval of my current project at work takes 35 minutes from a
clean /nix/store, but with a popullated /nix/store from a previous run it takes only 2.5 minutes.
Some of the savings is eaten up by data transfer and compression, but the net savings are still
massive.
I have already begun brainstorming ways we could elimnate that transfer cost entirely by introducing
an optional, dedicated [evaluation store](https://github.com/divnix/std-action/issues/10) for those
who would benefit from it. With that, there is no transfer cost at all during discovery, and the
individual task runners only have to pull the derivations for their particular task, instead of the
entire /nix/store produced by discovery, saving a ton of time in our case.
Either way, this is a special case optimization, and for those who are content to stick with the
default of using the action cache to share evaluation results, it should more than suffice in the
majority of cases.
## Wrap Up
So essentially, we make due with what we have in terms of eval performance, focus on ensuring we
never do the same work twice, and if breakthroughs are made in the Nix evaluator upstream at some
point in the future, great, but we don't have to wait around for it, we can minimize our burden
right now by thinking smart. After all, we are not doing Nix evaluations just for the sake of it,
but to get meaningful work done, and doing new and interesting work is always better than repeating
old tasks because we failed to strategize correctly.
If we do ever need to migrate to a more complex CI system, these principles themeselves are all
encapsulated in a few fairly minimal shell scripts and could probably be ported to other
systems without incredible effort. Feel free to take a look at the source to see what's really
goin on: [divnix/std-action](https://github.com/divnix/std-action).
There are some places where we could use some [help][7437] from [upstream][2946], but even then, the
process is efficient enough to be a massive improvement, both for my own personal setup, and for
work.
As I mentioned in the previous post though, Standard isn't just about convenience or performance,
but arguable the most important aspect is to assist us in being _thorough_. To ensure all
our tasks are run, all our artifacts are cached and all our images are published is no small feat
without something like Standard to help us automate away the tedium, and thank goodness for that.
For comments or questions, please feel free to drop by the official Standard [Matrix Room][matrix]
as well to track progress as it comes in. Until next time...
[action]: https://github.com/divnix/std-action
[haskell]: https://github.com/input-output-hk/haskell.nix
[nrdos]: https://github.com/nrdxp/nrdos/blob/master/.github/workflows/ci.yml
[key]: https://github.com/divnix/std-action/blob/6ed23356cab30bd5c1d957d45404c2accb70e4bd/discover/action.yml#L37
[7437]: https://github.com/NixOS/nix/issues/7437
[3946]: https://github.com/NixOS/nix/issues/3946#issuecomment-1344612074
[4279]: https://github.com/NixOS/nix/issues/4279#issuecomment-1343723345
[matrix]: https://matrix.to/#/#std-nix:matrix.org
[ci-api]: https://github.com/nrdxp/nrdos/blob/66149ed7fdb4d4d282cfe798c138cb1745bef008/flake.nix#L66-L68

218
content/blog/std.md Normal file
View File

@@ -0,0 +1,218 @@
---
title: From DevOS to Standard
description: Why we made Standard, and what it has done for us.
taxonomies:
tags:
- std
- nix
- devops
author: Tim D
authorGithub: nrdxp
authorImage: https://avatars.githubusercontent.com/u/34083928?v=4
authorTwitter: nrdxp52262
date: "2022-10-31"
category: dev
---
## Update: A Video is Worth 1000 Blogs
For those who would rather watch than read, a colleague of mine has whipped up a great video series
exploring Standard in depth, so drop by the [media secition](../media) for links.
## Two years later...
DevOS started as a fun project to try and get better with Nix and understand this weird new thing
called flakes. Since then and despite their warts, Nix flakes have experienced widespread use, and
rightfully so, as a mechanism for hermetically evaluating your system & packages that fully locks
your inputs and guarantees you some meaningful level of sanity over your artifacts.
Yet when I first released it, I never even imagined so many people would find DevOS useful, and I
have been truly humbled by all the support and contributions that came entirely spontaneously to the
project and ultmately culminated in the current version of [digga][digga], and the divnix org that
maintains it.
## Back to Basics
For whatever reason, it really feels like time to give a brief update of what has come of this
little community experiment, and I'm excited to hopefully clear up some apparent confusion, and
hopefully properly introduce to the world [Standard](https://github.com/divnix/std).
DevOS was never meant to be an end all be all, but rather a heavily experimental sketch while
I stumbled along to try and organize my Nix code more effectively. With Standard, we are able to
distill the wider experience of some of its contributors, as well as some new friends, and design
something a little more focused and hopefully less magical, while still eliminating a ton of
boilerplate. Offering both a lightly opinionated way to organize your code into logically typed
units, and a mechanism for defining "standard" actions over units of the same type.
Other languages make this simple by defining a module mechanism into the language where users are
freed from the shackles of decision overload by force, but Nix has no such advantage. Many people
hoped and even expected flakes to alleviate this burden, but other than the schema Nix expects
over its outputs, it does nothing to enforce how you can generate those outputs, or how to organize
the logical units of code & configuration that generate them.
## A Departure from Tradition
It is fair to say that the nixpkgs module system has become the sort of "goto" means of managing
configuration in the Nix community, and while this may be good at the top-level where a global
namespace is sometimes desirable, it doesn't really give us a generic means of sectioning off our
code to generate both configuration _and_ derivation outputs quickly.
In addition to that, the module system is fairly complex and is a bit difficult to anticate the
cost of ahead of time due to the fixed-point. The infamous "infinite traces" that can occur during
a Nix module evaluation almost never point to the actual place in your code where the error
originates, and often does even contain a single bit of code from the local repository in the trace.
Yet as the only real game in town, the module system has largely "de facto" dictated the nature
of how we organize our Nix code up til now. It lends itself to more of a "depth first" approach
where modules can recurse into other modules ad infinitum.
## A Simpler Structure
Standard, in contrast, tries to take an alternative "breadth first" approach, ecouraging code
organization closer to the project root. If true depth is called for, flakes using Standard can
compose gracefully with other flakes, whether they use Standard or not.
It is also entirely unopionated on what you output, there is nothing stopping you from simply
exporting NixOS modules themselves, for example, giving you a nice language level
compartmentalization strategy to help manager your NixOS, Home Manager or Nix Darwin configurations.
Advanced users may even write their own types, or even extend the officially supported ones. We
will expand more on this in a later post.
But in simple terms, why should we bother writing the same script logic over and over when we can be
guaranteed to recieve an output of a specific type, which guarantees any actions we define for the
type at large will work for us: be it deploying container images, publishing sites, running
deployments, or invoking tests & builds.
We can ensure that each image, site, or deployment is tested, built, deployed and published in
a sane and well-defined way, universally. In this way, Standard is meant to not only be convenient,
but comprehensive, which is an important property to maintain when codebases grow to non-trivial
size.
There is also no fixed-point so, anecdotably, I have yet to hit an eval error in Standard based
projects that I couldn't quickly track down; try saying that about the module system.
## A CLI for productivity
The Nix cli can sometimes feel a little opaque and low-level. It isn't always the best interface
to explain and explore what we can actually _do_ with a given project. To address this issue in
a minimal and clean way, we package a small go based cli/tui combo to quickly answer exactly this
question, "What can I do with this project?".
This interface is entirely optional, but also highly useful and really rather trivial thanks to a
predicatable structure and well typed outputs given to us in the Nix code. The schema for anything
you can do follows the same pattern: "std //$cell/$block/$target:$action". Here the "cell" is the
highest level "unit", or collection of "blocks", which are well-typed attribute sets of "targets"
sharing a colleciton of common "actions" which can be performed over them.
### At a Glance
The TUI is invaluable for quickly getting up to speed with what's available:
```console
┌────────────────────────────────────────────────────────────────────────────────┐┌───────────────────────────────────┐
│| Target ││ Actions │
│ ││ │
│ 176 items │││ build │
│ │││ build this target │
│ //automation/packages/retesteth ││ │
│ testeth via RPC. Test run, generation by t8ntool protocol ││ run │
│ ││ exec this target │
││ //automation/jobs/cardano-db-sync ││ │
││ Run a local cardano-db-sync against our testnet ││ │
│ ││ │
│ //automation/jobs/cardano-node ││ │
│ Run a local cardano-node against our testnet ││ │
│ ││ │
```
## A Concise Show & Tell
The central component of Standard is the cell block API. The heirarchy is "cell"→"block", where
we defined the individual block types and names directly in the flake.nix.
The function calls in the "cellBlocks" list below are the way in which we determine which "actions"
can be run over the contents of the given block.
```nix
# flake.nix
{
inputs.std.url = "github:divnix/std";
outputs = inputs: inputs.std.growOn {
inherit inputs;
systems = ["x86_64-linux"];
# Every file in here should be a directory, that's your "cell"
cellsFrom = ./nix;
# block API declaration
cellBlocks = [
(std.functions "lib")
(std.installables "packages")
(std.devshells "devshells")
];
};
}
# ./nix/dev/packages.nix
# nix build .#$system.dev.packages.project
# std //dev/packages/project:build
{
inputs, # flake inputs with the `system` abstracted, but still exposed when required
cell # reference to access other blocks in this cell
}: let
inherit (inputs.nixpkgs) pkgs;
in
{
project = pkgs.stdenv.mkDerivation {
# ...
};
}
# ./nix/automation/devshells/default.nix
# nix develop .#$system.dev.devshells.dev
# std //automation/devshells/dev:enter
{
inputs,
cell
}: let
inherit (inputs) nixpkgs std;
inherit (nixpkgs) pkgs;
# a reference to other cells in the project
inherit (inputs.cells) packages;
in
{
dev = std.mkShell { packages = [packages.project]; };
}
```
## Encouraging Cooperation
Standard has also given us a useful mechanism for contributing back to upstream where it makes
sense. We are all about maintaining well-defined boundaries, and we don't want to reimplement the
world if the problem would be better solved elsewhere. Work on Standard has already led to several
useful contributions to both nixpkgs and even a few in nix proper, as well as some in tangentially
related codebases, such as github actions and go libraries.
One very exciting example of this cooperation is the effort we've expended integrating
[nix2container][n2c] with Standard. The work has given us insights and position to begin defining an
officially supported specification for [OCI images][oci] built and run from Nix store paths, which
is something that would be a huge win for developers everywhere!
We believe interoperability with existing standards is how Nix can ultimately cement itself into
the mainstream, and in a way that is unoffensive and purely additive.
## CI simplified
Instead of making this a mega post, I'll just leave this as a bit of a teaser for a follow-up post
which will explore our recent efforts to bring the benefits Standard to GitHub Actions _a la_
[std-action][action]. The target is a Nix CI system that avoids ever doing the same work more than
once, whether its evaluating or building, and versatile enough to work from a single user project
all the way up to a large organization's monorepo. Stay tuned...
[digga]: https://github.com/divnix/digga
[nosys]: https://github.com/divnix/nosys
[action]: https://github.com/divnix/std-action
[grow]: https://std.divnix.com/guides/growing-cells.html
[harvest]: https://github.com/divnix/std/blob/main/src/harvest.nix
[n2c]: https://github.com/nlewo/nix2container
[oci]: https://github.com/opencontainers/image-spec/issues/922

View File

@@ -0,0 +1,5 @@
+++
title = "Projects"
sort_by = "weight"
template = "cards.html"
+++

9
content/projects/atom.md Normal file
View File

@@ -0,0 +1,9 @@
+++
title = "atom"
description = "A deterministic source packaging format built on Git's object model."
weight = 0
[extra]
link_to = "https://github.com/ekala-project/atom"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "atomix"
description = "A minimal, performant module system for Nix code."
weight = 0
[extra]
link_to = "https://github.com/ekala-project/atom/tree/master/atom-nix"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "bitte"
description = "Nix Ops for Terraform, Consul, Vault, Nomad."
weight = 3
[extra]
link_to = "https://github.com/input-output-hk/bitte"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "cfdyndns"
description = "CloudFlare Dynamic DNS Client."
weight = 2
[extra]
link_to = "https://github.com/nrdxp/cfdyndns"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "digga"
description = "A Nix flake utility library for managing nixos, hm and devshells"
weight = 3
[extra]
link_to = "https://digga.divnix.com/"
+++

9
content/projects/eka.md Normal file
View File

@@ -0,0 +1,9 @@
+++
title = "eka"
description = "An atomic, plugin-based CLI frontend to the Eos API."
weight = 0
[extra]
link_to = "https://github.com/ekala-project/eka"
+++

9
content/projects/eos.md Normal file
View File

@@ -0,0 +1,9 @@
+++
title = "eos"
description = "A work-in-progress distributed HTTP scheduler designed for store-driven build systems."
weight = 0
[extra]
link_to = "https://github.com/ekala-project"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "hackers-ethic"
description = "First, hinder no thought: A Code of Ethics for Digital Freedom."
weight = 0
[extra]
link_to = "https://github.com/EthicsCodes/hackers-ethic"
+++

9
content/projects/home.md Normal file
View File

@@ -0,0 +1,9 @@
+++
title = "home"
description = "Ekala based dotfiles leveraging Nix and home-manager."
weight = 1
[extra]
link_to = "https://github.com/nrdxp/home"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "nixpkgs"
description = "ex-maintainer & NixOS release manager."
weight = 2
[extra]
link_to = "https://github.com/NixOS/nixpkgs"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "nosys"
description = "A hassle free Nix flake system handler library."
weight = 1
[extra]
link_to = "https://github.com/divnix/nosys"
+++

9
content/projects/pc.md Normal file
View File

@@ -0,0 +1,9 @@
+++
title = "partnerchain"
description = "A Substrate-based blockchain running on top of the Cardano network."
weight = 0
[extra]
link_to = "https://github.com/input-output-hk/partner-chains"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "RFC 175"
description = "A proposal to enhance moderation practices within the Nix community."
weight = 1
[extra]
link_to = "https://github.com/nrdxp/rfcs/blob/rfc-175/rfcs/0175-appeals-council.md"
+++

View File

@@ -0,0 +1,9 @@
+++
title = "standard"
description = "A Nix-centric devops framework."
weight = 1
[extra]
link_to = "https://std.divnix.com/"
+++