{"id":50449742,"url":"https://github.com/bethropolis/podmgr","last_synced_at":"2026-06-01T00:00:41.359Z","repository":{"id":361600107,"uuid":"1255078718","full_name":"bethropolis/podmgr","owner":"bethropolis","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-31T11:48:43.000Z","size":75,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-31T13:14:15.935Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bethropolis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-31T11:29:58.000Z","updated_at":"2026-05-31T11:48:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bethropolis/podmgr","commit_stats":null,"previous_names":["bethropolis/podmgr"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/bethropolis/podmgr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bethropolis%2Fpodmgr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bethropolis%2Fpodmgr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bethropolis%2Fpodmgr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bethropolis%2Fpodmgr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bethropolis","download_url":"https://codeload.github.com/bethropolis/podmgr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bethropolis%2Fpodmgr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33753925,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-01T00:00:26.278Z","updated_at":"2026-06-01T00:00:41.316Z","avatar_url":"https://github.com/bethropolis.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# podmgr\n\nA Podman-native Rust workspace that turns a single TOML definition file into a\nfully integrated, systemd-managed container environment. Two binaries: `podmgr`\n(host CLI) and `podmgr-guest` (static musl sidecar baked into every built image).\nTogether they provide selective XDG directory sharing, Wayland/audio passthrough,\na bidirectional host-guest socket protocol, and `.desktop`/bin shim export -- all\nwithout a persistent daemon and without mounting the host home directory wholesale.\n\n## Quick Start\n\n```bash\n# 1. Install\ngit clone \u003crepo-url\u003e \u0026\u0026 cd podmgr\ncargo install --path crates/podmgr\n\n# 2. Build the guest binary (required before building images)\nrustup target add x86_64-unknown-linux-musl\ncargo build -p podmgr-guest --release --target x86_64-unknown-linux-musl\n\n# 3. Create a definition file\ncat \u003e myenv.toml \u003c\u003c 'EOF'\n[image]\nbase = \"fedora:41\"\nname = \"myenv\"\n\n[image.packages]\ninstall = [\"git\", \"gcc\", \"firefox\"]\n\n[container]\nname  = \"myenv\"\nhome  = \"~/containers/myenv\"\n\n[integration]\nwayland = true\naudio   = true\nnotify  = true\nxdg_open = true\n\n[lifecycle]\nquadlet   = true\nautostart = true\nEOF\n\n# 4. Build and enable\npodmgr --config myenv.toml build\npodmgr --config myenv.toml enable\n\n# 5. Start and enter\npodmgr --config myenv.toml shell\n# inside: notify-send \"hello\"    # → appears on host\n# inside: xdg-open https://...   # → opens in host browser\n```\n\n## Definition File Format\n\n```toml\n[image]\nbase = \"fedora:41\"          # base OCI image\nname = \"myenv\"              # image tag name\n\n[image.packages]\ninstall = [\"git\", \"gcc\"]\nremove  = []\n\n[image.run]\ncommands = [\"dnf clean all\"]\n\n[container]\nname  = \"myenv\"\nhome  = \"~/containers/myenv\"    # isolated container home\nshell = \"bash\"\n\n[container.mounts]\nextra = [\"~/Work:/home/user/Work:z\"]\n\n[container.env]\nEDITOR = \"nvim\"\n\n[integration]\nwayland     = true    # Wayland socket passthrough\naudio       = true    # PipeWire/PulseAudio passthrough\ngpu         = \"auto\"  # \"auto\"|true|false|\"nvidia\" — GPU passthrough\ndbus        = true    # D-Bus session passthrough\nnotify      = true    # notify-send → host notifications\nxdg_open    = true    # xdg-open → host browser\nclipboard   = true    # wl-copy/wl-paste bridge\nsync_themes = true    # mount ~/.themes :ro\nsync_icons  = true    # mount ~/.icons :ro\nsync_fonts  = true    # mount ~/.fonts + fontconfig :ro\n\n[integration.xdg_dirs]\ndocuments = true    # mount ~/Documents into container\ndownloads = true\npictures  = false   # opt-in only\n\n[integration.export]\napps = [\"gedit\"]    # export .desktop to host\nbins = [\"rg\"]       # export bin shim to ~/.local/bin\n\n[systemd]\nrequires = [\"db-container.service\"]  # systemd Requires= dependencies\nafter    = [\"network.target\"]         # systemd After= dependencies\n\n[lifecycle]\nquadlet     = true    # generate systemd Quadlet files\nautostart   = true    # start on login\non_stop     = \"keep\"  # \"keep\" or \"remove\"\nauto_update = false   # add auto-update label (io.containers.autoupdate=registry)\n```\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `podmgr build` | Generate Containerfile and build image |\n| `podmgr enable` | Install Quadlet systemd files |\n| `podmgr disable` | Remove Quadlet files |\n| `podmgr start` | Start the container |\n| `podmgr stop` | Stop the container |\n| `podmgr shell` | Open interactive shell (uses TTY) |\n| `podmgr exec -- \u003ccmd\u003e` | Execute command interactively |\n| `podmgr run \u003capp\u003e` | Run GUI app detached |\n| `podmgr status` | Show container state |\n| `podmgr logs [-f]` | Show container logs |\n| `podmgr export app \u003cname\u003e` | Export .desktop file to host |\n| `podmgr export bin \u003cname\u003e` | Export bin shim to host |\n| `podmgr remove [--all]` | Remove container |\n| `podmgr doctor` | Run diagnostic checks |\n| `podmgr translate-path --to-container \u003cpath\u003e` | Translate host path to container path |\n| `podmgr translate-path --to-host \u003cpath\u003e` | Translate container path to host path |\n| `podmgr completions \u003cshell\u003e` | Generate shell completions |\n\nAll commands support `--dry-run` to preview actions without executing.\n\n## Exit Codes\n\n| Code | Meaning |\n|------|---------|\n| 0 | Success |\n| 1 | General error (runtime, I/O, etc.) |\n| 2 | Configuration error (file not found, parse failure) |\n| 3 | Container missing |\n| 4 | Build or podman inspect failure |\n| 5 | Missing dependency (podman, podmgr-guest not found) |\n\n## Architecture\n\n```\nmyenv.toml → [podmgr build] → Containerfile → podman build → image\n           → [podmgr enable] → .build + .socket + .container → systemd\n\nContainer startup:\n  catatonit (PID 1)\n    → podmgr-entry.sh\n      → fork() → podmgr-guest --daemon (connects to host socket)\n      → exec() → bash (user shell)\n```\n\nThe `podmgr-guest` daemon inside the container:\n- Connects to a host Unix socket for bidirectional communication\n- Installs symlinks (`notify-send`, `xdg-open`) in `/run/podmgr/bin/`\n- Injects PATH via `/etc/environment.d/podmgr.conf`\n- Forwards notifications, xdg-open requests, and clipboard operations to the host\n\n## GPU Passthrough\n\n`podmgr` supports automatic GPU detection. The `gpu` field accepts:\n\n| Value | Behavior |\n|-------|----------|\n| `\"auto\"` (default) | Detects `/dev/dri` (Intel/AMD) and/or `/dev/nvidia*` at container start |\n| `true` | Enables `/dev/dri` for all GPU types |\n| `false` | No GPU passthrough |\n| `\"nvidia\"` | Enables DRI + NVIDIA device nodes (nvidiactl, nvidia0, nvidia-uvm) |\n\n## Systemd Dependencies\n\nCustom `Requires=` and `After=` directives in the generated Quadlet `[Unit]` section:\n\n```toml\n[systemd]\nrequires = [\"db-container.service\"]\nafter    = [\"network.target\", \"db-container.service\"]\n```\n\n## Selective XDG Directory Sharing\n\nInstead of mounting the entire host home, podmgr lets you opt-in to specific\nXDG user directories:\n\n```toml\n[integration.xdg_dirs]\ndocuments = true   # ~/Documents → /root/Documents\ndownloads = true   # ~/Downloads → /root/Downloads\npictures  = false  # not mounted\n```\n\n## Requirements\n\n- Podman \u003e= 4.6 (Quadlet support)\n- systemd (user session)\n- Linux with Wayland (for GUI passthrough)\n- xdg-user-dirs (for XDG directory resolution)\n- Rust toolchain (to build)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbethropolis%2Fpodmgr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbethropolis%2Fpodmgr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbethropolis%2Fpodmgr/lists"}