about summary refs log tree commit homepage
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/README4
-rw-r--r--examples/README.unsubscribe9
-rw-r--r--examples/apache2_cgi.conf34
-rw-r--r--examples/apache2_perl.conf25
-rw-r--r--examples/apache2_perl_old.conf38
-rw-r--r--examples/cgi-webrick.rb25
-rwxr-xr-xexamples/grok-pull.post_update_hook.sh2
-rw-r--r--examples/logrotate.conf4
-rw-r--r--examples/nginx_proxy9
-rw-r--r--examples/public-inbox-httpd.socket3
-rw-r--r--examples/public-inbox-httpd@.service9
-rw-r--r--examples/public-inbox-imap-onion.socket12
-rw-r--r--examples/public-inbox-imapd.socket17
-rw-r--r--examples/public-inbox-imapd@.service17
-rw-r--r--examples/public-inbox-imaps.socket12
-rw-r--r--examples/public-inbox-netd.socket45
-rw-r--r--examples/public-inbox-netd@.service66
-rw-r--r--examples/public-inbox-nntpd.socket21
-rw-r--r--examples/public-inbox-nntpd@.service14
-rw-r--r--examples/public-inbox-nntps.socket12
-rw-r--r--examples/public-inbox-watch.service2
-rw-r--r--examples/unsubscribe-milter@.service6
-rw-r--r--examples/unsubscribe.milter38
-rw-r--r--examples/varnish-4.vcl2
24 files changed, 232 insertions, 194 deletions
diff --git a/examples/README b/examples/README
index 1d5dcd34..5674d7ed 100644
--- a/examples/README
+++ b/examples/README
@@ -9,10 +9,6 @@ For PSGI/Plack (HTTP) servers
 -----------------------------
 public-inbox.psgi - starting point for PSGI/Plack users in production and dev
 
-For Apache2 users
------------------
-apache2_perl.conf - intended to be the basis of a production config
-
 Contact
 -------
 Please send any related feedback to public-inbox: meta@public-inbox.org
diff --git a/examples/README.unsubscribe b/examples/README.unsubscribe
index 3e80e838..3b407960 100644
--- a/examples/README.unsubscribe
+++ b/examples/README.unsubscribe
@@ -3,10 +3,9 @@ Unsubscribe endpoints for mlmmj users (and possibly Mailman, too)
 * examples/unsubscribe.milter filters outgoing messages
   and appends an HTTPS URL to the List-Unsubscribe header.
   This List-Unsubscribe header should point to the PSGI
-  described below.
-  Currently, this is only active for a whitelist of test
-  addresses in /etc/unsubscribe-milter.whitelist
-  with one email address per line.
+  described below.  You may edit the archive_addr sub
+  to disable List-Unsubscribe headers for well-known archiver
+  addresses to prevent saboteurs from stopping archival.
 
 * examples/unsubscribe.psgi is a PSGI which needs to run
   as the mlmmj user with permission to run mlmmj-unsub.
@@ -36,5 +35,5 @@ in /etc/postfix/main.cf:
   # This is not needed for mlmmj since mlmmj uses SMTP:
   # non_smtpd_milters = local:/var/spool/postfix/unsubscribe/unsubscribe.sock
 
-Copyright (C) 2016-2021 all contributors <meta@public-inbox.org>
+Copyright (C) all contributors <meta@public-inbox.org>
 License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
