SPAMD(8p)

🌸 SPAMD(8p) 🌸

okay bestie, buckle up, because this is spamd(8p) straight outta the User Contributed Perl Documentation section and it is iconic. it's the daemon-ified glow-up of spamassassin, and we're gonna go through the whole thing together, no skips, no skimming, fr fr. πŸ’–


✨ NAME ✨

spamd β€” daemonized version of spamassassin


πŸŽ€ SYNOPSIS πŸŽ€

spamd [options]

Options:

 -l, --allow-tell                  Allow learning/reporting
 -c, --create-prefs                Create user preferences files
 -C path, --configpath=path        Path for default config files
 --siteconfigpath=path             Path for site configs
 --cf='config line'                Additional line of configuration
 --pre='config line'               Additional line of ".pre" (prepended to configuration)
 -d, --daemonize                   Daemonize
 -h, --help                        Print usage message
 -i [ip_or_name[:port]], --listen=[ip_or_name[:port]] Listen on IP addr and port
 -p port, --port=port              Listen on specified port, may be overridden by -i
 -4, --ipv4-only, --ipv4           Use IPv4 where applicable, disables IPv6
 -6                                Use IPv6 where applicable, disables IPv4
 -A host,..., --allowed-ips=..,..  Restrict to IP addresses which can connect
 -m num, --max-children=num        Allow maximum num children
 --min-children=num                Allow minimum num children
 --min-spare=num                   Lower limit for number of spare children
 --max-spare=num                   Upper limit for number of spare children
 --max-conn-per-child=num          Maximum connections accepted by child
                                   before it is respawned
 --round-robin                     Use traditional prefork algorithm
 --timeout-tcp=secs                Connection timeout for client headers
 --timeout-child=secs              Connection timeout for message checks
 -q, --sql-config                  Enable SQL config (needs -x)
 -Q, --setuid-with-sql             Enable SQL config (needs -x,
                                   enables use of -H)
 --ldap-config                     Enable LDAP config (needs -x)
 --setuid-with-ldap                Enable LDAP config (needs -x,
                                   enables use of -H)
 --virtual-config-dir=dir          Enable pattern based Virtual configs
                                   (needs -x)
 -r pidfile, --pidfile             Write the process id to pidfile
 -s facility, --syslog=facility    Specify the syslog facility
 --syslog-socket=type              How to connect to syslogd
 --log-timestamp-fmt=fmt           strftime(3) format for timestamps, may be
                                   empty to disable timestamps, or 'default'
 -u username, --username=username  Run as username
 -g groupname, --groupname=groupname  Run as groupname
 -v, --vpopmail                    Enable vpopmail config
 -x, --nouser-config               Disable user config files
 -U username, --default-user=username  Fall back to this username if spamc user
                                   is not found (default: nobody)
 -D, --debug[=areas]               Print debugging messages (for areas)
 -L, --local                       Use local tests only (no DNS)
 -P, --paranoid                    Die upon user errors
 -H [dir], --helper-home-dir[=dir] Specify a different HOME directory
 --ssl                             Enable SSL on TCP connections
 --ssl-verify                      Request a client certificate and verify it
 --ssl-ca-file cafile              Certificate Authority certificate file
 --ssl-ca-path capath              Certificate Authority directory
 --ssl-port port                   Override --port setting for SSL connections
 --server-key keyfile              Specify an SSL keyfile
 --server-cert certfile            Specify an SSL certificate
 --socketpath=path                 Listen on a given UNIX domain socket
 --socketowner=name                Set UNIX domain socket file's owner
 --socketgroup=name                Set UNIX domain socket file's group
 --socketmode=mode                 Set UNIX domain socket file's mode
 --timing                          Enable timing and logging
 -V, --version                     Print version and exit

now, the tea on --listen (aka -i): you can drop this flag more than once, and the syntax goes like this: [ ssl: ] [ host-name-or-IP-address ] [ : port ], OR you can hand it an absolute path (filename) of a Unix socket instead. if the port isn't there, it defaults to --port, or to 783. and --ssl? yeah that just implies a prefix of 'ssl:', slay. πŸ¦‹

an IPv6 address should be wrapped in square brackets, like [::1]:783. an IPv4 address may be wrapped in square brackets but literally doesn't have to be, no pressure bestie. an asterisk '*' where a hostname would go means an unspecified address β€” '0.0.0.0' or '::' β€” so it binds to all interfaces. an empty option value also implies '*'. and the default? that's '--listen localhost', which only binds to a loopback interface. cozy. πŸŽ€


