Control and complexity: tension in systems design

The adoption of LLMs in software development has led countless organizations to rapidly change their practices and structures. Old methods are questioned, replaced, and repurposed as the economics around creating new code get shaken up. Because humans and LLMs aren’t interchangeable, the dynamics in play are also very different. Systems are systems, and so regardless of what is changing, there are known patterns on which we can draw to provide some guidance and warnings.

Without taking a step back and looking at the mindset behind the design of the system in which you operate, you’re likely to get somewhat incoherent (as in “clashing” and “conflicting,” not as in “nonsensical”) measures and policies. And so in this post I want to discuss how we organize systems by contrasting two families of approaches.

The first is about analytical decomposition that aims to maintain control over a system, and the other is based on a perspective of complex systems that resist analysis, which tend to focus on figuring out interactions and mechanisms to foster desirable emergent behaviour.

Comparing these has always been useful to tease apart assumptions and important elements of system design, and it is still relevant now with new types of changes being proposed.

The approaches

Analytical Decomposition and Control

At the core of classic science, engineering, and many forms of management, lies the idea that the whole can be understood from its parts. Decompose a complicated thing enough that you can get a thorough and detailed understanding of every component, and you should be able to know how the ensemble works. This approach, analytical decomposition (also sometimes described as “Cartesian-Newtonian”), has been trustworthy and reliable in countless parts of modern life.

This ability to divide, analyze, and understand generally extends to understanding causality over time: each action has a reaction, each event has a material cause, and these can be traced and evaluated or tested objectively. It follows that we can turn this around: if we understand an object well enough, then we can predict what it will do when acted upon.

This is foundational to building machines and processes with any sort of predictability and reliability. You can have a high-level goal and a lot of disjoint parts, break down the problem, assemble components that are well tested and within tolerances, and have a working solution. A corollary is that if every part in the machine plays its role well, then the machine itself ought to work well.

This requires taming a messy, chaotic world, and controlling parameters such that variability can be bounded. Design with enough tolerances and redundancy, and things should work. If not, we can dive in, take it apart, understand what broke, fix it, and be better for it.

This approach is everywhere, from signal processing and telecommunications, where lossy information transmission is detected and corrected through redundancy, up to industrial quality control, where statistical processes can be used to define the acceptable boundaries of production.

It also exists at the human level: in human factors engineering, concepts such as working memory (how many things the typical operator can hold in mind) or ideal observers (a theoretical person who monitors instruments at an optimal frequency against which we define “complacency”) have been constructed for the purpose of making sure that systems in which people participate will keep them acting within desirable parameters.

It’s also visible at organizational levels. Bureaucratic processes and hierarchies aim to keep alignment top-down such that the whole ensemble works coherently. Mechanisms of discipline and legibility are in play to keep the organization’s evolution under control. At broader scales, organizations often try to control their environment, their market, or the legislative context in which they operate.

Basically, by deciding how much of a mess is accepted on the inside of a process, we can define a clearer interface on the outside of it for others to interact with. This abstraction creates a simplified but effective way to group a complicated ensemble into a manageable unit.

Software ends up representing a sort of ideal for this mindset: systems can be written in languages that ensure some level of hard-won determinism. Execution is ideally always the same, there is no wear and tear, what worked yesterday will work tomorrow, everywhere. Policy decisions defined far away from the sharp end can be deterministically enforced at all levels.

This means systems can be built from components bottom-up, aligning with top-down intent, limiting variability that comes from either machining or human behaviour. The ideal is a highly predictable, controlled, competitive, and reactive system.

Complexity and Emergence

The problem is that by definition, complex systems resist analytical decomposition.

There are many competing descriptions of complex systems, some of which are behavioural and some of which are structural. They all boil down to something like “things are so interconnected and have so many states that they become either unrepresentable, unpredictable or uncontrollable.”

Other key elements are that these systems are dynamic, heavily influenced by their own history, and are also open—they continually change and interact in ways that don’t respect clean boundaries. This creates a tension where many participants have distinct goals, perspectives, representations, and degrees of freedom. By the time you’re done analyzing the system, it’s already something else. Even observing the system changes it in important ways.