diff --git a/examples/apache2_cgi.conf b/examples/apache2_cgi.conf
deleted file mode 100644
index 5ec64d72..00000000
--- a/examples/apache2_cgi.conf
+++ /dev/null
@@ -1,34 +0,0 @@
-# Example Apache2 configuration using CGI mod_cgi
-# If possible, use mod_perl (see apache2_perl.conf) or
-# a standalone PSGI/Plack # server instead of this.
-# Adjust paths to your installation.
-
-ServerName "public-inbox"
-ServerRoot "/var/www/cgi-bin"
-DocumentRoot "/var/www/cgi-bin"
-ErrorLog "/tmp/public-inbox-error.log"
-PidFile "/tmp/public-inbox.pid"
-Listen 127.0.0.1:8080
-LoadModule cgi_module /usr/lib/apache2/modules/mod_cgi.so
-LoadModule env_module /usr/lib/apache2/modules/mod_env.so
-LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
-LoadModule dir_module /usr/lib/apache2/modules/mod_dir.so
-LoadModule mime_module /usr/lib/apache2/modules/mod_mime.so
-TypesConfig "/dev/null"
-
-<Directory /var/www/cgi-bin>
-        Options +ExecCGI
-        AddHandler cgi-script .cgi
-
-        # we use this hack to ensure "public-inbox.cgi" doesn't show up
-        # in any of our redirects:
-        SetEnv NO_SCRIPT_NAME 1
-
-        # our public-inbox.cgi requires PATH_INFO-based URLs with minimal
-        # use of query parameters
-        DirectoryIndex public-inbox.cgi
-        RewriteEngine On
-        RewriteCond %{REQUEST_FILENAME} !-f
-        RewriteCond %{REQUEST_FILENAME} !-d
-        RewriteRule ^.* /public-inbox.cgi/$0 [L,PT]
-</Directory>
diff --git a/examples/apache2_perl.conf b/examples/apache2_perl.conf
deleted file mode 100644
index a4721b5b..00000000
--- a/examples/apache2_perl.conf
+++ /dev/null
@@ -1,25 +0,0 @@
-# Example Apache2 configuration using Plack::Handler::Apache2
-# Adjust paths to your installation
-
-ServerName "public-inbox"
-ServerRoot "/var/www"
-DocumentRoot "/var/www"
-ErrorLog "/tmp/public-inbox-error.log"
-PidFile "/tmp/public-inbox.pid"
-Listen 127.0.0.1:8080
-LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so
-
-# no need to set no rely on HOME if using this:
-PerlSetEnv PI_CONFIG /home/pi/.public-inbox/config
-
-<Location />
-        SetHandler perl-script
-        PerlResponseHandler Plack::Handler::Apache2
-        PerlSetVar psgi_app /path/to/public-inbox.psgi
-</Location>
-
-# Optional, preload the application in the parent like startup.pl
-<Perl>
-        use Plack::Handler::Apache2;
-        Plack::Handler::Apache2->preload("/path/to/public-inbox.psgi");
-</Perl>
diff --git a/examples/apache2_perl_old.conf b/examples/apache2_perl_old.conf
deleted file mode 100644
index a6de2304..00000000
--- a/examples/apache2_perl_old.conf
+++ /dev/null
@@ -1,38 +0,0 @@
-# Example legacy Apache2 configuration using CGI + mod_perl2
-# Consider using Plack::Handler::Apache2 instead (see apache2_perl.conf)
-# Adjust paths to your installation
-
-ServerName "public-inbox"
-ServerRoot "/var/www/cgi-bin"
-DocumentRoot "/var/www/cgi-bin"
-ErrorLog "/tmp/public-inbox-error.log"
-PidFile "/tmp/public-inbox.pid"
-Listen 127.0.0.1:8080
-LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so
-LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
-LoadModule dir_module /usr/lib/apache2/modules/mod_dir.so
-LoadModule mime_module /usr/lib/apache2/modules/mod_mime.so
-TypesConfig "/dev/null"
-
-# PerlPassEnv PATH # this is implicit
-<Directory /var/www/cgi-bin>
-        Options +ExecCGI
-        AddHandler perl-script .cgi
-        PerlResponseHandler ModPerl::Registry
-        PerlOptions +ParseHeaders
-
-        # we use this hack to ensure "public-inbox.cgi" doesn't show up
-        # in any of our redirects:
-        PerlSetEnv NO_SCRIPT_NAME 1
-
-        # no need to set no rely on HOME if using this:
-        PerlSetEnv PI_CONFIG /home/pi/.public-inbox/config
-
-        # our public-inbox.cgi requires PATH_INFO-based URLs with minimal
-        # use of query parameters
-        DirectoryIndex public-inbox.cgi
-        RewriteEngine On
-        RewriteCond %{REQUEST_FILENAME} !-f
-        RewriteCond %{REQUEST_FILENAME} !-d
-        RewriteRule ^.* /public-inbox.cgi/$0 [L,PT]
-</Directory>
diff --git a/examples/cgi-webrick.rb b/examples/cgi-webrick.rb
deleted file mode 100644
index 5554a012..00000000
--- a/examples/cgi-webrick.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env ruby
-# Sample configuration using WEBrick, mainly intended dev/testing
-# for folks familiar with Ruby and not various Perl webserver
-# deployment options.  For those familiar with Perl web servers,
-# plackup(1) is recommended for development and public-inbox-httpd(1)
-# is our production deployment server.
-require 'webrick'
-require 'logger'
-options = {
-  :BindAddress => '127.0.0.1',
-  :Port => 8080,
-  :Logger => Logger.new($stderr),
-  :CGIPathEnv => ENV['PATH'], # need to run 'git' commands
-  :AccessLog => [
-    [ Logger.new($stdout), WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
-  ],
-}
-server = WEBrick::HTTPServer.new(options)
-server.mount("/",
-             WEBrick::HTTPServlet::CGIHandler,
-            "/var/www/cgi-bin/public-inbox.cgi")
-['INT', 'TERM'].each do |signal|
-  trap(signal) {exit!(0)}
-end
-server.start
diff --git a/examples/grok-pull.post_update_hook.sh b/examples/grok-pull.post_update_hook.sh
index 77489472..4d303c03 100755
--- a/examples/grok-pull.post_update_hook.sh
+++ b/examples/grok-pull.post_update_hook.sh
@@ -111,7 +111,7 @@ case $cfg_dir in
                         "publicinbox.$inbox_name.infourl" "$url"
         done
         curl -sSfv "$remote_inbox_url"/description >"$inbox_dir"/description
-        echo "I: $inbox_name at $inbox_dir ($addresses) $local_url"
+        echo "# $inbox_name at $inbox_dir ($addresses) $local_url"
         ;;
 esac
 
diff --git a/examples/logrotate.conf b/examples/logrotate.conf
index 4ce08843..fad40cfc 100644
--- a/examples/logrotate.conf
+++ b/examples/logrotate.conf
@@ -18,7 +18,7 @@
                 # systemd users do not need PID files,
                 # only signal the @1 process since the @2 is short-lived
                 # For systemd users, assuming you use two services
-                systemctl kill -s SIGUSR1 public-inbox-httpd@1.service
-                systemctl kill -s SIGUSR1 public-inbox-nntpd@1.service
+                systemctl kill -s SIGUSR1 --kill-who=main \
+                                public-inbox-netd@1.service
         endscript
 }
