Pacm

Pacm

C++ package manager for distributing compiled plugins, model files, and binary assets. Async downloads, SHA-256 verification, SDK version locking, atomic installation.

C++20libuvHTTPSHA-256ZIP

Distributing compiled C++ code to end users is a problem nobody has solved cleanly. You can vendor binaries in your repo (disgusting), host them on a CDN with a shell script (fragile), or build a custom updater for every project (wasteful). There’s no npm install for native plugins.

Pacm is that missing piece. A C++ package manager designed specifically for distributing compiled plugins, model files, and binary assets to applications at runtime. It handles remote package querying, versioned downloads with mirror fallback, SHA-256 checksum verification, atomic multi-phase installation, and SDK version locking. Built on libuv for async IO, so installations don’t block your application.

How it works

The package lifecycle has three phases and a state machine that prevents every installation from becoming a partial-write catastrophe:

  Remote Index                Local State              Filesystem
       |                          |                        |
  queryRemotePackages()     loadLocalPackages()            |
       |                          |                        |
       v                          v                        |
  RemotePackage[]           LocalPackage[]                 |
       |                          |                        |
       +--- getPackagePairs() ----+                        |
       |                                                   |
       v                                                   |
  InstallTask                                              |
    Downloading -----> tmp/package-1.0.0.zip               |
    Extracting  -----> tmp/extracted/                      |
    Finalizing  -----> install/package-name/      <-- atomic move
    Installed   -----> data/package-name.json     <-- manifest

Downloading fetches the archive from remote mirrors with progress callbacks. Extracting decompresses to a staging directory with path traversal protection - no zip bomb can write outside the target. Finalizing atomically moves files to the install directory. If any phase fails, the previous phases are still intact. No partial installations, no corrupted state.

Package definitions

Packages live as JSON on any HTTP server - your own CDN, GitHub releases, S3, whatever. Each package declares versioned assets with platform-specific binaries:

{
    "id": "vision-plugin",
    "name": "Vision Plugin",
    "type": "Plugin",
    "author": "Sourcey",
    "description": "OpenCV-based image processing plugin",
    "assets": [
        {
            "version": "2.1.0",
            "sdk-version": "3.0.0",
            "platform": "linux",
            "file-name": "vision-2.1.0-linux.zip",
            "file-size": 4521984,
            "checksum": "a1b2c3...",
            "mirrors": [
                {"url": "https://cdn.example.com/vision-2.1.0-linux.zip"},
                {"url": "https://backup.example.com/vision-2.1.0-linux.zip"}
            ]
        }
    ]
}

Platform detection is automatic. Asset selection picks the right binary for the OS. Mirror fallback means a CDN outage doesn’t break your users’ installations.

Version intelligence

Not every update is safe. A plugin compiled against SDK 2.x might crash with SDK 3.x internals. Pacm handles this:

  • SDK version locking - latestSDKAsset(sdkVersion) returns the newest asset compatible with the running application’s SDK version
  • Version pinning - setVersionLock(version) freezes a package at a specific release, ignoring newer versions
  • Update detection - hasAvailableUpdates() compares local manifests against remote, respecting both locks
PackageManager pm(options);
pm.initialize();
pm.queryRemotePackages();

// Only install what's compatible
auto pairs = pm.getUpdatablePackagePairs();
pm.updatePackages(ids, &monitor);

monitor.Complete += [](LocalPackageVec& packages) {
    for (auto& pkg : packages)
        loadPlugin(pkg->getInstalledFilePath("lib/plugin.so"));
};

Batch operations

The InstallMonitor coordinates multiple concurrent installations with aggregate progress:

InstallMonitor monitor;
pm.installPackages({"plugin-a", "plugin-b", "model-data"}, &monitor);

monitor.Progress += [](int& percent) {
    updateProgressBar(percent);  // 0-100 across all tasks
};

monitor.InstallStateChange += [](auto& task, auto& from, auto& to) {
    log("Package {} moved from {} to {}", task.name(), from, to);
};

monitor.Complete += [](auto& packages) {
    // All done - load plugins, apply models
};

Individual tasks emit their own progress. The monitor aggregates. Your UI gets a single percentage without tracking individual downloads.

Security

Every installation verifies before finalizing:

  • Checksum validation - SHA-256 (or MD5 for legacy) against the declared hash. Mismatch aborts the install
  • Path traversal protection - validatePathComponent() rejects .., absolute paths, and path separators in archive entries. No zip file can write outside its target directory
  • Manifest verification - after extraction, every declared file is checked for existence. Missing files fail the install
  • Authentication - OAuth bearer tokens or HTTP basic auth for private repositories
  • TLS - all HTTP traffic over SSL with certificate verification

With Pluga

Pacm and Pluga form a complete plugin distribution system. Pacm handles the logistics - download, verify, extract, install. Pluga handles the runtime - load the shared library, check the ABI version, instantiate the plugin. Together:

  1. Host your compiled plugins as versioned ZIP archives on any HTTP server
  2. Pacm queries the index, downloads the right platform binary, verifies the checksum
  3. Application loads the installed .so/.dll through Pluga’s SharedLibrary wrapper
  4. ABI version check catches compiler mismatches before they become segfaults
  5. Updates check SDK compatibility before replacing a working plugin with an incompatible one

The same pipeline works for any binary asset - machine learning models, shader files, data bundles, configuration packages. If it’s a file that needs versioned distribution with integrity verification, Pacm handles it.