Put another way, if you find yourself surprised by the system’s behaviour, by the time you’ve pinned down what happened, it’s already a different system and your policy changes will be lagging or contributing to more counterintuitive surprises. Complex systems are more influenced than controlled.

This dynamism leads to strategies that encourage equally dynamic adjustments. Since you can’t make these predictable, interventions will often be small and iterative. Alternatively, if you can’t simplify the elements or interactions you’re trying to control, you can increase the variety of control behaviours in order to make ongoing adjustments better. This tends to mean “put a controller—human or otherwise—that has enough internal complexity to cancel out the complexity of the thing it controls.” This, in cybernetics speak, is an attempt at creating more adaptive and dynamic control mechanisms.

Balance is attained not by keeping things static, but by keeping them in motion.

The ideal system is self-aware and flexible such that it can endlessly adapt and sustain itself, despite ever-increasing challenges. It's unclear whether the ideal can be reached.

How they compose (or fail to do so)

Systems generally evolve from a constrained definition of the problem and its potential solutions, something that is tractable and effective. As the scope and scale of operations grow more comprehensive, further interventions trying to steer the system provide diminishing returns, and they increasingly produce unintended effects. These are the effects of complex systems showing up as things become tangled.

The coping mechanism I’ve seen the most often is one of doubling down by doing more analysis, more decomposition, and putting more effort into more flexible automation that covers more cases. This in turn changes the nature of success and failure, by creating sometimes less frequent but bigger incidents instead. This type of composition takes place by substituting what breaks when possible, or sometimes by pure accident. It’s rarely been an orderly process.

More rarely seen mechanisms seek to find out how much of the analytical and control-centric approaches we can afford to give up, identifying what can’t change at all, and then expanding complexity-aware mechanisms outwards from there. This is far less comfortable because this sort of stance demands that you give up on the idea that you actually are in control—a very unpleasant state of affairs to broadcast for a business.

There are in fact long-standing debates as to whether larger scale accidents can actually be avoided. For example, Jean-Christophe Le Coze offers the following categorization:

Diagram showing three theoretical explanations for the unpredictability of accidents: technology out of control (Ellul/Perrow), fallible human constructs (Kuhn, Turner, Weick, Vaughan), and self-organizing emergent systems (Ashby, Rasmussen, Snook, Hollnagel).
  1. A ‘deterministic’ thread, where the properties of the technological systems themselves (such as tight coupling and complexity) will eventually defeat efforts to prevent accidents.
  2. An ‘epistemic’ branch that focuses on the idea that organizations will suffer from 'failures of foresight' where weak signals and indicators that accidents are incubating will not be seen or accepted by the structures of power, and worldviews will fail to match new challenges, leading to accidents.
  3. A ‘self-organizing’ thread that considers systems as adaptive and therefore frames success and failures as consequences emerging from systems' self-organization, through an exploration of problem and solution spaces with their available resources.

These differing views are not fully incompatible, and authors from one category will frequently borrow from others. Each perspective will however come with a focal point, a thing that is seen as important and worthy of consideration: the structure of control, the historicity of the system, the dynamics of power structures, the adaptive and changing nature of systems, the limited perspectives of participants, concepts around culture, and so on.

Many contributors to these debates, while stating that accidents are unpredictable or hard to avoid, nevertheless seek explanations that can support making them less likely. They look at the limitations of known approaches, and expand the boundaries of what we should consider, adding new perspectives that can reveal new insights.

There’s a lot of existing literature across many disciplines to study and get a better grasp on what doesn’t work (and when), and what is contextually useful. The opposition of analytical decomposition for control and complexity for emergence I’m offering here is crude and lacks nuance, but that’s hopefully what makes it an acceptable tool to think about changing systems.

Oversimplification is what we’re doing here, and knowing what kind of wrong we’re going for is useful. As George Box (1976) said: “Since all models are wrong [we] must be alert to what is importantly wrong.”

Contrasting Approaches in Practice

In a bit of a caricatural manner, the following examples will show relatively stereotypical perspectives to topics relevant to software through both analytical decomposition (with a focus on control) and complexity (with a focus that deliberately limits itself to influence):

