Contents · Release notes

Release notes

What changed in each release of pkgproxy, newest first.

0.9.0

  • Upgrading from 0.8.0:

    - Re-run pkgproxyctl setup lxd or setup snapcraft, whichever you used (and use setup rockcraft or setup charmcraft for those tools, new in this release). The new LXD profile points snap download in the containers at pkgproxy, uses pkgproxy's real port and the bridge LXD manages, and keeps apt in the containers from hanging while pkgproxy is offline. - pkgproxy's own API moved from /v1/... to /pkgproxy/v1/... (gitlab.com/zygoon/pkgproxy#75). The old paths answer 404, so a script that reads the stats API (docs/statsapi.md) or calls the admin API needs the new paths, and a setup script saved from an older pkgproxyctl must be printed again. - On Linux pkgproxy now goes offline by itself while the machine has no network. To keep the old behaviour, run snap set pkgproxy auto-offline=off (or start pkgproxy with -auto-offline off). - The statistics file is converted on the first start, with nothing to do. Downloads saved now count packages, modules, images and blobs only, and only the bytes really sent, so the totals shown by pkgproxyctl stats and the cache page come out lower than before. - On a machine logged in to the Snap Store, install or refresh each snap once online with this version before relying on it offline. - Cached snap info answers are fetched again once after the upgrade. - Commands that take no arguments, pkgproxy included, now refuse stray ones instead of ignoring them. - Edge builds briefly had a snap option offline. If you set it, run sudo snap unset pkgproxy offline and use offline-mode=offline instead.

  • Work offline, or leave an overloaded store alone (gitlab.com/zygoon/pkgproxy#54, gitlab.com/zygoon/pkgproxy#57, gitlab.com/zygoon/pkgproxy#61).

    The offline mode offline serves only what is cached and contacts no mirror, store or registry; what is not cached fails at once, without making apt or snapd retry. The mode store-overloaded stays online but leaves an upstream that fails, times out or answers 429 alone for a while, serving from the cache. The default, never, goes online as needed. In the snap, snap set pkgproxy offline-mode=... and pkgproxyctl offline on|off|mode change the same setting, which survives a restart; without the snap, -offline-mode (or PKGPROXY_OFFLINE_MODE) sets it and pkgproxyctl offline changes it until a restart.

    A change of mode, by hand or with the machine's network, makes pkgproxy ask every upstream again at once, rather than serve cached copies stale for another minute because an upstream failed before.

  • Go offline with the machine (gitlab.com/zygoon/pkgproxy#55, gitlab.com/zygoon/pkgproxy#77).

    On Linux pkgproxy goes offline by itself while the machine has no network, airplane mode included, and comes back when it does. This is the default, auto-offline=network; auto-offline=airplane-mode follows airplane mode alone, and auto-offline=off turns it off. An explicit pkgproxyctl offline off holds until the machine next goes offline.

  • Install and refresh snaps offline (gitlab.com/zygoon/pkgproxy#58, gitlab.com/zygoon/pkgproxy#78).

    snap install, snap download and snap refresh of a snap that was once installed through pkgproxy now work offline, answered from what the store said then, as long as the snap and its assertions are cached. When pkgproxy records an install or refresh, it also fetches the assertions of the snap that it lacks, so an install that snapd half-satisfied from its own copies is still installable offline. This now works on a machine logged in to the Snap Store too: pkgproxy keeps the store's answers to a logged-in snapd (installs, refreshes, snap info, snap find, assertions) when they show only public snaps, and never one that shows a private snap.

  • Cache snap find, so a search made online is found again offline.

    Searches are kept for as long as the store says, and an expired answer is served when the store cannot answer. Offline, a search never made online fails at once with pkgproxy's message.

  • Show when pkgproxy is offline (gitlab.com/zygoon/pkgproxy#66).

    pkgproxyctl offline says whether and why pkgproxy is offline, and pkgproxyctl stats and the setup page say so too. /pkgproxy/v1/info on the stats socket reports the mode, why it applies, and in store-overloaded mode which upstreams pkgproxy is leaving alone.

  • Add pkgproxyctl setup rockcraft and setup charmcraft (gitlab.com/zygoon/pkgproxy#83).

    They do for rockcraft and charmcraft what setup snapcraft does for snapcraft: point the LXD build containers of rockcraft pack and charmcraft pack at pkgproxy. They work before the tool's first build, creating its LXD project as the tool would; so does setup lxd -project for any other project. A charm's Python packages and charm libraries still bypass pkgproxy.

  • Show visitors how to use pkgproxy from their own machine (gitlab.com/zygoon/pkgproxy#50).

    Opening http://<host>:3142/ in a browser, or fetching it with curl, shows the commands that point apt, snapd, Go, Docker and LXD image remotes at this pkgproxy, so machines without pkgproxy can be set up with curl -fsSL http://<host>:3142/setup/apt | sudo sh. The page uses the address the visitor reached pkgproxy at, or -public-url when set, and -setup-page=false turns it off; both are also snap options.

  • Open the setup page from the applications menu (gitlab.com/zygoon/pkgproxy#69).

    The snap, now titled PkgProxy, has a desktop entry of that name that opens the setup page in your browser. pkgproxyctl open (in the snap, pkgproxy.open) does the same from a terminal, and says why when it cannot.

  • Show what fills the cache (gitlab.com/zygoon/pkgproxy#68).

    Opened on the machine that runs pkgproxy, the setup page says how much space the cache takes and how much is left on its disk, and links to a new page, /pkgproxy/cache, with the downloading the cache has saved, the space each kind of download takes, and a map of the archives, snaps, Go modules, LXD images and Docker repositories in it. The sizes are measured in the background while someone looks, at most once a minute, and the page says how old they are. Visitors from other machines see none of this.

  • Publish the documentation at https://zygoon.pl/pkgproxy/ (gitlab.com/zygoon/pkgproxy#74), with a new troubleshooting guide.

    docs/troubleshooting.md says where the log is, what the errors that apt, snapd, Go, Docker and LXD print mean, why Docker falls back to Docker Hub without saying so, and how to tell whether a tool really uses pkgproxy.

  • Add pkgproxyctl version.

    It shows the version of pkgproxyctl, of the running pkgproxy and, in the snap, of the snap, and says when the running pkgproxy is another build. /pkgproxy/v1/info reports version, and the setup page shows it at the bottom. Snap builds no longer carry a spurious +dirty in their version.

  • Support more of what snapd asks the store (gitlab.com/zygoon/pkgproxy#53).

    pkgproxy now caches the store's older v1 API (sections, names, search, details), categories and repairs, caches deltas like snaps, follows the store's redirects to its CDN, and reads compressed store answers instead of passing them on unrewritten. Docker's referrers requests go to the registry rather than the snap store, and a request no part of pkgproxy handles gets a 404.

  • Be gentler on the Snap Store (gitlab.com/zygoon/pkgproxy#56).

    Answers are kept for the max-age the store announces, clients asking the same thing at once share one request to the store, and an expired answer is served when the store answers 429 or 5xx or cannot be reached, instead of passing the failure on.

  • Say in the access log how each request was served.

    The "handled request" line of every apt, snap, Go module, LXD and Docker request ends with an outcome: hit, fetched, revalidated, stale, joined, passthrough, synthesised, tunnel or error. A response with a 5xx status is logged at error level with the start of its body as response, and the line of a snap refresh answered online says what pkgproxy fetched ahead (assertions_fetched, chains_incomplete).

  • Count only content, and only what was sent, as downloads saved.

    pkgproxyctl stats and the cache page add up packages, modules, images and blobs, not the indexes that say what there is (such as apt's Packages and InRelease), and show the metadata served from the cache on its own (in parentheses in the SAVED column). A hit saves the bytes it sent: nothing for a HEAD or a 304, the part for a Range request. A request that waited for another's download counts as a hit and the one that started it as a fetch. Each entry of /pkgproxy/v1/stats has a new field, content.

  • Set up LXD and the craft tools more reliably (gitlab.com/zygoon/pkgproxy#62, gitlab.com/zygoon/pkgproxy#64, gitlab.com/zygoon/pkgproxy#67, gitlab.com/zygoon/pkgproxy#71, gitlab.com/zygoon/pkgproxy#73, gitlab.com/zygoon/pkgproxy#83).

    The setup scripts configure the local LXD even when another remote is the default, and put pkgproxy on the bridge LXD manages for the project, never Docker's docker0; when there is none, they stop before changing anything. They need the lxd group, not root, so pipe them to sh, not sudo sh; they check first that LXD can be used and stop with an explanation when it cannot, or when pkgproxy is not running. The standalone pkgproxy profile uses pkgproxy's real port, not always 3142. The profile now also sets SNAPPY_FORCE_API_URL, so the stage-snaps of craft builds (such as every rock's pebble) come through pkgproxy, and apt in the containers gets Acquire::Retries::Delay "false", so it cannot hang while pkgproxy is offline. The snap no longer has a network-observe plug.

  • Do not make apt wait while pkgproxy is offline (gitlab.com/zygoon/pkgproxy#72).

    In the mode offline, what is not cached is answered without a Retry-After, which apt 3 waited for, three times, so apt update took about 90 seconds instead of about 8. pkgproxyctl setup apt also adds Acquire::Retries::Delay "false"; where apt is older than 3.1.3, such as on Ubuntu 24.04: that apt can hang in apt update when one source fails and the others work (LP: #2003851).

  • Say what each option and setup step does, in plain words (gitlab.com/zygoon/pkgproxy#63).

    The help of pkgproxy and pkgproxyctl, the setup guides, the startup banner, errors and log messages say what happens rather than how it works inside, and no longer name source files or internal packages. The help of each command starts with a usage line that names its arguments, and a wrong number of arguments prints it.

  • Refuse a listen address pkgproxy cannot use in snap set.

    snap set pkgproxy addr=... or lxd-tls-addr=... with a value the daemon cannot listen on now fails and keeps the old value, instead of leaving a daemon that cannot start.

  • Revalidate cached account-key assertions, so that clients see the newest revision the store has re-issued.

  • Drain in-flight requests on SIGTERM, as on SIGINT.

    snap stop, systemctl stop and container runtimes send SIGTERM, which killed pkgproxy at once, cutting off downloads and losing the last statistics. It now shuts down gracefully, bounded by shutdown-grace (10s by default).

  • Make the setup scripts safe to pipe to sh, and to run twice.

    Every piped setup script is one { ... } block, which sh reads in full before running any of it; setup lxd | sh failed the first time because lxc profile create read the rest of the script as the profile. A setup command that fails makes the sh it is piped to fail too, instead of running nothing and succeeding. -undo before setting up, or twice, now succeeds and changes nothing for setup lxd, setup go and setup lxd-remote, and for setup snapcraft, setup rockcraft and setup charmcraft; setup lxd-remote -undo also touches only the remotes that point at pkgproxy, and setup docker -undo removes an emptied registry-mirrors key. setup docker no longer needs jq, so it works on Ubuntu Core.

  • Let pkgproxyctl setup lxd-remote run in a root login, and in a fresh LXD snap install.

    The script now refuses root only under sudo, which configures the wrong user, and puts the pinned certificates where a freshly installed LXD snap's lxc looks for them.

  • Write IPv6 listen addresses with brackets in printed setup commands: -addr [::1]:3142 gave http://::1:3142, which clients cannot parse.

  • Fix smaller problems.

    A cache entry whose file is missing, as in a partly restored cache directory, is fetched again instead of failing with a 500. Headers that a Connection header names are no longer passed between snapd and the store. /pkgproxy/v1/offline reports "auto_offline": "off" instead of leaving the field out. snap set pkgproxy no longer logs AppArmor denials. Commands on the setup and cache pages no longer render in a serif font in some browsers.

  • Test the snap end to end: a spread suite runs it with the real apt, snapd, Docker, Go, LXD, snapcraft, rockcraft and charmcraft of an Ubuntu VM (gitlab.com/zygoon/pkgproxy#82, docs/testing.md).

0.8.0

  • Make the listening ports configurable in the snap (gitlab.com/zygoon/pkgproxy#25).

    snap set pkgproxy addr=... and snap set pkgproxy lxd-tls-addr=... move pkgproxy off ports 3142 and 3143 when those are taken. The daemon reports its listen addresses at /v1/info on the stats socket, and pkgproxyctl setup uses them unless -addr or -lxd-tls-addr is given, so the printed client configuration follows the daemon.

  • Send resumed downloads of a file still being fetched as it arrives (gitlab.com/zygoon/pkgproxy#41).

    A Range request for a file that pkgproxy was still downloading could have up to 4 KiB of the data that had already arrived held back until more came in.

  • Say "no cache hits yet" instead of "none cached yet" in pkgproxyctl stats, which only lists entries that have been served from the cache at least once.

  • Fix a rare error 500 for an apt package list (gitlab.com/zygoon/pkgproxy#38).

    A request for a package list that arrived while pkgproxy was passing a corrupt copy of the same list straight from the mirror to another client got an error instead of the mirror's answer. Cached Go module metadata is now also served with Last-Modified and honours Range and If-Modified-Since, like every other kind of cached file.

  • Keep Go and LXD metadata quick while their upstream is down (gitlab.com/zygoon/pkgproxy#37).

    pkgproxy already served the cached copy of a Go module list or an LXD image index when its upstream failed, but only after the failure, which for an upstream that stopped answering meant a timeout per file. As for apt package lists, it now waits at most 10 seconds and leaves a failing upstream alone for a minute.

  • Stream snap and LXD downloads to every client (gitlab.com/zygoon/pkgproxy#36).

    When several machines downloaded the same uncached snap or LXD image at once, only the first got the file as it arrived; the others got nothing until the whole file was cached, long enough for snapd or LXD to give up on a large one, and a cut-short response if the download failed. Resuming such a download with a Range request also waited for the whole file. Every client now follows the download as it arrives, the same way apt packages and container image layers already were.

  • Relay ETag revalidation on uncached paths (gitlab.com/zygoon/pkgproxy#35).

    A client revalidating a file that pkgproxy passes straight to the mirror by If-None-Match got the whole file back instead of 304 Not Modified.

  • Give up on upstreams that stop sending (gitlab.com/zygoon/pkgproxy#26).

    A mirror or registry that went silent in the middle of a download used to keep that download open forever, and every later request for the same file joined it and hung too, until pkgproxy was restarted. A request now fails once its upstream has sent nothing for -upstream-idle-timeout (default 60s, also snap set pkgproxy upstream-idle-timeout=...), and the next request starts the download afresh. Slow but steady downloads are unaffected, as is time spent sending to slow clients.

  • Stop holding whole packages in memory after a corrupt download (gitlab.com/zygoon/pkgproxy#34).

    A package that failed its hash check, from a mirror that also does not announce the file size, was re-read into memory in full for every waiting client before any of them got a byte. It is now streamed.

  • Keep apt update working while a mirror is down (gitlab.com/zygoon/pkgproxy#33).

    When checking cached package lists against a mirror failed, apt got an error even though pkgproxy had a copy. pkgproxy now serves its copy and logs a warning, as it already did for Go and LXD. It waits at most 10 seconds for a mirror that does not answer, and leaves a failing mirror alone for a minute, so that an outage does not make each file wait out its own timeout. A list the mirror reports as gone (404 or 410) returns 404.

  • Fix shared downloads failing when the client that started them left (gitlab.com/zygoon/pkgproxy#32).

    When several clients asked for the same uncached file at once and the first of them hung up, the others got an error (apt and Go) or a truncated download (snap and LXD). For snap and LXD, a first client that stays connected but stops reading for a minute is now treated as gone too, instead of holding up everyone else's download.

  • Fix resumed downloads of uncached files (gitlab.com/zygoon/pkgproxy#31).

    A Range request that pkgproxy passes straight to the mirror got its 206 Partial Content answer without the Content-Range header that says which part of the file it is.

  • Translate every error and all setup instructions (gitlab.com/zygoon/pkgproxy#27).

    All errors pkgproxy reports are now translatable, and failure messages consistently read "cannot ...", in errors, log lines and HTTP error bodies alike. Log messages that operators may grep for changed wording accordingly: for example "failed to open cache directory" is now "cannot open cache directory", and "origin fetch failed" is now "cannot fetch from origin". pkgproxyctl setup output is translated paragraph by paragraph and reflowed to the terminal width, while commands and configuration are always printed verbatim. The Polish translation covers all of it.

  • Translate the daemon's log messages (gitlab.com/zygoon/pkgproxy#27).

    Log messages and the errors they carry are now logged in the daemon's locale. Attribute names and other values are unchanged, so log lines keep the same key=value shape in every language. The pkgproxy snap's service normally runs in the C locale, so its logs stay in English.

  • Fix flag help mangled by quoting (gitlab.com/zygoon/pkgproxy#29).

    pkgproxy -h showed -admin-socket-path pkgproxyctl setup lxd where it meant -admin-socket-path string, and pkgproxyctl setup subcommands printed the default of -addr and -base-path twice.

  • Name the internal handler in the access log.

    Each "handled request" line now carries handler= (apt, snap, gomod, lxd, docker, or lxd-cert), so snap store and Docker registry requests, which both look like /v2/..., can be told apart.

  • Cache Docker Hub images (gitlab.com/zygoon/pkgproxy#20).

    pkgproxy now acts as a registry mirror for Docker Hub: pkgproxyctl setup docker | sudo sh adds it to the Docker daemon's registry-mirrors, and image manifests and layers are then cached by digest, verified, and served from the cache on every later pull. Tags are revalidated after -docker-revalidate-ttl (default 5m) with a HEAD request, which does not count against Docker Hub's anonymous pull rate limit. Only anonymous pulls of public images are supported. The mirror is on by default and is disabled with -docker-upstream off. Docker requires a mirror at the root of its URL, so the registry API is served at /v2/..., told apart from the snap store's by path shape.

    pkgproxyctl stats and the stats API report the mirror's layer and config blobs as type docker, keyed <repository>@<digest>.

0.7.0

  • Stream cache-miss apt downloads instead of fetching the whole file first (gitlab.com/zygoon/pkgproxy#24).

    A miss on a pool/** package used to stay silent, with no headers, until the entire file had been downloaded from the mirror. Clients that give up on a silent connection (a 300 MB package took 38s; something in a snapcraft/LXD build hung up at ~30s) got 502 … context canceled, and because the download was tied to that client it was thrown away, so every retry started from zero and could never finish.

    The proxy now answers as soon as the mirror does and streams the body while it is cached. The download is independent of any one client, so it completes and is cached even if the client leaves; clients arriving meanwhile, including Range resumes, follow the same download. On a hash mismatch clients still receive the mirror's bytes unmodified and nothing is cached. dists/** metadata is unchanged. How long such a download waits for slow clients is -fill-drain-timeout (default 10m).

  • Tunable intervals (gitlab.com/zygoon/pkgproxy#23).

    New flags -min-forced-revalidate-interval, -stats-checkpoint- interval, -lxd-checkpoint-interval, -shutdown-grace and -read-header-timeout expose what were hardcoded constants. Every daemon flag can now also be set through a PKGPROXY_<FLAG> environment variable (flag beats environment beats default), and the snap accepts the interval options via snap set pkgproxy revalidate-ttl=10m etc. See the README's Configuration section.

  • Fix apt cache keys dropping the path prefix before dists//pool/ (gitlab.com/zygoon/pkgproxy#22).

    The key used to be <host>/<path from dists/ or pool/ onward>, so archives that differ only in that prefix shared cache entries and index hashes. For Ubuntu (/ubuntu/) the dropped segment was constant, but KDE Neon's /unstable/ and /release/ editions aliased to one entry, and clients were served the other edition's InRelease. The key is now <host>/<prefix>/<path>, e.g. archive.ubuntu.com/ubuntu/dists/....

    **Upgrade note:** existing apt cache entries are stored under the old keys and are no longer found; they are re-fetched once on demand and the old objects/<host>/{dists,pool} trees can be deleted by hand.

  • Honour If-Range on apt Range (resume) requests.

    apt resumes a partial download with Range plus If-Range set to the partial's Last-Modified. pkgproxy used to ignore If-Range and answer 206 from whatever was cached, so a partial left over from an older generation of an index file got the tail of the newer one spliced on, corrupting the download or failing signature checks. A Range is now served only if If-Range matches the cached entry (exact Last-Modified, or a strong ETag); otherwise the whole body is sent with 200. Uncached (passthrough) requests forward If-Range to the origin. The snap and LXD caches share the same range handling (new internal/httprange, replacing three copies) and so honour If-Range too.

  • Answer If-None-Match/If-Modified-Since with 304 Not Modified on cache hits.

    apt sends If-Modified-Since when refreshing an index it already has, but pkgproxy used to resend the whole file every time. A hit whose Last-Modified is not later than the client's date (or whose ETag matches If-None-Match, which takes precedence) now gets a bodyless 304 with the entry's validators. The decision follows the usual TTL/revalidation step, so a 304 is never answered from an entry pkgproxy knows to be stale. Shared by the apt, snap and LXD caches.

  • Honour a client's Cache-Control: no-cache/max-age on apt dists/** hits (apt's Acquire::http::No-Cache/Max-Age).

    A request that wants fresher content than the cached entry's age (no-cache, max-age=N exceeded, or Pragma: no-cache alone) now makes pkgproxy revalidate with the origin, with a conditional GET, before serving instead of waiting out -revalidate-ttl. At most one such check per entry every 30 seconds, so a client cannot turn every request into an origin round trip. pool/** and by-hash entries are immutable and unaffected. A forced check cannot replace content the origin keeps reporting as unmodified; use pkgproxyctl cache invalidate for that.

0.6.1

  • Fix a dists/** cache entry outliving its own suite's rotation.

    A cached Packages/Sources/Release/InRelease object could keep being served — within its -revalidate-ttl window, or indefinitely longer via a conditional-GET 304 from an origin whose Last-Modified lags its content — even after this proxy's own index (refreshed by a newer Release/InRelease fetch) already expected different content at that path. Real-world symptom: an apt-family client (reported with chisel/snapcraft) fetching a fresh Release through the proxy but a stale Packages.gz behind it, failing that file's checksum against the digest Release just gave it. Every serve of a dists/** object now cross-checks it against the index first; a disagreement forces a full unconditional refetch instead of trusting the TTL or the origin's conditional-GET response.

  • Add pkgproxyctl cache invalidate <url>.

    Evicts one cached apt object (identified by the exact URL a client would fetch it at) without root access to -cache-dir — the operator escape hatch for a stale entry that needs to go now, backed by a new DELETE /v1/cache admin-API route.

0.6.0

  • Add -content-socket-path/-lxd-tls-content-socket-path, and a sockets content-interface slot in the snap package.

    Additional UNIX-socket listeners alongside the normal -addr/-lxd-tls-addr TCP ones (unlike -socket-path/ -lxd-tls-socket-path, which replace TCP rather than adding to it), so a standalone pkgproxy install can share its cache with another snap via the content interface without giving up its own host-side TCP listeners. The snap package binds them under $SNAP_COMMON/sockets/ and exposes that directory via a new sockets slot (content: pkgproxy-sockets) — image-garden is the first consumer, bind-mounting them in place of its own bundled instance.

  • Warn instead of silently dropping -lxd-tls-socket-path/ -lxd-tls-content-socket-path when the LXD HTTPS listener isn't up.

    Those flags only take effect once the LXD TLS listener actually starts (LXD side on, -lxd-tls-addr not off); with it disabled, the sockets they name simply never appeared, with no indication why. A warning is now logged in that case.

  • Add -addr to pkgproxyctl setup apt/setup snap.

    Brings these two in line with setup go/setup lxd/ setup lxd-remote/setup snapcraft, which already let you point the printed instructions at a differently-addressed pkgproxy instance.

  • Make pkgproxyctl setup lxd's provisioning-file writes atomic and verify the pre-start hook.

    The admin-API writes (apt/snapd config, the https-rewrite hook script) now go through temp-file-plus-rename, so a reader never sees a partial file and a crash mid-write leaves the previous file (or nothing) rather than a truncated one — a missing or non-executable hook otherwise fails every container on that profile with an opaque forkstart: exit status 1. The write now also verifies the hook landed and is executable, and every write/remove is logged to the daemon journal.

  • Fix LXD provisioning-file placement for snap-installed LXD.

    Files written under /var/lib/pkgproxy are invisible to a snap-packaged LXD, which only sees /var/snap/<name> inside its own mount namespace — the pre-start hook then failed containers with run_buffer: Script exited with status 127. The provisioning directory is now resolved by how LXD itself is installed (snap vs. deb/package), not by how pkgproxy is installed, so a host-built pkgproxy driving a snap-installed LXD writes where that LXD can actually read.

0.5.1

  • No user-visible changes; internal clean-up of the setup commands.

0.5.0

  • Add pkgproxyctl setup {apt,snap,go,lxd,snapcraft} -undo.

    Prints the commands that reverse that subcommand's setup instead of applying it — same TTY-vs-piped split as the existing | sh one-liner, so pkgproxyctl setup apt -undo | sh is a complete undo. setup lxd/setup snapcraft -undo skip LXD bridge-IP detection entirely, since none of their undo commands are host-dependent.

  • Add pkgproxyctl setup snapcraft.

    Hardwires setup lxd's -project logic to the well-known "snapcraft" LXD project craft-providers/snapcraft builds in, so snapcraft pack users get correct instructions without needing to know craft-providers' project naming convention.

  • Fix LXD bridge gateway detection under snap strict confinement.

    Detection used to shell out through sh -c "... | awk ... | cut ...", which swallowed ip(8)'s own exit status — so a permission failure (netlink denied without the network-observe plug connected) looked identical to "no bridge configured yet" and silently fell back to this machine's hostname, which can itself be wrong (e.g. under WSL). Detection now runs ip directly and uses sysfs to tell "no such device" apart from "query failed", so a real failure now errors out — with a snap connect pkgproxy:network-observe hint, shown only when that plug isn't already connected — instead of guessing. The hostname fallback still applies to the ordinary "no bridge exists yet" case.

  • Fix pkgproxyctl stats' report header separator for translated locales.

    The === line under the report title was hardcoded to the English header's length, so it stopped lining up under longer/shorter translated headers. It's now sized to match.

0.4.0

  • Add pkgproxyctl stats -r/?since= windowed reports.

    pkgproxyctl stats -r <since> (a Unix timestamp, RFC3339 timestamp, or a duration meaning "ago", e.g. -r 30m) and the matching ?since=<unix-seconds> parameter on all three stats API routes show activity since a point in time instead of all-time totals — e.g. watch pkgproxyctl stats -r $(date +%s) for the cost of one workload in progress. Backed by a fixed-size, multi-resolution bucket ladder per cache key (exact to the minute for the last 30m, to the hour for 2d, to the day for ~2mo) that rides along in the existing checkpoint file. See docs/design.md §13.6.

  • Update the snap package description and add missing store metadata.

    Documents pkgproxyctl setup apt/snap/go/lxd (replacing the removed startup-banner snippets) and the previously-undocumented Go module caching. Also adds the title and contact fields snapcraft's metadata linter flags as missing on tagged releases.

0.3.0

  • Add a caching Go module proxy (the GOPROXY protocol).

    pkgproxy now also acts as a GOPROXY-protocol module proxy, sharing the same listener and persistent cache as apt and snap: point the go command at it with GOPROXY=http://<host>:3142/go,direct and .mod/ .zip content is fetched once from an upstream proxy (proxy.golang.org by default), hash-verified against the checksum database, and cached forever. New -go-upstream, -go-base-path, -go-revalidate-ttl and -go-sumdb daemon flags; pkgproxyctl setup go prints copy-paste-ready GOPROXY setup instructions, the same shape as setup apt/setup snap.

  • Route Go module traffic through pkgproxy in LXD build containers too.

    pkgproxyctl setup lxd now also sets environment.GOPROXY on the LXD profile it configures, so snapcraft/craft-providers build containers pick it up for every process automatically, not just apt/snap traffic.

  • Remove pkgproxyctl setup lxd's --apply flag.

    One way to actually apply the printed configuration now, consistent with every other setup subcommand: pipe the output through sh (pkgproxyctl setup lxd | sh).

0.2.1

  • Fix the pkgproxy.svg icon's aspect ratio.

    Its viewBox was wider than tall, so snapcraft.io's square icon tile stretched it non-uniformly and squished it horizontally. Padded the viewBox to a square 112x112 (8px added above and below, artwork unchanged) so it renders undistorted.

0.2.0

  • Add a read-only stats REST API and a pkgproxyctl CLI to read it.

    pkgproxy now tracks cache hit/fetch counts and bytes saved, exposed as JSON over a second UNIX socket (-stats-socket-path): GET /v1/stats/snaps (per-snap download/hit/revision counts and bandwidth saved, rolled up across every cached revision of that snap) and GET /v1/stats/debs (apt pool files ranked by bytes saved — the packages actually worth caching). Counts persist across restarts, checkpointed to <cache-dir>/stats.json every 30 seconds and on a clean shutdown.

    pkgproxyctl is a new CLI for reading it: pkgproxyctl stats renders a human-readable report (per-snap table, top-apt-packages table, overall bandwidth-saved summary), or the same data as JSON with -json for scripting. It's the same compiled binary as the pkgproxy daemon, dispatched by which name it was invoked as (a pkgproxyctl symlink to pkgproxy in the snap), so it adds negligible size.

    Both -stats-socket-path and pkgproxyctl's -socket-path are optional: they default to $SNAP_COMMON/stats.sock inside a snap, or /run/pkgproxy/stats.sock otherwise (with -cache-dir's own default moving to /var/cache/pkgproxy, the matching FHS/systemd convention) — the common case needs no flags on either side.

  • Add -socket-path: listen on a UNIX socket instead of TCP.

    Lets pkgproxy be bridged into an environment that only has a UNIX socket to offer it — e.g. a host proxy reached from inside a VM guest over a QEMU guestfwd forwarded straight to a UNIX socket (see image-garden, the motivating case) — without needing TCP at all.