diff --git a/examples/nginx_proxy b/examples/nginx_proxy
index d8d1e6df..754a4931 100644
--- a/examples/nginx_proxy
+++ b/examples/nginx_proxy
@@ -1,8 +1,14 @@
 # Example NGINX configuration to proxy-pass requests
-# to public-inbox-httpd or to a standalone PSGI/Plack server.
+# to varnish, public-inbox-(httpd|netd) or any PSGI/Plack server.
 # The daemon is assumed to be running locally on port 8001.
 # Adjust ssl certificate paths if you use any, or remove
 # the ssl configuration directives if you don't.
+#
+# Note: public-inbox-httpd and -netd both support HTTPS, but they
+# don't support caching which Varnish provides.  The recommended
+# setup is currently:
+#
+#   (nginx|any-HTTPS-proxy) <-> varnish <-> public-inbox-(httpd|netd)
 server {
         server_name _;
         listen 80;
@@ -14,6 +20,7 @@ server {
                 proxy_set_header    HOST $host;
                 proxy_set_header    X-Real-IP $remote_addr;
                 proxy_set_header    X-Forwarded-Proto $scheme;
+                proxy_buffering off; # lowers response latency
                 proxy_pass          http://127.0.0.1:8001$request_uri;
         }
 
diff --git a/examples/public-inbox-httpd.socket b/examples/public-inbox-httpd.socket
index 1a1ed735..3a6e4432 100644
--- a/examples/public-inbox-httpd.socket
+++ b/examples/public-inbox-httpd.socket
@@ -1,4 +1,7 @@
 # ==> /etc/systemd/system/public-inbox-httpd.socket <==
+# Consider looking at public-inbox-netd.socket instead of this file
+# to simplify management when serving multiple protocols.
+
 [Unit]
 Description = public-inbox-httpd socket
 
diff --git a/examples/public-inbox-httpd@.service b/examples/public-inbox-httpd@.service
index 147f7c6d..ca68fc7e 100644
--- a/examples/public-inbox-httpd@.service
+++ b/examples/public-inbox-httpd@.service
@@ -1,4 +1,7 @@
 # ==> /etc/systemd/system/public-inbox-httpd@.service <==
+# Consider looking at public-inbox-netd@.service instead of this file
+# to simplify management when serving multiple protocols.
+#
 # Since SIGUSR2 upgrades do not work under systemd, this service file
 # allows starting two simultaneous services during upgrade time
 # (e.g. public-inbox-httpd@1 public-inbox-httpd@2) with the intention
@@ -16,13 +19,13 @@ After = public-inbox-httpd.socket
 Environment = PI_CONFIG=/home/pi/.public-inbox/config \
 PATH=/usr/local/bin:/usr/bin:/bin \
 TZ=UTC \
+MALLOC_MMAP_THRESHOLD_=131072 \
 PERL_INLINE_DIRECTORY=/tmp/.pub-inline
 
 LimitNOFILE = 30000
 ExecStartPre = /bin/mkdir -p -m 1777 /tmp/.pub-inline
 ExecStart = /usr/local/bin/public-inbox-httpd \
 -1 /var/log/public-inbox/httpd.out.log
-StandardError = syslog
 
 # NonBlocking is REQUIRED to avoid a race condition if running
 # simultaneous services
@@ -30,8 +33,8 @@ NonBlocking = true
 Sockets = public-inbox-httpd.socket
 
 KillSignal = SIGQUIT
-User = nobody
-Group = nogroup
+User = news
+Group = ssl-cert
 ExecReload = /bin/kill -HUP $MAINPID
 TimeoutStopSec = 86400
 KillMode = process
diff --git a/examples/public-inbox-imap-onion.socket b/examples/public-inbox-imap-onion.socket
deleted file mode 100644
index 76b4e7ca..00000000
--- a/examples/public-inbox-imap-onion.socket
+++ /dev/null
@@ -1,12 +0,0 @@
-# ==> /etc/systemd/system/public-inbox-imap-onion.socket <==
-# This unit is for the corresponding line in torrc(5):
-# HiddenServicePort 143 unix:/run/imapd.onion.sock
-[Unit]
-Description = public-inbox-imap .onion socket
-
-[Socket]
-ListenStream = /run/imapd.onion.sock
-Service = public-inbox-imapd@1.service
-
-[Install]
-WantedBy = sockets.target
diff --git a/examples/public-inbox-imapd.socket b/examples/public-inbox-imapd.socket
index fcd924fd..22ce16fb 100644
--- a/examples/public-inbox-imapd.socket
+++ b/examples/public-inbox-imapd.socket
@@ -1,11 +1,26 @@
 # ==> /etc/systemd/system/public-inbox-imapd.socket <==
+# Consider looking at public-inbox-netd.socket instead of this file
+# to simplify management when serving multiple protocols.
+#
+# This contains 5 sockets for an public-inbox-imapd instance.
+# The TCP ports are well-known ports registered in /etc/services.
+# The /run/imapd.onion.sock entry is meant for the Tor hidden service
+# enabled by the following line in the torrc(5) file:
+#   HiddenServicePort 143 unix:/run/imapd.onion.sock
 [Unit]
-Description = public-inbox-imapd socket
+Description = public-inbox-imapd sockets
 
 [Socket]
 ListenStream = 0.0.0.0:143
+ListenStream = 0.0.0.0:993
+ListenStream = /run/imapd.onion.sock
+
+# Separating IPv4 from IPv6 listeners makes for nicer output
+# of IPv4 addresses in various reporting/monitoring tools
 BindIPv6Only = ipv6-only
 ListenStream = [::]:143
+ListenStream = [::]:993
+
 Service = public-inbox-imapd@1.service
 
 [Install]
diff --git a/examples/public-inbox-imapd@.service b/examples/public-inbox-imapd@.service
index e0446ed3..1aede65d 100644
--- a/examples/public-inbox-imapd@.service
+++ b/examples/public-inbox-imapd@.service
@@ -1,4 +1,7 @@
 # ==> /etc/systemd/system/public-inbox-imapd@.service <==
+# Consider looking at public-inbox-netd@.service instead of this file
+# to simplify management when serving multiple protocols.
+#
 # Since SIGUSR2 upgrades do not work under systemd, this service file
 # allows starting two simultaneous services during upgrade time
 # (e.g. public-inbox-imapd@1 public-inbox-imapd@2) with the intention
@@ -7,14 +10,14 @@
 
 [Unit]
 Description = public-inbox-imapd IMAP server %i
-Wants = public-inbox-imapd.socket public-inbox-imaps.socket \
-public-inbox-imap-onion.socket
-After = public-inbox-imapd.socket public-inbox-imaps.socket \
-public-inbox-imap-onion.socket
+Wants = public-inbox-imapd.socket
+After = public-inbox-imapd.socket
 
 [Service]
 Environment = PI_CONFIG=/home/pi/.public-inbox/config \
 PATH=/usr/local/bin:/usr/bin:/bin \
+TZ=UTC \
+MALLOC_MMAP_THRESHOLD_=131072 \
 PERL_INLINE_DIRECTORY=/tmp/.pub-inline
 
 LimitNOFILE = 30000
@@ -23,17 +26,15 @@ ExecStart = /usr/local/bin/public-inbox-imapd -W0 \
 -1 /var/log/public-inbox/imapd.out.log \
 --cert /etc/ssl/certs/news.example.com.pem \
 --key /etc/ssl/private/news.example.com.key
-StandardError = syslog
 
 # NonBlocking is REQUIRED to avoid a race condition if running
 # simultaneous services
 NonBlocking = true
 
-Sockets = public-inbox-imapd.socket public-inbox-imaps.socket \
-public-inbox-imap-onion.socket
+Sockets = public-inbox-imapd.socket
 
 KillSignal = SIGQUIT
-User = nobody
+User = news
 Group = ssl-cert
 ExecReload = /bin/kill -HUP $MAINPID
 TimeoutStopSec = 86400
diff --git a/examples/public-inbox-imaps.socket b/examples/public-inbox-imaps.socket
deleted file mode 100644
index b61cc742..00000000
--- a/examples/public-inbox-imaps.socket
+++ /dev/null
@@ -1,12 +0,0 @@
-# ==> /etc/systemd/system/public-inbox-imaps.socket <==
-[Unit]
-Description = public-inbox-imaps socket
-
-[Socket]
-ListenStream = 0.0.0.0:993
-BindIPv6Only = ipv6-only
-ListenStream = [::]:993
-Service = public-inbox-imapd@1.service
-
-[Install]
-WantedBy = sockets.target
diff --git a/examples/public-inbox-netd.socket b/examples/public-inbox-netd.socket
new file mode 100644
index 00000000..9a19602e
--- /dev/null
+++ b/examples/public-inbox-netd.socket
@@ -0,0 +1,45 @@
+# ==> /etc/systemd/system/public-inbox-netd.socket <==
+# This contains all the services that public-inbox-netd can run;
+# allowing it to replace (or run in parallel to) any existing -httpd,
+# -imapd, -nntpd, or -pop3d instances.
+#
+# The TCP ports are well-known ports registered in /etc/services.
+# The /run/*.sock entries are meant for the Tor hidden service
+# enabled by the following lines in the torrc(5) file:
+#   HiddenServicePort 110 unix:/run/pop3.sock
+#   HiddenServicePort 119 unix:/run/nntp.sock
+#   HiddenServicePort 143 unix:/run/imap.sock
+[Unit]
+Description = public-inbox-netd sockets
+
+[Socket]
+# for tor (see torrc(5))
+ListenStream = /run/imap.sock
+ListenStream = /run/pop3.sock
+ListenStream = /run/nntp.sock
+
+# this is for varnish:
+ListenStream = 127.0.0.1:280
+
+# public facing
+ListenStream = 0.0.0.0:110
+ListenStream = 0.0.0.0:119
+ListenStream = 0.0.0.0:143
+ListenStream = 0.0.0.0:563
+ListenStream = 0.0.0.0:993
+ListenStream = 0.0.0.0:995
+
+# Separating IPv4 from IPv6 listeners makes for nicer output
+# of IPv4 addresses in various reporting/monitoring tools
+BindIPv6Only = ipv6-only
+ListenStream = [::]:110
+ListenStream = [::]:119
+ListenStream = [::]:143
+ListenStream = [::]:563
+ListenStream = [::]:993
+ListenStream = [::]:995
+
+Service = public-inbox-netd@1.service
+
+[Install]
+WantedBy = sockets.target
diff --git a/examples/public-inbox-netd@.service b/examples/public-inbox-netd@.service
new file mode 100644
index 00000000..51f58fbb
--- /dev/null
+++ b/examples/public-inbox-netd@.service
@@ -0,0 +1,66 @@
+# ==> /etc/systemd/system/public-inbox-netd@.service <==
+# Since SIGUSR2 upgrades do not work under systemd, this service file
+# allows starting two simultaneous services during upgrade time
+# (e.g. public-inbox-netd@1 public-inbox-netd@2) with the intention
+# that they take turns running in-between upgrades.  This should
+# allow upgrading without downtime.
+# For servers expecting visitors from multiple timezones, TZ=UTC
+# is needed to ensure a consistent approxidate experience with search.
+[Unit]
+Description = public-inbox-netd server %i
+Wants = public-inbox-netd.socket
+After = public-inbox-netd.socket
+
+[Service]
+
+# Setting MALLOC_MMAP_THRESHOLD_=131072 reduces fragmentation by
+# disabling the sliding mmap window in glibc malloc.  An LD_PRELOAD for
+# libjemalloc may be added here, instead.  jemalloc is more resistant to
+# fragmentation in long-lived daemons than unconfigured glibc malloc.
+Environment = PI_CONFIG=/home/pi/.public-inbox/config \
+PATH=/usr/local/bin:/usr/bin:/bin \
+TZ=UTC \
+MALLOC_MMAP_THRESHOLD_=131072 \
+PERL_INLINE_DIRECTORY=/tmp/.netd-inline
+
+LimitNOFILE = 30000
+LimitCORE = infinity
+ExecStartPre = /bin/mkdir -p -m 1777 /tmp/.netd-inline
+
+# The '-l' args below map each socket in public-inbox-netd.socket to
+# the appropriate IANA service name:
+ExecStart = /usr/local/bin/public-inbox-netd -W0 \
+-1 /var/log/netd/stdout.out.log \
+--cert /etc/ssl/certs/news.example.com.pem \
+--key /etc/ssl/private/news.example.com.key
+-l imap:///run/imap.sock?out=/var/log/netd/imap.out,err=/var/log/netd/imap.err \
+-l nntp:///run/nntp.sock?out=/var/log/netd/nntp.out,err=/var/log/netd/nntp.err \
+-l pop3:///run/pop3.sock?out=/var/log/netd/pop3.out,err=/var/log/netd/pop3.err \
+-l imap://0.0.0.0/?out=/var/log/netd/imap.out,err=/var/log/netd/imap.err \
+-l nntp://0.0.0.0/?out=/var/log/netd/nntp.out,err=/var/log/netd/nntp.err \
+-l pop3://0.0.0.0/?out=/var/log/netd/pop3.out,err=/var/log/netd/pop3.err \
+-l imap://[::]/?out=/var/log/netd/imap.out,err=/var/log/netd/imap.err \
+-l nntp://[::]/?out=/var/log/netd/nntp.out,err=/var/log/netd/nntp.err \
+-l pop3://[::]/?out=/var/log/netd/pop3.out,err=/var/log/netd/pop3.err \
+-l imaps://0.0.0.0/?out=/var/log/netd/imap.out,err=/var/log/netd/imap.err \
+-l nntps://0.0.0.0/?out=/var/log/netd/nntp.out,err=/var/log/netd/nntp.err \
+-l pop3s://0.0.0.0/?out=/var/log/netd/pop3.out,err=/var/log/netd/pop3.err \
+-l imaps://[::]/?out=/var/log/netd/imap.out,err=/var/log/netd/imap.err \
+-l nntps://[::]/?out=/var/log/netd/nntp.out,err=/var/log/netd/nntp.err \
+-l pop3s://[::]/?out=/var/log/netd/pop3.out,err=/var/log/netd/pop3.err \
+-l http://127.0.0.1:280/?psgi=/etc/public.psgi,err=/var/log/netd/http.err
+
+# NonBlocking is REQUIRED to avoid a race condition if running
+# simultaneous services
+NonBlocking = true
+
+Sockets = public-inbox-netd.socket
+KillSignal = SIGQUIT
+User = news
+Group = ssl-cert
+ExecReload = /bin/kill -HUP $MAINPID
+TimeoutStopSec = 30
+KillMode = process
+
+[Install]
+WantedBy = multi-user.target
diff --git a/examples/public-inbox-nntpd.socket b/examples/public-inbox-nntpd.socket
index eeddf343..10335d8d 100644
--- a/examples/public-inbox-nntpd.socket
+++ b/examples/public-inbox-nntpd.socket
@@ -1,9 +1,26 @@
 # ==> /etc/systemd/system/public-inbox-nntpd.socket <==
+# Consider looking at public-inbox-netd.socket instead of this file
+# to simplify management when serving multiple protocols.
+#
+# This contains 5 sockets for an public-inbox-nntpd instance.
+# The TCP ports are well-known ports registered in /etc/services.
+# The /run/nntpd.onion.sock entry is meant for the Tor hidden service
+# enabled by the following line in the torrc(5) file:
+#   HiddenServicePort 119 unix:/run/nntpd.onion.sock
 [Unit]
-Description = public-inbox-nntpd socket
+Description = public-inbox-nntpd sockets
 
 [Socket]
-ListenStream = 119
+ListenStream = 0.0.0.0:119
+ListenStream = 0.0.0.0:563
+ListenStream = /run/nntpd.onion.sock
+
+# Separating IPv4 from IPv6 listeners makes for nicer output
+# of IPv4 addresses in various reporting/monitoring tools
+BindIPv6Only = ipv6-only
+ListenStream = [::]:119
+ListenStream = [::]:563
+
 Service = public-inbox-nntpd@1.service
 
 [Install]
diff --git a/examples/public-inbox-nntpd@.service b/examples/public-inbox-nntpd@.service
index 4dd2f5d7..556cb76f 100644
--- a/examples/public-inbox-nntpd@.service
+++ b/examples/public-inbox-nntpd@.service
@@ -1,4 +1,7 @@
 # ==> /etc/systemd/system/public-inbox-nntpd@.service <==
+# Consider looking at public-inbox-netd@.service instead of this file
+# to simplify management when serving multiple protocols.
+#
 # Since SIGUSR2 upgrades do not work under systemd, this service file
 # allows starting two simultaneous services during upgrade time
 # (e.g. public-inbox-nntpd@1 public-inbox-nntpd@2) with the intention
@@ -7,12 +10,14 @@
 
 [Unit]
 Description = public-inbox NNTP server %i
-Wants = public-inbox-nntpd.socket public-inbox-nntps.socket
-After = public-inbox-nntpd.socket public-inbox-nntps.socket
+Wants = public-inbox-nntpd.socket
+After = public-inbox-nntpd.socket
 
 [Service]
 Environment = PI_CONFIG=/home/pi/.public-inbox/config \
 PATH=/usr/local/bin:/usr/bin:/bin \
+TZ=UTC \
+MALLOC_MMAP_THRESHOLD_=131072 \
 PERL_INLINE_DIRECTORY=/tmp/.pub-inline
 
 LimitNOFILE = 30000
@@ -21,16 +26,15 @@ ExecStart = /usr/local/bin/public-inbox-nntpd \
 -1 /var/log/public-inbox/nntpd.out.log \
 --cert /etc/ssl/certs/news.example.com.pem \
 --key /etc/ssl/private/news.example.com.key
-StandardError = syslog
 
 # NonBlocking is REQUIRED to avoid a race condition if running
 # simultaneous services
 NonBlocking = true
 
-Sockets = public-inbox-nntpd.socket public-inbox-nntps.socket
+Sockets = public-inbox-nntpd.socket
 
 KillSignal = SIGQUIT
-User = nobody
+User = news
 Group = ssl-cert
 ExecReload = /bin/kill -HUP $MAINPID
 TimeoutStopSec = 86400
diff --git a/examples/public-inbox-nntps.socket b/examples/public-inbox-nntps.socket
deleted file mode 100644
index fa678196..00000000
--- a/examples/public-inbox-nntps.socket
+++ /dev/null
@@ -1,12 +0,0 @@
-# ==> /etc/systemd/system/public-inbox-nntps.socket <==
-[Unit]
-Description = public-inbox-nntps socket
-
-[Socket]
-ListenStream = 0.0.0.0:563
-BindIPv6Only = ipv6-only
-ListenStream = [::]:563
-Service = public-inbox-nntpd@1.service
-
-[Install]
-WantedBy = sockets.target
diff --git a/examples/public-inbox-watch.service b/examples/public-inbox-watch.service
index abb41469..0e4860f7 100644
--- a/examples/public-inbox-watch.service
+++ b/examples/public-inbox-watch.service
@@ -9,8 +9,6 @@ Environment = PI_CONFIG=/home/pi/.public-inbox/config \
 PATH=/usr/local/bin:/usr/bin:/bin
 ExecStart = /usr/local/bin/public-inbox-watch
 
-StandardOutput = syslog
-StandardError = syslog
 ExecReload = /bin/kill -HUP $MAINPID
 # this user must have read access to Maildirs it watches
 User = pi
diff --git a/examples/unsubscribe-milter@.service b/examples/unsubscribe-milter@.service
index eb5dcbe4..a68e6e81 100644
--- a/examples/unsubscribe-milter@.service
+++ b/examples/unsubscribe-milter@.service
@@ -24,7 +24,13 @@ Sockets = unsubscribe-milter.socket
 
 # the corresponding PSGI app needs permissions to modify the
 # mlmmj spool, so we might as well use the same user since
+# they both need to read /home/mlmmj/.unsubscribe.key
 User = mlmmj
 
+# only kill the parent process when using the default Sendmail::PMilter
+# postfork dispatcher, children will die naturally when they're done
+# with a given message.
+KillMode = process
+
 [Install]
 WantedBy = multi-user.target
diff --git a/examples/unsubscribe.milter b/examples/unsubscribe.milter
index 216b0ddd..8c682012 100644
--- a/examples/unsubscribe.milter
+++ b/examples/unsubscribe.milter
@@ -27,6 +27,28 @@ my $crypt = Crypt::CBC->new(-key => $key,
                         -cipher => 'Blowfish');
 $fh = $iv = $key = undef;
 
+my $allow_domains = '/etc/unsubscribe-milter.allow_domains';
+my $ALLOW_DOMAINS;
+if (open my $fh, '<', $allow_domains) {
+        local $/ = "\n";
+        chomp(my @l = <$fh>);
+        die "close: $!" unless eof($fh) && close($fh);
+        my %l = map { lc($_) => 1 } @l;
+        $ALLOW_DOMAINS = \%l;
+} else {
+        warn <<EOM;
+W: open $allow_domains: $! (all domains allowed)
+W: all mlmmj-looking messages will have List-Unsubscribe added,
+W: this is probably not what you want.
+EOM
+}
+
+# only allow users hitting SMTP server locally:
+# Is a config file necessary?  Regexps are ugly for IP addresses
+# but Net::Patricia (or similar) seems like overkill.  Ugly it is:
+my @ALLOW_ADDR = (qr/\A::1\z/, qr/\A127\./);
+my $ALLOW_ADDR = join('|', @ALLOW_ADDR);
+
 my %cbs;
 $cbs{connect} = sub {
         my ($ctx) = @_;
@@ -88,10 +110,24 @@ $cbs{eom} = sub {
         eval {
                 my $priv = $ctx->getpriv;
                 $ctx->setpriv({ header => {}, envrcpt => {} });
-                my @rcpt = keys %{$priv->{envrcpt}};
+
+                # XXX my postfix (3.5.18-0+deb11u1) + Sendmail::PMilter
+                # instance doesn't seem to get {client_addr}, but
+                # {daemon_addr} seems to make sense since I only want it
+                # to apply to users connecting to postfix locally:
+                if ($ALLOW_ADDR) {
+                        my $x = $ctx->getsymval('{daemon_addr}');
+                        return SMFIS_CONTINUE if $x && $x !~ /$ALLOW_ADDR/;
+                }
 
                 # one recipient, one unique HTTP(S) URL
+                my @rcpt = keys %{$priv->{envrcpt}};
                 return SMFIS_CONTINUE if @rcpt != 1;
+                if ($ALLOW_DOMAINS) {
+                        my $addr = $ctx->getsymval('{mail_addr}');
+                        my (undef, $d) = split /\@/, $addr;
+                        return SMFIS_CONTINUE if !$ALLOW_DOMAINS->{$d};
+                }
                 return SMFIS_CONTINUE if archive_addr(lc($rcpt[0]));
 
                 my $unsub = $priv->{header}->{'list-unsubscribe'} || [];
diff --git a/examples/varnish-4.vcl b/examples/varnish-4.vcl
index 5fc202ed..624f6013 100644
--- a/examples/varnish-4.vcl
+++ b/examples/varnish-4.vcl
@@ -28,7 +28,7 @@ sub vcl_recv {
 }
 
 sub vcl_pipe {
-        # By default Connection: close is set on all piped requests by varnish,
+        # By default, Connection: close is set on all piped requests by varnish,
         # but public-inbox-httpd supports persistent connections well :)
         unset bereq.http.connection;
         return (pipe);