TopicAnalytical Decomposition / ControlComplexity / Emergence

Training and education

Build a well-defined curriculum, best practices for teachers and trainers, and testing mechanisms to ensure predictable performance and uniformity across students.

Create environments that foster exploration, experimentation, and information exchange; provide guidance and support.

Safety

Prevent undesirable behaviours that lead to failure. Hazards are to be contained or designed out, and deviations from procedures or best practices are seen as a risk.

Foster positive behaviours that lead to success. Find how people bridge gaps in processes, work around obstacles, and recover from problems.

Correctness

The software does what the specification or API says it should. Tests pass, it is feature-complete, and operates within known boundaries.

Users or customers are able to successfully accomplish their tasks; goals can shift based on their needs.

Reliability

Uptime is within acceptable range, and is verifiable through SLAs, SLOs, etc. Load testing and thorough verification can prevent outages.

Nines don’t matter if customers aren’t happy. You also won’t know for sure if software works until you hit production. Plan for recovery and coping with surprise.

Approach to incidents

Runbooks define best practices. Protocols and processes are defined to investigate and triage problems as efficiently as possible. Build for clear information and rapid diagnostics. Investigate what broke so recurrence can be prevented.

Surprises may require improvisation. Who knows what will happen; build capacity to deal with the unknown. Investigations must look into normal work to understand how the system works in the first place.

Developing features

Understanding the needs of users and the strengths and gaps in current offerings lets you identify what to build and how to build it.

Experiments in the field with potential features that you iterate on is how you best find what features may prove useful.

Standards and norms

Written unambiguously based on verifiable processes and outcomes to make enforcement tractable, scalable, and clear.

Written in a goal-oriented manner as to support and guide the people who execute the work and who need to adapt rules to their reality.

For each category, the attitude taken can drive people to pick drastically different approaches and activities, some of which may or may never overlap—the drive to control costs and errors can hinder the effectiveness or desire to experiment, and beliefs about how complex systems work may oppose all sorts of measures that are typically used to demonstrate accountability.

I say this table is caricatural because in the real world, lines are often not this clean-cut, nor this superficial. It is possible for a control-centric hierarchy to align managers on goals and delegate authority down to cope with system complexity, and for control to be emphasized based on who people in power trust, for example. Centralized control tends to be most effective on the analytical decomposition side, but there are also approaches that aren’t control-centric that benefit from it.

In fact, many activities can be used in both approaches, and serve both for distinct people, or even at the same time for any given person:

ActivityAnalytical Decomposition / ControlComplexity / Emergence

Code Review

Find bugs and flaws; track and assign accountability; ensure quality.

Build awareness and provide a space for feedback within and across teams.

SLO adoption

Organizational tool to ensure all teams manage their reliability adequately.

Prioritization tool whose value comes from having teams discuss and define what is an acceptable level of reliability.

Refactoring

Pay down technical debt, reduce complexity, improve maintainability and flexibility, normalize used patterns.

Countering entropy, adapting a code base to changing contexts based on new information available or shifting requirements.

Chaos Engineering

Validating that expected failure cases are properly tolerated or recovered from

Experimentation-driven exercise in which participants form theories about their system’s behaviour in failure scenarios and try to confirm or disconfirm them.

Using a platform

A shared platform can encourage good architectural patterns and prevent undesirable ones, while abstracting away complexity for teams that build on it.

Platforms provide systems with means of commoditizing shared elements to benefit from economies of scale and specialization, and address organizational bottlenecks through self-serve access.

Even if activities in this list can serve both analytical decomposition and complexity-aligned approaches, that doesn’t mean that they will.

For example, code review approaches that are control-centric and aim to hammer out any deviation from established norms may be adversarial to the point of causing anxiety or hindering actual feedback. Some implementations may still be able to mix automation and the proper social norms to successfully support both purposes to varying degrees of success.

My experience has been that for these activities, the underlying position taken truly matters if you want to understand how they play out, and how they sometimes fail to meet someone’s expectations. This underlying position will also matter when it comes to prioritizing one activity against others. If participants or stakeholders do not agree to the higher-level purpose and desired outcome, then there will be a gap in ways these activities are expected to be carried out and how they take place, and in the relative importance they will be given across the system.

