Abbiategrasso (Mi), domenica 27 settembre: Ondata center. Fermiamo i datacenter ovunque
Riceviamo e diffondiamo
Riceviamo e diffondiamo
Riceviamo e diffondiamo:
DUE INIZIATIVE IN SOLIDARIETÀ CON UN COMPAGNO DELL’ASSEMBLEA CONTRO IL CARCERE E LA REPRESSIONE DEL FRIULI E DI TRIESTE.
DUE INIZIATIVE CONTRO IL REGIME DI TORTURA DEMOCRATICA DEL 41 BIS.
DUE INIZIATIVE CONTRO IL SISTEMA AUTORITARIO, PREVARICATORE, SFRUTTATORE E VIOLENTO CHE PRODUCE LA SOCIETÀ BASATA SUI PRIVILEGI.
Il prossimo 21 settembre, lunedì mattina alle h 9:00, si svolgerà a Trieste, presso il tribunale, l’udienza di appello per un procedimento che vede imputato un nostro compagno.
Saremo davanti al tribunale per manifestare la nostra solidarietà e ribadire che il regime di carcerazione del 41 bis è una tortura autorizzata dallo Stato democratico verso i soggetti che non accettano di piegarsi. È il tentativo di blindare questa situazione per accelerare il processo di criminalizzazione del conflitto e portarci alla guerra.
L’11 settembre, venerdì sera alle h 20:00, a Udine, allo Spazio autogestito (via de Rubeis), verrà proiettato il documentario autoprodotto 181 giorni contro il 41 bis. Lo sciopero della fame di Alfredo Cospito, che ripercorre e rievoca le mobilitazioni e le piazze “agitate” dell’inverno 2022-’23, al fianco del prigioniero rivoluzionario anarchico Alfredo Cospito, tutt’ora rinchiuso al regime infame del 41 bis. Sarà anche un’occasione di approfondimento e discussione sul recente passato, sulla gestione autoritaria della pandemia covid-19, sulla risposta repressiva dello Stato alle richieste, a volte molto lucide e coscienti, dei detenuti, sull’imposizione del bavaglio a chiunque abbia osato schierarsi al loro fianco. Sulla necessità di non dimenticare e continuare la lotta!
liberetutti [at] autistiche.org
zardinsmagneticsradio.noblogs.org
t.me/zardins
Associazione “Senza Sbarre”
casella postale 129
34121 TRIESTE
—
Assemblea permanente
contro il carcere e la repressione
del Friuli e di Trieste
Associazione “Senza sbarre”
c.p.129, 34121 Trieste
zardinsmagneticsradio.noblogs.org
Riceviamo e diffondiamo



Internet Society Italia (Società Internet), di cui GARR è socio e sostenitore, ha espresso forte preoccupazione per la disattivazione del dominio autistici.org da parte del Public Interest Registry (PIR).

