Why I Rewrote Portage in Go: Introducing GRPM v0.1.0

Published: (January 8, 2026 at 01:39 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

If you’ve ever used Gentoo Linux, you know Portage. It’s powerful, flexible, and… Python‑based. For years—literally years—I had this persistent idea: what if Portage were written in Go? Fast compilation, single binary, no runtime dependencies, native concurrency. The idea kept coming back.

Last spring I finally stopped thinking and started coding. What began as “let’s see how hard this is” turned into 8 months of intensive development, ~60 000 lines of Go, and a complete re‑implementation of a package manager.

Today I’m releasing GRPM v0.1.0 (Go Resource Package Manager)—a drop‑in replacement for Portage with modern architecture and guaranteed conflict‑free dependency resolution.

Why a SAT‑based resolver?

Traditional dependency resolution algorithms rely on heuristics. When they encounter conflicts they often give up or make sub‑optimal choices. Portage’s resolver is sophisticated, but it can still fail on complex graphs with circular dependencies and slot conflicts.

I wanted something mathematically guaranteed to find a solution if one exists.

Core idea

GRPM uses a Boolean Satisfiability (SAT) solver at its core.

  • Every package version becomes a Boolean variable.
  • Every dependency becomes a logical clause.
// internal/solver/gophersat_adapter.go
type GophersatAdapter struct {
    clauses  [][]int
    vars     map[string]int            // name@version -> var ID
    packages map[string][]*pkg.Package // name -> []versions
}

func (g *GophersatAdapter) AddPackage(p *pkg.Package) {
    key := p.Name + "@" + p.Version
    g.packages[p.Name] = append(g.packages[p.Name], p)
    g.getVarID(key)
}

This transforms dependency resolution into a well‑studied mathematical problem. If a valid installation exists, the SAT solver will find it.

PropertyBenefit
CompletenessIf a solution exists, it finds it
Conflict handlingMultiple slot versions, blockers, USE flag constraints → SAT clauses
SpeedModern SAT solvers handle millions of variables
DependenciesUses gophersat, a pure‑Go SAT solver (no CGO)

Architecture (Domain‑Driven Design)

┌─────────────────────────────────────┐
│  CLI / Daemon Layer                 │
├─────────────────────────────────────┤
│  Application Layer   ← Use cases    │
├─────────────────────────────────────┤
│  Domain Layer         ← Business   │
│   (pkg, solver)                    │
├─────────────────────────────────────┤
│  Infrastructure Layer ← Repos, sync, install │
└─────────────────────────────────────┘

The domain layer knows nothing about Portage file formats or filesystem details, making the codebase testable and extensible.

Package formats

GRPM supports both modern GPKG (.gpkg.tar) and legacy TBZ2 (.tbz2) formats.

# Install from binary package
sudo grpm install --binpkg www-servers/nginx

# Build binary package from installed
sudo grpm build app-misc/hello-2.10

The binary‑package subsystem handles:

  • Multiple compression formats (zstd, xz, gzip, bzip2)
  • Package signing (GPG, SSH, RSA)
  • Remote binhost support

Building from source

# Build from source with 8 parallel jobs
sudo grpm emerge --jobs 8 dev-lang/go

# Show build plan first
grpm emerge --pretend app-misc/hello

Output

*** Dependency resolution (--pretend mode):
[ebuild  N    ] sys-libs/zlib-1.2.13 [0]
[ebuild  N    ] app-misc/hello-2.10 [0]

Total: 2 package(s)

GRPM implements all PMS phases: pkg_setup, src_unpack, src_prepare, src_configure, src_compile, src_install.

Syncing repositories

No external rsync binary is required—GRPM uses gokrazy/rsync, a pure‑Go implementation.

# Auto‑select best method
sudo grpm sync

# Use Git with GPG verification
sudo grpm sync --method git

# Native Go rsync (faster, no GPG)
sudo grpm sync --method rsync

Daemon mode (gRPC & REST)

# Start daemon
sudo grpm daemon

# CLI auto‑connects to daemon if running
grpm status
  • gRPC on Unix socket (/var/run/grpm.sock)
  • REST API on HTTP (127.0.0.1:8080)
  • Job queue with conflict detection
  • Parallel operation support

Snapshots on Btrfs/ZFS

GRPM automatically creates snapshots before package operations.

# Snapshot created automatically
sudo grpm install --snapshot-dir /.snapshots sys-libs/zlib

If installation fails, you can roll back to the snapshot.

Regex performance boost

GRPM uses coregex instead of Go’s standard regexp package. The performance difference is dramatic:

Benchmarkstdlib regexpcoregexSpeedup
Compile5 100 ns23 ns221×
Match185 ns1.5 ns123×

All regex patterns are pre‑compiled at package initialization:

// internal/repo/ebuild_parser.go
var (
    ebuildVarRe = coregex.MustCompile(`(?m)^([A-Z_][A-Z0-9_]*)="([^"]*)"`)

    ebuildAtomVersionRe = coregex.MustCompile(`^(.+?)-(\d.*)$`)
)

Command reference (Portage‑familiar)

CommandDescription
grpm resolveResolve dependencies with SAT solver
grpm installInstall packages (binary or source)
grpm emergeBuild packages from source
grpm removeRemove installed packages
grpm searchSearch for packages
grpm infoDisplay package information
grpm syncSynchronize repository
grpm updateUpdate @world/@system packages
grpm depcleanRemove orphaned packages
grpm statusShow daemon status

Dry‑run & confirmation

# Show what would happen (dry‑run)
grpm install --pretend app-misc/hello

# Ask for confirmation
sudo grpm install --ask app-misc/hello

Output with --ask

*** Installation plan:
[ebuild  N    ] sys-libs/zlib-1.2.13 to / USE="..."
[ebuild  N    ] app-misc/hello-2.10 to / USE="..."

Total: 2 package(s)

Would you like to merge these packages? [Yes/No]

Installation

# Download
wget https://github.com/grpmsoft/grpm/releases/download/v0.1.0/grpm_0.1.0_linux_x86_64.tar.gz

# Extract and install
tar -xzf grpm_0.1.0_linux_x86_64.tar.gz
sudo install -m 0755 grpm /usr/bin/grpm

Enjoy a faster, dependency‑conflict‑free Gentoo experience with GRPM!

Quick Commands

ActionCommand
Sync repositorysudo grpm sync
Search for packagesgrpm search firefox
Show package infogrpm info dev-lang/go
Build from source (pretend)sudo grpm emerge --pretend app-misc/hello
Build from source (real)sudo grpm emerge app-misc/hello
Install from binarysudo grpm install --binpkg app-misc/hello

Project Overview

  • Language: Go 1.25+
  • License: Apache‑2.0
  • Supported Platforms: Linux (x86_64, arm64, armv7, armv6, i386)
  • Codebase Size: ~60 000 lines of Go
  • Test Coverage: ~70 %

Core Dependencies

DependencyPurpose
gophersatSAT solver for dependency resolution
cobraCLI framework
grpcDaemon communication
gokrazy/rsyncNative rsync implementation
modernc.org/sqlitePure‑Go SQLite for caching
coregexHigh‑performance regex

All dependencies are pure Go — no CGO required.

Current Limitations (v0.1.0)

  • Ebuild execution limited to the autotools workflow (./configure && make)
  • Limited eclass support (toolchain-funcs, eutils, multilib)
  • No EAPI 8 features
  • CMake/Meson build systems not supported

These issues will be addressed in future releases.

Roadmap – v1.0.0 (Q2 2026)

  • Complete Portage command compatibility
  • Full eclass support
  • EAPI 8 features
  • Support for CMake, Meson, and Cargo build systems
  • Performance optimisation for large dependency graphs
  • Production validation on real Gentoo systems

Why Build GRPM?

  1. Learning – Package managers are fascinating systems. Building one from scratch teaches you about dependency graphs, constraint solving, filesystem operations, and system integration.
  2. Performance – Go’s fast compilation, efficient memory usage, and excellent concurrency make it ideal for system tools.
  3. Community – Gentoo deserves modern tooling. If GRPM helps even a few users, the effort is worthwhile.

Try It Out

  • Repository:
  • Releases:
  • Documentation: CLI Reference (link to be added)

Contributing

Contributions are welcome! Whether it’s bug reports, feature requests, or code contributions — every bit helps.

Have you tried building your own package manager? Or have experience with SAT solvers in other domains? I’d love to hear about it in the comments.

Back to Blog

Related posts

Read more »