When someone wants to change, supplement, or remove some of these activities, it’s useful to wonder what’s the nature of the change and what’s the perspective it favours.

Flipping across approaches

As a heuristic, when multiple lenses are available, we can either try to find the best one (for some arbitrary criteria), or use a complementary or intersecting approach that uses as many of them as possible. Picking a single lens can lead to seeking implementations that maximize one type of activity contextually—whether control or emergence—whereas a combined approach can seek to make sure chosen activities are able to serve multiple properties, as a sort of tradeoff.

Sometimes, what you get is not what you intend. An organization that sets up activities for control may find itself relying on practitioners invisibly repurposing them for complexity-aligned contributions. Meanwhile, the organization’s decision-makers exercise less control than they believe, or misattribute benefits to their own acts. They can then lose what they had when altering control mechanisms and incidentally hindering the hidden adaptations.

Conversely, if activities are set up for emergence but are instead done mechanically as if intended for control, they won’t provide the expected benefits and might look and feel like busy work: the organization then neither controls nor benefits from adaptive effects.

For broad topics and categories such as reliability or correctness, there are often no clearly defined choices or principles that are written down and that you can use. Organizations however tend to have some general tools that line up on the control-to-emergence spectrum, usually around process design and enforcement mechanisms.

If you’re faced with behaviour you dislike, let’s say people from other teams modifying sensitive code your team owns unannounced, you can take measures such as having discussions with them reasserting ownership, and mentioning the expected process. You could require a preliminary RFC document or ticket before any change request is submitted. You can rely on code ownership files to prevent any unexpected change from going further without your agreement. You can move that key code to repositories which other teams cannot access.

All of these are relatively local and play on the direct surrounding structure to modify actions and prevent undesirable acts. These approaches may be tremendously effective with little effort, but can also inadvertently fail to make desirable behaviour likelier.

Closer to emergence’s perspective, it may be more typical to figure out what drives other teams to send these changes unannounced. What are the constraints and pressures they see that makes their current behaviour reasonable to them? If everyone agrees the process is a good ideal state but it frequently gets ignored, what is perceived as more important than that? Only once this is understood should you then design an intervention. This type of questioning—often informed by patterns such as those highlighted previously by Le Coze—tends to have you pull on a thread that unravels through the whole organization. It can be time consuming and difficult to do without established trust, but it can reshape expectations, and as easily lead to major change as to minor interventions upstream.

A combined approach would be one where a broad understanding of the situation is obtained by leaning on complexity-aware methods, and is then used to design simple but high-leverage checks and barriers such that minimal control yields high rewards. This relies on the complexity stance to look not just at the system’s structure and purposes, but at how its various components and participants interact. Once the interactions make more sense, then the analytical approach is hopefully more effective.

A risk here is to find yourself with a system that either feels so intractable, resistant to complexity approaches, or inflexible to cross-cutting interventions that you’re back to purely local defences, except they are late, with more work needed to get to the same place.

The question then is not which approach is better, but how do we know when the current approach reaches its limits and what do we do then?

Pitfalls of uncritical system design

People change their systems all the time, with or without this knowledge. They’re often successful, but not always, or at least not in the ways they had planned. Knowing what to look for doesn’t mean you’ll get it right, but it increases your odds.

This might be true in the current LLM-driven shakeups as well. Because the technology is new and design patterns aren’t crystallized yet, a lot of people experiment a bit haphazardly. Many of their ideas have interesting elements or aspects to them that are worth learning from, but glaring omissions from a systems perspective that will still need to be handled.

It’s almost impossible not to find examples of wide sweeping changes proposed when reading tech opinion pieces, which I’ll avoid linking to here. But they include ideas such as:

  • Replacing code reviews with various types of barriers (tests and automated checks), rarely questioning what emergent roles the practice may have nor how static barriers may qualitatively differ from more adaptive ones.
  • Splitting software work into high-level specs to be translated to code in a black box with external checks only, without offering explanations around how the specs may cover varying abstraction layers, how the external checks can remain tractable, or how information worth learning should cross these boundaries in each direction.
  • Asking for everyone to become a sort of manager-of-agents while keeping agents under tight control loops, without asking what you may lose (or at least cause as second-order effects) in this analogy by changing the delegation and control mechanisms wholesale.
  • Focusing on system-level observable outcomes and letting go of imposing the structure within, trusting that the system will self-organize itself adequately.

