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.

22×
faster than emerge for planning
55 ms
median time to a proven plan
>3,500
plans proposed where emerge doesn't
>32,000
ebuilds proven in memory

See the benchmark ↓

?- 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.

01

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.

02

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.

03

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
~ ❯ 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

~ ❯ 

?- 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 --pretendMedianTotalvs emerge
emerge1,234 ms6.09 hours1.0×
cave (Paludis)520 ms6.22 hours2.4×
pmerge (pkgcore)245 ms1.28 hours5.0×
portage-ng — SWI IPC client82 ms32.4 minutes15.1×
portage-ng — C++ IPC client55 ms25.0 minutes22.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.

Reader / Parser
Parses the md5-cache into Prolog facts via the EAPI grammar
Resolver
First prover pass — derives the configuration proof: versions, slots and USE, with assumptions and learned constraints
Orderer
Second prover pass — plans the proof into parallel waves over generic planning laws
Printer
Renders plans, proofs, graphs, Gantt charts and web views
Builder
Executes via Portage's ebuild contract, feeding discoveries back

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

Installation guide

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

quickstart.sh
# 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