How to pronounce “Nixtamal”?
/nɪʃ.təˈmal/ or /ˈnɪkstəˌmɑːl/
The Nahuatl pronunciation is a bit better, but no qualms with an anglicized pronunciation — as folks are probably used to Nix as /ˈnɪks/.
Additionally, ·𐑯𐑦𐑖𐑑𐑩𐑥𐑭𐑤 is a valid spelling for en-Shaw.
What does the name mean?
: limed kernels of [maize] that is ready to be ground into masa
This loosens the maize’s outer hull, so it grinds easier & feeds you better — making it key to Mesoamerican food & health.
What is “freshness”?
The dual to fresh is stale. Is your input stale, let’s check the fresh-cmd to find out!
The word ‘latest’ wasn’t quite appropriate since it’s up the user to decide what fresh means for their context — which often isn’t ‘latest’.
The idea is: so long as it can be executed in your shell & returns a string that can be compared against, you can write honestly whatever you want.
This also pairs well with the command named nixtamal refresh (same as pkcon refresh) which runs the fresh-cmd which can short-circuit trying to prefetch your input to prevent wasteful network usage to download inputs or getting versions you don’t want (it’s your logic).
Can I get $X fetcher supported? How to add a fetcher?
We don’t believe it is in this project’s or the Commons’ interest to carve out special support for any fetchers (patches had to do this for fetchpatch2, but this was to enable a broader feature).
What you should do is submit a patchset to upstream Nixpkgs with your fetcher — ideally with JSON support like the others along with being added to pkgs.nix-prefetch-scripts.
This enables Nix community to have access to your prefetcher, not just Nixtamal which would be selfishly hoarding that capability.
Once it is merged, ping me & we can write or help you write the code needed.
Isn’t Nixpkgs big/overkill to do pinning?
Big, yes.
Overkill… it’s not as simple.
In order to get access to the fetchers, one would need to bootstrap their setup & build their own derivation.
This includes at minimum a C compiler & some form of coreutils… except you need more than this.
For instance if you want to use pkgs.fetchdarcs or even create your own, you need a Darcs binary, which requires the Haskell tooling (or all static binaries built for specific platforms).
When you take a look at how that would be set up, you would effectively be reimplementing Nixpkgs.
As such, the simpler answer is to say Nixpkgs is effectively required.
We can giggle about the size of that dependency, but the reality is 98% of projects using Nix are going to be using Nixpkgs anyhow.
Nixtamal does offer configuration when importing it which can be used to provide a minimal package set if needed.
Wishful thoughts would be that if Nixpkgs weren’t so monolithic, core features like compilers & fetchers could be separate from general packaging, but where these lines get drawn are neither clear nor simple.
Why OCaml as the main programming language?
Nix the programming language falls in the ML family & share a lot of syntax & conventions with other functional languages in the family. This makes the code more approachable to those familiar with Nix rather than needing to learn a very different language like Npins (Rust) or Yae (Go). The compile times are fast & the ecosystem is sufficient.
Why is the Nix closure so large?
There’s a bug in buildDunePackage’s behavior.
If this gets fixed (& hopefully will), the closure should be tiny.
Why KDL for the manifest language?
KDL has a syntax that isn’t a burden to use a configuration language — which helps explain why is very popular for its age.
JSON has no comments, one must watch commas, & is verbose with quotations.
TOML doesn’t nest well.
YAML is overly complex.
Nickel is great, but needs to be transformed into one of these others.
EDN was considered, but KDL felt better to write – especially the fresh-cmd syntax using $ & | as node names.
KDL has a schema language & a best-attempt schema.kdl should be shipped as well for future KDL tooling or LLM assistance.
Why even have a manifest file at all?
Have you ever needed to switch branches on an input to something stable or next to get code working?
The reality is your inputs evolve over time.
If you have ever used flake.nix’s inputs you probably had a good experience of say changing a Git input’s ?ref=… to point to a different branch/revision.
These CLI-only options don’t make this a nice experience—you either need to know the input key name to upsert it at the new reference point or you had to go spelunking in the lockfile (meaning you needed to be wary of JSON rules, but also wasn’t the lockfile supposed to be machine-generated… & why do these lockfiles have data unrelated to locking?).
The reality is that betting on text has been a long-standing practice for a reason.
In the long run, a manifest file makes changing reference points, adding patches, changing hash algorithms easier—as well as being easier for humans (& prehaps LLMs) to understand.
Why not KDL for the lockfile?
Nix does does not have a builtins.fromKDL (tho there has been some rumblings for formats.kdl in Nixpkgs).
As such your options are builtins.fromJSON or builtins.fromTOML & the Rust community’s obsession with TOML & that TOML needs to integrate JSON syntax anyway (just like YAML), it tips the scales in JSON’s favor.
Why is the project using Darcs & not Git?
Patch Theory is cool where patches commute (order doesn’t matter) which eliminates an entire set of merge conflicts present in snapshot-based VCSs. Pijul has the same theory behind it, is faster, & the new identity concept is what should have been (instead of name + email burned in the commits), but still lacks tooling. By using Darcs, we can also dogfood support for Nixtamal. Git uses an arcane, obtuse CLI commands that everyone knows is overly complicated & inconsistent, so why not try something fundamentally different?
However, do not be surprised if this project moves to Pijul if the tooling gets better since Darcs has been tested well enough, which would mean it would be Pijul’s turn next! Darcs is still worked on albeit it slower, but it also has some warts time has left on it. If there is a simple, self-hostable HTTP option for the server & a better story for rebase, toastal could switch.
Will the project make a Git mirror?
No. This defeats the purpose of dogfooding actual alternative VCSs. If you see a Git mirror, it’s 100% unofficial — if not illegitimate.
What is wrong with with forge-specific URL schemes or semantics?
Trends shift — what’s beloved now likely won’t be tomorrow. Rather than a minor convenience for what is popular now, a better design is to not give any place special privilege. This special privilege can make folks feel peer pressured into using the current trend platform (especially proprietary code forges for free software). We don’t want to foster this behavior — no, we want to see more self-hosting where a code base is truly owned by its makers, even if just for a backup mirror. This also lessens the maintenance burden as APIs shift & ‘mine too’-ing of someone’s unsupported platform.
How can I lock the fresh-cmd CLI tools?
This is on you & if this actually bothers you (since it might not, which we will get to).
In most cases, we would assume you will be using old stable tools — such as curl, jq, htmlq, git, coreutils — which don’t really change much in practice.
If you want, you can add these tools to your development shell & run nixtamal … from the shell.
You can also use something like nix run or nix-run in the fresh-cmd.
But is it so bad that this could be *gasp* ‘impure’?
What we mean by this is that the generated lockfile is still pure regardless after fresh-cmd is ran — which is the part that really matters here — & other than the maintainer, I wouldn’t expect contributors or downstream to need to touch your $NIXTAMAL_DIRECTORY.
Also did you ever think about nix flake update?
This is usually ran with the whimsy of the Nix version that is on your system meaning something equally ‘bad’ or ‘breaking’ could happen here.
Perhaps not even “equally” but potentially worse… as the nix binary is usually system-dependent & flakes.nix manifest file doesn’t have a version so its behavior could be interpretted differently depending you & your team’s versions being slightly out of sync.
What is wrong with Nix flakes?
Well this is a hairy topic that is sure to ruffle feathers… First, we should address that flakes aren’t just version pinning (Nixtamal’s current focus) but an enforced pattern of project layout & composition (or lack thereof depending on how you argue that). So let’s focus on the differences from the context of input pinning for now… Flakes might be “built in”, but it requires enabling an experimental flag; this might sound innocuous — especially given the prevelance of enabling it in the Nix community — but it has some serious downsides.
-
Flakes were originally designed for specific client’s needs which might not match your own
-
Since it’s experimental, its manifest is not versioned (meaning no
version = "1.0.0";inflake.nix) which ties the implementation to a version of Nix without a reasonable indicator to migrate or rollback to a version which leads can lead to unforeseen or catastrophic issues now that we are relying on the state of Nix executing it -
Flakes have been largely “stable” in so far as they are now a political topic meaning all proposals have been gridlocked on both bikeshedding & serious topics
-
“We’ll stablize soon™”, has been going on since like 2023
-
There have been multiple external efforts to stablize like Deteriminate Nix, Lix, & so forth, but now you are reliant on a specific fork for stability. Like how many projects claim to have a syntax that is “just Markdown”, but Markdown being underspecified for the task means each project maintains a fork of the syntax (sometimes disguised as “flavor”). Just like the collisions you probably noticed trying to use the same Markdown file in 2 separately platforms, these flake forks will always be incompatible despite sharing
flake.nix+flake.lockfiles -
Being built-in & not a third-party tool or pattern makes it harder to fork it reasonably & also stifles diversity & innovation as other options will be seen as more friction even if offering something simpler since an extra tool is involed
Outside of the experimental nature we have other issue that affect pinning:
-
We are limited to the built-in fetchers/fetch-tree & if you read the bug tracker for
nixthey don’t seem terribly interested in adding more fetcher support since they already have a lot to maintain — including the burden of trying to maintain these shorthands for specific hosts; if you wanted it to support Darcs, Pijul, Fossil, or the next big VCS, tough luck -
The URI scheme is nice until it isn’t — where expressing more complexity become a subjectively unreadably-long string of query parameters
-
The URI scheme doesn’t support mirrors
-
Overlays offer better compositionality than
input.*.followsbutfollowshas been treated more as the preferred option, with many not exposing an overlay -
Dependency explosion as all inputs are now added to your lock & fetched (not just the millons of separately-pinned
nixpkgsif anyone in the chain forgot or can’t usefollows, but inputs that are just used for ‘development’, or largely useless ones likeflake-utils’seachDefaultSystemwhich hides the complexity of the Flake API in a dangerous way since the whole point was to be explicit about declaring what systems are supported); by providing an overlay you can skip this -
Patching inputs are not intuitive & some really wild options like involving the module system have been proposed to try to deal with it
If you want to know about flakes schema criticisms, you can ask other places 🙂
But can I use Nixtamal with flakes?
You are welcome to use Nixtamal inside a flake.nix to get access to the the better experience that new Nix command setting offers (which sadly just work better with flakes instead of being more generic).
In the basic/easiest case, you don’t let flakes manage inputs anymore like:
{ # No longer any need for inputs # inputs = { }; outputs = { self }: let inputs = import ./nix/tamal { }; pkgs = import inputs.nixpkgs { system = "x86_64-linux"; config = { }; overlay = [ (import "${inputs.MY-PROJECT}/nix/overlay") ]; }; in { # … }; }
If you want that forAllSystem function, you can build your own with builtins, or the standalone nixpkgs.lib mirror, or add flake-utils — which now has a use case since we don’t have access to Nixpkgs or lib (but don’t blindly use eachDefaultSystem).
If this feels unergonomic, this is tradeoff for using flakes — which has pros & cons against classic Nix as you must define upfront your supported systems (& you must do it correctly, meaning you don’t declare systems you don’t support or haven’t tested as well as supporting all systems that are actually supported else users that could be supported have their ability to try removed).
Classic Nix, you let the downstream user decide (even unsafely) what system means & only MY-PACKAGE.meta.platform to assert system support.
Otherwise you could glue together some things from Nix flakes inputs + Nixtamal inputs:
{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; OTHER-FLAKE.url = "hg+https://some.dev/project"; # …more inputs }; outputs = { self, nixpkgs, OTHER-FLAKE, }: let inputs = import ./nix/tamal { bootstrap-nixpkgs = nixpkgs; }; pkgs = import nixpkgs { system = "x86_64-linux"; config = { }; overlay = [ (import "${inputs.MY-PROJECT}/nix/overlay") (import OTHER-FLAKE.overlays.default) ]; }; in { # … }; }
Does Nixtamal resolve recursive inputs?
No.
Could it?
Maybe.
Is it a good idea?
Ehhhh…
There is a lot of convenience that comes from recursive solving, but it relies on some implicit downloading behavior that can be harder to follow if not audit for security.
You wouldn’t want to be like Nix flakes & download a million nixpkgs instances… nor is emulating follows the right design.
Stacking overlays might be the easier decision.
But couldn’t the overlays get out of sync with my one Nixpkgs pin?
Yes, but this same thing happens when you follows.nixpkgs as many forget.
You can leave one input as not following & now get the aforementioned nixpkgs explosion issue potentially.
With Nixtamal, you would do a second Nixpkgs import explicitly, say in your release.nix, rather than implicitly, or will have to find another way to stitch it all together (you can even use Nixtamal’s patches feature to patch up their code!).
All to say that this isn’t really ‘solved’ by anything today.
Recursive inputs won’t be a near-term focus either unless the 💡 really goes off.
I like Nix flakes better.
Not a question, but okay. You may continue to use flakes. You won’t be the target audience — which is stable Nix users, users not requiring the extra abstraction + hassle of flakes, & users that want features flakes can never support given the current design limitations.