kdos is the command-line front door to a KDOS machine. It answers what the machine is and what
is wrong with it, sets the accent, manages applications, keeps a live session across reboots,
upgrades the system, and gives scripts a handle on the desktop. The same binary also answers to
eleven other names, among them the service supervisor ksvc, the boot-slot tool kdos-bootctl
and the screenshot tool kdos-shot, and this chapter is the reference for all of them.
It is written for anyone working at a KDOS prompt, whether diagnosing a fault, changing the look
of the desktop or scripting it. If you are new to KDOS, run kdos help, then kdos status and
kdos doctor: those three say what the machine is, what is running and what is wrong, and
everything else here is something you reach for with a specific job in mind. Terms such as
box, pack, accent, surface and binhost are defined in the
glossary. A toast is the transient notification popup that
kdos-notifyd draws. The user-facing tour of the same commands, arranged by task, is
Administration.
kdos <subcommand> [arguments...]
kdos help [--pager]
kdos version
kdos with no subcommand is kdos help. -h and --help are accepted as spellings of help,
and -V of version. An unknown subcommand prints unknown command '<name>' — try: kdos help
and exits 1. Colour is used only when standard output is a terminal, so output piped into another
program is plain text.
Most subcommands follow one exit-status convention, so a script can test the answer without parsing text:
| Exit | Means |
|---|---|
| 0 | Nothing to report: clean, up to date, done |
| 1 | Something was found (a warning, a stale process, a vulnerable package), or the action failed |
| 2 | The command was used wrongly, or could not run at all |
| 127 | The program this subcommand hands over to is not installed |
Where a subcommand differs, its section says so.
| Subcommand | Answers |
|---|---|
app |
Install, launch, remove, export and import containerised applications |
appid |
Do launcher icons match the windows they open? |
clone |
Copy this boot medium onto another, verified by read-back |
cve |
Offline vulnerability tracking against a vendored database |
doctor |
Check the machine for the faults KDOS is known to have |
explain |
Browse the recorded reasons behind the system’s design |
help |
The command list and the key-binding sheet |
hey |
Ask the compositor about windows, outputs and boxes |
march |
Measure whether a processor tuning flag is worth keeping |
menu |
Open the palette on a named route |
notify |
Raise a toast, or drive the notification daemon |
oracle |
One recorded lesson, picked for today |
panel |
Put the bar away and bring it back |
persist |
Keep a live session’s writes across a reboot |
places |
The places column, and the way to add to it |
rebuild |
Rebuild KDOS from the sources on this machine, offline |
remind |
Schedule a toast for later |
restarts |
Which running processes still use code an upgrade replaced |
sandbox |
Run a native program under a Landlock profile |
settings |
The control centre, or one of its pages |
share |
Send a file to another machine over one code word |
speech |
Fetch and manage transcription models |
status |
What this machine is and what it is running |
stutter |
Why the desktop hiccuped, with the application’s name |
theme |
Set, preview and audit the accent |
thumb |
Put a thumbnail in the shared cache |
toggle |
Stay-awake, night light and do-not-disturb |
trash |
The desktop’s trash, from a prompt |
update |
Check for and apply upgrades across the A/B slots |
version |
Kernel, C library, userland, session and accent |
why |
What provides a path or a port, and why it is that way |
The other names on the binary (the service supervisor, the boot-slot tool, the screenshot tool and the rest) are covered in The other names on this binary.
Tab completion in bash comes from /usr/share/bash-completion/completions/kdos, and it covers
less than this table does, so a subcommand that Tab does not offer can still exist. For the first
word it offers ten subcommands: help, theme, status, doctor, version, app, share,
remind, notify and toggle. After the first word it completes these arguments only:
| After | Offers |
|---|---|
theme |
phosphor, amber, ice, bone, next, prev, list, current; the other four accents are not offered |
status |
--bar |
help |
--pager |
remind |
in, at, tomorrow, ls, clear |
notify |
--time, --battery |
The same file completes the first word of kdos-shot from region, screen,
window, qr and colour (it omits full, and colour is a mode kdos-shot refuses), and
completes kdos-share with file names plus its --clipboard flag. bash-completion loads a
completion file on demand, by the name of the command being completed, and there is no file named
kdos-shot or kdos-share: those two complete only after kdos has been completed once in the
same shell.
The shipped ~/.config/kdos-comp/rc.xml binds these commands, and some of the other names on
this binary, to keys:
| Chord | Runs |
|---|---|
Super+Space |
kdos-palette, the program kdos menu opens |
Super+Ctrl+c / Super+Ctrl+h |
kdos-palette --route capture / --route setup |
Super+Shift+Space |
kdos panel toggle |
Super+Ctrl+Alt+t / Super+Ctrl+Alt+b |
kdos notify --time / --battery |
Super+x / Super+Shift+x |
kdos notify --dismiss / --dismiss-all |
Super+Ctrl+x / Super+Alt+x |
kdos notify --dnd / --raise |
Super+Ctrl+i |
kdos toggle stay-awake |
Super+Ctrl+Shift+n |
kdos toggle night-light |
Super+Ctrl+r / Super+Ctrl+Alt+r / Super+Ctrl+Shift+r |
kdos remind --ask / ls / clear |
Print / Shift+Print / Super+Shift+p |
kdos-shot screen / region / region |
| The play, stop, next and previous media keys | kdos-mpctl toggle / stop / next / prev |
kdos help # print the sheet
kdos help --pager # the same, through $PAGER (default less)
The sheet has three blocks:
kpkg for the host system
(compiled from source against musl), kdos app for applications, and kdos-box for
environments. Most questions of the form “how do I install X” start with which of the three X
belongs to.kdos-display, kdos-power, kdos-energy, kdos-res, kdos-desktop, kinstall).kdos-keys is installed, a first line points at
Super+F1, which shows the full card generated from your own rc.xml. The table itself is
fixed text, so it answers on a machine with no desktop installed.--pager renders the sheet into memory and feeds it to $PAGER on standard input, adding -R
only when the pager is less, because another pager may not accept the flag. If the pager is not
installed or fails, the sheet is printed to standard output instead. The KDOS Help menu entry
runs kdos help --pager in a terminal.
This book is on every image, as HTML under /usr/share/doc/kdos: open
/usr/share/doc/kdos/index.html in a browser for the title page and the table of contents.
man 7 kdos says where it is and lists its chapters. Every kdos-* command has a manual page,
man kdos-appbox for example, whose description is the section of the book that documents the
command, so the page and the book cannot disagree; apropos kdos lists them all with the one-line
description from the command index. The kdos-doc port writes
all of it from docs/ at build time; see Developing.
kdos status
kdos status --bar # one line: running boxes and exported applications
Prints what this machine is and what it is running:
| Line | From |
|---|---|
KDOS Linux <release> |
The running kernel |
Theme |
The accent in force |
Packages |
Entries in /var/lib/kpkg/db |
Exported apps |
.desktop files in ~/.local/share/applications |
Alien apps |
Non-comment lines in /usr/share/kdos/alien-apps, the table baked at build time |
Session |
tty when WAYLAND_DISPLAY is unset, kdos-comp when the compositor is running, wayland (not kdos-comp) otherwise |
| CONTAINERS | podman ps -a: every container, its status and image |
| EXPORTED APPS | The exported entries by name |
The alien application count reads the baked table rather than asking the container engine, so it answers on a machine where no box has ever been created.
--bar prints N box · M app: the number of running containers and of exported entries.
kdos version
kdos -V
KDOS — KD's Homebrew Linux Distro
kernel 7.2.11
libc musl
userland GNU
session kdos-comp (bone)
The session line reads kdos-comp whenever WAYLAND_DISPLAY is set and none otherwise,
followed by the accent in force. It does not check that the compositor is actually running;
kdos status does, and reports wayland (not kdos-comp) when it is not.
kdos version does not print the release number or the commit the image was built from. The
release is VERSION in /etc/os-release.
kdos doctor [--json]
kdos doctor --cve [...] # hands the rest of the line to kdos cve
Checks the faults that are known to occur on KDOS rather than running a generic health sweep. Each check reports one of three levels:
ok: checked and fine;warn: checked and wrong, with the fix where there is one (“start with: service powerd
start”, “run: kdos theme phosphor”);skip: could not be checked here, with the reason. Much of the hardware section cannot be
answered in a virtual machine (no SOF audio device, no wireless, no NVIDIA GPU, no boot
medium), and a green line for something never tested would be as misleading as a warning that
made every virtual machine look broken.| Section | Checks |
|---|---|
| Kernel | The Landlock ABI level (and whether Landlock is compiled out or merely disabled), and whether the initramfs carries microcode for this processor’s vendor |
| Hardware | The wireless regulatory database, its signature and the regulatory domain; SOF audio firmware and topologies; NVIDIA GSP firmware when nouveau is bound; whether the calling user can open each attached serial, camera and instrument device (/dev/ttyUSB*, /dev/ttyACM*, /dev/video*, /dev/usbtmc*), naming the owning group of any it cannot; and, on a live session, whether the boot medium is reachable at /mnt/iso |
| Boxes | Whether erofs is available to the kernel, whether kdos-packd answers and by which mount route, whether the user is uid 1000 (packs are built for it), whether the home directory’s filesystem can hold a container’s writable layer, and whether every mounted pack still has a file behind it |
| Session | Whether the compositor’s socket exists (not only whether WAYLAND_DISPLAY is set), XDG_RUNTIME_DIR, and whether kdos-comp, kdos-shell and xdg-desktop-portal-wlr are running |
| Containers | That the mount-namespace root is /, and the /etc/subuid and /etc/subgid entries rootless containers need; run as root, a warning instead |
| Desktop | The accent state file, the foot theme, the KDOS scheme in ~/.config/kdeglobals, an Xwayland socket in /tmp/.X11-unix, five setuid helpers (below), /etc/subuid and /etc/subgid, kdos-powerd, kdos-energyd (or the absence of any RAPL energy counter), fcitx5 when installed, kdos-oomd, the compositor’s frame-timing and command sockets, kdos-keys, the screen-capture portal and its kdos-portals.conf, and ~/.local/bin on PATH |
| Security | Whether the kdos password is still the shipped default. The section appears only when /etc/shadow is readable, so an ordinary user gets no section rather than a check that pretends it looked |
The session check looks for the socket because a session that died leaves WAYLAND_DISPLAY set in
every shell that inherited it. A doctor that reported that as fine would send you looking
everywhere but at the dead compositor. The Xwayland check looks for the socket for the opposite
reason: the compositor exports DISPLAY only to what it starts, so a shell reached over ssh has
none while Xwayland is running.
On a live session the box check reports that the home directory is on an overlay, so a box’s
writes go to memory and end with the session. To keep them, create a persistence store with
kdos persist.
Losing a setuid bit is silent, and each of the five fails differently:
| Helper | Without its bit |
|---|---|
kdos-checkpass |
Every password at the lock screen is refused |
kdos-resctl |
kdos-res cannot end or renice a process it does not own |
newuidmap, newgidmap |
The container engine exits 125 and no box starts |
dbus-daemon-launch-helper (also needs group messagebus) |
No D-Bus system service is ever activated: the Wi-Fi supplicant, fingerprints and firmware updates among them |
| Exit | Means |
|---|---|
| 0 | No warnings |
| 1 | At least one warning |
| 2 | Unknown option |
The exit status is the same in text and JSON mode. --json prints
{"checks": [{"section", "level", "message"}, …], "warnings": N}, one record per line of the
text report.
--cve hands the rest of the arguments to kdos cve, and the exit status is
kdos cve’s. The vulnerability report is a table with its own exit code and database date, and
folding it into doctor’s lines would flatten “17 packages are behind a recorded fix” into one
warning.
kdos theme # print the accent in force (also: show, current)
kdos theme <name> # phosphor amber ice bone norton borland perfect paper
kdos theme list | next | prev
kdos theme style <file>
kdos theme --audit [accent] # also: audit
kdos theme --preview <accent>
KDOS has eight accents, compiled into libkcolor. bone is the default: a machine with no
accent state file uses it. list marks the one in force with * and prints each accent’s theme
name beside it; next and prev step through the table and wrap around. The user-facing guide is
Theming.
The desktop’s own programs carry the palette in libkcolor and need only the accent’s name.
Everything else a theme switch writes is for software that is not KDOS’s and cannot be told.
Setting an accent regenerates those files for your user and then repaints the running desktop, in
this order:
~/.config/kdeglobals (the terminal keys name
foot, so a KDE application opens the desktop’s terminal), the qt5ct and qt6ct palettes
and settings, the window-frame theme (~/.config/kdos-comp/themerc-override), and the
configuration of foot, kdos-term, bat, micro, helix, neovim, delta, newsboat, aerc, fzf,
tmux, btop, mc, yazi, starship and LS_COLORS. The files applications also write into
(kdeglobals, qt5ct.conf, qt6ct.conf, mc’s ini, starship.toml) are merged, keeping
every key the theme does not own;
Theming lists which.~/.cache/kdos/wallpaper.png.~/.cache/kdos/theme, atomically.SIGHUP goes to each long-lived KDOS surface by exact name: kdos-shell, kdos-desk,
kdos-notifyd, kdos-slit, kdos-res, kdos-comp and kdos-term.kdos-power accent <name> asks kdos-powerd to retint the boot menu and the splash. When the
daemon does not answer (on the live medium, or inside a box) the command prints
boot menu and splash unchanged and the accent is still applied everywhere else.The order matters. The wallpaper and the state file are both inputs to the signal, so both are written before it is sent; a surface that received the signal first would re-read the accent it already had. The state file is written atomically because the surfaces signalled in step 4 read it the moment the signal lands, and a truncating write would show them an empty file.
The match on names is exact for two reasons. The panel, the desktop icons and the notification
daemon are three names of one binary, so signalling only kdos-shell would leave the desktop and
any toast in the old accent. And kdos-desk is a substring of kdos-desktop-start, the shell
script that owns the session, which would die of a signal it does not handle. A program is on the
list only if it installs a SIGHUP handler, because the default action of the signal is to end
the process.
Programs that are not KDOS surfaces pick up the change when they next start: foot, btop and every application in a box. Starship picks it up at the next prompt.
Because the retinted wallpaper in ~/.cache/kdos/wallpaper.png takes precedence over the
wallpaper = key in comp.conf, an accent switch replaces a picture of your own with the
retinted default. wallpaper = none is never overridden.
Theming explains how to keep your own picture.
--preview <accent> does only steps 3 and 4: it writes the state file and sends the signal. No GTK
stylesheet, icons, cursors or foreign configuration files are generated. Those take seconds and are
read by programs that are not running, so a preview repaints every KDOS surface at once and leaves
GTK and Qt applications, boxed or native, in the old accent. It is what the arrow keys in the
kdos-theme picker run, and it is why that picker restores the accent it opened on unless you tell
it to keep one.
kdos theme style <file> applies a shareable look: an accent plus the settings that make a
desktop someone’s own, in one flat file. Each line is key = value or key: value; whichever
separator comes first on the line wins, so chrome_font = Terminus:pixelsize=64 keeps its colon.
Blank lines and lines starting with # are skipped.
accent = amber
crt = 40
crt_scanlines = 50
chrome_font = Terminus (TTF):pixelsize=32
osd.bg.color: #1a1a1a
| Key | Goes to |
|---|---|
accent |
The accent; without one, the accent in force is kept |
crt, crt_scanlines, crt_curve, crt_fullscreen, chrome_font, clock_format |
Rewritten in ~/.config/kdos/comp.conf, keeping every other line as it was; a key the file lacks is appended. The crt* keys take effect on the signal when the session started with the pass on; raising crt from 0 takes a new login, because the pass is created at startup or not at all. chrome_font and clock_format take effect at the next login |
Any dotted key (osd.bg.color, …) |
A window-frame theme line, kept in ~/.config/kdos/style-themerc and appended after the generated block every time the theme is regenerated |
| Anything else | Ignored, with a warning naming the key |
A style with no dotted keys removes style-themerc, because a style is a whole look rather than a
patch on the last one. The accent is then applied exactly as kdos theme <name> applies it.
--audit checks that every generated file still matches the palette. It runs the same generators
with $HOME and the XDG directories pointed at a temporary directory and compares the result with
your files byte for byte, symbolic links included (the icon theme is mostly links). Anything that
differs, differs from what this machine’s palette produces now. It writes nothing outside its
temporary directory and signals nothing.
Give it an accent (kdos theme --audit amber) to see what would change if you switched to that
accent, before you switch.
| Exit | Means |
|---|---|
| 0 | Every generated file matches |
| 1 | Something has drifted |
| 2 | The audit could not run |
kdos toggle # list them and their state
kdos toggle <name> # flip it
kdos toggle <name> on|off # set it
Three switches, each a flag file under ~/.local/state/kdos/toggles/ (or
$XDG_STATE_HOME/kdos/toggles/) whose presence means on. There is no file format: a toggle is a
name and whether its file exists, which a shell script, a key binding and a surface can all read.
| Name | Does | Read by |
|---|---|---|
stay-awake |
Never dim, lock or blank on idle | kdos-comp’s idle policy, each time it re-arms its idle timer |
night-light |
Warm the palette | Every surface, on the retint signal |
dnd |
Hold notifications back | kdos-notifyd |
An unknown name, or a second argument other than on or off, exits 2. The files and their
readers are also listed in Configuration.
night-light is read on the same retint signal kdos theme sends, so the toggle writes its file
first and then sends the signal. The other two are read by their consumers without a signal.
dnd is the notification daemon’s only Do Not Disturb flag, so the notification centre’s own
button and this command change the same state.
Typed at a prompt, the new state is printed. Run from a key binding, where standard output is not
a terminal, it is shown as a one-line toast instead, because two of the three switches change
nothing visible and a silent keystroke cannot be told from a broken one. Super+Ctrl+i is
stay-awake and Super+Ctrl+Shift+n is night-light; Super+Ctrl+n is taken by kdos-note.
kdos notify <summary> [body]
kdos notify --time | --battery
kdos notify --dismiss | --dismiss-all | --raise | --dnd
With a summary, this raises a toast, which is useful at the end of a long job:
make && kdos notify "the build finished"
The toast is sent through kb_notify() in libkbase, the same call a terminal makes for a
program’s OSC 9 notification, so the two look and behave the same.
--time and --battery compute their text, which a key binding cannot do: rc.xml binds a fixed
command string. They answer the two questions a bar answers by being on screen, for a desktop
whose bar can be put away.
--time shows the time as %H:%M, with the full date as the body.--battery shows the charge and state, plus wear when the kernel reports it (for example
Battery 90% / Discharging, 70% of its original capacity). A battery at 90% charge can hold
only 70% of what it held new, and someone deciding whether to unplug wants both numbers. With
no battery it says On mains or No battery. The figures come from the kernel through
libkproc, the same reader the resource monitor uses; kdos-energyd estimates what programs
cost and holds no battery state.--dismiss, --dismiss-all, --raise and --dnd send one line (dismiss, dismiss all,
raise, dnd toggle) down $XDG_RUNTIME_DIR/kdos-notify.sock, the socket kdos-notifyd
serves. The daemon owns the toasts, the history and the Do Not Disturb flag; these flags are the
command a key binding needs to reach it. When no daemon is listening they print nothing and exit
1, because a chord has nowhere to show an error.
--raise moves the newest entry out of the history and back onto the screen rather than copying
it, so dismissing it again does not file a second copy. It comes back without its buttons: the
notification it came from is closed, and its actions belonged to the program that sent it.
kdos remind in 20m tea
kdos remind at 15:30 call back
kdos remind tomorrow 9 stand-up
kdos remind ls | clear
kdos remind fire ID
kdos remind --ask # the key binding's form
Schedules a toast for a later time. Everything after the time is the text; a -- before it is
optional.
| Form | Means |
|---|---|
in N<s\|m\|h\|d> |
That long from now, at most 300 days |
at HH[:MM] |
The next time the clock reads that; a time already past today means tomorrow |
tomorrow HH[:MM] |
That time tomorrow |
in is capped because a timer pattern carries a month and a day but no year, so past a year the
same pattern would mean a different date. Any other form is refused rather than guessed at.
Each reminder is a row in your personal timer table,
~/.config/kdos/timers.d/remind-<id>.timer (see
Configuration), so it
survives a logout and comes back with the session. snooze does the waiting.
The session reads the timer table only at login, so a reminder set now would wait for the next
login. kdos remind therefore starts its own snooze as well, and the next login starts another
from the same file. Only one delivers, because kdos remind fire removes the file before it
returns and the other then finds nothing: the file is the reminder. With no gdbus or no session
bus when the time comes, nothing is delivered and the file stays.
The text is a comment line in the file, because both timer parsers split a row into words with no
quoting, so "make tea" would arrive as two broken words; ls reads the text back from there.
snooze -t counts from the file’s modification time, the moment the reminder was set, with a day
of slack, so a reminder due while the machine was off fires when it comes back.
clear removes every reminder file. An armed snooze is left running; when it wakes it finds no
file and delivers nothing, by the same test fire applies.
--ask opens a one-line kdos-prompt and takes what you type (in 20m tea) as the whole
argument. It exists because a key binding runs one command with no shell, so
kdos-prompt --input | kdos remind cannot be a binding.
ls (when there is at least one reminder) and clear answer with a toast when standard output
is not a terminal, because the key bindings that run them have nowhere to print. With no
reminders, ls prints no reminders to standard output wherever it goes.
kdos panel toggle
Hides the bar, or brings it back. It sends SIGUSR1 to every process whose name (comm) is
exactly kdos-shell, which reaches the panel and not the desktop icons or the notification
daemon, the other names of the same binary. It is bound to Super+Shift+Space. The command walks
/proc itself rather than running pkill -USR1: a pkill that reads -U as its user option
refuses -USR1 as -U SR1, so the signal would depend on which pkill is installed.
A bar put away this way stays away while the pointer crosses the bottom row, even with autohide on. Nothing is reported when no panel is running. Any other argument exits 2.
kdos menu summon <route> # open the palette on a named place
kdos menu toggle [<route>] # close it if it is open, else open it
A route is a name for a place in the system, such as setup.network, defined in
/etc/kdos/menu.conf and your own ~/.config/kdos/menu.conf; see
Configuration.
A script holds a route rather than a chord, because chords can be rebound and menu rows move.
Both verbs open kdos-palette, the program Super+Space opens, with --route <route>, so
summoning a route is a search with the name already typed. toggle first runs
pkill -x kdos-palette; if that found a palette, it is closed and the command returns, and
otherwise a new one is opened. The match is on the exact program name, so the palette must be
started as kdos-palette and not through a wrapper. Exit 2 for a missing or unknown verb, 127
when kdos-palette is not installed.
kdos settings # the grid
kdos settings hardware # straight to a page
Replaces itself with kdos-settings, passing a page name through as --page. The page name is
not checked here: kdos-settings owns the list of pages, and a second copy would be a second list
to keep in step. When kdos-settings is not installed it says so and exits 127.
kdos places # the column, one per line
kdos places add DIR [NAME] # keep one
Prints the places column the desktop shows, the same list kdos-start, kdos-menu and the file
chooser (kdos-pick, Ctrl+P) show, read through the same kxdg_places() call. Each line is the
name and the path.
add keeps a folder in that column, appending NAME = PATH to ~/.config/kdos/places. The name
defaults to the folder’s own name and may not contain = or a newline. The path is made absolute
before it is stored, because the row is read back by programs with a different working directory,
so kdos places add . means the directory you are in. A folder that is already a place is
reported and not added twice. The desktop can do the same from its context menu; F2 in mc
runs this command, because mc has no other way to do it.
kdos thumb <file>...
kdos thumb --path <file> # where its thumbnail would be
kdos thumb --ppm <file> <out.ppm> # a small P6, for a caller with no image library
Makes a 128-pixel picture of a file in the shared freedesktop thumbnail cache, where file managers, image viewers and the KDOS desktop all look.
A thumbnail lives at $XDG_CACHE_HOME/thumbnails/normal/<md5>.png, where the hash is taken over
the file’s escaped file:// URI. The escaping and hashing come from libkbase, shared by every
KDOS caller, because one character escaped differently makes a thumbnail nothing else can find.
Each thumbnail carries Thumb::URI and Thumb::MTime. A reader checks them before trusting the
picture: without the modification time an edited file’s old thumbnail would be served forever,
and without the URI a hash collision would go unnoticed. The file is written with mode 0600,
because a thumbnail can reveal what a protected file contains, and renamed into place so no
reader finds half a picture.
A PNG is its own source. Every other picture comes from a helper already on the image:
ffmpegthumbnailer for video, pdftoppm for PDF, and magick for every other still (JPEG, GIF,
WebP, camera raw). A helper must write to the file name it is given, which is why exiv2 is not
used: its -ep1 picks its own output name and extension.
--ppm writes a small binary PPM for a caller with no image library. kdos-pick’s preview pane
uses it.
kdos trash <file>... # move to the trash
kdos trash --list # what is in it (also -l)
kdos trash --restore <name> # put one back where it came from
kdos trash --rm <name> # delete one for good
kdos trash --empty [-y] # delete all of it
Manages the freedesktop trash at ~/.local/share/Trash from a prompt. --list shows each entry
with its size, deletion time and original path, newest first. --empty is the only irreversible
verb that acts on everything, and it asks first unless given -y; with no terminal to ask on, it
refuses rather than assume yes. To browse the trash on the desktop, open
kdos-trash, which the desktop’s Trash icon opens.
This command, the desktop and kdos-trash share one implementation in libkbase, so deleting at
a prompt and deleting on the desktop are the same recoverable operation. That implementation
guarantees:
notes.txt becomes notes.txt.1, up to .999, then a
name derived from the clock. If even that is taken the call fails with EEXIST rather than
destroy the earlier file and its restore record.--restore
says so and leaves both files where they are.kdos share FILE...
kdos share --clipboard
kdos share # asks kdos-pick for a file
Sends files to another machine, paired by one code word. croc does the transfer; this command is
the desktop around it. There is no account and no server: the two ends derive a key from the code
word. The other machine receives with croc <code> in a terminal, and the sending window says so.
Up to 32 files go in one transfer; a file:// URI is accepted wherever a path is.
On KDOS, /usr/bin/croc is a wrapper that always passes --local, so a transfer stays on the
local network and reaches no relay. croc-relay is the same binary with upstream’s default, for
when you mean to use the public relay. The sending window therefore shows the code word and not
the getcroc.com link croc prints beside it. croc is built with upstream’s croc_no_tailcat tag,
which leaves out the Tailscale transport and croc ssh, because they run over Tailscale’s public
servers and take no --local.
Started without a terminal (from the desktop’s icons, the file chooser or a menu), the command
opens a terminal window (foot, inside a Wayland session) and runs itself inside it, so croc runs
in the foreground where its own progress output is visible unchanged. At the end that window
waits for Enter, so the result stays on screen. Typed at a prompt, it runs where it was typed.
Outside a Wayland session there is no terminal emulator to open, so it also runs in place.
When croc prints the code word, the command also raises a toast naming it and draws the code as a
QR code, in full blocks, so a phone can scan it. The QR is drawn only when it fits the window
whole, because a partial QR cannot be read. The code is fed to qrencode on standard input rather
than as an argument, because it is the transfer’s whole secret and a process’s command line is
readable by every other process.
--clipboard sends the clipboard as clipboard.txt. croc sends files, so the text is written to
$XDG_RUNTIME_DIR/kdos/clipboard.txt with mode 0600 rather than beside your documents, and it
is read once, before any window opens.
The Share row on the desktop’s icons and in the file chooser (Shift+F10) appears only when a
program called kdos-share is on PATH; mc’s F2 menu is a text file and lists it
unconditionally. kdos-share is a link to this binary, and
kdos share is the same program under the spelling a person types.
kdos hey list [--json]
kdos hey run <action> <id> [--json]
kdos hey outputs [--json]
kdos hey boxes [--json]
Asks the compositor about the desktop, so a script can find a window and act on it. It sends one
JSON request line down the compositor’s
command socket, $XDG_RUNTIME_DIR/kdos-cmd.sock, and reads one
line back.
| Verb | Prints or does |
|---|---|
list |
Every window: id, workspace, state letters (F focused, m minimised, M maximised, X fullscreen, s shaded), app_id, box and title (cut to 48 characters) |
run <action> <id> |
Runs a labwc action (the window-action vocabulary kdos-comp takes from labwc, the compositor it is derived from) on the window with that id: Close, Focus, Iconify, ToggleMaximize, ToggleShade, ToggleFullscreen, Raise and the rest |
outputs |
Each output’s name, size, scale and position |
boxes |
The boxes that currently have a window on screen |
--json prints the compositor’s reply as it arrived. For list that document also carries each
window’s geometry, process id and instance, which the table leaves out.
kdos hey list # find the id
kdos hey run Close 42
Only actions that need no argument can be run this way; Execute and SnapToEdge, for example,
are refused by the compositor. A window’s box comes from its Wayland security context, not from
walking its process tree, and a host window shows -. The box column is never truncated, because
a box is named after its pack and a cut name would read as a window with no box. kdos-box gc
uses boxes to ask whether a box still has a window before stopping it.
Exit 0 on success, 1 when the compositor is unreachable or refuses the request (for example, no
window with that id), and 2 for a bad verb or an id that is not a number. With --json a refusal
is in the printed document and the exit status is 0; only an unreachable compositor exits 1.
kdos app list [--all]
kdos app search <text>
kdos app info <id> # also: show
kdos app groups
kdos app install <id|group>... [--dry-run]
kdos app install --pending
kdos app launch <id>
kdos app remove <id|group>...
kdos app export <file.ktar> <id|group>...
kdos app import <file.ktar> [<id>...]
kdos app tui add <name> <command> [--float] [--size COLSxROWS] [--icon NAME] [--category X]
kdos app tui rm <slug>
kdos app tui ls
The command-line half of the application store, over the same catalogue the graphical store
reads (/usr/share/kdos/appstore/catalogue, asked through kdos-appbox catalogue). The user’s
guide is Applications, and the program that does the work is
kdos-appbox.
| Verb | Does |
|---|---|
list |
What is installed. --all lists the whole catalogue: 75 entries, the 73 applications and 2 datasets, which would bury the handful you have |
search |
Entries whose id, name, category or tagline contain the text, ignoring case; exit 1 when none do |
info |
One entry: name, category, state, the row it is built on, and its size labelled an estimate |
groups |
The catalogue’s named groups, which install, remove and export accept in place of an id |
install |
Builds the application on this machine, which needs a network. --dry-run shows what would be built. Installing an application that is already installed rebuilds nothing: each image in its chain prints kdos/<id> is already here, the box create is refused because the box exists, and the command reports <id>: the image built and the box did not and exits non-zero. To take a newer build, remove the application and install it again, which rebuilds its own image and any runtime image no remaining box uses. There is no update verb |
install --pending |
Installs what the installer was asked for and could not install itself, listed in /var/lib/kdos/apps-pending. The command deletes the file after a clean run, but the installer writes it as root and kdos app runs as the user, so the deletion fails and the file stays; a second run builds the whole list again |
launch |
Installs the application if needed, then runs its launcher from ~/.local/bin |
remove |
Uninstalls |
export |
Writes the named applications into one .ktar set for another machine |
import |
Installs from a .ktar set, offline, verified by kdos-packd as it installs each pack; name ids to take only some |
Every verb that builds, removes, exports or imports runs kdos-appbox as a child, so the command
line and the store’s buttons cannot disagree about what installing means.
Sizes are always labelled estimates. What apt resolves on the day depends on the snapshot, and shared runtime layers are counted once on disk however many applications use them.
Of these verbs only import needs kdos-packd, because it is the only one that mounts anything;
the others work when the daemon is down. The path is never passed to the daemon: the set is
copied into the daemon’s own staging directory and only a file name inside it is handed over, so
a member of wheel cannot use it to mount an arbitrary device on an arbitrary directory.
tui turns a terminal program into a menu entry without editing a file by hand:
kdos app tui add "Disk usage" ncdu --float --size 100x30 --icon disk-usage-analyzer
kdos app tui ls
kdos app tui rm disk-usage
add writes ~/.local/share/applications/kdos-tui-<slug>.desktop, where the slug is the name in
lower case with every run of other characters turned into one dash. The entry has
Terminal=true, X-KDOS-TUI=true, Icon= (default system-run), Categories= (default
Utility) and, when asked, X-KDOS-Float=true and X-KDOS-Size=COLSxROWS, the two KDOS keys
that say how the window opens. --size must be at least 4x2. The Exec line is written one
quoted field at a time, so a path with a space in it survives. Every % in the command is written
doubled, because a single % starts a field code in a desktop entry. An entry added this way
opens its program; it is not a file handler.
rm accepts the slug with or without the kdos-tui- prefix, and deletes an entry only if it
carries X-KDOS-TUI=true, so it cannot delete an application the image shipped. The prefix keeps
new entries from shadowing shipped ones: a file in your data directory with the same name as one
in /usr/share/applications would take the shipped entry off the menu.
tui writes only in your own data directory and needs neither kdos-packd nor membership of
wheel.
kdos sandbox [profile] [--read P] [--write P] [--no-network] [--tcp PORT] -- <cmd> [args...]
kdos sandbox [profile] [options] --explain
Runs a native (not boxed) program under a filesystem and network filter built with Landlock, the kernel’s unprivileged access-control interface. It needs no container and no root. Every option maps onto a Landlock access right; confinement Landlock cannot enforce is not offered as an option.
| Option | Allows |
|---|---|
--read P |
Reading under P |
--write P |
Reading and writing under P |
--no-network |
No TCP bind or connect at all (needs Landlock ABI 4) |
--tcp PORT |
With --no-network, still allow outgoing TCP connections to PORT; without it the network is not restricted and this has no effect |
--explain |
Print what would be allowed and what Landlock cannot police (UDP, unix sockets by path, processes and IPC), and run nothing |
Everything else is refused, except a fixed base: /usr, /lib, /lib64, /bin, /sbin,
/etc and /proc are readable, and /dev/null, /dev/zero, /dev/full, /dev/random,
/dev/urandom, /dev/tty, /dev/ptmx and /dev/pts are writable. The rest of /dev is not,
because that would hand over every disk and input device. A base path this system lacks is
skipped; a path you name that does not exist is an error. At most 64 paths of each kind are
accepted.
A profile is ~/.config/kdos/sandbox/<profile>.conf, parsed and never executed, one
key = value per line; a key it does not know is reported and skipped. See
Configuration.
read = /home/kdos/Music
write = /home/kdos/.cache/cmus
network = off
tcp-connect = 6600
The command is executed directly, never through a shell.
It refuses to run rather than run unconfined: with no Landlock in the kernel it exits with
no Landlock (<reason>) — refusing to run unconfined, and --no-network on a kernel below ABI 4
exits with --no-network needs Landlock ABI 4, kernel has <n> — refusing to run with the network
open. --explain reports the same conditions instead of running: it exits 1 when Landlock is
unavailable, and marks the network REQUESTED OFF BUT NOT ENFORCED below ABI 4. A Landlock
ruleset cannot be inspected from outside the process, so --explain describes what will be asked
for, not what a running program holds.
kdos appid [--quiet]
Checks that the identifier each window presents matches the file name of an installed launcher. A dock matches a running window to its launcher by that identifier, so a mismatch shows a second, generic icon beside the pinned one.
The window side comes from ~/.local/share/kdos/observed-app-ids, a ledger the compositor appends
to the first time each application maps a window. When there is no ledger yet it falls back to
the windows open now, through kdos hey, and says so. The ledger covers every window this
machine has shown; the fallback covers only what is on screen. The launcher side is every
.desktop file in /usr/share/applications, /usr/local/share/applications and
~/.local/share/applications.
Each observed identifier is ok or MISMATCH; a launcher never yet observed is counted as not
yet observed rather than reported as fine. --quiet (-q) leaves out the ok lines. The fix
for a mismatch is to rename the .desktop file to the observed identifier: the file name is what
is matched, not Name and not StartupWMClass. Exit 1 when a mismatch is found, 2 for a bad
option.
kdos why <path|port>
kdos explain [topic]
Both read the reasons corpus: 78 short documents installed under /usr/share/kdos/reasons, each
stating a constraint in the system and its consequence. $KDOS_REASONS names another directory,
which is how the corpus is read from a checkout. Each document starts with a header of
title:, cite:, path: and port: lines.
why answers what provides a path or a port and why it is configured that way. For an absolute
path it first prints the owning package and version, from the package database’s own file list,
and the recipe it was built from. A path no package owns is reported as none — provided by fs/,
or not installed: files from the base filesystem tree are copied into the image rather than
installed by a package, so that is a common answer. It then prints in full every reason whose
path: names the path or a directory above it, or whose port: names the port.
explain browses the corpus. With no topic it lists every reason’s title. With a topic it prints
in full each reason that mentions the topic anywhere, ignoring case, so kdos explain musl finds
the reasons that mention musl in passing. Exit 1 when no reasons are installed.
testing/preflight.sh fails when a reason’s path: or port: names something the tree does not
have, so a reason cannot describe a file or a port that has gone.
kdos oracle [--plain]
Prints one reason from the same corpus as title · citation. The choice is keyed on the day
combined with the boot’s identifier, so the line stays the same all day and changes after a
reboot. On a terminal it is printed in the accent’s secondary colour; --plain prints it without
colour, which is also what happens when the output is not a terminal. The login banner shows the
same line. Exit 1 when no reasons are installed.
kdos restarts [--quiet] [--json]
Lists running processes that still use code an upgrade has replaced or removed:
These are running code that has been replaced or removed.
They keep the old version until they restart:
pipewire (pid 2841) pipewire, alsa-lib
kdos-shell (pid 3122) kdos-shell
2 processes. Restarting them puts the new code in effect.
After an upgrade, a process that had the old shared library mapped keeps running it until it
restarts. That is what makes upgrading a running system safe, and it also means a security fix is
not in effect for that process yet. This command joins two exact sources: /proc/<pid>/maps,
where the kernel marks a mapping whose file was deleted, and kpkg’s file manifest, which says
which package owned it. Only mappings under /usr, /lib*, /bin, /sbin and /opt are
considered, because a deleted shared-memory mapping is normal. A mapping no installed package owns
is reported as no installed package owns it rather than guessed at, and a process whose maps
cannot be read (another user’s, or one that exited during the scan) is skipped.
The list stops at 256 processes and says so. --quiet (-q) prints nothing. --json prints
{"truncated", "processes": [{"pid", "comm", "exe", "packages"}], "count"}.
| Exit | Means |
|---|---|
| 0 | Nothing is running replaced code |
| 1 | At least one process should be restarted, or /proc is not mounted |
| 2 | Bad option |
kdos stutter [--json] [--fixture DIR]
Watches the compositor’s frame timing and, whenever a frame is late, says what the machine was doing at that moment. It runs until you stop it.
14:02:31 7 frames dropped on eDP-1 (133 ms)
the compositor's own render took 2.1 ms of a 16.7 ms frame, cpu pressure 12%, io 48%
busiest just then: tracker-miner (waiting on the disk), hugin (appbox app.hugin) (92% of a core)
It joins three sources, none of which is an answer alone:
| Source | Knows | Does not know |
|---|---|---|
| The compositor’s frames socket | A frame was late, by how much, and what the compositor’s own render cost | Who caused it |
Pressure statistics (/proc/pressure, the 10-second average) |
The machine was starved, and of what | By whom |
A /proc sample taken about every half second |
Who used processor time and who sat blocked on I/O | Whether it mattered |
The render cost separates the two explanations. When it is more than 60% of the frame budget, the report says the desktop itself was late, which is the one causal claim it makes, because it has both halves. Otherwise it reports what it measured and names up to three processes that were busy, and never says “X caused this”: a half-second sample cannot prove cause, and two busy programs at once would make any such claim wrong. When nothing was measurably busy it says so, pointing at a driver stall or something too short to sample.
Blocked processes are listed before busy ones. A process asleep in uninterruptible I/O shows
almost no processor time while it is the one holding the disk, so sorting by processor time alone
would hide it. Box names come from the container supervisor, found by walking the parent chain
through libkproc, not from control groups. Without cgroup delegation a rootless container
often sits in the root group, which names nothing. The walk costs a few file reads and no
container-engine call, which matters on a machine that is already struggling.
--json prints one NDJSON record per stutter, with the output, lateness, frames dropped, render
time, frame budget, the three pressure figures and the busiest processes. --fixture DIR replays
a recorded system state (two /proc snapshots in DIR/t0 and DIR/t1, and DIR/events.jsonl)
instead of watching, which is how the attribution is tested against testing/fixtures/stutter.
Exit 2 when there is no frames socket to connect to.
kdos cve [--json] [<port>]
Offline vulnerability tracking: every installed package is compared against a pruned copy of the
Alpine security database vendored into the image at /usr/share/kdos/secdb.txt (override with
$KDOS_SECDB). Alpine is the proxy because it also builds on musl from the same upstream
releases. The comparison uses kpkg’s version comparator: a package is behind when its version is
older than the one Alpine records as fixing a CVE. Nothing is scanned and nothing is fetched.
Details are in Packaging.
Name a port to check only that one. On a system with no package database, the recipes in the ports tree are checked instead. The report gives the database’s date and age, each package that is behind a recorded fix with the first three CVEs and a count of the rest, and a summary line. A database more than 180 days old is flagged.
A package is looked up under its own name, or under the name its recipe gives in secdb =. One
the database does not carry is unknown, never clean, and the summary says how many are in
that state. --json prints every finding with all its CVEs.
A library a port compiles into its own binary is in no package database, so a second file,
/usr/share/kdos/bundled.txt (override with $KDOS_BUNDLED), lists each one as `