portage-ng
::- every plan is a proof.
portage-ng is a declarative reasoning engine for software configuration management, applied to Gentoo Linux.
Logic produces the plans to install, upgrade or remove software while keeping the system in a consistent state. Each plan records which rule justified every package and under what constraints.
?- written in Prolog.
Why Prolog? It's the artificial-intelligence language built for automated reasoning: programs are logical rules, and running them means deriving proofs. Inference, backtracking and explanation aren't bolted-on machinery — they're the language itself.
?- applied to Gentoo.
Why Gentoo? It's a metadistribution: it distributes knowledge, not binaries — recipes for building every component of a system, and the parameters to tailor each one. A combinatorially vast configuration space — the perfect proving ground for a reasoning engine.
?- why.
Logic-driven configuration management.
A prover, using logic rules, derives the complete configuration — packages, settings and build order — and keeps the reasoning behind every choice.
Logic proofs
Every plan is a formal derivation you can inspect and replay. Each package carries its justification — which rule selected it, which constraints were imposed, which assumptions were made.
Every step can be explained.
Automatic conflict resolution
When no valid plan exists outright, portage-ng keeps reasoning: it untangles complex USE and keyword conflicts itself and proves a plan under explicit assumptions — exact package.use, package.accept_keywords and package.unmask entries, embedded in the plan, ready to execute.
No trial and error.
Parallel by design
Wave planning projects maximal parallelism straight from the availability proofs. Downloads, installs and runtime checks are independently scheduled actions — packages install while others are still downloading.
Faster upgrades.
Assembling thousands of interdependent components with user-specified feature selections is configuration management, not logistics — it calls for reasoning, not graph traversal. The result: no graph-traversal errors to decipher, but actionable plans where others fail.
?- features.
Key features.
What each feature does, why it matters and what it looks like in practice. Browse the complete feature list.
Self-describing plan steps
Each step is a single line: where the package comes from, the exact version, what to do with it, and why. That includes the flags it must be built with, and what it has to come after. When two dependencies need the same package, those requirements are combined.
How it works →
Learns from every conflict
Version domains narrow incrementally across retries; conflicts are recorded as no-goods — closer to CDCL than to traditional backtracking. Feature-term unification merges constraints when packages appear from multiple paths.
How it works →
Keyword, mask and blocker resolution
Your settings are tried first. If they aren't enough — a testing keyword, a mask, or a blocker — you still get a plan, with the exact lines to add to package.accept_keywords, package.unmask, or package.use.
How it works →
Multiple solutions
The engine reasons about the configuration space rather than committing to a single path. --variants compares how toggling a USE flag or switching a slot changes the proven result.
How it works →
Dependency discovery
Undeclared build dependencies discovered at build time are diagnosed, learned as durable knowledge, and the plan is re-derived — never patched in flight. Each discovery doubles as an upstream bug report draft.
How it works →
Self-healing builds
Transient failures pass through signature-keyed retry chains; packaging exceptions are repaired in-transaction. Sub-slot ABI rebuilds, GHC and OCaml breakage detection included — every recovery logged, never silent.
How it works →
Semantic search and LLM
Natural-language package search via vector embeddings. --explain turns a plan into prose, --diagnose runs meta-circular self-repair on failed builds; Ollama, Claude, ChatGPT, Gemini and Grok backends query the knowledge base through sandboxed Pengines.
How it works →
Distributed proving
Client / server / worker architecture over a Pengine-based HTTPS API with job queues. mDNS/Bonjour discovery forms clusters automatically; mutual TLS secures every connection.
How it works →
Portage-compatible
Building delegates to Portage's own ebuild infrastructure — the full ecosystem of ebuilds, eclasses and phase functions works unchanged. Emerge-compatible flags, pre-upgrade snapshots, rollback.
How it works →
Beyond Gentoo
Gentoo's rules are a plugin. The planner works on goals and constraints, so the same engine can plan any system you can describe the same way.
How it works →
?- show.
Readable plans, interactive graphs.
Build plans, searches and running builds in the terminal; dependency graphs, Gantt charts and detail views in the browser. Browse the full tree →
~ ❯ portage-ng --pretend neovim >>> Emerging : portage://app-editors/neovim-0.11.6-r2:run?{[]} Calculating dependencies... done! └─step 1─┤ download portage://dev-python/tree-sitter-0.25.2 │ └─ file ─┤ 170.27 Kb tree-sitter-0.25.2.tar.gz │ download portage://dev-lua/luv-1.50.0.1 │ download portage://dev-libs/unibilium-2.1.2 ⋮ 16 more downloads └─step 2─┤ install portage://dev-lang/lua-5.1.5-r200 │ └─ conf ─┤ USE = "readline deprecated" │ install portage://dev-libs/tree-sitter-c-0.24.1 │ └─ conf ─┤ USE = "python -debug -test" │ │ PYTHON_TARGETS = "python3_13 -python3_12 -python3_14" │ install portage://dev-lua/mpack-1.0.13 │ └─ conf ─┤ LUA_TARGETS = "lua5-1 -lua5-3 -lua5-4 -luajit" ... └─step 7─┤ install portage://app-editors/neovim-0.11.6-r2 │ └─ conf ─┤ USE = "nvimpager -test" │ │ LUA_SINGLE_TARGET = "lua5-1 -luajit" └─step 8─┤ run portage://app-editors/neovim-0.11.6-r2 Total: 57 actions (19 downloads, 19 installs, 19 runs), grouped into 8 steps. 16.85 Mb already downloaded — nothing left to fetch. >>> Blockers added during proving & planning: [blocks B] !dev-lang/lua:0 — soft, phase: install, required by lua-5.1.5-r200 ~ ❯
~ ❯ portage-ng --pretend app-editors/gvim >>> Emerging : portage://app-editors/gvim-9.1.1234:run?{[]} Calculating dependencies... done! └─step 1─┤ useflag portage://app-editors/vim-core-9.1.1234 (X) │ useflag portage://x11-libs/cairo-1.18.4 (X) │ useflag portage://x11-libs/gtk+-3.24.43 (X -wayland) └─step 2─┤ download portage://app-editors/vim-core-9.1.1234 │ download portage://x11-libs/cairo-1.18.4 │ download portage://x11-libs/gtk+-3.24.43 ⋮ 11 more downloads └─step 3─┤ install portage://app-editors/vim-core-9.1.1234 (USE modified) │ └─ conf ─┤ USE = "X* acl nls -minimal" │ install portage://x11-libs/cairo-1.18.4 (USE modified) │ └─ conf ─┤ USE = "X* glib -aqua -wayland" │ install portage://x11-libs/gtk+-3.24.43 (USE modified) │ └─ conf ─┤ USE = "X* introspection -wayland*" ... └─step 6─┤ install portage://app-editors/gvim-9.1.1234 │ └─ conf ─┤ USE = "acl cscope python -lua -perl" │ │ PYTHON_SINGLE_TARGET = "python3_13 -python3_12" └─step 7─┤ run portage://app-editors/gvim-9.1.1234 Total: 41 actions (3 useflags, 14 downloads, 12 installs, 12 runs), grouped into 7 steps. 8.12 Mb to be downloaded. >>> Assumptions taken during proving & planning: USE flag change (3 packages): Add to /etc/portage/package.use: app-editors/vim-core X x11-libs/cairo X x11-libs/gtk+ X -wayland ~ ❯
~ ❯ portage-ng --build app-editors/neovim >>> Emerging : portage://app-editors/neovim-0.11.6-r2:run?{[]} Calculating dependencies... done! └─step 1─┤ download portage://dev-python/tree-sitter-0.25.2 ✓ │ └─ file ─┤ 170.27 Kb tree-sitter-0.25.2.tar.gz ✓ │ download portage://dev-lua/luv-1.50.0.1 ✓ │ └─ file ─┤ 138.72 Kb luv-1.50.0.1.tar.gz ✓ ⋮ 17 more downloads └─step 2─┤ install portage://dev-lang/lua-5.1.5-r200 ✓ │ └─ exec ─┤ ACTION = clean → setup → unpack → prepare → configure → compile → install → merge ✓ │ │ LOG = dev-lang--lua-5.1.5-r200.log │ └─ conf ─┤ USE = "readline deprecated" ... └─step 7─┤ install portage://app-editors/neovim-0.11.6-r2 ⣷ │ └─ exec ─┤ ACTION = clean → setup → unpack → prepare → configure → compile → install → merge (63%) 6/8 │ │ LOG = app-editors--neovim-0.11.6-r2.log │ └─ conf ─┤ USE = "nvimpager -test" │ │ LUA_SINGLE_TARGET = "lua5-1 -luajit" └─step 8─┤ run portage://app-editors/neovim-0.11.6-r2
~ ❯ portage-ng --search name=portage category=sys-apps portage://sys-apps/portage-9999 portage://sys-apps/portage-3.0.77-r3 portage://sys-apps/portage-3.0.77-r1 portage://sys-apps/portage-3.0.77 portage://sys-apps/portage-3.0.72-r1 pkg://sys-apps/portage-9999 ~ ❯ portage-ng --search text editor with syntax highlighting Semantic search: "text editor with syntax highlighting" 84.7% app-editors/micro Modern and intuitive terminal-based text editor 82.3% app-editors/neovim Vim-fork focused on extensibility and usability 79.1% app-editors/vim Vim, an improved vi-style text editor ~ ❯
~ ❯ portage-ng --search-bugs neovim ### Bug search Searching for "neovim" on https://bugs.gentoo.org... Found 6 bugs for "neovim": #586882 [CONFIRMED ] vim-plugin.eclass should allow for neovim as alternate DEPENDS instead of just vim/gvim https://bugs.gentoo.org/show_bug.cgi?id=586882 #612644 [UNCONFIRMED] app-vim/* Add support for neovim https://bugs.gentoo.org/show_bug.cgi?id=612644 #932671 [CONFIRMED ] app-editors/neovim-0.10.0: unresolved soname dependency on /usr/lib64/lua/5.1/lpeg.so https://bugs.gentoo.org/show_bug.cgi?id=932671 #935641 [CONFIRMED ] tree-sitter-grammar.eclass: installs neovim libraries as broken symlinks https://bugs.gentoo.org/show_bug.cgi?id=935641 #965872 [CONFIRMED ] app-editors/neovim-0.11.5 - QA Notice: Unresolved soname dependencies: https://bugs.gentoo.org/show_bug.cgi?id=965872 #967125 [UNCONFIRMED] revdep-rebuild (Python version) falsely reports app-editors/neovim as broken while legacy script does not https://bugs.gentoo.org/show_bug.cgi?id=967125 ~ ❯
~ ❯ portage-ng --upstream neovim ### Upstream version check Checking 1 package against Repology... app-editors/neovim-0.11.6-r2 — upstream: 0.11.6 (legacy) ~ ❯
~ ❯ portage-ng --pretend --explain app-editors/neovim >>> Emerging : portage://app-editors/neovim-0.11.6-r2:run?{[]} Calculating dependencies... done! Total: 57 actions (19 downloads, 19 installs, 19 runs), grouped into 8 steps. Plan explainer ready. Ask questions about the build plan. Type "quit" or "exit" to leave. explain> why is dev-lang/lua in the plan? app-editors/neovim is built with LUA_SINGLE_TARGET=lua5-1, so its Lua modules — dev-lua/luv and dev-lua/mpack — are compiled against Lua 5.1. Both depend on dev-lang/lua-5.1.5-r200, which is why the plan installs it in step 2, before anything that links against it. It carries a soft blocker against the unslotted dev-lang/lua:0; the plan orders the replacement so the conflict never materialises. explain> why is tree-sitter-c in the plan? neovim uses tree-sitter for its built-in syntax highlighting and ships each grammar as a separate package. dev-libs/tree-sitter-c-0.24.1 is a runtime dependency of app-editors/neovim, so it is installed in step 2 — built with USE="python" and PYTHON_TARGETS="python3_13" for the grammar tooling. explain>
~ ❯ portage-ng --llm claude Chat session with claude (claude-sonnet-4-6). Type "quit" or "exit" to leave. claude> how many ebuilds are in the portage tree? I'll query the knowledge base directly rather than guess: <call:swi_prolog> :- aggregate_all(count, query:search([repository(portage)],_), N), format('The portage tree contains ~D ebuilds.~n', [N]). </call:swi_prolog> --- computer ------------------------------------------------------------------ The portage tree contains 32,147 ebuilds. ------------------------------------------------------------------------------- The knowledge base holds 32,147 ebuilds — every version of every package in the configured repository, loaded in memory as indexed Prolog facts. That count comes from executing the query against the running system, not from my training data. claude>
~ ❯ portage-ng --shell ::- portage-ng 2026.08.13 standalone ?- query:search([name(neovim), description(D)], R://E). D = "Vim-fork focused on extensibility and agility", R = portage, E = 'app-editors/neovim-0.11.6-r2'. ?- query:search([name(neovim), slot(S), keywords(K)], R://E). S = '0', K = unstable(amd64), R = portage, E = 'app-editors/neovim-0.11.6-r2'. ?- aggregate_all(count, query:search([repository(portage)],_), N). N = 32147. ?- resolver:test(portage). % Proving 32147 portage entries took 0m 59s. (56 threads) true. ?-
?- benchmark.
Insanely fast.
Running as a daemon with a C++ client, portage-ng answers in 55 ms at the median — 22× faster than emerge — and plans all 16,313 packages in 25 minutes, against emerge's 6 hours.
Speed is only part of it. Where emerge stops at a conflict and asks for manual package.use or keyword edits, portage-ng produces a plan that includes those changes. On the latest full-tree build (19,384 packages), it built everything emerge could, plus 3,523 packages more — 18.2% of the tree.
Cold --pretend | Median | Total | vs emerge |
|---|---|---|---|
| emerge | 1,234 ms | 6.09 hours | 1.0× |
| cave (Paludis) | 520 ms | 6.22 hours | 2.4× |
| pmerge (pkgcore) | 245 ms | 1.28 hours | 5.0× |
| portage-ng — SWI IPC client | 82 ms | 32.4 minutes | 15.1× |
| portage-ng — C++ IPC client | 55 ms | 25.0 minutes | 22.4× |
Four package managers, one benchmark: the same Portage tree (~32,000 ebuilds), the same installed-package database, the same /etc/portage. The clock measures the wall time of a cold --pretend, from keypress to merge plan.
In-memory reasoning
All 32,000 ebuilds live in memory as indexed Prolog facts — loaded once in a resident daemon, queried in microseconds; a thin IPC client connects in milliseconds. Portage re-parses metadata on every invocation.
Single-pass proving
No separate graph-construction phase, no conflict checking afterwards. Over 99% of packages prove in a single pass.
Learned constraints
On conflict, Portage discards the whole graph and starts over. portage-ng retries only the affected subtree, armed with learned constraints.
Parallel waves
The ordering pass identifies parallel build waves automatically — parallelism is a property of the plan, not an afterthought.
?- architecture.
A five-stage pipeline.
Reader, resolver, orderer, printer, builder. Each stage is independently testable and replaceable; the resolver and orderer are two passes of one prover — what to build, then when.
Contextual logic programming
A runtime object system for Prolog — contexts, classes, instances, inheritance and access control — used internally and available as a general-purpose paradigm. Chapter 21 →
Builder-to-prover feedback
Build-time discoveries (missing providers, wrong USE sets) become durable learned knowledge, and the plan is re-derived — the plan stays prove_plan(Goals, KB), always. Chapter 16 →
?- quickstart.
Up and running in minutes.
Prerequisites
- SWI-Prolog ≥ 10.0.0 — built with SSL, PCRE, editline, HTTP and pengines support
- A Gentoo Portage tree — ebuilds + md5-cache
- bash 5, git, curl, openssl — already present on most systems
Learn more
Installation guide — prerequisites, building, first run
portage-ng(1) manpage — the full command reference
CLI chapter — emerge-compatible flags, resolver hints, output styles
# clone and build $ git clone https://github.com/pvdabeel/portage-ng.git $ cd portage-ng && make build && make install # sync the Portage tree (builds Knowledge/kb.qlf — not shipped) $ portage-ng --mode standalone --sync # pretend (dry-run) a build plan $ portage-ng --mode standalone --pretend app-editors/neovim # interactive Prolog shell $ portage-ng --mode standalone --shell
?- handbook.
Human readable documentation.
Ease of understanding has been a design goal from the start: as a declarative program, portage-ng specifies what must hold rather than how to compute it — its inner workings are meant to be read. Twenty-nine chapters, from installation to contextual logic programming. Self-aware portage-ng, capable of explaining its reasoning to the user in human language.
What you'll learn
Getting Started
Introduction, installation and quick start, configuration
Architecture and Internals
Pipeline, prover, assumptions, version domains, rules, planning, output
Features
CLI, building, LLM integration, distributed proving, upstream bugs, GLSA
Foundations
Contextual logic programming, context terms, resolver comparison, ordering, solver literature
Development
Testing and regression, performance and profiling, contributing