If you design a system with control in mind—the use of barriers (think of the Swiss cheese model), the presence of extensive testing, of processes and procedures guaranteeing best practices—then you should pay as much attention to the mechanisms that will be needed to figure out if control actually works. This means asking questions like:

  • How do we know our observations remain relevant, and that we surface the right signals?
  • How can we know if our understanding of the system loses accuracy?
  • What important elements is our analysis leaving out or obscuring when trying to make things legible?
  • How much variability is tolerated, and are we suppressing necessary types of it?
  • Are the things we optimize for creating brittleness elsewhere?
  • Is our control real or illusory? How would we know if that changes?

Well-regulated systems compensate for disruptions in ways that hide or suppress the signals of accumulating problems, both at technical and cultural levels. These questions aim to figure out whether any thought is given to what hides such behaviours.

When you design for emergence—think of self-organization, market-like mechanisms, or delegation of decisions to participants with local context—other questions come up:

  • Are local parts of the system working at cross purposes?
  • Is goal alignment effective? What maintains coherence?
  • What capabilities or efficiencies are we sacrificing when giving up on legibility?
  • Can we afford to lose the efficiency of a control-centric system? When might we need it?
  • How do we differentiate adaptation from drift?
  • What preserves dissent and carries information from the edges of the system?

Since complexity-aware approaches tend to resist prescriptive stances, there are often risks of increased inertia or widespread misalignment. Emergent properties will be key to success and failure, but without some careful thinking and influence, things can take on a life of their own.

Whenever someone pushes for a system design that focuses on analytical decomposition or control, ask how they know they’re doing what’s needed, and the mechanisms by which they adapt. Whenever someone pushes for a design that seems to promise self-regulation and endless flexibility, ask how they’ll maintain coherence and the conditions they rely on for good outcomes. Whenever someone pushes to switch from one to the other, ask what depends on current behaviour and consider what the second-order impacts might be there.

Tech companies often rush to reinvent themselves around the outsized promises of new technology. Integrating new technology into existing workflows generally demands transforming the workflows. These changes often aim at reducing variability and increasing control, but cross subsystem boundaries in ways that disrupt tangled interactions that were dynamically stable.

Automation that makes things predictable necessarily removes elements of unpredictability that can be useful to adaptation and evolution. Likewise, trying to make a part of the system more adaptive may necessarily make it less predictable. Both have knock-on effects on the rest of the system.

Where and how does the system migrate from one mode of operation to the other? Where is control necessary and where is it not? What do we choose to analyze and decompose and what do we treat like an ecosystem instead?

If we don’t have an answer to these, we also don’t have a good answer to how our systems will avoid failure or meet success. Systems are systems. They will keep acting like systems, and failing like systems.

Permalink

Building Erlang + ZX on Devuan Excalibur

With Ubuntu/Kubuntu out of the running for “works and is mostly sane”, we are having to shift once again over to Devuan, which we have been using for servers for several years now at Tsuriai and QPQ, but not as often on desktops (because Kubuntu was just so convenient and changing habits is annoying).

This post will cover Erlang and ZX, specifically, and I’ll do another one about how the app-level transition feels moving from Kubuntu for desktop to Devuan. (Migrating servers from Ubuntu to Devuan is really easy and a major relief: everything is just simpler, flatter, more boring, and works as any SysV init system should.)

TL;DR

As a pseudo script:

# To run the next stuff as root we 'su -'
su -
apt update
apt upgrade
# If installing on a non-graphical system, omit the libwxgtk dep
apt install \
    gcc curl g++ dpkg-dev build-essential automake autoconf \
    libncurses-dev libssl-dev flex xsltproc libwxgtk3.2-dev \
    wget vim git
# Get out of root now and run everything else as a normal user
exit