πŸ’… DESCRIPTION πŸ’…

so the whole point of this program, bestie, is to give you a daemonized version of the spamassassin executable. the goal is improving throughput performance for automated mail checking. it's giving main character energy for your mail pipeline. ✨

this is meant to be used alongside "spamc", which is a fast, low-overhead C client program. they're a duo, iykyk. πŸ‘‘

if you want more deets, go read the README file in the "spamd" directory of the SpamAssassin distribution. it's all there.

note, and this one matters: even though "spamd" will check per-user config files for every single message, any changes to the system-wide config files require either restarting spamd or forcing it to reload itself via SIGHUP for the changes to actually take effect. no cap.
and another one, listen up: if "spamd" receives a SIGHUP, it internally reloads itself, which means it will change its pid and might not restart at all if its environment changed (ie. if it can't change back into its own directory). so if you plan to use SIGHUP, you should always start "spamd" with the -r switch to know its current pid. πŸ’€ don't say i didn't warn you.

🌈 OPTIONS 🌈

okay so before we get into the individual flags: long-form options can be shortened as long as they stay unambiguous. (i.e. --dae can be used instead of --daemonize.) also, boolean options (like --user-config) can be negated by adding no (--nouser-config), however, this is usually unnecessary. cute, right?

  • -l, --allow-tell β€” allow learning and forgetting (to a local Bayes database), plus reporting and revoking (to a remote database), by spamd. the client issues a TELL command to say what type of message is being processed and whether local (learn/forget) or remote (report/revoke) databases should be updated. πŸ“
heads up though: spamd always trusts the username passed in, so clients could maliciously learn messages for other users. (this is not usually a concern with an SQL Bayes store, because users will typically have read-write access directly to the database, and can also use "sa-learn" with the -u option to achieve the same result.) πŸ™ˆ
  • -c, --create-prefs β€” create user preferences files if they don't exist (default: don't). simple as.
  • -C path, --configpath=path β€” use the specified path for locating the distributed configuration files. ignore the default directories (usually "/usr/share/spamassassin" or similar). your path, your rules. πŸ’…
  • --siteconfigpath=path β€” use the specified path for locating site-specific configuration files. ignore the default directories (usually "/etc/spamassassin" or similar). ✨
  • --cf='config line' β€” add additional lines of configuration directly from the command-line, parsed after the configuration files are read. multiple --cf arguments can be used, and each one will be considered a separate line of configuration. stack 'em. 🫢
  • --pre='config line' β€” add additional lines of .pre configuration directly from the command-line, parsed before the configuration files are read. multiple --pre arguments can be used, and each will be considered a separate line of configuration. yes it's the same vibe as --cf but it goes in early. πŸ‘‘
  • -d, --daemonize β€” detach from the starting process and run in the background (daemonize). go be free, bestie. πŸ¦‹
  • -h, --help β€” print a brief help message, then exit without further action. short and sweet.
  • -V, --version β€” print version information, then exit without further action. πŸŽ€
  • -i [ipaddress[:<port>]], --listen[=ipaddress[:<port>]] β€” additional alias names for this option are --listen-ip and --ip-address. tells spamd to listen on the specified IP address, defaults to a loopback interface, i.e. "--listen localhost". if no value is specified after the switch, or if an asterisk '*' stands in place of an <ipaddress>, spamd will listen on all interfaces β€” equivalent to address '0.0.0.0' for IPv4 and '::' for IPv6. you can also use a valid hostname, which will make spamd listen on all addresses that the name resolves to. this option may be specified multiple times. see also -4 and -6 for restricting address family to IPv4 or IPv6. if a port is specified, it overrides for this socket the global --port (and --ssl-port) setting. an IPv6 address should be enclosed in square brackets, e.g. [::1]:783. for compatibility, square brackets on an IPv6 address may be omitted if a port number specification is also omitted. 🌈
