{"id":50884131,"url":"https://github.com/ehsan18t/nanodock","last_synced_at":"2026-06-15T15:02:47.518Z","repository":{"id":351898910,"uuid":"1212931870","full_name":"ehsan18t/nanodock","owner":"ehsan18t","description":"Zero-bloat Docker/Podman daemon client for container detection, port mapping, and lifecycle control","archived":false,"fork":false,"pushed_at":"2026-04-24T11:00:10.000Z","size":141,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T12:35:13.546Z","etag":null,"topics":["container-detection","containers","daemon-client","docker","high-performance","lightweight","podman","port-mapper","port-scanner","rootless-podman","synchronous","system-utility"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/nanodock","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ehsan18t.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE","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-04-16T21:56:50.000Z","updated_at":"2026-04-24T10:59:52.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ehsan18t/nanodock","commit_stats":null,"previous_names":["ehsan18t/nanodock"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ehsan18t/nanodock","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehsan18t%2Fnanodock","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehsan18t%2Fnanodock/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehsan18t%2Fnanodock/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehsan18t%2Fnanodock/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ehsan18t","download_url":"https://codeload.github.com/ehsan18t/nanodock/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehsan18t%2Fnanodock/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34367696,"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-06-15T02:00:07.085Z","response_time":63,"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":["container-detection","containers","daemon-client","docker","high-performance","lightweight","podman","port-mapper","port-scanner","rootless-podman","synchronous","system-utility"],"created_at":"2026-06-15T15:02:46.512Z","updated_at":"2026-06-15T15:02:47.508Z","avatar_url":"https://github.com/ehsan18t.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003ch1\u003enanodock\u003c/h1\u003e\n    \u003cp\u003eA lightweight zero-bloat Rust library for detecting Docker and Podman containers, mapping their published ports to host sockets, and controlling container lifecycle. Built for embedding into CLI tools and system utilities that need container awareness without pulling in a full Docker SDK.\u003c/p\u003e\n\n[![CI](https://github.com/ehsan18t/nanodock/actions/workflows/ci.yml/badge.svg)](https://github.com/ehsan18t/nanodock/actions/workflows/ci.yml)\n[![Crates.io](https://img.shields.io/crates/v/nanodock.svg)](https://crates.io/crates/nanodock)\n[![docs.rs](https://docs.rs/nanodock/badge.svg)](https://docs.rs/nanodock)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n\u003c/div\u003e\n\n\n## Features\n\n- **Container detection** - Queries Docker and Podman daemons to discover\n  running containers and their published port bindings.\n- **Port-to-container mapping** - Resolves which container owns a given host\n  `(ip, port, protocol)` tuple, with wildcard and proxy-fallback matching.\n- **Container lifecycle control** - Stop or kill containers by ID through the\n  daemon API (graceful SIGTERM or immediate SIGKILL).\n- **Multi-transport support** - Connects via Unix domain sockets, Windows named\n  pipes, or TCP (`DOCKER_HOST`), with automatic discovery of socket paths.\n- **Rootless Podman support** - Resolves rootless Podman containers on Linux by\n  reading overlay storage metadata and matching network namespace paths.\n- **Background detection** - Spawns detection on a background thread so callers\n  can do other work (socket enumeration, process lookup) concurrently.\n- **Minimal dependencies** - Only `serde`, `serde_json`, `httparse`, and `log`\n  at runtime. No async runtime, no `tokio`, no `hyper`.\n- **Cross-platform** - Works on Linux (x86-64) and Windows (x86-64).\n\n## Why nanodock?\n\nMost Rust Docker libraries (`bollard`, `docker-api`) are full API clients that\nrequire an async runtime and pull in 30-50+ transitive dependencies. nanodock\ntakes the opposite approach: synchronous, minimal, and focused.\n\n| Crate        | Async | Runtime Deps | Scope                         |\n| ------------ | ----- | ------------ | ----------------------------- |\n| `bollard`    | Yes   | ~50+         | Full Docker API               |\n| `docker-api` | Yes   | ~30+         | Full Docker API               |\n| **nanodock** | No    | **4**        | Detection + Ports + Lifecycle |\n\nUse nanodock when you need container awareness (detection, port mapping,\nlifecycle control) without pulling in an async runtime or a full Docker SDK.\nIdeal for CLI tools, system utilities, and monitoring agents.\n\n## Quick Start\n\nAdd nanodock to your `Cargo.toml`:\n\n```toml\n[dependencies]\nnanodock = \"0.1\"\n```\n\n### Detect containers and map ports\n\nTwo detection paths are available:\n\n**Best-effort path** (background thread, never errors):\n\n```rust,no_run\nuse nanodock::{start_detection, await_detection};\n\nfn main() {\n    // Spawn background detection (queries Docker/Podman daemon).\n    let handle = start_detection(None);\n\n    // ... do other work while detection runs ...\n\n    // Collect results (blocks up to 3 seconds).\n    let port_map = await_detection(handle);\n\n    for ((ip, port, proto), info) in \u0026port_map {\n        println!(\n            \"{proto} port {port} -\u003e container '{}' (image: {})\",\n            info.name, info.image\n        );\n    }\n}\n```\n\n**Strict path** (synchronous, returns errors):\n\n```rust,no_run\nuse nanodock::detect_containers;\n\nfn main() {\n    match detect_containers(None) {\n        Ok(port_map) =\u003e {\n            for ((ip, port, proto), info) in \u0026port_map {\n                println!(\n                    \"{proto} port {port} -\u003e container '{}' (image: {})\",\n                    info.name, info.image\n                );\n            }\n        }\n        Err(e) =\u003e eprintln!(\"detection failed: {e}\"),\n    }\n}\n```\n\n### Look up which container owns a socket\n\n```rust,no_run\nuse std::net::{IpAddr, Ipv4Addr, SocketAddr};\nuse nanodock::{\n    start_detection, await_detection,\n    lookup_published_container, PublishedContainerMatch, Protocol,\n};\n\nfn main() {\n    let port_map = await_detection(start_detection(None));\n\n    let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5432);\n    match lookup_published_container(\u0026port_map, socket, Protocol::Tcp, false) {\n        PublishedContainerMatch::Match(info) =\u003e {\n            println!(\"Port 5432 belongs to '{}' ({})\", info.name, info.image);\n        }\n        PublishedContainerMatch::NotFound =\u003e {\n            println!(\"No container found for port 5432\");\n        }\n        PublishedContainerMatch::Ambiguous =\u003e {\n            println!(\"Multiple containers match port 5432\");\n        }\n        _ =\u003e {}\n    }\n}\n```\n\n### Stop a container\n\n```rust,no_run\nuse nanodock::{stop_container, StopOutcome};\n\nfn main() {\n    let container_id = \"abc123def456\";\n    match stop_container(container_id, false, None) {\n        StopOutcome::Stopped =\u003e println!(\"Container stopped\"),\n        StopOutcome::AlreadyStopped =\u003e println!(\"Container was already stopped\"),\n        StopOutcome::NotFound =\u003e println!(\"Container not found\"),\n        StopOutcome::Failed =\u003e println!(\"Could not reach daemon\"),\n        _ =\u003e println!(\"Unexpected outcome\"),\n    }\n}\n```\n\n## How It Works\n\nnanodock communicates directly with the Docker/Podman daemon using the\n`/containers/json` REST API endpoint over local transports:\n\n```\n┌─────────────┐     HTTP/1.0 GET /containers/json\n│  nanodock   │ ──────────────────────────────────────┐\n│ (your app)  │                                       │\n└─────────────┘                                       ▼\n                                              ┌─────────────────┐\n    Unix socket  (/var/run/docker.sock)  ───► │                 │\n    Named pipe   (\\\\.\\pipe\\docker_engine) ──► │  Docker/Podman  │\n    TCP          (DOCKER_HOST=tcp://...) ───► │  Daemon         │\n                                              │                 │\n                                              └─────────────────┘\n```\n\n### Transport Discovery Order\n\n1. **`DOCKER_HOST` environment variable** - If set, the specified transport\n   (tcp://, unix://, npipe://) is used first.\n2. **Platform-native sockets** - On Linux, well-known Unix socket paths are\n   probed (rootful Docker, rootless Docker, Podman). On Windows, named pipes\n   for Docker Desktop and Podman Machine are tried.\n3. **Rootless Podman overlay** (Linux only) - For containers managed by rootless\n   Podman, nanodock reads the overlay storage metadata to resolve container\n   names from network namespace paths. This handles the case where\n   `rootlessport` is the process holding the socket instead of the container\n   itself.\n\n### Supported Daemon Paths\n\n| Platform | Transport   | Path                                 |\n| -------- | ----------- | ------------------------------------ |\n| Linux    | Unix socket | `/var/run/docker.sock`               |\n| Linux    | Unix socket | `/run/user/{uid}/docker.sock`        |\n| Linux    | Unix socket | `$HOME/.docker/desktop/docker.sock`  |\n| Linux    | Unix socket | `$HOME/.docker/run/docker.sock`      |\n| Linux    | Unix socket | `/run/user/{uid}/podman/podman.sock` |\n| Linux    | Unix socket | `/run/podman/podman.sock`            |\n| Windows  | Named pipe  | `\\\\.\\pipe\\docker_engine`             |\n| Windows  | Named pipe  | `\\\\.\\pipe\\podman-machine-default`    |\n| Both     | TCP         | `DOCKER_HOST=tcp://host:port`        |\n\n## API Reference\n\nFull API documentation is available on [docs.rs](https://docs.rs/nanodock).\n\n### Core Types\n\n| Type                      | Description                                               |\n| ------------------------- | --------------------------------------------------------- |\n| `Protocol`                | Network protocol enum (`Tcp`, `Udp`)                      |\n| `ContainerInfo`           | Container metadata (id, name, image)                      |\n| `ContainerPortMap`        | HashMap mapping `(ip, port, protocol)` to `ContainerInfo` |\n| `PublishedContainerMatch` | Result of looking up a socket in the port map             |\n| `StopOutcome`             | Result of a stop/kill request                             |\n| `DetectionHandle`         | Handle for in-progress background detection               |\n| `Error`                   | Error type for strict-path detection failures             |\n\n### Core Functions\n\n| Function                          | Description                                         |\n| --------------------------------- | --------------------------------------------------- |\n| `detect_containers(home)`         | Synchronous detection, returns `Result\u003cMap, Error\u003e` |\n| `start_detection(home)`           | Spawn background daemon query, returns handle       |\n| `await_detection(handle)`         | Block for results (3s timeout), returns map         |\n| `lookup_published_container()`    | Match a socket against the port map                 |\n| `stop_container(id, force, home)` | Stop or kill a container by ID                      |\n| `parse_containers_json(body)`     | Parse raw `/containers/json` response               |\n| `parse_containers_json_strict()`  | Strict parse that returns `Result` on invalid JSON  |\n\n### Linux-only Functions\n\n| Function                               | Description                               |\n| -------------------------------------- | ----------------------------------------- |\n| `is_podman_rootlessport_process(name)` | Check if a process name is `rootlessport` |\n| `lookup_rootless_podman_container()`   | Resolve container from rootlessport PIDs  |\n| `RootlessPodmanResolver`               | Cached resolver for rootless Podman       |\n\n## Architecture\n\n```\nsrc/\n├── lib.rs      — Public API, detection orchestration, port matching\n├── api.rs      — JSON response parsing, container name resolution\n├── http.rs     — Minimal HTTP/1.0 response parser (via httparse)\n├── ipc.rs      — OS-specific transport (Unix socket, named pipe, TCP)\n└── podman.rs   — Rootless Podman resolver via overlay metadata (Linux)\n```\n\n### Module Boundaries\n\n- **`lib.rs`** owns the public API surface, detection orchestration, and\n  port-to-container matching logic. All public types are defined here.\n- **`api.rs`** owns JSON response parsing. It converts raw daemon responses\n  into `ContainerPortMap` entries.\n- **`http.rs`** owns HTTP protocol handling. It formats requests and parses\n  responses using `httparse`. No Docker-specific logic lives here.\n- **`ipc.rs`** owns OS-specific transport code. Unix sockets, Windows named\n  pipes, TCP connections, and `DOCKER_HOST` parsing all live here.\n- **`podman.rs`** owns rootless Podman resolution. It reads overlay storage\n  metadata and OCI runtime configs to match network namespace paths to\n  container names.\n\n## Building\n\n```bash\n# Debug build\ncargo build\n\n# Run tests\ncargo test --lib --tests\ncargo test --doc\n\n# Compile benchmarks\ncargo bench --no-run\n\n# Run benchmarks on Linux with valgrind and gungraun-runner installed\ncargo bench --bench benchmarks\n\n# Check formatting\ncargo fmt --check\n\n# Run clippy (all+pedantic+nursery at deny level)\ncargo clippy --all-targets -- -D warnings\n\n# Build documentation\ncargo doc --no-deps --open\n\n# Dependency audit (requires cargo-deny)\ncargo deny check\n```\n\n## Quality Gates\n\nAll of the following must pass before merging:\n\n| Gate | Command                                        | Purpose                   |\n| ---- | ---------------------------------------------- | ------------------------- |\n| 1    | `cargo fmt --check`                            | Consistent formatting     |\n| 2    | `cargo clippy`                                 | Zero lint warnings        |\n| 3    | `cargo test --lib --tests \u0026\u0026 cargo test --doc` | All tests pass            |\n| 4    | `cargo bench --no-run`                         | Benchmarks compile        |\n| 5    | `cargo build`                                  | Library compiles          |\n| 6    | `cargo doc --no-deps`                          | Documentation builds      |\n| 7    | `cargo deny check`                             | No vulnerable/banned deps |\n\n## Instruction Benchmarks\n\nnanodock ships deterministic instruction-count benchmarks via\n[Gungraun](https://crates.io/crates/gungraun) (requires Linux + Valgrind).\nSee [CONTRIBUTING.md](docs/CONTRIBUTING.md) for setup and usage details.\n\n### Git Hooks\n\nInstall local quality gates (runs fmt, clippy, and tests before each commit):\n\n**Windows (PowerShell):**\n```powershell\n.\\scripts\\install-hooks.ps1\n```\n\n**Linux / macOS:**\n```bash\nbash scripts/install-hooks.sh\n```\n\n## Minimum Supported Rust Version\n\nnanodock requires the latest stable Rust toolchain (currently 1.93+) and uses\nedition 2024 features.\n\n## Dependencies\n\nnanodock keeps its dependency tree intentionally small:\n\n| Crate        | Purpose                                |\n| ------------ | -------------------------------------- |\n| `serde`      | Container metadata serialization       |\n| `serde_json` | JSON response parsing                  |\n| `httparse`   | HTTP/1.x response header parsing       |\n| `log`        | Debug diagnostics via log facade       |\n| `libc`       | Unix-only: `getuid()` for socket paths |\n\nNo async runtime. No TLS. No network client libraries.\n\n## Contributing\n\nSee [CONTRIBUTING.md](docs/CONTRIBUTING.md) for development setup, coding\nstandards, commit message format, and the full quality gate reference.\n\n## License\n\nLicensed under the [MIT License](LICENSE).\n\nCopyright (c) 2026 Ehsan Khan\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehsan18t%2Fnanodock","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fehsan18t%2Fnanodock","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehsan18t%2Fnanodock/lists"}