# Create directories we'll need
mkdir -p ~/vcs ~/bin
# Open a new terminal session for ~/bin to get into $PATH

cd ~/vcs
git clone https://github.com/kerl/kerl.git
cd ~
ln -s ~/vcs/kerl/kerl ~/bin/kerl
kerl update releases
# Pick any version in the list -- I'm working on R27 right now
ERL_VER=27.3.4.12
kerl build $ERL_VER $ERL_VER
kerl install $ERL_VER ~/.erts/$ERL_VER
echo ". \"\$HOME\"/.erts/$ERL_VER/activate" >> .bashrc
echo ". \"\$HOME\"/.erts/$ERL_VER/activate" >> .bash_profile
. ~/.erts/$ERL_VER/activate

# You already have Erlang, this will get you ZX
wget -q https://zxq9.com/projects/zomp/get_zx && bash get_zx

Discussion

The above scriptish version is the cut sheet for me to use later. I haven’t done a discussion explaining what is going on above in a while, and humans stopped reading (and writing) but AI sucks, so here we go, old school HOWTO blog style…

GUI vs TUI

The difference between the non-graphical and graphical build of Erlang in this context is simple:

  • For GUIs, include the libwxgtk3.2-dev dependency.
  • For TUIs, omit the libwxgtk3.2-dev dependency.

I’ll include it below for completeness, but that’s the only difference.

root vs sudo

On Devuan the default is to use a root account rather than sudo. I tend to log in as root using su - instead of actually logging in as root, but that’s mostly because I do this mostly over SSH, and login as root should always be disabled in the SSH config. If you add yourself to the sudoers file, though, you can do it the sudo way you may be familiar with from Ubuntu.

Installing the deps

Everything in this section will either have to be run as root or with sudo.

apt update
apt upgrade
apt install \
    gcc curl g++ dpkg-dev build-essential automake autoconf \
    libncurses-dev libssl-dev flex xsltproc libwxgtk3.2-dev \
    wget vim git

Setting up for, Building and Installing Erlang

First, we are going to be depending on the ~/bin directory being in our path for a bit of this, so create it if it doesn’t already exist. We’ll also use a directory ~/vcs to keep version controlled stuff in one place (so if you have sync scripts for your $HOME, omit both of those from it):

mkdir -p ~/vcs ~/bin

If ~/bin didn’t exist previously, close your terminal and open a new one — this should show up in your default $PATH now. If it doesn’t take because you have chosen a funky window manager, just log out and log back in to force it to take effect.

Once ~bin exists and is in our $PATH we’re going to jump through the following steps:

cd ~/vcs
git clone https://github.com/kerl/kerl.git
ln -s ~/vcs/kerl/kerl ~/bin/kerl
cd ~
kerl update releases
ERL_VER=27.3.4.12
kerl build $ERL_VER $ERL_VER
kerl install $ERL_VER ~/.erts/$ERL_VER
echo ". \"\$HOME\"/.erts/$ERL_VER/activate" >> .bashrc
echo ". \"\$HOME\"/.erts/$ERL_VER/activate" >> .bash_profile
. ~/.erts/$ERL_VER/activate

I installed 27.3.4.12 in the example above because I’m working on something that still uses it, but you can substitute any current version and it should just work. The current latest stable is 28.5.

At this point you have Erlang. Try running erl at your terminal and it should pop up:

ceverett@soba:~$ erl
Erlang/OTP 27 [erts-15.2.7.8] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]

Eshell V15.2.7.8 (press Ctrl+G to abort, type help(). for help)
1>

Installing ZX

This is the easiest part:

wget -q https://zxq9.com/projects/zomp/get_zx && bash get_zx

You should see roughly this output:

ceverett@soba:~$ wget -q https://zxq9.com/projects/zomp/get_zx && bash get_zx
2026-06-08 17:37:47 URL:https://zxq9.com/projects/zomp/zx-0.14.0.tar.gz [151486/151486] -> "zx-0.14.0.tar.gz" [1]
Erlang found at /home/ceverett/.erts/27.3.4.12/bin/erl
/home/ceverett/bin was found in $PATH. Good to go.
zx found at /home/ceverett/bin/zx. Checking for upgrade.
Running `zx upgrade`...
Recompile: src/zx_zsp
Recompile: src/zx_userconf
Recompile: src/zx_tty
Recompile: src/zx_sup
Recompile: src/zx_proxy
Recompile: src/zx_peers
Recompile: src/zx_peer_sup
Recompile: src/zx_peer_man
Recompile: src/zx_peer
Recompile: src/zx_net
Recompile: src/zx_local
Recompile: src/zx_lib
Recompile: src/zx_key
Recompile: src/zx_daemon
Recompile: src/zx_conn_sup
Recompile: src/zx_conn
Recompile: src/zx_auth
Recompile: src/zx
Current version: otpr-zx-0.14.0
Running latest version.

Now try a zx command, like zx describe gajudesk:

ceverett@soba:~$ zx describe gajudesk
Package : otpr-gajudesk-0.9.0
Name    : GajuDesk
Type    : gui
Desc    : A desktop client for the Gajumaru network of blockchain networks
Author  : Craig Everett <craigeverett@qpq.swiss>
Web     : https://gajumaru.io
Repo    : https://git.qpq.swiss/QPQ-AG/GajuDesk
Tags    : ["gaju","gm","gajumaru","wallet","blockchain","cryptocurrency",
           "crypto","puck"]

Yay! And now you’re all set.

A note on other systems (MacOS and Windows)…

There are signed one-shot installers for Windows and MacOS that will bring in Erlang R27 and ZX with integrated shortcuts available at the ZX/Zomp downloads page.

If you came here to get GajuDesk, there are installers for GajuDesk and GajuDesk + GajuMine that integrate those apps with the desktop for Windows and MacOS (clicky icons and launchers and so on) available here: GajuMining Downloads

A note on GajuMining…

If you came here to run GajuMining on Linux, the above steps all still apply, but there are a handful of additional steps needed, such as adding the qpq and uwiger code realms. The community setup scripts are maintained at Shane Preater’s repo here: https://github.com/shanepreater/gajumaru

But if you already have zx installed by following the steps above, you can shortcut this with:

wget -q https://github.com/shanepreater/gajumaru/raw/refs/heads/main/qpq.zrf
wget -q https://github.com/shanepreater/gajumaru/raw/refs/heads/main/uwiger.zrf
zx import realm qpq.zrf
zx import realm uwiger.zrf
rm uwiger.zrf qpq.zrf

This is how things should go:

ceverett@soba:~$ wget -q https://github.com/shanepreater/gajumaru/raw/refs/heads/main/qpq.zrf
ceverett@soba:~$ wget -q https://github.com/shanepreater/gajumaru/raw/refs/heads/main/uwiger.zrf
ceverett@soba:~$ zx import realm qpq.zrf 
SHA-512 of qpq.zrf: 59082251ED81C63DFC6C3678926DF503CB42118291B3271D3612BD1E7F63AEBDEB296D81AF7446451555B7085E726B6B9C1EC8DBBF3C4C6EDB296B9C257E3AD4
Imported record locally, including a public key.
Added realm qpq.
ceverett@soba:~$ zx import realm uwiger.zrf 
SHA-512 of uwiger.zrf: 704530801DF3E54D069FACD93F772FCAC8A6E776389CDE8F4F034DE1A0A516B0D44FFB3DDD88C2A82C015CB14218C17172099E477E28B3A5E293D2CC08F20000
Imported record locally, including a public key.
Added realm uwiger.
ceverett@soba:~$ zx list realms
otpr
qpq
uwiger
ceverett@soba:~$ zx describe qpq-gajumine
Package : qpq-gajumine-0.4.2
Name    : GajuMine
Type    : gui
Desc    : Mining client for the Gajumaru Root
Author  : Craig Everett <craigeverett@qpq.swiss>
Web     : https://gajumining.com
Repo    : https://git.qpq.swiss/zxq9/GajuMine
Tags    : ["qpq","gaju","gajumaru","hive","mining","crypto"]

And that’s it. You can do CPU mining with the GUI client by running zx run qpq-gajumine or headless with a GPU setup by following the advice on Shane’s repo.