important vibe check: listening on non-loopback addresses does not automatically permit connections from those addresses. the -A / --allowed-ips option controls which source addresses may connect, and defaults to localhost only (127.0.0.1 and ::1). πŸ”₯
  • -p port, --port=port β€” optionally specifies the port number for the server to listen on (default: 783). if the --ssl switch is used without --ssl-port, this port accepts SSL connections instead of unencrypted connections. if --ssl-port is also set (which itself implies --ssl), then unencrypted connections are accepted on --port and encrypted connections are accepted on --ssl-port simultaneously. use -i / --listen with an "ssl:" prefix for finer control over which addresses and ports carry SSL vs plain-TCP traffic. πŸ’œ
  • -q, --sql-config β€” turn on SQL lookups even when per-user config files have been disabled with -x. this is useful for spamd hosts which don't have user's home directories but do want to load user preferences from an SQL database. if your spamc client does not support sending the "User:" header, like "exiscan", then the SQL username used will always be nobody. this inhibits the setuid() behavior, so the "-u" option is required. if you want the setuid() behaviour, use "-Q" or "--setuid-with-sql" instead. 🌸
  • --ldap-config β€” turn on LDAP lookups. this is completely analog to "--sql-config", only it's using an LDAP server. like "--sql-config", this disables the setuid behavior, and requires "-u". if you want it, use "--setuid-with-ldap" instead. 🧚
  • -Q, --setuid-with-sql β€” turn on SQL lookups even when per-user config files have been disabled with -x, and also setuid to the user. this is useful for spamd hosts which want to load user preferences from an SQL database but also wish to support the use of -H (Helper home directories.) πŸ’–
  • --setuid-with-ldap β€” okay so this one turns LDAP lookups ON even when you've killed per-user config files with -x, and it also setuids to the user. It's literally the exact same energy as --setuid-with-sql, just… LDAP flavored instead of SQL flavored πŸ’…
  • --virtual-config-dir=pattern β€” this is where you tell spamd to look for per-user preferences for virtual users, the ones the -x switch is about. The pattern is a base pattern for the directory name. And bestie, you get escapes ✨:
    • %u β€” swapped out for the full name of the current user, exactly as spamc sent it.
    • %l β€” swapped out for the local part of the current username. Like, if the username is an email address, this is everything before the @.
    • %d β€” swapped out for the domain of the current username. So if the username is an email address, this is everything after the @.
    • %x β€” swapped out for the full name of the current user as spamc sent it, BUT if that resulting config directory doesn't exist, it gets replaced with the domain part instead, so you get a domain-wide default 🫢
    • %% β€” swapped out for one single percent sign (%).

So like, for example, if "/vhome/users/%u/spamassassin" is what you specified, and spamc sends a virtual username of "jm@example.com", the directory used will be "/vhome/users/jm@example.com/spamassassin". Slay.

The characters allowed in the virtual username for this path are restricted to:

A-Z a-z 0-9 - + _ . , @ =

Anything else gets replaced with underscores ("_"). Rude but fair.

This path has to be a writable directory. It will be created if it doesn't already exist. If a file called user_prefs lives in this directory (note: NOT in a ".spamassassin" subdirectory!!), it gets loaded as the user's preferences. And the Bayes databases for that user get stored in this directory too.

Heads up: this requires -x to be used, and it cannot be combined with SQL- or LDAP-based configuration.

The pattern must expand to an absolute directory when spamd is running daemonized (-d).

Currently, using this without -u is not supported. And that inhibits setuid. 😭

  • -r pidfile, --pidfile=pidfile β€” writes the process ID of the spamd parent into the file you name with pidfile. The file gets unlinked when the parent exits. Note that when you're running with the -u option, that file has to be writable by that user.
  • -v, --vpopmail β€” turns on vpopmail config. If you specify this with -u set to the vpopmail user, spamd can look up / create user_prefs in the vpopmail user's own maildir. Super handy for vpopmail virtual users who don't have an entry in the system /etc/passwd file πŸ‘‘

Currently, using this without -u is not supported. And yeah, this inhibits setuid too.

  • -s facility, --syslog=facility β€” picks the syslog facility to use (default: mail). If you say "stderr", output goes to stderr instead. (Which is so useful if you're running "spamd" under the "daemontools" package btw.) With a facility of "file", all output goes to spamd.log. And facility is interpreted as a file name to log to if it contains any characters other than a-z and 0-9. "null" disables logging completely (it's used internally).

Examples, no cap:

