Your computer
Daimond can run programs on your computer (a build, a test suite, a linter, a script) inside a compartment the kernel enforces. It is free, it takes two commands at a terminal, and it is Linux only. Read the warning first, before you type either command.
What it gives you
With the hand installed, a daimon can run a build, a test suite, a linter or a script against the folder you granted, and read the result. Nothing else on the machine is in reach.
The tools it uses are named in the Tools panel, and four of them are worth knowing by name.
- run runs one command, given as a list of arguments, and answers with the exit code, the tail of the output and the time taken.
- verify runs your project's own check. Daimond reads the argv from
.daimond/verify.json, or infers it fromCargo.toml,package.json,pyproject.tomlorgo.mod. The exit code is the verdict. - runs lists what is still running, and how to stop one. A command can outlive its turn, because something started in the background holds its ports until it is stopped, and nothing else on the computer can reach it, since the compartment scopes signals to itself.
- serve starts a read-only file server for a folder, so a built page can be looked at in the Web panel. It stays up after the turn, so stop it by its id.
A refusal names the path, the cause and the fix, so there is no code to look up. The turn then ends the way any turn does, as Answered, Stopped or Step limit reached, so a refused command never reads as one that worked.
Before you start
Three things decide whether any of this will work. Checking them takes a minute, and discovering them afterwards takes an hour, because the error the browser reports names none of them.
1. A browser installed from a .deb
A snap or flatpak browser confines the programs it starts, and that confinement reaches Daimond's hand. The home interface a snap gets does not grant the hidden directories at the top of your home directory, and the hand keeps its journal at ~/.local/share/daimond/hand/journal/, behind one of them. The hand cannot open the file it would have recorded its reason in, so it exits without a word, and Chrome reports only Native host has exited. That sentence gives you nothing to act on, and the confinement is what a snap is for. Moving the journal does not help, because the browser hands the program its own environment.
You need not work this out yourself. The installer writes into the profiles that can use it and names the ones that cannot, and where every browser it finds is confined it stops, so you are not left with an install that appears to have worked. --check flags a confined browser even with a usable one beside it, because the browser you are actually using decides.
To see what you have before you start:
snap list 2>/dev/null | grep -Ei 'chrom|brave|vivaldi|edge'
flatpak list 2>/dev/null | grep -Ei 'chrom|brave|vivaldi|edge'
Anything either command names is confined. What works is Chrome, Brave, Vivaldi or Edge installed as a .deb, or a Chromium .deb from somewhere other than Ubuntu's archive.
apt install chromium-browser gives you the snap. Ubuntu's chromium-browser package has been a stub that installs the snap since 20.04, so the usual way of getting a browser without the Software app puts you in the same place. Install one of the four .debs above instead.2. Run that browser once before you install
The installer writes one small file into the browser's profile directory, and that directory is created the first time the browser starts. Install a browser and never open it, and install.sh reports finding nothing, correctly, because there is nothing there yet.
3. Linux, with Landlock in the kernel
The compartment is built from Landlock, which fences files, and seccomp, which filters system calls, and both are Linux kernel features. Landlock arrived in Linux 5.13, and Unconventional systems sets out what older and unusual machines get. The hand builds and runs on macOS and Windows, but reports that it can fence nothing, and Daimond then refuses every command instead of running one unfenced. The refusal names what is missing. Neither platform’s fence is built.
Installing it
Installing takes two commands at a terminal, from the top of the Daimond repository, then two things in the browser. They add up to one small JSON file per browser profile plus a binary you built. The install leaves nothing listening on a port, no daemon running, and no secret to steal.
-
Build the hand
cargo build --release --manifest-path hand/Cargo.tomlUse
--manifest-pathand not-p, because the hand is a cargo workspace of its own, so-preports no such package and builds nothing. -
Grant a folder, and register the hand
hand/install/install.sh --workspace ~/workPut your own folder in place of
~/work. That folder bounds everything any command can read or write, so choose one for the work. The script refuses your home directory and/outright, since granting either grants everything. The folder has to exist already.That one run does three things, each with a failure you would otherwise meet later.
- Writes your folder into
root.txt, at~/.local/share/daimond/hand/journal/root.txt. The hand will not serve a page until it has been told the folder, and it never guesses, because a guessed folder is a guess about what a command may touch. To change it later, run the same command with a different folder. - Creates that journal directory at mode 700. It holds the record of every command every diamond has run, so the hand refuses to write it anywhere other users can read. A directory made under the usual default permissions is 755, and the hand would not start.
- Writes the registration into every usable browser profile, and prints which. That file is what lets the browser start the hand at all. It is JSON and nothing else, so the script builds nothing, downloads nothing, starts nothing and needs no root.
DAIMOND_HAND_ROOTis a trap. The environment variable does the same job asroot.txtand takes precedence, and it will not work, because your browser starts the hand with its own environment, and yours is not in it. Use the file. - Writes your folder into
-
Load the extension
The previous step printed the path. Open
chrome://extensions, turn on Developer mode, choose Load unpacked, and select theext/directory.The extension carries a fixed public key, so its id is the same on every machine, which is how the registration written a moment ago can already name it. Its manifest names a single origin,
https://daimond.oxedyne.com, as the only page allowed to speak to it, so no other page in your browser can reach it at all. -
Restart the browser
The browser reads those profile directories only when it starts, so a file that appeared while it was running is invisible until then. A missed restart is the commonest reason a correct install looks broken.
Then check it
hand/install/install.sh --check
The check changes nothing, and prints one line per thing that has to be true: a usable browser, the registration, the binary, the journal directory and its mode, root.txt, the journal sitting outside the granted folder, and the extension. A failing line carries its fix on the line below it. Work from the top, since a later line often fails only because an earlier one did.
The last two steps are yours
None of the steps above made a decision for you. The two things left are the two that are decisions.
Open the same folder in Daimond. On the Workspace panel, use the Machine chip to open the folder you granted. It has to be the same one. The hand writes a token into <folder>/.daimond/workspace.id and names it when it introduces itself, and the page reads that file through the handle it already holds and compares the two. A workspace that is the browser's own storage, or a different folder from the one in root.txt, has its commands refused with a line saying which. The token lives inside .daimond because the compartment always denies that directory, so a command cannot read the token and cannot answer for a folder it is not in.
Allow the first command. The first time Daimond wants to run something, a Daimond Hands window opens and asks. No browser permission covers “may run programs on this computer”, so that window is the approval, and the extension records your answer itself. That is also what makes it revocable, because the browser has nothing to take away. Until you allow it, nothing runs.
The window names the folder and the page that asked, and says in one line how far a command can reach on this machine. What this covers, and what it does not opens the rest, which is what the compartment stops, what it does not, and how to withdraw the permission.
When it does not work
Start with install.sh --check again. It walks every check here in order and stops at the first thing that is wrong. The three steps it automates are useful on their own, and they are what to reach for when --check passes and Daimond still says the hand is not there.
Run the hand yourself
hand/target/release/daimond-hand < /dev/null
This starts the hand exactly the way your browser will, with no browser at the other end, so whatever it would have said to the browser it says to you. There are three answers.
daimond-hand: the page closed the pipe.The hand is configured and ready. Whatever is wrong is on the browser's side, and restarting it is the first thing to try.- This hand has not been told which folder it may work in means
root.txtis missing. Runinstall.sh --workspaceagain. - …is readable by users other than its owner means the journal directory is not 700, and the hand will not write the record of your commands where another account can read it.
chmod 700it.
Anything else is a whole sentence naming the path, the cause and the fix, because the hand has nowhere to print a code anyone could look up.
hand/target/release/daimond-hand --report
This prints what the compartment can enforce on this kernel and, at greater length, what it cannot. Read the second list closely, because on a kernel below Linux 7.1 it includes a route out of the compartment that Landlock leaves open and the hand closes itself, by refusing unix sockets. It also names the folder from root.txt, so a stale one shows up here and not at the first command.
Read the journal
tail -n 5 ~/.local/share/daimond/hand/journal/hand-*.jsonl
The journal holds one JSON object per line, in the order things happened: the handshake, every command, and every refusal with its reason. If the hand started at all it wrote something here. An empty directory after a failed attempt means the hand never got far enough, which on Ubuntu almost always means the confined browser at the top of this page.
Ask the extension
In the browser, on the Daimond tab, open the developer console and run:
await DaimondHand.status()
The extension answers with JSON saying whether a hand is paired, which folder it says it was granted, what it can enforce, and, where the answer is no, a reason written as a whole sentence. Daimond shows the model that same sentence, so the JSON explains what the daimon has been told.
What it actually is
A command is an ordinary process, owned by you, on your own kernel, against your own files, with capabilities removed first. There is no virtual machine, no container and no emulation, so nothing is simulated and nothing copied in. Four parts each do one job.
- The page asks. Daimond works out what this turn may touch and sends the whole rule with the request. It never sends a shell line, because
argv, the list of arguments, is an array, and there is nothing to inject into. - The extension relays. Daimond Hands is the only thing the page can hand a command to, and one origin may speak to it.
- A small local program runs it. The hand builds the compartment out of the rule it was sent, applies it to itself, and then becomes the command. No supervisor watches a child, because nothing is left of the hand once the command starts.
- Your browser starts the hand. That is why there is no port and no password. A service on a port would be reachable by every page you visit.
Daimond implements no tools
There is no built-in ls, no built-in grep, no bundled toolbox and no shell. ls is /usr/bin/ls, the one already on your machine, at the version you installed.
Every program under the system directories is reachable, because the compartment adds them to the grant read-only: /usr, /bin, /sbin, /lib, /lib32, /lib64, /libx32, /etc, /opt, and the harmless devices /dev/null, /dev/zero, /dev/full, /dev/random and /dev/urandom. Without them nothing starts, not even cat, because the loader has to read the shared objects and the process has to open something for the standard streams.
A bare name is looked up on PATH, which is /usr/local/bin:/usr/bin:/bin unless a granted toolchain has added to it. What that finds is resolved to an absolute path and checked against the compartment like anything else, so finding is not permission. A PATH pointing outside finds a program the command may not run, and the refusal names it.
["cargo", "test", "--lib"]), and a semicolon, pipe, redirection, backtick, $(…) or && arrives at the program as a literal argument. To chain two commands the daimon runs the tool twice, which is better anyway, because it reads the first result before choosing the second.What a command can and cannot do
Files
- Read and write inside the granted folder. That folder, minus Daimond's own
.daimonddirectory, is the writable world. - A private temporary directory. It is writable, with
TMPDIRpointing at it, removed when the run ends, unreachable by any other run, and the one place outside your folder a command may write./tmpis outside the compartment, and a build that cannot write a temporary file fails part-way through for a reason nobody can read. - The system, read-only.
/usr,/etc,/optand the rest of the list above. - Everything else is refused by the kernel, not by a check somebody wrote. A file one folder outside the grant comes back
Permission denied, and the daimon is shown that refusal instead of the file.
One consequence is worth knowing before it looks like a broken tool. Where the whole granted folder is writable, that folder itself cannot be listed and a file cannot be created directly in it, because Landlock cannot grant a directory and withhold part of it, so the children are granted one by one. Everything one level down works normally.
Named local sockets are refused, always
A command cannot create a unix socket, whatever else it was allowed. That costs a local database over a socket file, docker, anything wanting X11, and ssh-agent. socketpair, the paired socket a build uses to talk to its own child processes, is untouched, so cargo build is unaffected.
Below Landlock ABI 9 (Linux 7.1) the compartment does not govern connecting to a socket file at all. We measured it. With the fence in force and the network refused, one message to the desktop's session bus starts a process that was never fenced and reads a file the fence denies. That is a way out of the compartment, and no leak at its edge, so it is closed unconditionally.
ssh does not work
Two independent reasons stop it, and closing either alone would not be enough. ~/.ssh is not in the compartment, because every path is built from the folder you granted. And the agent socket is a unix socket, refused above. git push over SSH fails for both reasons at once. Granting your home directory would remove the first reason and most of the point of the compartment, which is why the installer refuses it.
Operations that touch only the repository do work. git status, diff, log, add and a local commit are ordinary file operations inside the folder you granted.
A toolchain needs a grant
Open a diamond and the Workspace panel shows a Toolchains row with five buttons: Rust, Node, Python, Go, Git. All five start off, so cargo is refused until you turn Rust on, because ~/.cargo and ~/.rustup sit under your home directory, and your home directory is not in the compartment.
Git is the odd one out. git is already in the read-only base, which is why git status and a local commit work with no grant. The button adds your own configuration, read-only, meaning your name, your email, and the hooks core.hooksPath names. Your stored passwords and ~/.ssh are denied by name.
Three things about that are deliberate.
- It is never inferred from what was asked to run. A compartment that widened itself to fit the requested binary would be a compartment the model chooses, and the whole arrangement depends on the compartment being something the model cannot choose.
cargois reachable because you granted the Rust toolchain, and for no other reason. - It is per diamond. A grant belongs to one diamond, alongside the folders that diamond holds, and is taken back the same way.
- It is a short list of named directories, never the home directory. Rust grants
~/.cargo/binand~/.rustupread-only, makes the registry and caches writable because cargo cannot build without writing them, and denies~/.cargo/credentials.toml, where your crates.io token is written. The other four are drawn the same way, each leaving out the file that holds a secret.
The network
Whether a command may reach the network depends on the permission mode and on what the turn has already read. Under Ask and Guarded, once a turn has taken in content from outside (a web page it read or fetched, an email, the output of an earlier command), every command in that turn runs with TCP refused, and the daimon is told why, so it reports the cause instead of a broken project. Under Bypass the network is kept whatever the turn has read.
What is refused is TCP bind and connect, which is what Landlock governs. UDP and raw sockets are outside it.
32-bit binaries are killed
The system-call filter is compiled for one architecture and checks the architecture first, because the compatibility ABI numbers its calls differently, and a filter built for the wrong table would refuse the wrong things and report success. A program that declares a different architecture is therefore killed outright instead of filtered. That fails closed, and a build that execs a 32-bit helper will not work here.
The permission ladder
One setting has three rungs, and the axis is what Daimond does without asking. It starts on Guarded.
Ask every time
Every command is put to you before it runs, with the command and the directory on the prompt, and Daimond asks once in each conversation before it reaches the web. Choose it for watching each step, and for a machine holding something you cannot lose.
Guarded
The default. Commands run without asking. A turn that has read outside content loses the network, and Daimond asks once, in that conversation, before reaching anywhere the model chose. One yes covers every site until the conversation ends, a new chat or a fresh daimon asks again, and an unusually long address is always put to you on its own.
Bypass
Nothing is asked, apart from a click an unattended agent wants to make on a page, which is always put to you. Commands run, pages are fetched, and a build needing the network still has it on the turn's tenth command as well as its first. Chosen once, deliberately, then quiet.
A rung never changes what is possible. The compartment a command runs inside, the system-call filter under it, the folders a diamond is scoped to, a granted toolchain, the marking on content from outside, and the journal are identical on all three. A rung changes only what you are asked.
The rung is a word in the chat header beside the model, because a mode you have to remember is one you will be wrong about. Click it to change it, or press Ctrl K (Cmd K on a Mac) and type the rung's name.
Unconventional systems
The compartment is built out of a fixed list of absolute paths, and that list decides whether a given machine works. Run --report on yours before assuming either way.
Will not work
- NixOS and Guix. Programs live in
/nix/storeor/gnu/store, neither of which is granted, and/usr/binholds almost nothing. A bare name resolves throughPATHto a store path outside the compartment, and the command is refused by name. The app cannot widen it today. - A kernel without Landlock. That means before Linux 5.13, or compiled out, or absent from the active LSM list, or removed by a hardening patch. The hand reports
fence:noneand Daimond refuses every command instead of running one unfenced. Check thatlandlockappears in/sys/kernel/security/lsm. - An architecture other than x86-64 or aarch64. The filter's syscall numbers are written out per architecture and there is no third table. The hand will not guess one, so there is no filter, and without a filter no command runs.
Partly
- Linux 5.13 to 6.6. Files are fenced, but Landlock has no network rules before Linux 6.7, so a command that would have to run with the network withheld is refused outright, with a sentence saying so, instead of running with the network it was supposed to lose. Bypass works, and Guarded works until the turn reads something.
- Homebrew on Linux, Flatpak and Snap. None of
/home/linuxbrew/.linuxbrew, the Flatpak trees or/snapis granted, so a program installed that way is refused, while what your distribution put under/usris reachable in the same session. That concerns the programs a command runs. A browser packaged that way is a separate problem, and a fatal one, covered under Before you start. - Version managers other than the two that are known. The toolchain paths are a fixed list, and
~/.nvmand~/.pyenvare the only version managers on it.asdf,mise,volta,rbenvandsdkmaninstall under~/.asdf,~/.local/share,~/.volta,~/.rbenvand~/.sdkman, and nothing grants any of those. The Python grant covers~/.local/binand~/.local/liband pointedly leaves out~/.local/share.
Fine
- Debian, Ubuntu, Fedora, Arch, openSUSE with their own packages, on a kernel new enough for
--reportto name a Landlock ABI. - musl and Alpine, given the same.
- BusyBox coreutils. A multi-call binary decides what it is from the name it was invoked by, and the hand execs the resolved binary under the name that was asked for, so it still knows which tool it is meant to be.
A terminal, too
The Terminal panel opens a real terminal on the same machine, inside the same compartment, and you type into it. It exists because some programs ask their questions of a terminal and not of standard input. Keystrokes are not written to the journal. It needs a hand paired, like everything else here.
What is recorded
Every run (what was asked, what it was refused, what it returned) is appended to the journal in ~/.local/share/daimond/hand/journal/, as ordinary JSON lines you can read. The entries are hash-chained, and the journal sits outside every compartment, because the hand refuses a fence that would reach it. So a command cannot rewrite the record of itself, and no permission mode turns this off.
What is not protected
--report prints two lists, and the second is the reason to run it. Every entry in it was measured on a running kernel and not inferred, and it changes with your kernel, so read your own. The entries fall into a few groups.
- Asking about a file is not opening it.
statstill answers outside the compartment, so sizes, timestamps, ownership and the presence or absence of a file are readable. The compartment governs opening. - The system grant is wide.
/usr,/etcand/optread-only is what lets a command run at all, and it also means every world-readable file under/etccan be read and every tool on the machine executed./etcin particular holds a great deal of machine-identifying detail. - The filter is a deny-list. It removes named capabilities from a command and is not a syscall sandbox. Anything it did not name is permitted, including a call a future kernel adds.
- A filter cannot follow a pointer. It sees a syscall number and six registers, never what they point at, so it sees no path, no address and no filename. A refused
chmodis therefore refused everywhere and an allowed one allowed everywhere.chmod 777is refused, andchmod 644on a private key is not, because 644 adds no write and is the mode cargo sets on everything it unpacks. - Timestamps are not protected anywhere. A command can rewrite any file's times, on any part of the machine, because cargo cannot unpack a crate from the registry without doing it. That is the clearest place where keeping builds working cost coverage.
- Things already open, and names already made. A file descriptor opened before the compartment took hold keeps working, and a hard link made earlier is a second name for the same file. A command cannot create one into a denied subtree, and cannot undo one that exists.
- The one exception to “only inside your folder” is the private temporary directory described above.
Taking it back
Three different things can be taken back, and all three come up.
- Withdraw the permission. Click the Daimond Hands icon in the toolbar. Running commands on this computer is listed there beside the sites you have approved, with a Revoke button. Revoking stops whatever is running at that moment, immediately, and does not wait for the end of the current build.
- Take the folder away. Delete
root.txt. The hand then refuses to serve at all, whatever the browser says, and the refusal is a whole sentence. - Remove the registration. Run
hand/install/uninstall.sh, or with--dir DIRfor one directory. That deletes the file the installer wrote, so the browser can no longer start the hand. The binary is left where it is, because the script did not put it there.