Guix system and Guix home introduce the concept of services. These provide users
with a way to control background processes, commonly refereed as
daemons, as well as ways of
controlling the setup of files. For example,
openssh-service-type
is a service which controls a SSH
daemon. In contrast,
etc-service-type
is a service that populates the contents of the /etc directory.
One peculiarity of Guix services is that it's customary to provide Scheme bindings for the different fields. By that I mean that the different fields of the configuration of most services will be a type. The benefit of this is that users get a uniform configuration language for their services, at the cost of additional complexity when developing the service. Which is why, for a long time, Guix users seem to struggle with defining complex configurations. There are a number of reasons for this, we will try to close the gap today by going through defining a configuration for Goimapnotify.
This blog post assumes that the reader is somewhat familiar with Guix and knows how to setup a development environment for it. If that's not the case, read The Perfect Setup and Using Guix interactively.
A Goimapnotify configuration is written in
YAML. Therefore, we will need to serialize
the different Guile Scheme fields to this format. Let's take the example
configuration that the author gives in the project's
README.md:
configurations:
- host: example.com
port: 143
tls: true
tlsOptions:
rejectUnauthorized: false
starttls: true
idleLogoutTimeout: 15
username: USERNAME
alias: ExampleCOM
password: PASSWORD
xoAuth2: false
boxes:
- mailbox: INBOX
onNewMail: 'mbsync examplecom:INBOX'
onChangedMail: 'mbsync examplenet:INBOX'
onChangedMailPost: SKIP
onNewMailPost: SKIP
- hostCMD: COMMAND_TO_RETRIEVE_HOST
port: 993
tls: true
tlsOptions:
rejectUnauthorized: true
starttls: true
username: ''
usernameCMD: ''
password: ''
passwordCMD: ''
xoAuth2: false
onNewMail: ''
onNewMailPost: ''
onChangedMail: ''
onChangedMailPost: ''
onDeletedMail: ''
onDeletedMailPost: ''
boxes:
- mailbox: INBOX
onNewMail: 'mbsync examplenet:INBOX'
onNewMailPost: SKIP
onChangedMail: 'mbsync examplenet:INBOX'
- mailbox: Junk
onNewMail: 'mbsync examplenet:Junk'
onNewMailPost: SKIPJust by looking at the hierarchy we can already envision how to organize our scheme records. We will need the following:
home-goimapnotify-configuration-fieldsgoimapnotify-configurationgoimapnotify-box-configurationgoimapnotify-tls-options-configurationThe final configuration that the service for Goimapnotify will rely on, will be
home-goimapnotify-configuration-fields. It will contain a configurations
field where each item will be a goimapnotify-configuration; each box in those
configurations will be a goimapnotify-box-configuration. Additionally, each of
those configurations will have an optional
goimapnotify-tls-options-configuration.
The (gnu services configuration) module provides us with the API that we need
to define these configurations. The most important helpers for defining
configurations are:
define-configuration: For configurations that need to serialize fields into
a different format, generally configuration files.
define-configuration/no-serialization: For configurations that do not need
to emit any files. Generally all the fields are consumed by the Shepherd
services that rely on the configuration but no translation from Scheme to a
different format is needed.
In our case, we need to translate the different configuration fields to YAML, so
we will need to use define-configuration.
The following sections are written so you can follow along, you are encouraged to drop into a REPL (short for read-eval-print loop) and import the required module:
,use (gnu services configuration)goimapnotify-tls-options-configurationTo make it easy for ourselves, we will start form the in-out, after all, you wouldn't want to build a house from the rooftop, would you?
(define-configuration goimapnotify-tls-options-configuration
(reject-unauthorized?
(boolean #f)
"Whether to reject unauthorized TLS certificates.")
(starttls?
(boolean #f)
"Whether to use STARTTLS.")
(prefix goimapnotify-))evaluating the above snippet will throw an unbound variable exception, bear with me.
Refer to the
manual
for an in-depth explanation of the syntax of define-configuration.
The first argument is the name of the configuration object,
goimapnotify-tls-options-configuration in this case. After it, we define the
different typed fields. We are defining boolean fields that are, by default, set
to false. The biggest source of confusion when defining configurations comes
because define-configuration is a macro that introduces identifiers that do
not appear in the source code—it's an unhygienic
macro. That
means that the macro will expand to code which defines symbols that are not
visible when reading the source file.
One can inspect the expansion of the macro by using the ,expand REPL command. It will be quite verbose, so don't try to read all of it, instead search through it; you will find some revealing things, such as:
scheme@(gnu home services mail)> ,expand (define-configuration goimapnotify-tls-options-configuration
(reject-unauthorized?
(boolean #f)
"Whether to reject unauthorized TLS certificates.")
(starttls?
(boolean #f)
"Whether to use STARTTLS.")
(prefix goimapnotify-))
$20 = (begin ...
(define goimapnotify-tls-options-configuration? ...)
(define goimapnotify-tls-options-configuration-reject-unauthorized? ...)
(define goimapnotify-tls-options-configuration-starttls? ...)
...
(define <goimapnotify-tls-options-configuration> ...)
...
(define goimapnotify-tls-options-configuration ...)
(define goimapnotify-tls-options-configuration-fields
((@@ (gnu services configuration) list)
(let* ((name (let ((x 'reject-unauthorized?)) x)) ...
(serializer (let ((x goimapnotify-serialize-boolean)) x)) ...)
...)
(let* ((name (let ((x 'starttls?)) x)) ...
(serializer (let ((x goimapnotify-serialize-boolean)) x)) ...)
...))))In the previous snippet, ... represents omitted code. As you can see from the
macro expansion, define-configuration introduces quite a few identifiers. You
may recognize the shared prefix in those identifiers, that's right, it's the
prefix specified by that last field that we didn't explain from the
configuration definition, (prefix goimapnotify-).
By inspecting the macro expansion, it's easy to understand what happens under the hood. When a prefix is specified, we instruct the macro to append that prefix to all the generated identifiers. This is useful to avoid naming collisions; for example, when defining the configuration in a module with other configuration records that serialize to different formats. After all, it's not the same to serialize to YAML than to INI, or any other format a tool may require.
Remember that unbound variable problem I mentioned before? If you paid close
attention to the macro expansion, you may have noticed those references to
goimapnotify-serialize-boolean, those are our unbound variables. The macro
expects us to define these serialization procedures, let's do that.
Since the configuration machinery knows nothing about the output format, we must
define how this translation happens. In our case, we are translating to YAML. By
looking at the example configuration provided by the Goimapnotify developers, we
can see that the field names are written in camel case, and their respective
values are separated from the names through the : character.
Let's start by making a function that takes a symbol and transforms it into a
string that's a camelized version of that symbol. We will use
object->camel-case-string from the (gnu home services utils) module:
,use (gnu home services utils)(define (camelize-field-name field-name)
(let ((str (object->camel-case-string field-name)))
(if (string-suffix? "?" str)
(string-drop-right str 1)
str)))
;; Usage: (camelize-field-name 'reject-unauthorized?) => "rejectUnauthorized"In YAML, there is no convention of suffixing booleans with a ?, so our
camelizer drops it when found.
camelize-field-name gives us a field name, but we want to serialize the value
too. Let's define a field serialization procedure to help us:
(define (goimapnotify-serialize-field field-name val)
"The mapping is used to serialize certain FIELD-NAMES specially."
(let* ((field-name-mapping '((host-command . hostCmd)
(user-name . username)
(user-name-command . usernameCmd)
(password-command . passwordCmd)))
(field-name* (or (assq-ref field-name-mapping field-name)
field-name)))
(format #f "~a: ~s~%"
(camelize-field-name field-name*)
val)))
;; Usage: (goimapnotify-serialize-field 'reject-unauthorized? 'true) => "rejectUnauthorized: true\n"Notice how we introduced field-name-mapping to tailor the field name passed to
camelize-field-name to emit the specific naming that Goimapnotify expects. In
Guix, we have a specific naming convention, so we want fields like user-name
to map to username (instead of userName) and fields like password-command
to map to passwordCmd (instead of passwordCommand).
That's enough to serialize most values to YAML, but there's an extra Guix-specific feature we should make use of to make the service more convenient to users: G-Expressions.
Are you familiar with them? If not, I encourage you to read this wonderful trilogy of blog posts: Dissecting Guix. It took me some time to wrap my head around these concepts, but once you do, I think you will like them too. In any case, that serializer is using those expressions because we want users of our configuration to be able to intermingle packages and other file-like objects in their fields. Since this is a blog post about writing configurations, I won't dive deep into G-Expressions, but let's try to get you a sense for them.
You will need to import (guix gexp) for the following snippet to work.
(define (goimapnotify-serialize-field field-name val)
"The mapping is used to serialize certain FIELD-NAMES specially."
(let* ((field-name-mapping '((host-command . hostCmd)
(user-name . username)
(user-name-command . usernameCmd)
(password-command . passwordCmd)))
(field-name* (or (assq-ref field-name-mapping field-name)
field-name)))
#~(format #f "~a: ~s~%"
#$(camelize-field-name field-name*)
#$val)))That's not so bad, is it? What was that? 6 more characters? Surely you wouldn't be scared of that, but just in case, let me give you some extra reassurance on what's happening here. This G-Expression thing, also known as gexp, is just the way you let Guix know that this code is for later. "When is later?", you may wonder. Simplifying it, that "later" is when Guix knows where file-like objects will be located in your disk; that long path on the store you may have seen before.
For example, a package is a file-like object, so when you build the package Guix will tell you where it was stored:
$ guix build cowsay
/gnu/store/gz170ppi2hxssp4h3yw5jh4gdw6x9ws0-cowsay-3.8.4Preceding an expression with #~ (or gexp), one effectively tells Guix "don't
evaluate this code until you know what this expression should expand to". And
that other syntax, #$ (or ungexp), is telling Guix to replace the file-like
object for its lowered representation, usually a path in the store. For a
package like cowsay, that would be that
/gnu/store/gz170ppi2hxssp4h3yw5jh4gdw6x9ws0-cowsay-3.8.4 paths we saw before.
With that said, if you are like me, you won't be comfortable if you cannot debug this expressions, so let me give you some supper powers.
In your REPL, import the (guix) module. This will augment the REPL with some
additional commands that will simplify our life:
scheme@(guile-user)> ,use (guix)
scheme@(guile-user)> ,help guix
Guix Commands [abbrev]:
,run-in-store EXP - Run EXP through the store monad.
,verbosity LEVEL - Change build verbosity to LEVEL.
,lower OBJECT - Lower OBJECT into a derivation or store file and return it.
,build OBJECT [BUILD-MODE] - Lower OBJECT and build it, returning its output file name(s).
,build-options OPTIONS - Set build options to OPTIONS. Print previous value (to allow easy restore).
,build-graft GRAFT? - Set whether grafts should be performed.
,enter-store-monad - Enter a REPL for values in the store monad.
,phases - Return the build phases of the package defined by FORM.
,configure-flags - Return the configure flags of the package defined by FORM.
,make-flags - Return the make flags of the package defined by FORM.Let's see what that gexp is up to:
scheme@(gnu home services mail)> (goimapnotify-serialize-field 'reject-unauthorized? ''true)
$6 = #<gexp (format #f "~a: ~s\n" #<gexp-input "rejectUnauthorized":out> #<gexp-input (quote true):out>) gnu/home/services/mail.scm:252:2 7f4c7d9deb40>There is a little helper to approximate a gexp to it's output:
scheme@(gnu home services mail)> (gexp->approximate-sexp $6)
$7 = (format #f "~a: ~s\n" "rejectUnauthorized" (quote true))
scheme@(gnu home services mail)> (primitive-eval $7)
$8 = "rejectUnauthorized: true\n"That gets us an idea of what will be emitted to disk, but for more complex procedures, this is not going to cut it, specially if there are multiple gexps combined. So let's build that gexp:
;; 'gexp->file' comes from the '(guix gexp)' module. You can ask the REPL more
;; information about a symbol through ',a SYMBOL'.
scheme@(gnu home services mail)> ,a gexp->file
(guix gexp): gexp->file #<procedure gexp->file (name exp #:key guile set-load-path? module-path splice? system target)>
scheme@(gnu home services mail)> (gexp->file "test.scm" $6)
$8 = #<procedure 7f3f7d456ea0 at guix/gexp.scm:2098:2 (state)>In order for Guix to build a value, it needs to be something Guix can lower—an
object that can be "compiled" down to a file in the store (these are referred to
as file-like objects, because they can be inserted in any piece of code that
expects a file name). A gexp cannot be built by itself, because there is nowhere
to output it to. The above snippet creates a file-like object that will be
emitted to a file named test.scm in the store. That file will contain our
expression. As you see, we've got a procedure. The REPL printer tells us that
this procedure needs some state; this is just a way of indicating you that
this is a monadic procedure that can only be run in the context of a store
connection. Read The Store
Monad for
more information.
I will build it manually just once for demonstration purposes, don't blink:
scheme@(gnu home services mail)> ,use (guix store)
scheme@(gnu home services mail)> (run-with-store (open-connection)
$8)
$9 = #<derivation /gnu/store/gq71r3sgilfvsicfnifln8n0wfksam13-test.scm.drv => /gnu/store/c3hxw7pgnxchw1zldnc9nrb8iwwrqf78-test.scm 7f3f7e2de000>Where we are saying "run the procedure we got in the context of a store
connection", we opened that connection through open-connection. Let's not do
that again... Fortunately we have those useful REPL commands I just told you
about, so we can archive the same result by doing this:
scheme@(gnu home services mail)> ,run-in-store $8
$10 = #<derivation /gnu/store/gq71r3sgilfvsicfnifln8n0wfksam13-test.scm.drv => /gnu/store/c3hxw7pgnxchw1zldnc9nrb8iwwrqf78-test.scm 7fcd92f7f0f0>That's better, isn't it?
Now, this is a derivation. That's something Guix can build:
scheme@(gnu home services mail)> ,build $10
building /gnu/store/gq71r3sgilfvsicfnifln8n0wfksam13-test.scm.drv...
$11 = "/gnu/store/c3hxw7pgnxchw1zldnc9nrb8iwwrqf78-test.scm"What's that? You don't want this magic command? Okay, the same thing can be done with:
scheme@(gnu home services mail)> (run-with-store (open-connection)
(built-derivations (list $10)))
$12 = #t
scheme@(gnu home services mail)> (derivation->output-path $10)
$13 = "/gnu/store/c3hxw7pgnxchw1zldnc9nrb8iwwrqf78-test.scm"At last, a file! What's in there?
$ cat /gnu/store/c3hxw7pgnxchw1zldnc9nrb8iwwrqf78-test.scm
(format #f "~a: ~a\n" "rejectUnauthorized" "true")Okay that seems about right. We can even run it:
scheme@(gnu home services mail)> (call-with-input-file $13
(lambda (port)
(primitive-eval (read port))))
$14 = "rejectUnauthorized: true\n"You've seen the secret sauce, let's continue with what brought us here.
(define (goimapnotify-serialize-boolean field-name val)
(goimapnotify-serialize-field field-name (if val ''true ''false)))With this our configuration won't complain about the missing serializer. Let's continue.
One last thing, notice how, after moving to the gexp version of
goimapnotify-serialize-field, we started double quoting the symbols true and
false, this is because the #$ syntax will replace the value in place, and
the value of 'true is true, without the quote. If we didn't double quote,
the staged code after expansion would look like this:
(format #f "~a: ~s\n" "rejectUnauthorized" true)When what we really want is to expand to this:
(format #f "~a: ~s\n" "rejectUnauthorized" 'true)The reason for this is that we want users to be able to write staged code on the different fields. If one of the values of a field where to be something like this:
(goimapnotify-serialize-field 'favorite-game #~(string-append #$cowsay "/bin/cowsay"))The expansion would be this:
(format #f "~a: ~s\n" "favoriteGame" (string-append "/gnu/store/gz170ppi2hxssp4h3yw5jh4gdw6x9ws0-cowsay-3.8.4" "/bin/cowsay"))The string-append procedure is evaluated right before the format call, not
when the gexp is getting lowered.
The (gnu services configuration) module we imported earlier provides us with
serialize-configuration, it takes two arguments, a configuration object and the
fields that compose that configuration.
The define-configuration macro defined the
goimapnotify-tls-options-configuration constructor for us, we can create a
configuration with the default values like this:
scheme@(gnu home services mail)> (goimapnotify-tls-options-configuration)
$10 = #<<goimapnotify-tls-options-configuration> reject-unauthorized?: #f starttls?: #f %location: #f>Then, we can serialize it like this:
scheme@(gnu home services mail)> (serialize-configuration $10 goimapnotify-tls-options-configuration-fields)
$11 = #<gexp gnu/services/configuration.scm:165:2 7ff1fd2cbf60>If you recall form the macro expansion we saw earlier,
goimapnotify-tls-options-configuration-fields, was one of those identifiers
that got generated.
You should already know how to build that gexp we've got:
scheme@(gnu home services mail)> (gexp->file "test.yaml" $11)
$12 = #<procedure 7efd8b10c2d0 at guix/gexp.scm:2098:2 (state)>
scheme@(gnu home services mail)> ,use (guix)
scheme@(gnu home services mail)> ,run-in-store $12
$13 = #<derivation /gnu/store/z59sf3nhh80668z8njljfxcymgi071pr-test.yaml.drv => /gnu/store/xam1210yl1vdxix0bgpfalf6drpv6xbx-test.yaml 7efd8a80fd70>
scheme@(gnu home services mail)> ,build $13
$14 = "/gnu/store/xam1210yl1vdxix0bgpfalf6drpv6xbx-test.yaml"
scheme@(gnu home services mail)> (call-with-input-file $14
(lambda (port)
(display (primitive-eval (read port)))))
rejectUnauthorized: false
starttls: falseThat's some nice YAML syntax... We better speed up the pace or I will retire before finishing up this blog post.
goimapnotify-box-configurationNext in line is goimapnotify-box-configuration, you know the drill. We start
by declaring the configuration with define-configuration, specifying each
field name, type and docstring:
(define-configuration goimapnotify-box-configuration
(mailbox
string
"The mailbox to monitor.")
(on-new-mail
maybe-string-or-gexp
"The command to execute when new mail arrives.")
(on-new-mail-post
maybe-string-or-gexp
"The command to execute after the new-mail command.")
(on-changed-mail
maybe-string-or-gexp
"The command to execute when mail is changed.")
(on-changed-mail-post
maybe-string-or-gexp
"The command to execute after the changed-mail command.")
(on-deleted-mail
maybe-string-or-gexp
"The command to execute when mail is deleted.")
(on-deleted-mail-post
maybe-string-or-gexp
"The command to execute after the deleted-mail command.")
(prefix goimapnotify-))You may have noticed that we have two new types. strings, easy enough, and
maybe-string-or-gexp, not so easy; right? Worry not, here comes the
explanation.
Let's start by defining the string type:
(define-maybe string (prefix msmtp-configuration-))You may be very confused right now. What has msmtp-configuration to do with
our goimapnotify-configuration example? Well, I want this blog post to get you
ready for the real world, and in the wild, you will make configurations in
modules that contain other configurations. The example we are looking up today
is a narration of my adventures making the Goimapnotify service from the (gnu services mail) module, in that module, there is already a configuration for
msmtp.
With that said, if we tried to do this:
(define-maybe string (prefix goimapnotify-))We would be surprised with a warning that looks something like this:
$ make
...
[ 94%] GUILEC gnu/home/services/mail.go
gnu/home/services/mail.scm:67:0: warning: shadows previous definition of `maybe-string?' at gnu/home/services/mail.scm:63:0
gnu/home/services/mail.scm:278:0: warning: shadows previous definition of `goimapnotify-serialize-maybe-string' at gnu/home/services/mail.scm:67:0That's unexpected, isn't it? Nothing we've seen so far points to a
maybe-string? procedure, let's look at what is going under the hood of that
define-maybe macro:
scheme@(gnu home services mail)> ,expand (define-maybe string (prefix msmtp-configuration-))
$15 = (begin
(define (maybe-string? val)
(or ((@@ (gnu services configuration) not)
((@@ (gnu services configuration) maybe-value-set?) val))
(string? val)))
(define (msmtp-configuration-serialize-maybe-string field-name val)
(if (string? val)
(msmtp-configuration-serialize-string field-name val)
"")))Do you see it? The macro is expanding to some code that introduces two new
procedures into the module, one following the prefix, that would be
msmtp-configuration-serialize-maybe-string, and one that just declares the
predicate for the maybe type.
Given that, the warning is now apparent. If we call again that macro with a
string as the first argument, we will get the same predicate after the
expansion, leading to the redefinition warning.
So, for this particular case, instead of using the macro, we will define manually the two procedures required for our configuration:
;; The module already had this helper defined.
(define (string-or-gexp? obj)
(or (string? obj)
(gexp? obj)))
;; ... omitted lines ...
(define (goimapnotify-serialize-string field-name val)
(goimapnotify-serialize-field field-name val))
(define (goimapnotify-serialize-maybe-string field-name val)
(if (maybe-value-set? val)
(goimapnotify-serialize-string field-name val)
""))
(define goimapnotify-serialize-string-or-gexp
goimapnotify-serialize-string)
(define (goimapnotify-serialize-maybe-string-or-gexp field-name val)
(if (and (maybe-value-set? val)
(string-or-gexp? val))
(goimapnotify-serialize-string-or-gexp field-name val)
""))The above snippet defines all the serializers we need for the new types. Notice
that they are just some simple wrappers around the
goimapnotify-serialize-field helper. That procedure is doing all the heavy
lifting here, and fortunately for us, it already handles gexps. Therefore, the
fields that have a type that accepts a gexp are straightforward to declare.
That would be it for the goimapnotify-box-configuration declaration. As showed
in the previous section, the REPL is your friend. I had never written such a
complex configuration before, but thanks to this "elegant weapon for a more
civilized age", I could find my way by poking things
around.
Let's continue!
goimapnotify-configurationYou know the drill, we start by defining the fields we need. Again, I'm doing this just by looking at the README example provided by the developers of Goimapnotify:
(define-configuration goimapnotify-configuration
(host
string
"The IMAP server hostname.")
(host-command
maybe-string-or-gexp
"The command to retrieve the IMAP server hostname.")
(port
(integer 993)
"The port that the IMAP server listens on.")
(tls?
(boolean #f)
"Enable or disable TLS.")
(tls-options
maybe-goimapnotify-tls-options-configuration
"TLS options for the IMAP connection."
(serializer serialize-maybe-goimapnotify-tls-options-configuration))
(idle-logout-timeout
maybe-integer
"The idle logout timeout in minutes.")
(user-name
maybe-string
"The user-name for authentication.")
(user-name-command
maybe-string-or-gexp
"The command to retrieve the user-name.")
(alias
maybe-string
"An alias for the account.")
(password
maybe-string
"The password for authentication.")
(password-command
maybe-string-or-gexp
"The command to retrieve the password.")
(xo-auth2?
(boolean #f)
"Enable or disable XOAUTH2 authentication.")
(wait
maybe-integer
"The delay in seconds before the mail syncing is triggered.")
(boxes
list-of-goimapnotify-boxes-configurations
"The mailboxes to monitor."
(serializer serialize-list-of-goimapnotify-boxes-configurations))
(prefix goimapnotify-))You are already familiar with most of those types, but there are some new things here:
integer and maybe-integermaybe-goimapnotify-tls-options-configurationlist-of-goimapnotify-boxes-configurationsLet's start with the obvious ones first.
integerThe module already had a maybe definition for the integer. If you recall from
the macro expansion earlier, that means that there is already a symbol for the
predicate maybe-integer?:
(define-maybe integer (prefix msmtp-configuration-))We still need a serializer that follows our prefix:
(define (goimapnotify-serialize-integer field-name val)
(goimapnotify-serialize-field field-name val))
(define (goimapnotify-serialize-maybe-integer field-name val)
(if (maybe-value-set? val)
(goimapnotify-serialize-integer field-name val)
""))Simple enough. Moving on!
maybe-goimapnotify-tls-options-configuration(define (serialize-goimapnotify-tls-options-configuration field-name val)
(let ((serialization (serialize-configuration val goimapnotify-tls-options-configuration-fields)))
#~(begin
(use-modules (ice-9 format) (ice-9 string-fun))
(format #f "~a:
~a~%"
'#$(camelize-field-name field-name)
(string-replace-substring #$serialization "\n" "\n ")))))
(define-maybe goimapnotify-tls-options-configuration)You are already familiar with the define-maybe macro. The serializer is also
quite simple. Since we already defined a record that contains within all the
required information to serialize it, we just need to make a simple wrapper
around serialize-configuration.
Before moving forward, notice how the serializers from this section doesn't
contain that goimapnotify- prefix. This is an arbitrary decision, but since
there is already goimapnotify in the symbol name, I think it's a bit redundant
to add it. For the configuration definition to know which serializer to use, we
have to be specific in the serializer argument of the fields, that's why in the
configuration we had this declaration specifying a (serializer ...) for the
field:
(define-configuration goimapnotify-configuration
;; ... omitted lines ...
(tls-options
maybe-goimapnotify-tls-options-configuration
"TLS options for the IMAP connection."
(serializer serialize-maybe-goimapnotify-tls-options-configuration))
;; ... omitted lines ...
(prefix goimapnotify-))list-of-goimapnotify-boxes-configurationsFirst we need a prefix to know if we have a list of
goimapnotify-box-configuration objects, that one is simple:
(define (list-of-goimapnotify-boxes-configurations? lst)
(and (not (null? lst))
(every goimapnotify-box-configuration? lst)))Now we need a serializer that knows how to handle a list of these objects:
(define (serialize-list-of-goimapnotify-boxes-configurations field-name value)
(let ((serializations (cons 'list
(map (cut serialize-configuration <>
goimapnotify-box-configuration-fields)
value))))
#~(begin
(use-modules (ice-9 format) (ice-9 string-fun))
(format #f "~a:
~{ - ~a~%~}"
'#$(camelize-field-name field-name)
(map (lambda (s)
(string-replace-substring s "\n" "\n "))
#$serializations)))))Looks a bit daunting, but it's just staged code, remember our earlier explanation of gexps. This code is only constructing a string with the shape we need.
As a reminder, we are serializing to YAML, that means that lists are prefixed by
a - character. Following the Goimapnotify README, the indentation would be
something like this:
boxes:
- mailbox: INBOX
onNewMail: 'mbsync examplenet:INBOX'
onNewMailPost: SKIP
onChangedMail: 'mbsync examplenet:INBOX'
- mailbox: Junk
onNewMail: 'mbsync examplenet:Junk'
onNewMailPost: SKIPThis is what that format call is doing. Refer to
Formatted-Output
for more information.
We are only lacking a way to generate a complete configuration, remember that
goimapnotify-configuration is an object for a single configuration. According
to the README of Goimapnotify, the configuration file can take a list of
configurations.
home-goimapnotify-configurationThis is the last configuration we will need, it will be used directly by the service:
(define-configuration home-goimapnotify-configuration
(goimapnotify
(file-like goimapnotify)
"The @code{goimapnotify} package to use."
empty-serializer)
(configurations
(list-of-goimapnotify-configurations)
"A list of @code{goimapnotify-configuration} records which contain
information about all your accounts configurations."))Simple enough, the first field is the Guix package that provides the
goimapnotify program. This one is used by the service to start the
process. Since it doesn't need to appear in any configuration file, we don't
need to serialize it, hence the empty-serializer.
On last serializer:
(define (serialize-list-of-goimapnotify-configurations field-name value)
(let ((serializations (cons 'list
(map (cut serialize-configuration <>
goimapnotify-configuration-fields)
value))))
#~(begin
(use-modules (ice-9 format) (ice-9 string-fun))
(format #f "~a:
~{ - ~a~%~}"
'#$(camelize-field-name field-name)
(map (lambda (s)
(string-replace-substring s "\n" "\n "))
#$serializations)))))The rationale for this code is the same as the one explained for
list-of-goimapnotify-boxes-configurations.
The service definition; at last!
(define (home-goimapnotify-shepherd-service config)
(let ((log-file #~(string-append %user-log-dir "/goimapnotify.log")))
(list
(shepherd-service
(provision '(goimapnotify))
(modules '((shepherd support))) ;for '%user-log-dir'
(documentation "Run a goimapnotify process")
(start #~(make-forkexec-constructor
(list
#$(file-append
(home-goimapnotify-configuration-goimapnotify config)
"/bin/goimapnotify")
"-conf" #$(mixed-text-file "goimapnotify.yaml"
(serialize-configuration config
home-goimapnotify-configuration-fields)))
#:log-file #$log-file))
(stop #~(make-kill-destructor))))))Refer to Shepherd Services for more information on how to write Shepherd services. I will only highlight how to handle the configuration we just wrote.
We have already done all the hard work, the configuration declaration contains
all the information needed to perform the serialization of the different fields
to YAML, we just need to call serialize-configuration. For example:
scheme@(gnu home services mail)> (define test-config
(home-goimapnotify-configuration
(configurations
(list
(goimapnotify-configuration
(host "test.example.com")
(boxes
(list
(goimapnotify-box-configuration
(mailbox "Test")))))))))
scheme@(gnu home services mail)> (serialize-configuration test-config home-goimapnotify-configuration-fields)
$6 = #<gexp gnu/services/configuration.scm:165:2 7f3448e678a0>That serialization gives a gexp ready to be wrapped in a file-like so it can be
lowered to the store. That's what mixed-text-file is doing:
scheme@(gnu home services mail)> (mixed-text-file "goimapnotify.yaml"
(serialize-configuration test-config
home-goimapnotify-configuration-fields))
$7 = #<<computed-file> name: "goimapnotify.yaml" gexp: #<gexp guix/gexp.scm:2171:6 7f343915ef60> guile: #f options: (#:local-build? #t)>
scheme@(gnu home services mail)>We've got ourselves a service, but there is one last thing before we go!
As mentioned in the previous section, refer to Defining Services for what's going on here. The last thing we need do is declare the relation that this service has with respect to others:
(define home-goimapnotify-service-type
(service-type
(name 'home-goimapnotify)
(extensions
(list (service-extension home-shepherd-service-type
home-goimapnotify-shepherd-service)))
(description "Configures the @code{goimapnotify} IMAP Mailbox notifier.")))We did it!
Still here? That was long... But here we are, we defined our complex configuration.
Congratulations on reading till the end, by this time you should already be an expert on defining Guix configurations.
We acknowledge that there are some improvements to do on the API for defining configurations. We would like to have a way to specify symbol mapping for field names in the declaration, so that serializers can be generalized better. It would also be nice if we could refactor some of our configuration definitions so the serializers that can be generalized are reused between declarations. After all, there are many configuration files in similar formats.
For all of these improvements, we are counting on you! I wrote this blog post to empower you to participate in the development. If this is something that resonates with you, come join the fun!
Call for talks aperta il 31 agosto 2026. Ventiquattresimo anno del gruppo, dodicesimo Linux Day irpino documentato.
[IT]
Non appena abbiamo scoperto di essere finite sulla lista delle organizzazioni terroristiche globali, abbiamo scritto, dichiarato e promesso che avremmo cercato di resistere fino a che fosse stato possibile, che non ci saremmo arrese finché avessimo intravisto delle possibilità legali. Abbiamo mantenuto e difeso con orgoglio un’infrastruttura libera, riservata, autonoma e soprattutto partigiana.
Per noi restare umani non è un semplice slogan.
Significa che, prima di tutto, sono le utenti, le persone che costruiscono le reti umane a cui ci appoggiamo e le comunità che ci hanno inondato di messaggi e di amore e solidarietà, che devono essere tutelate.
Non possiamo accettare uno scontro o prestarci a strumentalizzazioni che mettano in pericolo le loro vite, le vite dei loro cari, lasciandole alla mercé di autocrati, fascisti e agenzie che agiscono per vie extra-legali.La concreta possibilità di poter essere la causa di conseguenze penali o finanziarie per qualcuno che ci è vicino, o che anche solo interagisce con noi non ci permette altre scelte.
A/I chiude. Il collettivo Autistici/Inventati chiude e sospenderà tutti i servizi a breve termine.
Non c’è un modo facile per dirlo o per spiegarlo.
Ogni giorno in cui siamo rimaste online a partire dal 26 agosto è stata una vittoria, ma noi dobbiamo fermarci oggi.
Nessuna di noi ha simpatia per i gesti eroici e men che meno per i martiri, quindi non ne faremo e non ne chiederemo. In questo clima continuare a offrire i nostri servizi è solo un pericolo in più per le nostre utenti, e per tutte le persone che fanno parte delle nostre comunità. In un mondo di accuse slegate dai fatti non possiamo che aspettarci un’ulteriore repressione sproporzionata. Questo ci mette di fronte all’impossibilità di mantenere il nostro progetto originario: offrire strumenti sicuri e non mercificati.
Sono stati 25 anni bellissimi. Sono stati una “bella storia”.
Ora mollate il pc, uscite, lottate, abbracciatevi e non perdete il sorriso. Perché noi ci fermiamo. Ma la resistenza e le idee no. Restiamo umani.
A brevissimo spiegheremo come salvare il contenuto dei blog, mail, siti e altri suggerimenti di tipo tecnico. Tenete comunque sempre presente che, così come è stato chiuso senza alcun preavviso il dominio autistici.org, nel frattempo potrebbero esserci altri problemi del genere che non siamo in grado di prevedere.
Collettivo Autistici/Inventati
[EN]
The day we discovered we had been designated a global terrorist organization, we promised that we would try to resist as long as this was possible, that we would not back down as long as we could see options to stand our ground. For years, we have proudly maintained and defended a free, privacy-friendly, autonomous and politically committed infrastructure.
“Stay human” is not an empty phrase to us.
It means, above all, that we have a responsibility to protect our users, those who build the human networks we rely, and for the communities who flooded us with messages of support and solidarity.
We cannot engage in a fight or expose ourselves to manipulations that threaten the lives of these people and their loved ones, leaving them at the mercy of autocrats, fascists and agencies that take extra-legal courses of action. The possibility that our work may cause legal and financial consequences to those who are close to us – or even only have something to do with us – leaves us no choice.
A/I is shutting down. The Autistici/Inventati collective is shutting down and will soon discontinue all of the services we provide.
There is no easy way to announce or explain this.
Every day we stayed online after August 26, 2026, has been a victory, but now we are forced to stop.
None of us holds eroic gestures and martyrs in high esteem, therefore we will demand no sacrifices. Not from us, not from anyone else. In this political climate, continuing to offer our services endangers our users and anyone who is part of our communities. In a world where allegations are disconnected from reality, we can only expect repression to be increasingly disproportionate. Under these circumstances, we are no longer able to maintain our original mission – offering secure and non-commercial digital tools.
These 25 years have been amazing. It’s been an “incredible ride”.
Now turn off your PCs, get out of your homes, struggle, hug each other and keep on smiling. We are stopping, but the Resistance and ideas do not stop. Stay human.
We will soon send instructions on how to back up the content of your blogs, mailboxes and websites, along with more technical recommendations. But keep in mind that, just as the autistici.org domain was made unreachable without previous notice, in the next few days we might suffer similar issues that we may not be able to predict.
Autistici/Inventati Collective
[IT]
Venerdì 4 settembre Banca Etica ci ha comunicato in una riunione che eserciterà l’opzione di rescissione unilaterale dal contratto con l’Associazione AI ODV e che i fondi attualmente presenti sul conto frutto delle donazioni di migliaia di persone, associazioni e collettivi non saranno nella disponibilità dell’Associazione.
Al di là delle molte parole spese dai dirigenti della Banca per lavarsi la coscienza, i fatti dicono che Banca Etica non solo si adegua al diktat dell’autocrate statunitense, ma ne rincara la dose mettendosi al suo stesso livello.
E tutto questo senza alcun preavviso e senza darci alcuna possibilità di disporre dei fondi dell’Associazione in maniera adeguata o di trovare una soluzione condivisa.
La decisione di Banca Etica di fatto ci mette nell’impossibilità di onorare i nostri impegni amministrativi, pagare i nostri fornitori ed erogare i servizi che le persone che hanno scelto A/I si aspettano.
L’Associazione non ha altra soluzione che adire tutte le vie legali a nostra disposizione contro questa decisione, che riteniamo illegittima e immotivata (allo stato attuale delle cose), per fare in modo che le donazioni che le persone ci hanno affidato possano tornare a nostra disposizione.
[EN]
On Friday, September 4th, Banca Etica met the AI ODV association in a videocall and told us that they are going to shut down our bank account unilaterally, thus terminating our contract for good, and that the funds still on the account, which we raised through donations from thousands of people, organizations and collectives, will not be at the association’s disposal.
Banca Etica has been spending many words to justify their actions and pretend they are on our side, but their actions counter all of their statements, clearly showing how they are just faithful servants of the US autocrat.
And this has happened without us being notified or allowed to adequately use our funds or agree upon some kind of solution.
Banca Etica’s decision has made it impossible for us to honour our administrative committments, to pay our service providers and to provide our users with the services they have chosen us for.
The AI ODV association has no other option than to pursue all the legal tools available to counter this decision, which we deem illegitimate and unmotivated (at least as things stand at the present moment), so as to be able to access the funds people donated to us.
[IT]
Dal 26 agosto, quando il Dipartimento del Tesoro statunitense, tramite OFAC, ci ha sanzionato come “Specially Designated Global Terrorist” (SDGT), sono successe tante cose, tanto si è mosso. Ed è solo l’inizio.
Il 28 agosto il dominio autistici.org è sparito dalla faccia del globo terracqueo.
Tutto questo, è bene sottolinearlo ogni volta, nonostante A/I non abbia compiuto alcun reato, né negli USA e tanto meno in Italia, e nonostante nessun giudice, USA o italiano, ci abbia mai contestato alcunché.
Non siamo e mai saremo una struttura “neutra”. Siamo un collettivo antifascista e anticapitalista, che usa la tecnologia per rendere possibile una comunicazione libera in cui la privacy non sia un mero dato di marketing, ma un fatto reale.
Abbiamo deciso di creare uno spazio unico per raccogliere quello che sta avvenendo dopo il 26 agosto, per permettere a tutta quella imprevedibile, caotica, magnifica comunità solidale di restare aggiornata più facilmente sulle conseguenze della decisione del governo USA e su quello che stiamo facendo.
Su #KeepItFree troverete comunicati, aggiornamenti tecnici, rassegna stampa, solidarietà, campagne, lettere aperte, banner, petizioni e speriamo molto di più.
Sono stati giorni convulsi, e i prossimi non saranno più leggeri. Per questo vi chiediamo di continuare a sostenerci e a costruire con noi una comunicazione resistente. Perché comunicare non è terrorismo. Perché noi siamo la Resistenza.
Perché crediamo in infrastrutture solidali, antifasciste, ma soprattutto libere. KEEP THE INTERNET FREE!
https://keepitfree.ai/it/announcements/presentiamo-https/keepitfree.ai/
[EN]
Many things happened since August 26, 2026, when the Office of Foreign Assets Control (OFAC) at the US Department of the Treasury included us in their list of “Specially Designated Global Terrorists” (SDGT). This is just the beginning.
On August 28 our autistici.org domain was wiped from the face of Earth.
Bear in mind: all of this happened without A/I committing any crime, neither in Italy nor in the US, and without any prosecutor ever indicting us either for felonies or for misdemeanors.
We are not a “neutral” structure, and we will never be. We are an anti-fascist and anti-capitalist collective, we use technology to enable independent communications and we believe privacy is not a mere marketing claim, but an actual value.
We have now created a dedicated space to document what has been unfolding since August 26, to empower our unpredictable, chaotic and magnificent solidarity network by keeping it up to date with the consequences of the US government designation and our responses.
On #KeepItFree you will find press releases, technical updates, news articles, expressions of solidarity, support campaigns, open letters, banners, petitions, and much more.
These days have been crazy, and the next to come won’t be calmer. So we ask you to keep supporting us and stand with us in building a communication infrastructure that speaks Truth to Power. Speech is not terrorism. Expect resistance.
KEEP THE INTERNET FREE!
https://keepitfree.ai/announcements/announcing-https/keepitfree.ai/