I hate being told I have to use Windows because it's the operating system a company knows how to manage. I want to use Linux on my development machine, while the company wants to install software, keep it updated, and know what its machines are doing. Those seem like reasonable things to want at the same time. Giving every developer root doesn't settle that disagreement, it gives us freedom to install things, but also freedom to change everything else. If a compiler needs updating, give me the compiler. Ownership of the entire operating system is more than I asked for.

I'm making a Linux fleet-management prototype for managed service providers. They look after other companies' computers. Setting up a development machine shouldn't make its developer a part-time administrator.

This started with the Terraform-like language I was exploring for Kube.ist. I'm returning to its lexer and language for a more concrete job. A company should be able to describe its developers' Python setup and keep it working across the machines.

Desktops have other plans

Someone installs a tool in their home directory. A package update changes a library. An installation gets interrupted, leaving the directory there and half the files missing. A laptop goes offline for a week and returns with a different collection of changes to catch up on.

And somebody is working on it throughout all this. I can't treat the machine as an empty target that politely waits for my deployment. (It goes home in a bag.)

Terraform refreshes the resources it manages, and its state records which objects belong to which declarations. Even with relatively predictable cloud resources, a corrupted or badly out-of-sync state can be a nightmare to untangle.

Keeping a second inventory of a desktop worries me. Packages, files, and services can change through several routes. Mirroring them in a separate state file is the mess I want to avoid.

An operator could inspect the machine before acting, plan changes from what it finds and work out how to repair a half-finished installation. What version is installed? Does it run? Is this directory ours to manage? The declaration asks for what I want there.

Git holds the desired configuration for review. There's no single desktop API to ask. The agent must inspect the package manager, filesystem, and service manager.

Give the machine something to aim for

A Python team needs an approved interpreter, tools for building native dependencies, and access to its package repository, with the tooling team choosing the approved build for the requested version. That should be enough. Why collect installation instructions from everyone in the office? I'm leaving installation checks and upgrade commands out of the declaration, so whoever changes the version doesn't have to know whether Python is installed or how far its last installation got. The company-managed installation root is separate from the distribution's system Python. A Python sketch:

python_environment "development" {
  version = "3.11"
  root = "/opt/company/python"
}

Let the operator work out the changes

Suppose we send that declaration to four machines. One has no managed Python installation, another has an older version, and the third reports the right interpreter version number but is missing required files. Installing, upgrading and repairing are three different jobs.

The fourth already has the requested setup. Leave it alone.

Those jobs would make a mess of every company's configuration if each one had to describe all the branches. I'm borrowing the idea of Kubernetes operators. An operator is software that knows how to look after a particular kind of resource as its state changes over time. Here it'd inspect and manage the company's Python installation, comparing what it finds with the declaration to work out what to do. That includes migrations when the installation layout changes. The declaration asks for Python, while the operator carries the knowledge of how to get there from the machine in front of it.

I like having that fiddly work in one place. When I fix a detection bug in the Python operator, every policy using it can benefit from the fix. I don't have to hunt down scripts with copies of the old check.

Which installation belongs to the operator? Finding Python in somebody's home directory isn't permission to take it over. The declared company resource gives it a boundary. Something unexpected in the way should stop it and produce a useful error, before files disappear.

The operator also checks that the interpreter runs and records changes and failures. Last Tuesday's successful installation doesn't make it healthy today. A healthy installation should need no work on the following pass.

Can't inspect it? Find out why before starting a repair. Treating “couldn't read it” as “it doesn't exist” is a rather enthusiastic way to start an installation.

Scripts with a job and a boundary

I'd be annoyed if getting a useful development interpreter meant replacing one the operating system depends on. (There is enough to do here already.)

Developers keep their own projects and virtual environments, so their projects can use different Python packages. I'd still like to choose my own editor and keep my files in my home directory. Teams can have different profiles too.

A script can still calculate a setting or bring resources together, asking the operator to ensure Python is ready without repeating the installation checks.

But what is that script allowed to touch? Its manifest should show the permissions it needs. For the Python setup, the request could look like this:

script "python_setup" {
  permissions {
    resource = "python_environment.development"
    operations = ["ensure"]
    write_paths = ["/opt/company/python/"]
    network_hosts = ["packages.company.example"]
  }
}

It requests one resource operation, write access under one path and permission to contact the repository. I can read that request before approving it.

If the script also wants to change account permissions, that's a different request for me to review.

Declaring permissions is only asking. The company must approve access, and the runtime must reject anything outside it.

The developer still doesn't receive root. A trusted agent or resource handler does the approved work that needs it. Scripts call that controlled interface. A list of paths and hosts won't safely contain arbitrary commands in a root shell.

This follows the same idea as the smaller agents I'm taking from Kube.ist. Request the job without receiving all the authority needed to do it. Here that job is getting developers' tools ready.

I don't want “no root” to mean “open a ticket for everything”, developers can still manage their own project environments while changes to the shared setup go through configuration and review.

An update should be a small edit

I'd like a version edit to upgrade Python, well, to request that upgrade. A plan would show the work each machine needs. Sending the file everywhere isn't the same as having Python working everywhere.

For a new Python minor version, I'd install it alongside the old one and check it before switching the company default. Existing virtual environments may still depend on the older interpreter. Removing it needs a separate decision about what's using it. People's projects stay theirs when the default changes.

I'd be unimpressed if keeping Python tidy meant interrupting the build I was waiting for. That upgrade can wait. The agent needs to arrange a suitable time and report that it's waiting, rather than claim the change is done.

A useful first morning

The first morning is where I'd most like to see this pay off. A clone, an environment, my editor.... Enrol the machine, get the team's tools and settings, then clone a project and create its environment. If a native dependency needs a compiler, the compiler should already be there.

Getting started shouldn't depend on finding the one person who remembers an extra installation step.

The team can add missing requirements to the shared profile. Review it and let the operators bring machines into line. Then the next colleague gets it too.

The first test uses the Python baseline on machines with different starting conditions. Can the same declaration handle fresh and half-configured machines, without giving either developer root? Then I'd sit down, clone a project, and see how far I get before asking anyone for help.

This entry came over from blog.haddon.me. I’ve tidied up the grammar and flow, bringing it closer to how I write now. I used AI to help with the grammar, structure and flow. I have dyslexia, and my writing often comes out as a rambling scrawl, so it can be hard to see what I was actually working on at the time. I think the tidied versions are easier to digest and more interesting to read. The artefacts got the same tidy-up while I was at it.