spamd -s mail                 # use syslog, facility mail (default)
spamd -s ./mail               # log to file ./mail
spamd -s stderr 2>/dev/null   # log to stderr, throw messages away
spamd -s null                 # the same as above
spamd -s file                 # log to file ./spamd.log
spamd -s /var/log/spamd.log   # log to file /var/log/spamd.log

If logging to a file is enabled and that log file gets rotated, the spamd server must be restarted with a SIGHUP. (If the log file is just truncated, you don't need to, but it's still recommended.)

Note that logging to a file does not use locking, so you cannot intermix logging from spamd and other processes into the same file. If you want to mix logging like this, use syslog instead. πŸ’€

And if you use syslog logging, it is essential to send a SIGHUP to the spamd daemon when you restart the syslogd daemon. (This is because of a shortcoming in Perl's syslog handling, where losing the connection to syslogd is treated as a fatal error. Not dramatic at all.)

  • --syslog-socket=type β€” tells spamd how it should send messages to syslogd. type can be any of the socket types or logging mechanisms that the subroutine Sys::Syslog::setlogsock() accepts. Depending on your version of Sys::Syslog and on the underlying OS, one of these values (or some subset of them) can be used: "native", "eventlog", "tcp", "udp", "inet", "unix", "stream", "pipe", or "console". The value "eventlog" is specific to the Win32 events logger and requires the perl module Win32::EventLog to be installed. Want more info? Go read the Sys::Syslog documentation πŸ’œ

A historical setting, --syslog-socket=none, is mapped to --syslog=stderr.

The default for Windows platforms is "none"; otherwise the default is to try "unix" first, then fall back to "inet" if perl detects errors in its "unix" support.

Some platforms, or some versions of perl, ship with old or straight-up dysfunctional versions of the Sys::Syslog module that don't support some socket types, so you may need to set this option explicitly. If you're getting error messages about __PATH_LOG or similar from spamd, try changing this setting πŸ™ˆ

The socket type "file" is used internally and should not be specified. Use the -s switch instead, bestie.

  • --log-timestamp-fmt=format β€” this one hands you a POSIX strftime(3) format for the timestamps included in each logged message. Each logger (stderr, file, syslog) has its own default timestamp format, and that default kicks in whenever --log-timestamp-fmt isn't given, or when it's given with --log-timestamp-fmt=default. You can turn timestamps off entirely by specifying an empty string, like --log-timestamp-fmt='' or just --log-timestamp-fmt=. Typical use cases: --log-timestamp-fmt='%a %b %e %H:%M:%S %Y' (gives you localized weekday and month names in the ctime(3) style), or '%a, %e %b %Y %H:%M:%S %z (%Z)' for an RFC 2822 format, or maybe '%Y-%m-%d %H:%M:%S%z' for an ISO 8601 (EN 28601) format, or just '%Y%m%dT%H%M%S'. Pick your fighter ✨
  • -u username, --username=username β€” run as the named user. If this option isn't set, the default behaviour is to setuid() to the user running "spamc", if "spamd" is running as root.
Note: "--username=root" is not a valid option. If you specify it, "spamd" will exit with a fatal error on startup. No root era for you πŸ’€
  • -g groupname, --groupname=groupname β€” run as the named group if --username is being used. If you don't set this while using --username, then the primary group of the user you gave to --username is used.
  • -x, --nouser-config, --user-config β€” turns off (or on) reading of per-user configuration files (user_prefs) from the user's home directory. The default behaviour is to read per-user configuration from the user's home directory (--user-config).

This option does not disable or otherwise influence the SQL, LDAP or Virtual Config Dir settings. Those are their own whole thing πŸ¦‹

  • -U username, --default-user=username β€” fall back to this username if the username provided by spamc isn't found. Default is nobody, which might not exist or might not have a usable home directory, so use this setting to define a suitable user if you need one.
  • -A host,..., --allowed-ips=host,... β€” a comma-separated list of authorized hosts or networks allowed to connect to this spamd instance. Each element of the list is either a single IP address, or a range of IP addresses in address/masklength CIDR notation, or ranges of IPv4 addresses by specifying 3 or fewer octets with a trailing dot. Hostnames are not supported, only IPv4 or IPv6 addresses. This option can be specified multiple times, or it can take a list of addresses separated by commas. IPv6 addresses may be (but need not be) enclosed in square brackets for consistency with the --listen option. Examples:
-A 10.11.12.13 -- only allow connections from 10.11.12.13.

-A 10.11.12.13,10.11.12.14 -- only allow connections from
10.11.12.13 and 10.11.12.14.

-A 10.200.300.0/24 -- allow connections from any machine in the
range "10.200.300.*".

-A 10. -- allow connections from any machine in the range
"10.*.*.*".

-A [2001:db8::]/32,192.0.2.0/24,::1,127.0.0.0/8 -- only accept
connections from specified test networks and from localhost.

In the absence of the -A option, connections are only accepted from IP address 127.0.0.1 or ::1, i.e. from localhost on a loopback interface. And the allowed-IP list is global across all listening sockets β€” there is no per-socket access control. One list to rule them all πŸ‘‘

  • -D [area,...], --debug [area,...] β€” produce debugging output. If no areas are listed, all debugging information gets printed. Diagnostic output can also be enabled for each area individually; area is the area of the code to instrument. So like, to produce diagnostic output on bayes, learn, and dns, you'd use:
spamassassin -D bayes,learn,dns

Higher priority informational messages that are suitable for logging in normal circumstances are available with an area of "info".

Wanna know more about which areas (also known as channels) are available? The documentation lives at:

https://wiki.apache.org/spamassassin/DebugChannels
  • -4, --ipv4only, --ipv4-only, --ipv4 β€” use IPv4 where applicable, don't use IPv6. This option affects a set of listen sockets (see the --listen option) and disables IPv6 for DNS tests.
  • -6 β€” use IPv6 where applicable, don't use IPv4. This option affects a set of listen sockets (see the --listen option) and disables IPv4 for DNS tests. Installing a module IO::Socket::IP is recommended if spamd is expected to receive requests over IPv6. Foreshadowing ✨
  • -L, --local β€” perform only local tests on all mail. In other words, skip DNS and other network tests. Works the same as the -L flag to spamassassin(1).
  • -P, --paranoid β€” die on user errors (for the user passed from spamc) instead of falling back to user --default-user and using the default configuration. Maximum drama, on purpose πŸ’…
  • -m number, --max-children=number β€” bestie, this is how many children spamd is allowed to spawn. It spawns that many, then vibes in the background sleeping until a child passes away, at which point it goes and spawns a brand new one. πŸ’€

Incoming connections can still happen even if every single child is busy β€” they just get queued up waiting for a free child, like a line at the boba shop. The minimum value is 1, the default value is 5.

Heads up tho: there's an OS-specific maximum of connections that can be queued. Wanna find yours? Run perl -MSocket -e'print SOMAXCONN' and it'll spill the tea. πŸ”

Also β€” and this part matters β€” if you run too many servers for the amount of free RAM you've got, you're lowkey in danger of hurting performance by causing a high swap load, as server processes get swapped in and out continually. Not the vibe.

  • --min-children=number β€” the minimum number of children that will be kept running, no matter what. The minimum value is 1, the default value is 1. If you have lots of free RAM, you may want to increase this. ✨
  • --min-spare=number β€” the lower limit for the number of spare children allowed to run. A spare (aka idle) child is one that is not handling a scan request, just chilling. If there are too few spare children available, a new server will be started every second or so β€” it's giving "hiring spree". The default value is 1.
  • --max-spare=number β€” the upper limit for the number of spare children allowed to run. If there are too many spare children, one will be killed every second or so until the number of idle children is in the desired range. The default value is 2. 😭
  • --max-conn-per-child=number β€” the max number of connections each child should process before dying and letting the master spamd process spawn a new child. The minimum value is 1, the default value is 200.
  • --round-robin β€” okay so by default, spamd tries to keep a small number of "hot" child processes as busy as possible while keeping any others as idle as possible, using something similar to the Apache httpd server scaling algorithm. The master process coordinates the children's activities to pull this off. This switch disables that scaling algorithm, and instead you get the behaviour seen in the 3.0.x versions, where all processes receive an equal load and no scaling takes place. ✨ throwback era ✨
  • --timeout-tcp=number β€” how many seconds to wait for headers from a client (spamc) before closing the connection. The minimum value is 1, the default value is 30, and a value of 0 will disable socket timeouts completely.
  • --timeout-child=number β€” how many seconds to wait for a spamd child to process or check a message. The minimum value is 1, the default value is 300, and a value of 0 will disable child timeouts completely.
  • -H directory, --helper-home-dir=directory β€” specify that external programs such as Razor, DCC, and Pyzor should have a HOME environment variable set to a specific directory. The default is to use the HOME environment variable setting from the shell running spamd. And if you specify no argument at all? spamd will use the spamc caller's home directory instead. 🏠
  • --ssl β€” accept only SSL connections on the associated port. The IO::Socket::SSL perl module must be installed. πŸ‘‘

The details, bestie, because they matter: when used without -i / --listen and without --ssl-port, the single default listener (--port) is converted to SSL. When used together with --ssl-port (which also implies --ssl), two default listeners are created: a plain-TCP listener on --port and an SSL listener on --ssl-port. When -i / --listen options are present, --ssl has no effect on individual sockets β€” use the ssl: prefix on -i arguments instead (e.g. -i ssl:*:784 -i *:783).

  • --ssl-verify β€” implies --ssl. Request a client certificate and verify the certificate. Requires --ssl-ca-file or --ssl-ca-path.

Ngl, verification is limited to confirming the certificate is signed by the specified CA. Hostname (CN) matching and CRL checking are not performed, because spamd has no options to configure either. That's just how it is. 🫢

  • --ssl-ca-file=cafile β€” implies --ssl-verify. Use the specified Certificate Authority certificate to verify the client certificate. The client certificate must be signed by this certificate.
  • --ssl-ca-path=capath β€” implies --ssl-verify. Use the Certificate Authority certificate files in the specified directory to verify the client certificate. The client certificate must be signed by one of these Certificate Authorities. The directory must be hashed in the usual manner (see c_rehash(1)).
  • --ssl-port=port β€” specifies the port number for SSL connections, implies --ssl. When -i / --listen options are given without explicit ssl: prefixes, each address is expanded into a plain-TCP listener on --port and an SSL listener on --ssl-port, matching the no--i behaviour.
  • --server-key keyfile β€” specify the SSL key file to use for SSL connections. Defaults to /etc/spamassassin/certs/server-key.pem.
  • --server-cert certfile β€” specify the SSL certificate file to use for SSL connections. Defaults to /etc/spamassassin/certs/server-cert.pem.
  • --socketpath pathname β€” listen on a UNIX domain socket at path pathname, in addition to sockets specified with a --listen option. This option is provided for compatibility with older versions of spamd. Starting with version 3.4.0 the --listen option can also take a UNIX domain socket as its value (an absolute path name). Unlike --socketpath, the --listen option may be specified multiple times if spamd needs to listen on multiple UNIX or INET or INET6 sockets.
⚠️ Warning: the Perl support on BSD platforms for UNIX domain sockets seems to have a bug regarding paths of over 100 bytes or so (SpamAssassin bug 4380). If you see a 'could not find newly-created UNIX socket' error message, and the path appears truncated, this may be the cause. Try using a shorter path to the socket. πŸ’€

By default, use of --socketpath without --listen will inhibit SSL connections and unencrypted TCP connections. To add other sockets, specify them with --listen, e.g. '--listen=:' or '--listen=*:'.

  • --socketowner name β€” set UNIX domain socket to be owned by the user named name. Note that this requires that spamd be started as root, and if -u is used, that user should have write permissions to unlink the file later, for when the spamd server is killed.
  • --socketgroup name β€” set UNIX domain socket to be owned by the group named name. See --socketowner for notes on ownership and permissions.
  • --socketmode mode β€” set UNIX domain socket to use the octal mode mode. Note that if -u is used, that user should have write permissions to unlink the file later, for when the spamd server is killed.
  • --timing β€” enable timing measurements and output the information for logging. This is the same information as provided by the TIMING tag. ⏱️

🌈 SEE ALSO

spamc(1) spamassassin(1) Mail::SpamAssassin::Conf(3) Mail::SpamAssassin(3)


πŸŽ€ PREREQUISITES

Mail::SpamAssassin


πŸ‘‘ AUTHORS

The SpamAssassin(tm) Project (https://spamassassin.apache.org/)


πŸ’œ LICENSE

SpamAssassin is distributed under the Apache License, Version 2.0, as described in the file LICENSE included with the distribution.


okay bestie that's the whole page, we ate and left absolutely no crumbs πŸ’… this was SPAMD(8p) from perl v5.40.1, dated 2026-07-23, and like… go forth and filter that spam, main character. πŸ¦‹βœ¨

πŸ“¦ where this page comes from