Permalink

Erlang/OTP 29.0

OTP 29.0

Erlang/OTP 29 is a new major release with new features, improvements as well as a few incompatibilities. Some of the new features are highlighted below.

Many thanks to all contributors!

Highlights

  • Added support for -unsafe attributes for marking functions as unsafe to use. The compiler will by default now generate warnings for calls to functions in Erlang/OTP known to be always unsafe. Furthermore, xref can now be used to find calls to unsafe functions and functions that lack documentation.

  • The SSH daemon now defaults to disabled for shell and exec services, implementing the “secure by default” principle. This prevents authenticated users from executing arbitrary Erlang code unless explicitly configured.

  • The SFTP subsystem is no longer enabled by default when starting an SSH daemon.

  • In SSL, the post quantum hybrid algorithm x25519mlkem768 is now the most preferred key exchange group in the default configuration.

  • The module io_ansi allows the user to emit Virtual Terminal Sequences (also known as ANSI sequences) to the terminal in order to add colors/styling to text or to create fully fledged terminal applications.

  • The new ct_doctest module allows the user to test documentation examples in Erlang module docs and documentation files.

  • The ignore_xref attribute has been handled as a post-analysis filter by build tools such as Rebar3. In this release, [xref] itself does the filtering, ensuring that all tooling that calls xref for any purpose can rely on these declarations to just work.

General

  • In the default code path for the Erlang system, the current working directory (.) is now in the last position instead of the first.

  • There is no longer a 32-bit Erlang/OTP build for Windows.

New language features

  • Native records as described in EEP-79 has been implemented. A native record is a data structure similar to the traditional tuple-based records, except that is a true data type. Native records are considered experimental in Erlang/OTP 29 and possibly also in Erlang/OTP 30.

  • The new is_integer/3 guard BIF makes it possible to easily verify that a value is both an integer and within a certain range. For example: is_integer(I, 0, 100)

  • Multi-valued comprehensions according to EEP 78 are now supported. For example, [-I, I || I <- [1, 2, 3]] will produce [-1,1,-2,2,-3,3].

  • By enabling the compr_assign feature, it is now possible to bind variables in a comprehensions. For example: [H || E <- List, H = erlang:phash2(E), H rem 10 =:= 0]

Compiler and JIT improvements

  • In the documentation for the [compile] module, there is now a section with recommendations for implementors of languages running on the BEAM.

  • The JIT now generates better code for matching or creating binaries with multiple little-endian segments.

  • The compiler will generate more efficient code for map comprehensions with constant values that don’t depend on the generator. Example: #{K => 42 || K <- List}

Compiler warnings

There are several new compiler warnings enabled by default. For each such warning, there is an option to disable it.

  • There will now be a warning when using the catch operator, which has been deprecated for a long time. It is recommended to instead use trycatch but is also possible to disable the warning by using the nowarn_deprecated_catch option.

  • There will now be a warning when exporting variables out of a subexpression. For example: file:open(File, AllOpts = [write, {encoding,utf8}]). This warning can be disabled using the nowarn_export_var_subexpr compiler option.

  • The compiler will now warn for uses of the and and or operators. This warning can be disabled using the nowarn_obsolete_bool_op compiler option.

  • The compiler will now warn for matches such as {a,B} = {X,Y}, which is better written as {a=X,B=Y}. This warning can be disabled using the nowarn_match_alias_pats option.

For a long time, there has been a warning for using the obsolete guard tests (such as list(L) instead of is_list(L). In Erlang/OTP 30, the old guards will be removed from the language.

STDLIB

  • There are new functions for randomly permutating a list: rand:shuffle/1 and rand:shuffle_s/2.

SSH

  • The default key exchange algorithm is now mlkem768x25519-sha256, a hybrid quantum-resistant algorithm combining ML-KEM-768 with X25519. This provides protection against both classical and quantum computer attacks while maintaining backward compatibility through automatic fallback to other algorithms when peers don’t support it.

For a full list and more details about new features and potential incompatibilities see the README.

Permalink

Copyright © 2016, Planet Erlang. No rights reserved.
Planet Erlang is maintained by Proctor.