portage-ng

::- every plan is a proof.

portage-ng is a declarative reasoning engine for large scale software configurations, applied to Gentoo Linux. It uses logical reasoning to produce a plan, recording which rule justified each 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

Time to a merge plan across 16,313 packages — identical tree, VDB and configuration; build outcomes from the latest 19,384-package full-tree run. See the numbers ↓

?- why.

Not a package manager. A reasoning engine.

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.

01

Proofs, not guesses

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

Explicit assumptions

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 construction

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.

?- features.

The full toolbox.

Every card links to a plain-language explanation with examples — or see the complete feature list for everything.

One literal, the whole story

Everything in the pipeline is a single self-describing term: repository, exact version and phase form the spine, and the context — a list of feature terms — carries the metadata: which USE flags the package must be built with, what it must come after. When dependency paths meet, contexts merge by feature unification, a logic programming paradigm.

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 →

A plan for the hard cases

Strict proving first; if it fails, keyword acceptance, blocker resolution and unmasking are tried in tiers. Each tier is a weaker proof that still carries formal guarantees — every relaxation tracked and reported.

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 →

Not just for Gentoo

The prover and planning laws operate on abstract literals; all Gentoo-specific logic lives in a pluggable rules layer behind a rule/2 hook. The same engine can reason about any rule-encoded domain.

How it works →

?- show.

See it think.

Proven plans in the terminal, interactive graphs and Gantt charts in the browser — and the builder executing a plan, phase by phase.

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  (blocked: soft by dev-lang/lua)
            │           └─ 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

~ ❯ 

click to enlarge

?- benchmark.

Faster overall. First to a working plan.

The benchmark is a level playing field: four engines, same Portage tree snapshot (~32,000 ebuilds), same VDB, same /etc/portage — and the clock measures what you actually feel, the wall time of a cold --pretend from keypress to merge plan. Running as a warm daemon with an ultralight C++ IPC client, portage-ng answers in 55 ms at the median — 22× faster than emerge — and sweeps all 16,313 packages in 25 minutes to emerge's 6 hours. But the metric that really counts is time to a working plan: where emerge hands a conflict back for manual package.use and keyword edits, portage-ng derives those changes itself. On the latest full-tree build run (19,384 packages), that meant building 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×

Wall clock per package across the 16,313 packages where emerge -vp produced a plan; portage-ng timed as warm daemon plus IPC client — connect, prove, print. Speedups are median ratios — 22.4× = 1,234 ms ÷ 55 ms; cave's total is inflated by 120 s timeouts. From the v1.5.0 pm-bench run — methodology and all-package tables in Chapter 26 — Performance.

Qcompiled knowledge base

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 without a single retry.

Subtree retries

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.

Five stages, one prover.

Each stage is independently testable and replaceable. The resolver and orderer are thin wrappers around one generic prover: pass one derives what to build, pass two derives 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

# pretend (dry-run) a build plan
$ portage-ng --mode standalone --pretend app-editors/neovim

# interactive Prolog shell
$ portage-ng --mode standalone --shell

# sync the Portage tree
$ portage-ng --mode standalone --sync

?- handbook.

Human readable documentation and explanations.

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-eight 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