Linux-native container management with a Rust CLI, desktop GUI,
AI-agent sandboxing, and lightweight multi-distro environments.
# Latest stable release (default)
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash
# Latest main HEAD (development; may be unstable)
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash -s -- --main
# Uninstall (keeps local linpodx data; pass --purge to wipe data/config)
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/uninstall.sh | bash -s -- --confirmEnglish · 한국어 · Changelog · Architecture · Contributing · Security
linpodx is preparing its first
v0.1.0release. Phase 0..17 implementation is in-tree: local daemon, CLI, Tauri-based desktop GUI, AI-agent sandbox, audit log, snapshots, host-stdio bridge, GUI passthrough, multi-distro templates, remote daemon, plugin hooks, cluster scaffolding, and snapshot encryption hardening. The current release gate is conservative: 829 unit tests pass, 54 host/runtime-dependent integration tests are ignored by default, and the project still expects sharp edges outside development workstations.
No Docker Desktop VM. linpodx talks to rootless Podman on Linux, keeps the daemon local by default, and exposes the same container state through CLI, GUI, and event subscriptions.
linpodx pairs an 18-tab daemon-served web UI (Dashboard, Containers, Pods, Stacks, Images, Volumes, Networks, Secrets, Snapshots, Sessions, Sandbox, Audit, Cluster, Pinned Clients, Plugins, Disk, Disk Usage, Settings) with a thin Tauri 2 desktop shell that displays that same UI locally. Pods and compose-style Stacks group related containers, a Secrets tab manages the Podman secret store, a Disk center reclaims space across images/containers/volumes, and resource rows carry right-click context menus for common actions. Alongside the container manager sits the AI-agent sandbox suite: YAML policy profiles, approval gates, a tamper-evident hash-chained audit log, per-container session timelines, and snapshot/rollback workflows.
| Tool | Gap linpodx targets |
|---|---|
| Docker Desktop | Heavy Linux story, license friction, weak desktop passthrough, no AI-agent sandbox model |
| Rancher Desktop | Kubernetes-first; too much machinery for daily local container work |
| Podman Desktop | Strong general container UI, but not built around sandbox approvals, snapshots, or multi-distro shells |
| distrobox / toolbx | Great lightweight environments, but CLI-first and light on policy/audit controls |
| Full VMs | Strong isolation, but slower boot and heavier CPU/RAM/storage footprint |
linpodx bundles a desktop container manager, a safe AI-agent execution sandbox, and GUI-integrated Linux environments into one Linux-native toolchain.
- Desktop container management — daily container, image, volume, and network work through a CLI and GUI backed by the same daemon.
- AI-agent sandbox execution — run risky agentic shell workflows in containers with approval gates, audit trails, resource limits, snapshots, and rollback.
- Lightweight distro shells — keep Ubuntu, Fedora, Arch, Debian, Alpine, and NixOS environments side-by-side without full VMs.
- GUI-integrated containers — forward Wayland/X11, audio, GPU, clipboard, DBus, theme, and HiDPI state into selected containers.
- Local-first remote access — stay Unix-socket local by default, then opt into WebSocket, bearer tokens, mTLS, and cert pinning when needed.
One-liner, any supported Linux distro:
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bashInstall from a local checkout or offline source tree:
git clone https://github.com/kernalix7/linpodx.git
cd linpodx
./install.sh --source .Optional L4 egress helper capabilities:
./install.sh --source . --setcap-helperUninstall:
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/uninstall.sh | bash -s -- --confirmUse --purge to remove local linpodx data/config as well. Podman containers, images, volumes, Rust, Podman, and system packages are left alone.
The installer follows the linpodx release posture: default to the latest published release, make main and arbitrary refs explicit.
# Latest stable release (default)
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash
# Latest main HEAD
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash -s -- --main
# Specific tag, branch, or commit
curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash -s -- --ref v0.1.0
# Env-var equivalents, useful under curl | bash
LINPODX_REF=main curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash
LINPODX_REF=v0.1.0 curl -fsSL https://raw.githubusercontent.com/kernalix7/linpodx/main/install.sh | bash# Copy from a local clone instead of git clone
./install.sh --source /media/usb/linpodx
# Skip distro dependency installation; fail early if tools are missing
./install.sh --skip-deps
# Build CLI + daemon only
./install.sh --source . --no-gui
# Skip the privileged L4 egress helper
./install.sh --source . --no-helperEnvironment variables mirror the flags: LINPODX_SOURCE, LINPODX_REF, LINPODX_SKIP_DEPS, LINPODX_NO_GUI, LINPODX_NO_HELPER, LINPODX_INSTALL_DIR, and LINPODX_BIN_DIR.
- Linux x86_64 or aarch64, with Wayland or X11 for the desktop GUI.
- Podman 4.6.0 or newer, rootless preferred.
- Rust 1.85+ for source builds.
rust-toolchain.tomlpins the workspace baseline. rustfmtandclippyfor development.- Build-time dependencies for the
linpodx-guidesktop shell (Tauri 2 over the system WebKitGTK 4.1 + GTK 3 stack — see Troubleshooting iflinpodx-guifails to start):- Debian/Ubuntu:
libwebkit2gtk-4.1-dev libgtk-3-dev librsvg2-dev - Fedora:
webkit2gtk4.1-devel - openSUSE Tumbleweed:
webkitgtk3-devel
- Debian/Ubuntu:
- Optional:
nftables,util-linux/nsenter, andsetcapfor the privileged L4 egress helper.
The helper is intentionally opt-in because it needs CAP_NET_ADMIN and CAP_SYS_ADMIN:
sudo setcap cap_net_admin,cap_sys_admin+ep ~/.local/bin/linpodx-netfilter-helper
sudo install -d -m 0755 /run/linpodx
linpodx-netfilter-helper --daemon-uid "$(id -u)" &Without the helper, DNS allowlist filtering still works and network egress apply reports helper_applied: false.
linpodx-daemon # Start the local Unix-socket daemon
linpodx ps --all # Query containers from the CLI
linpodx-gui # Open the desktop dashboardThe daemon binds $XDG_RUNTIME_DIR/linpodx.sock by default, falling back to /tmp/linpodx-$UID.sock.
linpodx-gui is a thin Tauri 2 shell: it auto-starts linpodx-daemon if it isn't already
running, then opens the same web UI the daemon serves locally on 127.0.0.1 with a
one-shot token (see Web UI). The identical UI is reachable from any browser by
starting the daemon with --remote-listen and opening /ui?token=<token>.
|
Desktop container manager
|
AI-agent sandbox
|
|
GUI passthrough
|
Multi-distro environments
|
|
Snapshots & storage
|
Remote, plugins, and cluster
|
See CHANGELOG.md for the full v0.1.0 feature list.
Container lifecycle:
linpodx-daemon &
linpodx run --name demo docker.io/library/alpine:latest sleep 30
linpodx ps --all
linpodx logs demo
linpodx rm -f demoSandbox profile:
mkdir -p ~/.config/linpodx/profiles
cp examples/profiles/read-only-net-disabled.yaml ~/.config/linpodx/profiles/
linpodx sandbox reload
linpodx run --sandbox read-only-net-disabled --name probe alpine sleep 5
linpodx sandbox audit --profile read-only-net-disabledSnapshots:
linpodx run --name work alpine sleep 600
linpodx snapshot create --label before-experiment work
linpodx snapshot list
linpodx snapshot rollback --new-name work-restored 1Multi-distro shell:
linpodx distro list
linpodx distro build --kind ubuntu --include git,curl,python3
linpodx distro create --kind ubuntu --vm-mode my-ubuntu
linpodx distro enter my-ubuntuRemote daemon:
linpodx-daemon --socket /tmp/lp.sock --remote-listen 127.0.0.1:8443 --remote-token dev
linpodx --remote ws://127.0.0.1:8443/ipc --token dev ps --allRun a daemon with a WebSocket listener when another process or host needs access to the same JSON-RPC surface:
linpodx-daemon \
--socket /tmp/lp.sock \
--remote-listen 127.0.0.1:8443 \
--remote-token hunter2
linpodx --remote ws://127.0.0.1:8443/ipc --token hunter2 version
linpodx --remote 127.0.0.1:8443 --token hunter2 psEnvironment variables work too:
export LINPODX_REMOTE=ws://daemon.internal:8443/ipc
export LINPODX_REMOTE_TOKEN=hunter2
linpodx psKeep plain ws:// behind loopback, a firewall, or an SSH tunnel. For untrusted networks, use TLS and client certificates.
Generate local test certificates:
linpodx daemon cert generate --out ./certsStart a remote daemon with TLS and client-certificate verification:
linpodx-daemon \
--socket /tmp/lp.sock \
--remote-listen 127.0.0.1:8443 \
--remote-token hunter2 \
--tls-cert ./certs/server.pem \
--tls-key ./certs/server-key.pem \
--client-ca ./certs/ca.pem \
--pin-clientsThen connect with the client certificate:
linpodx \
--remote wss://127.0.0.1:8443/ipc \
--token hunter2 \
--client-cert ./certs/client.pem \
--client-key ./certs/client-key.pem \
--client-ca ./certs/ca.pem \
ps --allPinned clients are managed through linpodx daemon pin-client {add,list,remove,tofu}. TOFU enrollment can be enabled for controlled first-contact windows, then disabled once the expected clients are pinned.
The daemon can serve a browser UI on the same listener as the remote /ipc endpoint:
linpodx-daemon \
--socket /tmp/lp.sock \
--remote-listen 127.0.0.1:8443 \
--remote-token hunter2Open http://127.0.0.1:8443/ui/ and provide the bearer token. The Web UI shares the remote listener's security posture, so use mTLS for untrusted networks.
This is the same UI linpodx-gui opens locally: the desktop shell calls the daemon's WebUiEnsure IPC method to bind an ephemeral loopback listener and mint a one-shot token, then points its webview at it — no separate GUI codebase to maintain.
The Leptos/WASM UI is opt-in at build time:
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
LINPODX_WASM=1 cargo build -p linpodx-daemon --releaseWithout LINPODX_WASM=1, the daemon serves the lightweight built-in fallback UI. For air-gapped terminal modals, vendor xterm.js assets at build time:
LINPODX_VENDOR_XTERM=1 cargo build --release -p linpodx-daemonCompile a sandbox profile's seccomp and AppArmor artifacts:
linpodx sandbox profile compile read-only-net-disabled --secprofile-out /tmp/secprofilesWhen the profile has syscall_allowlist or apparmor_extra, the daemon applies the generated files through Podman's --security-opt flags. Hosts without apparmor_parser keep the seccomp half and skip AppArmor.
SELinux profile synthesis is available on hosts with checkmodule, semodule_package, semodule, and SELinux in enforcing/permissive mode. Set selinux_type: <type_name> in a sandbox profile to synthesize, package, install, and apply a per-profile label. Hosts without SELinux tooling fall back gracefully.
Interactive PTY mode is available over the WebSocket remote listener:
linpodx --remote 127.0.0.1:8443 --token hunter2 \
exec -it <container_id> -- bashThe daemon allocates a PTY pair, the CLI switches the local terminal into raw mode, and /pty/<bridge_id> carries the interactive stream. Each bridge is single-use and closes when the process exits or the WebSocket disconnects.
linpodx can call the standard Kubernetes discovery chain (KUBECONFIG, ~/.kube/config, or in-cluster service account) through the daemon:
linpodx k8s pod create ./pod.yaml -n my-ns
cat pod.yaml | linpodx k8s pod create - -n my-ns
linpodx k8s pod delete hello -n my-ns
linpodx k8s ns create my-ns
linpodx k8s scale web --replicas 3 -n prodCluster-mutating operations are recorded in the local audit log.
Criterion benches live under the relevant crates and have a baseline in bench-results/:
cargo bench -p linpodx-runtime --bench snapshot --bench container --bench cgroup \
-p linpodx-mcp --bench policy \
-p linpodx-plugin --bench invoke -- --quickThe bench workflow compares quick-mode means against the checked-in baseline and flags large regressions without failing the build.
| Version | Focus |
|---|---|
v0.1.x |
Stabilize the local daemon, installer, GUI dashboard, and core sandbox flows |
v0.2.x |
Packaging, systemd user units, Web UI polish, and remote daemon hardening |
v0.3.x |
More distro workflows, richer GUI actions, and plugin author ergonomics |
v0.4.x |
Multi-host/cluster usability and stronger operational recovery |
- Replacing Kubernetes, Rancher, k3s, or k0s.
- First-class Windows/macOS hosts; linpodx is Linux-native.
- Hiding Podman. The runtime remains visible, debuggable, and compatible with normal Podman workflows.
- Removing user judgment from sensitive operations. Approval gates and audit logs are part of the product, not a temporary limitation.
linpodx CLI / GUI / Web UI
|
| JSON-RPC 2.0 over Unix socket or WebSocket
v
linpodx-daemon
|-- Podman runtime adapter
|-- sandbox policy manager
|-- audit/event/session/snapshot managers
|-- plugin registry
|-- remote daemon transport
`-- cluster / distro / passthrough adapters
|
v
Rootless Podman + Linux desktop integrations
| Crate | Purpose |
|---|---|
linpodx-cli |
linpodx command-line client |
linpodx-daemon |
Unix-socket API server, dispatcher, event bus, remote transport |
linpodx-gui |
Tauri 2 desktop shell for the daemon-served web UI |
linpodx-runtime |
Podman wrapper, images, volumes, networks, snapshots, passthrough |
linpodx-sandbox |
profiles, approvals, audit, sessions, snapshot triggers |
linpodx-common |
shared IPC, state, errors, database migrations |
linpodx-distro |
distro templates and VM-mode helpers |
linpodx-plugin |
Wasmtime plugin loading, manifests, signing, registry |
linpodx-cluster |
gossip, Raft, Kubernetes adapter |
linpodx-webui |
browser UI bundle, served by the daemon both locally (linpodx-gui) and to the remote listener |
| Document | What's inside |
|---|---|
| CHANGELOG.md | v0.1.0 release notes and pre-release phase history |
| docs/README.ko.md | Korean overview |
| docs/INSTALL.md | Installer, uninstall, offline/source install, prerequisites |
| docs/TROUBLESHOOTING.md | First-run snags and recovery steps |
| docs/RELEASE.md | Versioning, tag discipline, release checklist |
| docs/architecture.md | System architecture, data flow, trust boundaries |
| docs/scenarios/ai-agent-sandbox.md | Sandbox workflow |
| docs/scenarios/gui-app.md | GUI passthrough scenario |
| docs/scenarios/multi-distro-shell.md | Multi-distro shell scenario |
| docs/scenarios/plugin-author.md | Plugin author workflow |
| docs/scenarios/remote-daemon.md | Remote daemon workflow |
| CONTRIBUTING.md | Development setup and workflow |
| SECURITY.md | Security disclosure process |
| Distro | Package manager | Status |
|---|---|---|
| openSUSE Tumbleweed / Leap / Slowroll | zypper | Supported |
| Fedora / RHEL family / AlmaLinux / Rocky | dnf | Supported |
| Debian / Ubuntu / Linux Mint / Pop!_OS | apt | Supported |
| Arch / Manjaro / EndeavourOS | pacman | Supported |
Podman 4.6.0 or newer is required. Rust 1.85+ is required for source builds.
cargo +1.85 fmt --all -- --check
cargo +1.85 clippy --workspace --all-targets --all-features -- -D warnings
cargo +1.85 build --workspace
cargo +1.85 test --workspace
cargo +1.85 doc --workspace --no-depsIgnored integration tests touch host runtimes, networking helpers, Podman lifecycle, or external services:
cargo +1.85 test --workspace -- --ignored --test-threads=1git clone https://github.com/kernalix7/linpodx.git
cd linpodx
rustup toolchain install 1.85 --component clippy --component rustfmt
cargo +1.85 build --workspaceRun from a checkout without installing:
cargo +1.85 run -p linpodx-daemon
cargo +1.85 run -p linpodx-cli -- ps --all
cargo +1.85 run -p linpodx-guiSnapshots can be stored encrypted at rest with AES-256-GCM. Encryption is opt-in through environment variables read by the daemon at startup:
| Variable | Meaning |
|---|---|
LINPODX_SNAPSHOT_ENCRYPT_PASSPHRASE |
Derive the encryption key from this passphrase. Required for the passphrase path. |
LINPODX_SNAPSHOT_KEY |
Use this raw base64-encoded 32-byte key directly. Mutually exclusive with the passphrase. |
LINPODX_SNAPSHOT_KDF |
argon2id (default; OWASP 2023 baseline m=19456, t=2, p=1) or sha256-rounds-1k for backward compatibility. |
When neither variable is set, snapshots are written unencrypted (the original
v0.1.0 behaviour). Existing snapshots written under one KDF keep their original
KDF tag; use linpodx snapshot key-rotate and linpodx snapshot re-encrypt-all
to migrate the on-disk corpus. Inspect a single snapshot with:
linpodx snapshot encryption-status <snapshot_id>Common first-run snags:
daemon: connection refused— start the daemon (linpodx-daemon &).podman: command not found/ version too old — install Podman 4.6.0+ for your distro.linpodx-guiexits with alibwebkit2gtk/libgtk-3error — install the WebKitGTK 4.1 + GTK 3 runtime libraries listed under Prerequisites.snapshot decryption failed— match the sameLINPODX_SNAPSHOT_ENCRYPT_PASSPHRASE/LINPODX_SNAPSHOT_KEY/LINPODX_SNAPSHOT_KDFthe snapshot was created with.plugin signature rejected— install the publisher's public key under~/.config/linpodx/plugins/keys/, or setLINPODX_ALLOW_UNSIGNED_PLUGINS=1for local development.- Container starts but has no network egress — the active sandbox profile
is in
network: kind: allowlist; widen it or run without--sandbox.
See docs/TROUBLESHOOTING.md for the full list (daemon connectivity, podman runtime, GUI startup, snapshot encryption + key-rotation, plugin signatures + revocation, remote daemon mTLS + pinning, cluster leader routing, sandbox approvals).
linpodx defaults to rootless Podman and local Unix-socket IPC. Optional features such as the netfilter helper, remote daemon listener, mTLS, certificate pinning, host mounts, and plugin loading expand the trust boundary; enable them deliberately and review SECURITY.md for reporting guidance.
MIT - Kim DaeHyun (kernalix7@kodenet.io)



