$ man 7 dok
How dok turns the Docker API into something a person can read.
Pipeline
- socket
/var/run/docker.sockor DOCKER_HOST, or a Windows named pipe - client
bollardthe Docker API over HTTP, in Rust - views
src/dk.rsnames, compose labels, state, health - layout
src/table.rsmeasure, cap, shrink, pad - paint
src/theme.rsroles, glyphs, layout - out
your terminalor a pipe, or --json
No shell-out
dok never runs the docker CLI. It talks to the daemon’s HTTP API through bollard, over the same socket the CLI uses, so it needs nothing installed beyond a reachable daemon.
--demo builds a client pointed at a dead port and answers every query from fixtures instead, which is why it works on a machine with no Docker at all:
pub fn connect() -> Result<Docker> {
// Demo mode answers every query from fixtures, but the commands still need
// a client value to pass around. Build an HTTP one pointed at a dead port:
// it constructs without touching the filesystem, so `--demo` works on a
// machine that has no docker socket at all.
if crate::demo::enabled() {
return Docker::connect_with_http("http://127.0.0.1:1", 1, bollard::API_DEFAULT_VERSION)
.context("building the offline demo client");
}
Docker::connect_with_defaults()
.context("cannot reach the docker daemon (is it running? check DOCKER_HOST)")
}Read-only, by construction
These are all the Docker API calls in dok’s source. The list is generated from the code every time this page is built:
dfeventsinspect_containerlist_containerslist_imageslist_networkslist_volumeslogspingstatstop_processes
Every one of them reads. There is no create, start, stop, kill, remove or prune anywhere in the codebase, so a bug in dok cannot change a container.
Tables that fit
Every cell arrives already coloured, so its length in bytes says nothing about its width on screen. All width maths goes through one function that skips escape sequences:
pub fn visible_width(s: &str) -> usize {
let mut w = 0usize;
let mut chars = s.chars();
while let Some(ch) = chars.next() {
if ch == '\x1b' {
// Skip through the terminating byte of the escape sequence.
for c in chars.by_ref() {
if c.is_ascii_alphabetic() {
break;
}
}
continue;
}
w += char_width(ch);
}
w
}With widths measured, table.rs caps each column at a share of the terminal, shrinks flexible columns widest-first until the table fits, and pads. Truncation copies escape sequences through whole, so a cut never leaves a stray ESC byte behind.
Themes are roles, not colours
No command names a colour. They ask for a role, and the theme decides what it looks like. Structural glyphs (tree stubs, bars, arrows, state dots) come from the theme too, which is how the ascii theme stays pure ASCII.
/// Nine semantic roles. Commands never name a colour, only a role.
#[derive(Copy, Clone, Debug)]
pub struct Palette {
pub green: Rgb,
pub red: Rgb,
pub yellow: Rgb,
pub orange: Rgb,
pub blue: Rgb,
pub cyan: Rgb,
pub magenta: Rgb,
pub gray: Rgb,
pub fg: Rgb,
}Output modes
When stdout is a terminal, dok colours output and picks an icon set. Piped, both turn off. --json replaces the table with a document, or NDJSON for the two streaming commands. dok stats alone takes over the screen, and refuses --json rather than drawing into a pipe.