Update README.html. main
authorMatt Birkholz <matt@birchwood-abbey.net>
Sun, 23 Nov 2025 22:28:40 +0000 (15:28 -0700)
committerMatt Birkholz <matt@birchwood-abbey.net>
Sun, 23 Nov 2025 22:28:40 +0000 (15:28 -0700)
README.html

index 11f0bd5df42feb0ab321422b91ee8db03e96070e..2a72a89f98cce8a369f28ff162c23357739d0036 100644 (file)
@@ -3,7 +3,7 @@
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
 <head>
-<!-- 2025-09-18 Thu 17:59 -->
+<!-- 2025-11-23 Sun 15:25 -->
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1" />
 <title>A Small Institute</title>
@@ -24,8 +24,8 @@ an expendable public face (easily wiped clean) while maintaining a
 secure and private campus that can function with or without the
 Internet.
 </p>
-<div id="outline-container-org05da664" class="outline-2">
-<h2 id="org05da664"><span class="section-number-2">1.</span> Overview</h2>
+<div id="outline-container-org09c78f0" class="outline-2">
+<h2 id="org09c78f0"><span class="section-number-2">1.</span> Overview</h2>
 <div class="outline-text-2" id="text-1">
 <p>
 This small institute has a public server on the Internet, Front, that
@@ -48,7 +48,7 @@ connects to Front making the institute email, cloud, etc. available to
 members off campus.
 </p>
 
-<pre class="example" id="org12c31c3">
+<pre class="example" id="org2176ef1">
                 =                                                   
               _|||_                                                 
         =-The-Institute-=                                           
@@ -95,8 +95,8 @@ uses OpenPGP encryption to secure message content.
 </p>
 </div>
 </div>
-<div id="outline-container-orge711bbd" class="outline-2">
-<h2 id="orge711bbd"><span class="section-number-2">2.</span> Caveats</h2>
+<div id="outline-container-orgd1e8425" class="outline-2">
+<h2 id="orgd1e8425"><span class="section-number-2">2.</span> Caveats</h2>
 <div class="outline-text-2" id="text-2">
 <p>
 This small institute prizes its privacy, so there is little or no
@@ -144,8 +144,8 @@ month) because of this assumption.
 </p>
 </div>
 </div>
-<div id="outline-container-org5822d39" class="outline-2">
-<h2 id="org5822d39"><span class="section-number-2">3.</span> The Services</h2>
+<div id="outline-container-org51fc37e" class="outline-2">
+<h2 id="org51fc37e"><span class="section-number-2">3.</span> The Services</h2>
 <div class="outline-text-2" id="text-3">
 <p>
 The small institute's network is designed to provide a number of
@@ -157,8 +157,8 @@ policies.  On first reading, those subsections should be skipped; they
 reference particulars first introduced in the following chapter.
 </p>
 </div>
-<div id="outline-container-orgda548cf" class="outline-3">
-<h3 id="orgda548cf"><span class="section-number-3">3.1.</span> The Name Service</h3>
+<div id="outline-container-org2ae4fc9" class="outline-3">
+<h3 id="org2ae4fc9"><span class="section-number-3">3.1.</span> The Name Service</h3>
 <div class="outline-text-3" id="text-3-1">
 <p>
 The institute has a public domain, e.g. <code>small.example.org</code>, and a
@@ -172,8 +172,8 @@ names like <code>core</code>.
 </p>
 </div>
 </div>
-<div id="outline-container-orgff9aad1" class="outline-3">
-<h3 id="orgff9aad1"><span class="section-number-3">3.2.</span> The Email Service</h3>
+<div id="outline-container-org9ef70e4" class="outline-3">
+<h3 id="org9ef70e4"><span class="section-number-3">3.2.</span> The Email Service</h3>
 <div class="outline-text-3" id="text-3-2">
 <p>
 Front provides the public SMTP (Simple Mail Transfer Protocol) service
@@ -247,8 +247,8 @@ setting for the maximum message size is given in a code block labeled
 configurations wherever <code>&lt;&lt;postfix-message-size&gt;&gt;</code> appears.
 </p>
 </div>
-<div id="outline-container-org178261e" class="outline-4">
-<h4 id="org178261e"><span class="section-number-4">3.2.1.</span> The Postfix Configurations</h4>
+<div id="outline-container-org9ae381e" class="outline-4">
+<h4 id="org9ae381e"><span class="section-number-4">3.2.1.</span> The Postfix Configurations</h4>
 <div class="outline-text-4" id="text-3-2-1">
 <p>
 The institute aims to accommodate encrypted email containing short
@@ -263,7 +263,7 @@ handle maxi-messages.
 </p>
 
 <div class="org-src-container">
-<code>postfix-message-size</code><pre class="src src-conf" id="org4c70dd8"><code>- { p: message_size_limit, v: 104857600 }
+<code>postfix-message-size</code><pre class="src src-conf" id="org9020215"><code>- { p: message_size_limit, v: 104857600 }
 </code></pre>
 </div>
 
@@ -278,7 +278,7 @@ re-sending the bounce (or just grabbing the go-bag!).
 </p>
 
 <div class="org-src-container">
-<code>postfix-queue-times</code><pre class="src src-conf" id="org51033b5"><code>- { p: delay_warning_time, v: 1h }
+<code>postfix-queue-times</code><pre class="src src-conf" id="org85d4f75"><code>- { p: delay_warning_time, v: 1h }
 - { p: maximal_queue_lifetime, v: 4h }
 - { p: bounce_queue_lifetime, v: 4h }
 </code></pre>
@@ -292,7 +292,7 @@ disables relaying (other than for the local networks).
 </p>
 
 <div class="org-src-container">
-<code>postfix-relaying</code><pre class="src src-conf" id="org1a0b527"><code>- p: smtpd_relay_restrictions
+<code>postfix-relaying</code><pre class="src src-conf" id="org7c0674e"><code>- p: smtpd_relay_restrictions
   v: permit_mynetworks reject_unauth_destination
 </code></pre>
 </div>
@@ -304,7 +304,7 @@ effect.
 </p>
 
 <div class="org-src-container">
-<code>postfix-maildir</code><pre class="src src-conf" id="org6695ef5"><code>- { p: home_mailbox, v: Maildir/ }
+<code>postfix-maildir</code><pre class="src src-conf" id="org5c0a414"><code>- { p: home_mailbox, v: Maildir/ }
 </code></pre>
 </div>
 
@@ -315,8 +315,8 @@ in the respective roles below.
 </p>
 </div>
 </div>
-<div id="outline-container-org0376ca6" class="outline-4">
-<h4 id="org0376ca6"><span class="section-number-4">3.2.2.</span> The Dovecot Configurations</h4>
+<div id="outline-container-orgbda22e0" class="outline-4">
+<h4 id="orgbda22e0"><span class="section-number-4">3.2.2.</span> The Dovecot Configurations</h4>
 <div class="outline-text-4" id="text-3-2-2">
 <p>
 The Dovecot settings on both Front and Core disable POP and require
@@ -330,7 +330,7 @@ The official documentation for Dovecot once was a Wiki but now is
 </p>
 
 <div class="org-src-container">
-<code>dovecot-tls</code><pre class="src src-conf" id="org047115a"><code><span class="org-variable-name">protocols</span> = imap
+<code>dovecot-tls</code><pre class="src src-conf" id="org3789883"><code><span class="org-variable-name">protocols</span> = imap
 <span class="org-variable-name">ssl</span> = required
 </code></pre>
 </div>
@@ -342,7 +342,7 @@ configuration keeps them from even listening at the IMAP port
 </p>
 
 <div class="org-src-container">
-<code>dovecot-ports</code><pre class="src src-conf" id="org594736d"><code><span class="org-type">service imap-login</span> {
+<code>dovecot-ports</code><pre class="src src-conf" id="org34b4ac7"><code><span class="org-type">service imap-login</span> {
   <span class="org-type">inet_listener imap</span> {
     <span class="org-variable-name">port</span> = 0
   }
@@ -356,7 +356,7 @@ directories.
 </p>
 
 <div class="org-src-container">
-<code>dovecot-maildir</code><pre class="src src-conf" id="org533a1b3"><code><span class="org-variable-name">mail_location</span> = maildir:~/Maildir
+<code>dovecot-maildir</code><pre class="src src-conf" id="orgbe991c0"><code><span class="org-variable-name">mail_location</span> = maildir:~/Maildir
 </code></pre>
 </div>
 
@@ -368,15 +368,15 @@ common settings with host specific settings for <code>ssl_cert</code> and
 </div>
 </div>
 </div>
-<div id="outline-container-org5da08ea" class="outline-3">
-<h3 id="org5da08ea"><span class="section-number-3">3.3.</span> The Web Services</h3>
+<div id="outline-container-org7434abb" class="outline-3">
+<h3 id="org7434abb"><span class="section-number-3">3.3.</span> The Web Services</h3>
 <div class="outline-text-3" id="text-3-3">
 <p>
 Front provides the public HTTP service that serves institute web pages
 at e.g. <code>https://small.example.org/</code>.  The small institute initially
 runs with a self-signed, "snake oil" server certificate, causing
 browsers to warn of possible fraud, but this certificate is easily
-replaced by one signed by a recognized authority, as discussed in <a href="#org3fbcb30">The
+replaced by one signed by a recognized authority, as discussed in <a href="#org3b1843c">The
 Front Role</a>.
 </p>
 
@@ -431,15 +431,15 @@ will automatically wipe it within 15 minutes.
 </p>
 </div>
 </div>
-<div id="outline-container-orgdabefd7" class="outline-3">
-<h3 id="orgdabefd7"><span class="section-number-3">3.4.</span> The Cloud Service</h3>
+<div id="outline-container-org44dc60e" class="outline-3">
+<h3 id="org44dc60e"><span class="section-number-3">3.4.</span> The Cloud Service</h3>
 <div class="outline-text-3" id="text-3-4">
 <p>
 Core runs Nextcloud to provide a private institute cloud at
 <code>https://core.small.private/nextcloud/</code>.  It is managed manually per
-<a href="https://docs.nextcloud.com/server/latest/admin_manual/">The Nextcloud Server Administration Guide</a>.  The code <i>and</i> data,
+<a href="https://docs.nextcloud.com/server/stable/admin_manual/">The Nextcloud Server Administration Guide</a>.  The code <i>and</i> data,
 including especially database dumps, are stored in <q>/Nextcloud/</q> which
-is included in Core's backup procedure as described in <a href="#org2ceac0b">Backups</a>.  The
+is included in Core's backup procedure as described in <a href="#orgc4a221a">Backups</a>.  The
 default Apache2 configuration expects to find the web scripts in
 <q>/var/www/nextcloud/</q>, so the institute symbolically links this to
 <q>/Nextcloud/nextcloud/</q>.
@@ -453,15 +453,15 @@ private network.
 </p>
 </div>
 </div>
-<div id="outline-container-org56d4f14" class="outline-3">
-<h3 id="org56d4f14"><span class="section-number-3">3.5.</span> Accounts</h3>
+<div id="outline-container-org6cfe96b" class="outline-3">
+<h3 id="org6cfe96b"><span class="section-number-3">3.5.</span> Accounts</h3>
 <div class="outline-text-3" id="text-3-5">
 <p>
 A small institute has just a handful of members.  For simplicity (and
 thus security) static configuration files are preferred over complex
 account management systems, LDAP, Active Directory, and the like.  The
 Ansible scripts configure the same set of user accounts on Core and
-Front.  <a href="#org79b145a">The Institute Commands</a> (e.g. <code>./inst new dick</code>) capture the
+Front.  <a href="#org85c4d59">The Institute Commands</a> (e.g. <code>./inst new dick</code>) capture the
 processes of enrolling, modifying and retiring members of the
 institute.  They update the administrator's membership roll, and run
 Ansible to create (and disable) accounts on Core, Front, Nextcloud,
@@ -476,8 +476,8 @@ accomplished via the campus cloud and the resulting desktop files can
 all be private (readable and writable only by the owner) by default.
 </p>
 </div>
-<div id="outline-container-org323d3a2" class="outline-4">
-<h4 id="org323d3a2"><span class="section-number-4">3.5.1.</span> The Administration Accounts</h4>
+<div id="outline-container-orge6d4f31" class="outline-4">
+<h4 id="orge6d4f31"><span class="section-number-4">3.5.1.</span> The Administration Accounts</h4>
 <div class="outline-text-4" id="text-3-5-1">
 <p>
 The institute avoids the use of the <code>root</code> account (<code>uid 0</code>) because
@@ -486,21 +486,21 @@ command is used to consciously (conscientiously!) run specific scripts
 and programs as <code>root</code>.  When installation of a Debian OS leaves the
 host with no user accounts, just the <code>root</code> account, the next step is
 to create a system administrator's account named <code>sysadm</code> and to give
-it permission to use the <code>sudo</code> command (e.g. as described in <a href="#orge899b2b">The
+it permission to use the <code>sudo</code> command (e.g. as described in <a href="#orga8651ea">The
 Front Machine</a>).  When installation prompts for the name of an
 initial, privileged user account the same name is given (e.g. as
-described in <a href="#org4d61626">The Core Machine</a>).  Installation may <i>not</i> prompt and
+described in <a href="#org641fa41">The Core Machine</a>).  Installation may <i>not</i> prompt and
 still create an initial user account with a distribution specific name
 (e.g. <code>pi</code>).  Any name can be used as long as it is provided as the
 value of <code>ansible_user</code> in <a href="hosts"><q>hosts</q></a>.  Its password is specified by a
 vault-encrypted variable in the <a href="Secret/become.yml"><q>Secret/become.yml</q></a> file.  (The
-<a href="hosts"><q>hosts</q></a> and <a href="Secret/become.yml"><q>Secret/become.yml</q></a> files are described in <a href="#orgb216592">The Ansible
+<a href="hosts"><q>hosts</q></a> and <a href="Secret/become.yml"><q>Secret/become.yml</q></a> files are described in <a href="#orgc259203">The Ansible
 Configuration</a>.)
 </p>
 </div>
 </div>
-<div id="outline-container-org3dabc90" class="outline-4">
-<h4 id="org3dabc90"><span class="section-number-4">3.5.2.</span> The Monkey Accounts</h4>
+<div id="outline-container-org9efe027" class="outline-4">
+<h4 id="org9efe027"><span class="section-number-4">3.5.2.</span> The Monkey Accounts</h4>
 <div class="outline-text-4" id="text-3-5-2">
 <p>
 The institute's Core uses a special account named <code>monkey</code> to run
@@ -511,8 +511,8 @@ account is created on Front as well.
 </div>
 </div>
 </div>
-<div id="outline-container-orgaf187bb" class="outline-3">
-<h3 id="orgaf187bb"><span class="section-number-3">3.6.</span> Keys</h3>
+<div id="outline-container-orga69013c" class="outline-3">
+<h3 id="orga69013c"><span class="section-number-3">3.6.</span> Keys</h3>
 <div class="outline-text-3" id="text-3-6">
 <p>
 The institute keeps its "master secrets" in an encrypted
@@ -597,8 +597,8 @@ the administrator's password keep, to install a new SSH key.
 </p>
 </div>
 </div>
-<div id="outline-container-org2ceac0b" class="outline-3">
-<h3 id="org2ceac0b"><span class="section-number-3">3.7.</span> Backups</h3>
+<div id="outline-container-orgc4a221a" class="outline-3">
+<h3 id="orgc4a221a"><span class="section-number-3">3.7.</span> Backups</h3>
 <div class="outline-text-3" id="text-3-7">
 <p>
 The small institute backs up its data, but not so much so that nothing
@@ -634,7 +634,7 @@ files mentioned in the Nextcloud database dump).
 </p>
 
 <div class="org-src-container">
-<a href="private/backup"><q>private/backup</q></a><pre class="src src-sh" id="org2d7d21c"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
+<a href="private/backup"><q>private/backup</q></a><pre class="src src-sh" id="orgdb96821"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
 </span><span class="org-comment-delimiter">#</span><span class="org-comment">
 </span><span class="org-comment-delimiter"># </span><span class="org-comment">DO NOT EDIT.
 </span><span class="org-comment-delimiter">#</span><span class="org-comment">
@@ -738,8 +738,8 @@ finish
 </div>
 </div>
 </div>
-<div id="outline-container-orgc0dda53" class="outline-2">
-<h2 id="orgc0dda53"><span class="section-number-2">4.</span> The Particulars</h2>
+<div id="outline-container-orgd083684" class="outline-2">
+<h2 id="orgd083684"><span class="section-number-2">4.</span> The Particulars</h2>
 <div class="outline-text-2" id="text-4">
 <p>
 This chapter introduces Ansible variables intended to simplify
@@ -751,13 +751,13 @@ stored in separate files: <a href="public/vars.yml"><q>public/vars.yml</q></a> a
 
 <p>
 The example settings in this document configure VirtualBox VMs as
-described in the <a href="#org903c8fd">Testing</a> chapter.  For more information about how a
+described in the <a href="#org4ca9f6d">Testing</a> chapter.  For more information about how a
 small institute turns the example Ansible code into a working Ansible
-configuration, see chapter <a href="#orgb216592">The Ansible Configuration</a>.
+configuration, see chapter <a href="#orgc259203">The Ansible Configuration</a>.
 </p>
 </div>
-<div id="outline-container-orgee49a43" class="outline-3">
-<h3 id="orgee49a43"><span class="section-number-3">4.1.</span> Generic Particulars</h3>
+<div id="outline-container-orgaf793e2" class="outline-3">
+<h3 id="orgaf793e2"><span class="section-number-3">4.1.</span> Generic Particulars</h3>
 <div class="outline-text-3" id="text-4-1">
 <p>
 The small institute's domain name is used quite frequently in the
@@ -796,8 +796,8 @@ domain_priv: small.private
 </div>
 </div>
 </div>
-<div id="outline-container-orgcda4063" class="outline-3">
-<h3 id="orgcda4063"><span class="section-number-3">4.2.</span> Subnets</h3>
+<div id="outline-container-org7d27a48" class="outline-3">
+<h3 id="org7d27a48"><span class="section-number-3">4.2.</span> Subnets</h3>
 <div class="outline-text-3" id="text-4-2">
 <p>
 The small institute uses a private Ethernet, two VPNs, and a "wild",
@@ -897,7 +897,7 @@ example result follows the code.
 </code></pre>
 </div>
 
-<div class="TEXT" id="org8910fe3">
+<div class="TEXT" id="org3ebf7ce">
 <p>
 =&gt; 10.62.17.0/24
 </p>
@@ -910,7 +910,7 @@ code block below.  The small institute treats these addresses as
 sensitive information so again the code block below "tangles" into
 <a href="private/vars.yml"><q>private/vars.yml</q></a> rather than <a href="public/vars.yml"><q>public/vars.yml</q></a>.  Two of the
 addresses are in <code>192.168</code> subnets because they are part of a test
-configuration using mostly-default VirtualBoxes (described <a href="#org903c8fd">here</a>).
+configuration using mostly-default VirtualBoxes (described <a href="#org4ca9f6d">here</a>).
 </p>
 
 <div class="org-src-container">
@@ -931,7 +931,7 @@ e.g. <code>_net_and_mask</code> rather than <code>_net_cidr</code>.
 </p>
 
 <div class="org-src-container">
-<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>private_net:
+<code>network-vars</code><pre class="src src-conf" id="org5e2b681"><code>private_net:
            <span class="org-string">"{{ private_net_cidr | ansible.utils.ipaddr('network') }}"</span>
 private_net_mask:
            <span class="org-string">"{{ private_net_cidr | ansible.utils.ipaddr('netmask') }}"</span>
@@ -953,10 +953,18 @@ campus_wg_net:
 campus_wg_net_mask:
          <span class="org-string">"{{ campus_wg_net_cidr | ansible.utils.ipaddr('netmask') }}"</span>
 campus_wg_net_and_mask:
-    <span class="org-string">"{{ campus_wg_net }} {{ campus_wg_net_mask }}"</span>
+                       <span class="org-string">"{{ campus_wg_net }} {{ campus_wg_net_mask }}"</span>
 </code></pre>
 </div>
 
+<p>
+This is obvious, site-independent, non-private boilerplate and so goes
+in a <q>defaults/main.yml</q> file in each role.  The variables can then be
+overridden by adding them to the site-specific <a href="private/vars.yml"><q>private/vars.yml</q></a>.
+The block is referenced with <code>&lt;&lt;network-vars&gt;&gt;</code> and tangled into each
+role's <q>defaults/main.yml</q> file.
+</p>
+
 <p>
 The institute prefers to configure its services with IP addresses
 rather than domain names, and one of the most important for secure and
@@ -965,35 +973,33 @@ the institute's Internet domain name.
 </p>
 
 <div class="org-src-container">
-<a href="public/vars.yml"><q>public/vars.yml</q></a><pre class="src src-conf"><code>front_addr: 192.168.15.5
+<a href="public/vars.yml"><q>public/vars.yml</q></a><pre class="src src-conf"><code>front_addr: 192.168.15.4
 </code></pre>
 </div>
 
 <p>
 The example address is a private network address because the example
 configuration is intended to run in a test jig made up of VirtualBox
-virtual machines and networks, and the VirtualBox user manual uses
-<code>192.168.15.0</code> in its example configuration of a "NAT Network"
-(simulating Front's ISP's network).
+virtual machines and networks.
 </p>
 
 <p>
-Finally, four host addresses are needed frequently in the Ansible
-code.  The first two are Core's and Gate's addresses on the private
-Ethernet.  The other two are Gate's and the campus Wi-Fi's addresses
-on the wild Ethernet.  The following code block chooses host 1 for
-Core and host 2 for Gate on the private Ethernet.  On the wild
-Ethernet, host 1 is Gate and host 2 is the access point (or wired
-IoT appliance).
+Finally, five host addresses are needed frequently in the Ansible
+code.  Each is made available in both CIDR and IPv4 address formats.
+Again this is site-independent, non-private boilerplate referenced
+with <code>address-vars</code> in the <q>default/main.yml</q> files.
 </p>
 
 <div class="org-src-container">
-<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>core_addr_cidr:  <span class="org-string">"{{ private_net_cidr | ansible.utils.ipaddr('1') }}"</span>
+<code>address-vars</code><pre class="src src-conf" id="orga27551d"><code>
+core_addr_cidr:  <span class="org-string">"{{ private_net_cidr | ansible.utils.ipaddr('1') }}"</span>
 gate_addr_cidr:  <span class="org-string">"{{ private_net_cidr | ansible.utils.ipaddr('2') }}"</span>
 gate_wild_addr_cidr:
                     <span class="org-string">"{{ wild_net_cidr | ansible.utils.ipaddr('1') }}"</span>
 front_wg_addr_cidr:
                <span class="org-string">"{{ public_wg_net_cidr | ansible.utils.ipaddr('1') }}"</span>
+core_wg_addr_cidr:
+               <span class="org-string">"{{ public_wg_net_cidr | ansible.utils.ipaddr('2') }}"</span>
 
 core_addr:   <span class="org-string">"{{ core_addr_cidr | ansible.utils.ipaddr('address') }}"</span>
 gate_addr:   <span class="org-string">"{{ gate_addr_cidr | ansible.utils.ipaddr('address') }}"</span>
@@ -1001,24 +1007,26 @@ gate_wild_addr:
         <span class="org-string">"{{ gate_wild_addr_cidr | ansible.utils.ipaddr('address') }}"</span>
 front_wg_addr:
          <span class="org-string">"{{ front_wg_addr_cidr | ansible.utils.ipaddr('address') }}"</span>
+core_wg_addr:
+          <span class="org-string">"{{ core_wg_addr_cidr | ansible.utils.ipaddr('address') }}"</span>
 </code></pre>
 </div>
 </div>
 </div>
 </div>
-<div id="outline-container-org6a2440c" class="outline-2">
-<h2 id="org6a2440c"><span class="section-number-2">5.</span> The Hardware</h2>
+<div id="outline-container-orgef4d876" class="outline-2">
+<h2 id="orgef4d876"><span class="section-number-2">5.</span> The Hardware</h2>
 <div class="outline-text-2" id="text-5">
 <p>
 The small institute's network was built by its system administrator
 using Ansible on a trusted notebook.  The Ansible configuration and
 scripts were generated by "tangling" the Ansible code included here.
-(<a href="#orgb216592">The Ansible Configuration</a> describes how to do this.)  The following
+(<a href="#orgc259203">The Ansible Configuration</a> describes how to do this.)  The following
 sections describe how Front, Gate and Core were prepared for Ansible.
 </p>
 </div>
-<div id="outline-container-orge899b2b" class="outline-3">
-<h3 id="orge899b2b"><span class="section-number-3">5.1.</span> The Front Machine</h3>
+<div id="outline-container-orga8651ea" class="outline-3">
+<h3 id="orga8651ea"><span class="section-number-3">5.1.</span> The Front Machine</h3>
 <div class="outline-text-3" id="text-5-1">
 <p>
 Front is the small institute's public facing server, a virtual machine
@@ -1031,8 +1039,8 @@ possible to quickly re-provision a new Front machine from a frontier
 Internet café using just the administrator's notebook.
 </p>
 </div>
-<div id="outline-container-org47d5cd9" class="outline-4">
-<h4 id="org47d5cd9"><span class="section-number-4">5.1.1.</span> A Digital Ocean Droplet</h4>
+<div id="outline-container-orgecc72e6" class="outline-4">
+<h4 id="orgecc72e6"><span class="section-number-4">5.1.1.</span> A Digital Ocean Droplet</h4>
 <div class="outline-text-4" id="text-5-1-1">
 <p>
 The following example prepared a new front on a Digital Ocean droplet.
@@ -1056,7 +1064,7 @@ root@ubuntu#
 <p>
 The freshly created Digital Ocean droplet came with just one account,
 <code>root</code>, but the small institute avoids remote access to the "super
-user" account (per the policy in <a href="#org323d3a2">The Administration Accounts</a>), so the
+user" account (per the policy in <a href="#orge6d4f31">The Administration Accounts</a>), so the
 administrator created a <code>sysadm</code> account with the ability to request
 escalated privileges via the <code>sudo</code> command.
 </p>
@@ -1079,7 +1087,7 @@ notebook$
 The password was generated by <code>gpw</code>, saved in the administrator's
 password keep, and later added to <a href="Secret/become.yml"><q>Secret/become.yml</q></a> as shown below.
 (Producing a working Ansible configuration with <a href="Secret/become.yml"><q>Secret/become.yml</q></a>
-file is described in <a href="#orgb216592">The Ansible Configuration</a>.)
+file is described in <a href="#orgc259203">The Ansible Configuration</a>.)
 </p>
 
 <pre class="example">
@@ -1093,7 +1101,7 @@ notebook_     &gt;&gt;Secret/become.yml
 <p>
 After creating the <code>sysadm</code> account on the droplet, the administrator
 concatenated a personal public ssh key and the key found in
-<a href="Secret/ssh_admin/"><q>Secret/ssh_admin/</q></a> (created by <a href="#org274b741">The CA Command</a>) into an <q>admin_keys</q>
+<a href="Secret/ssh_admin/"><q>Secret/ssh_admin/</q></a> (created by <a href="#org49e2120">The CA Command</a>) into an <q>admin_keys</q>
 file, copied it to the droplet, and installed it as the
 <q>authorized_keys</q> for <code>sysadm</code>.
 </p>
@@ -1137,27 +1145,50 @@ notebook$ ssh-keygen -f ~/.ssh/known_hosts -R 159.65.75.60
 </pre>
 
 <p>
-The last command removes the old host key from the administrator's
-<q>known_hosts</q> file.  The next SSH connection should ask to confirm the
-new host identity.
+The last command removed the old host key from the administrator's
+<q>known_hosts</q> file.  The next few commands served to test
+password-less login as well as the privilege escalation command
+<code>sudo</code>.
 </p>
 
 <p>
-The administrator then tested the password-less ssh login as well as
-the privilege escalation command.
+The Droplet needed a couple additional software packages immediately.
+The <code>wireguard</code> package was needed to generate the Droplet's private
+key.  The <code>systemd-resolved</code> package was installed so that the
+subsequent reboot gets ResolveD configured properly (else <code>resolvectl</code>
+hangs, causing <code>wg-quick@wg0</code> to hang&hellip;).  The rest are included just
+to speed up (re)testing of "prepared" test machines, e.g. prepared as
+described in <a href="#org675a9cc">The Test Front Machine</a>.
 </p>
 
 <pre class="example">
 notebook$ ssh sysadm@159.65.75.60
-sysadm@ubuntu$ sudo head -1 /etc/shadow
-[sudo] password for sysadm:
-root:*:18355:0:99999:7:::
+sysadm@ubuntu$ sudo apt install wireguard systemd-resolved \
+    unattended-upgrades postfix dovecot-imapd rsync apache2 kamailio
+</pre>
+
+<p>
+With WireGuard™ installed, the following commands generated a new
+private key, and displayed its public key.
+</p>
+
+<pre class="example">
+sysadm@ubuntu$ umask 077
+susadm@ubuntu$ wg genkey \
+sysadm@ubuntu_ | sudo tee /etc/wireguard/private-key \
+sysadm@ubuntu_ | wg pubkey
+S+6HaTnOwwhWgUGXjSBcPAvifKw+j8BDTRfq534gNW4=
 </pre>
 
 <p>
-<i>After</i> passing the above test, the administrator disabled root logins
-on the droplet.  The last command below tested that root logins were
-indeed denied.
+The public key is copied and pasted into <a href="private/vars.yml"><q>private/vars.yml</q></a> as the
+value of <code>front_wg_pubkey</code> (as in the example <a href="#org4c3c04f">here</a>).
+</p>
+
+<p>
+<i>After</i> collecting Front's public key, the administrator disabled root
+logins on the droplet.  The last command below tested that root logins
+were indeed denied.
 </p>
 
 <pre class="example">
@@ -1177,8 +1208,8 @@ address.
 </div>
 </div>
 </div>
-<div id="outline-container-org4d61626" class="outline-3">
-<h3 id="org4d61626"><span class="section-number-3">5.2.</span> The Core Machine</h3>
+<div id="outline-container-org641fa41" class="outline-3">
+<h3 id="org641fa41"><span class="section-number-3">5.2.</span> The Core Machine</h3>
 <div class="outline-text-3" id="text-5-2">
 <p>
 Core is the small institute's private file, email, cloud and whatnot
@@ -1202,7 +1233,7 @@ The following example prepared a new core on a PC with Debian 11
 freshly installed.  During installation, the machine was named <code>core</code>,
 no desktop or server software was installed, no root password was set,
 and a privileged account named <code>sysadm</code> was created (per the policy in
-<a href="#org323d3a2">The Administration Accounts</a>).
+<a href="#orge6d4f31">The Administration Accounts</a>).
 </p>
 
 <pre class="example">
@@ -1218,7 +1249,7 @@ Is the information correct? [Y/n]
 The password was generated by <code>gpw</code>, saved in the administrator's
 password keep, and later added to <a href="Secret/become.yml"><q>Secret/become.yml</q></a> as shown below.
 (Producing a working Ansible configuration with <a href="Secret/become.yml"><q>Secret/become.yml</q></a>
-file is described in <a href="#orgb216592">The Ansible Configuration</a>.)
+file is described in <a href="#orgc259203">The Ansible Configuration</a>.)
 </p>
 
 <pre class="example">
@@ -1236,10 +1267,9 @@ modem and installed them as shown below.
 </p>
 
 <pre class="example">
-$ sudo apt install netplan.io systemd-resolved unattended-upgrades \
-_                  chrony isc-dhcp-server bind9 apache2 wireguard \
-_                  postfix dovecot-imapd fetchmail expect rsync \
-_                  gnupg openssh-server
+$ sudo apt install wireguard systemd-resolved unattended-upgrades \
+_                  chrony isc-dhcp-server bind9 apache2 postfix \
+_                  dovecot-imapd fetchmail rsync gnupg
 </pre>
 
 <p>
@@ -1268,7 +1298,7 @@ final configuration "in position" (on a frontier).
 <pre class="example">
 $ sudo apt install mariadb-server php php-{apcu,bcmath,curl,gd,gmp}\
 _                  php-{json,mysql,mbstring,intl,imagick,xml,zip} \
-_                  libapache2-mod-php
+_                  imagemagick libapache2-mod-php
 </pre>
 
 <p>
@@ -1283,7 +1313,7 @@ _                  nagios-nrpe-plugin
 
 <p>
 Next, the administrator concatenated a personal public ssh key and the
-key found in <a href="Secret/ssh_admin/"><q>Secret/ssh_admin/</q></a> (created by <a href="#org274b741">The CA Command</a>) into an
+key found in <a href="Secret/ssh_admin/"><q>Secret/ssh_admin/</q></a> (created by <a href="#org49e2120">The CA Command</a>) into an
 <q>admin_keys</q> file, copied it to Core, and installed it as the
 <q>authorized_keys</q> for <code>sysadm</code>.
 </p>
@@ -1323,7 +1353,7 @@ a new, private IP address and a default route.
 <p>
 In the example command lines below, the address <code>10.227.248.1</code> was
 generated by the random subnet address picking procedure described in
-<a href="#orgcda4063">Subnets</a>, and is named <code>core_addr</code> in the Ansible code.  The second
+<a href="#org7d27a48">Subnets</a>, and is named <code>core_addr</code> in the Ansible code.  The second
 address, <code>10.227.248.2</code>, is the corresponding address for Gate's
 Ethernet interface, and is named <code>gate_addr</code> in the Ansible
 code.
@@ -1339,8 +1369,8 @@ At this point Core was ready for provisioning with Ansible.
 </p>
 </div>
 </div>
-<div id="outline-container-orgd15e60d" class="outline-3">
-<h3 id="orgd15e60d"><span class="section-number-3">5.3.</span> The Gate Machine</h3>
+<div id="outline-container-orgcbbedd8" class="outline-3">
+<h3 id="orgcbbedd8"><span class="section-number-3">5.3.</span> The Gate Machine</h3>
 <div class="outline-text-3" id="text-5-3">
 <p>
 Gate is the small institute's route to the Internet, and the campus
@@ -1360,7 +1390,7 @@ modem, a USB port tethered to a phone, a wireless adapter
 connected to a campground Wi-Fi access point, etc.</li>
 </ol>
 
-<pre class="example" id="orga138a6e">
+<pre class="example" id="org64d9a76">
 =============== | ==================================================
                 |                                           Premises
           (Campus ISP)                                              
@@ -1373,8 +1403,8 @@ connected to a campground Wi-Fi access point, etc.</li>
                 +----Ethernet switch                                
 </pre>
 </div>
-<div id="outline-container-org3727c43" class="outline-4">
-<h4 id="org3727c43"><span class="section-number-4">5.3.1.</span> Alternate Gate Topology</h4>
+<div id="outline-container-org2dcd89c" class="outline-4">
+<h4 id="org2dcd89c"><span class="section-number-4">5.3.1.</span> Alternate Gate Topology</h4>
 <div class="outline-text-4" id="text-5-3-1">
 <p>
 While Gate and Core really need to be separate machines for security
@@ -1383,7 +1413,7 @@ This avoids the need for a second Wi-Fi access point and leads to the
 following topology.
 </p>
 
-<pre class="example" id="orgc471b07">
+<pre class="example" id="orga699029">
 =============== | ==================================================
                 |                                           Premises
            (House ISP)                                              
@@ -1407,12 +1437,12 @@ its Ethernet and Wi-Fi clients are allowed to communicate).
 </p>
 </div>
 </div>
-<div id="outline-container-org4338252" class="outline-4">
-<h4 id="org4338252"><span class="section-number-4">5.3.2.</span> Original Gate Topology</h4>
+<div id="outline-container-orgdb209f2" class="outline-4">
+<h4 id="orgdb209f2"><span class="section-number-4">5.3.2.</span> Original Gate Topology</h4>
 <div class="outline-text-4" id="text-5-3-2">
 <p>
 The Ansible code in this document is somewhat dependent on the
-physical network shown in the <a href="#org05da664">Overview</a> wherein Gate has three network
+physical network shown in the <a href="#org09c78f0">Overview</a> wherein Gate has three network
 interfaces.
 </p>
 
@@ -1421,7 +1451,7 @@ The following example prepared a new gate on a PC with Debian 11
 freshly installed.  During installation, the machine was named <code>gate</code>,
 no desktop or server software was installed, no root password was set,
 and a privileged account named <code>sysadm</code> was created (per the policy in
-<a href="#org323d3a2">The Administration Accounts</a>).
+<a href="#orge6d4f31">The Administration Accounts</a>).
 </p>
 
 <pre class="example">
@@ -1437,7 +1467,7 @@ Is the information correct? [Y/n]
 The password was generated by <code>gpw</code>, saved in the administrator's
 password keep, and later added to <a href="Secret/become.yml"><q>Secret/become.yml</q></a> as shown below.
 (Producing a working Ansible configuration with <a href="Secret/become.yml"><q>Secret/become.yml</q></a>
-file is described in <a href="#orgb216592">The Ansible Configuration</a>.)
+file is described in <a href="#orgc259203">The Ansible Configuration</a>.)
 </p>
 
 <pre class="example">
@@ -1455,9 +1485,9 @@ cable modem and installed them as shown below.
 </p>
 
 <pre class="example">
-$ sudo apt install netplan.io systemd-resolved unattended-upgrades \
-_                  ufw isc-dhcp-server postfix wireguard \
-_                  openssh-server
+$ sudo apt install systemd-resolved unattended-upgrades \
+_                  ufw postfix wireguard lm-sensors \
+_                  nagios-nrpe-server
 </pre>
 
 <p>
@@ -1469,7 +1499,7 @@ ready to proceed.
 
 <p>
 Next, the administrator concatenated a personal public ssh key and the
-key found in <a href="Secret/ssh_admin/"><q>Secret/ssh_admin/</q></a> (created by <a href="#org274b741">The CA Command</a>) into an
+key found in <a href="Secret/ssh_admin/"><q>Secret/ssh_admin/</q></a> (created by <a href="#org49e2120">The CA Command</a>) into an
 <q>admin_keys</q> file, copied it to Gate, and installed it as the
 <q>authorized_keys</q> for <code>sysadm</code>.
 </p>
@@ -1509,7 +1539,7 @@ a new, private IP address.
 <p>
 In the example command lines below, the address <code>10.227.248.2</code> was
 generated by the random subnet address picking procedure described in
-<a href="#orgcda4063">Subnets</a>, and is named <code>gate_addr</code> in the Ansible code.
+<a href="#org7d27a48">Subnets</a>, and is named <code>gate_addr</code> in the Ansible code.
 </p>
 
 <pre class="example">
@@ -1521,8 +1551,7 @@ Gate was also connected to the USB Ethernet dongles cabled to the
 campus Wi-Fi access point and the campus ISP and the values of three
 variables (<code>gate_lan_mac</code>, <code>gate_wild_mac</code>, and <code>gate_isp_mac</code> in
 <a href="private/vars.yml"><q>private/vars.yml</q></a>) match the actual hardware MAC addresses of the
-dongles.  (For more information, see the Gate role's <a href="#orgc350fb4">Configure Netplan</a>
-task.)
+dongles.  (For more information, see the tasks in section <a href="#orgdc2e00c">9.3</a>.)
 </p>
 
 <p>
@@ -1532,37 +1561,36 @@ At this point Gate was ready for provisioning with Ansible.
 </div>
 </div>
 </div>
-<div id="outline-container-orgad416df" class="outline-2">
-<h2 id="orgad416df"><span class="section-number-2">6.</span> The All Role</h2>
+<div id="outline-container-orgb0b1e53" class="outline-2">
+<h2 id="orgb0b1e53"><span class="section-number-2">6.</span> The All Role</h2>
 <div class="outline-text-2" id="text-6">
 <p>
 The <code>all</code> role contains tasks that are executed on all of the
 institute's servers.  At the moment there is just the one.
 </p>
 </div>
-<div id="outline-container-org67e12b4" class="outline-3">
-<h3 id="org67e12b4"><span class="section-number-3">6.1.</span> Include Particulars</h3>
+<div id="outline-container-orgca52274" class="outline-3">
+<h3 id="orgca52274"><span class="section-number-3">6.1.</span> Include Particulars</h3>
 <div class="outline-text-3" id="text-6-1">
 <p>
 The <code>all</code> role's task contains a reference to a common institute
 particular, the institute's <code>domain_name</code>, a variable found in the
 <q>public/vars.yml</q> file.  Thus the first task of the <code>all</code> role is to
-include the variables defined in this file (described in <a href="#orgc0dda53">The
+include the variables defined in this file (described in <a href="#orgd083684">The
 Particulars</a>).  The code block below is the first to tangle into
 <a href="roles/all/tasks/main.yml"><q>roles/all/tasks/main.yml</q></a>.
 </p>
 
 <div class="org-src-container">
-<a href="roles/all/tasks/main.yml"><q>roles/all/tasks/main.yml</q></a><pre class="src src-conf"><code>---
+<a href="roles_t/all/tasks/main.yml"><q>roles_t/all/tasks/main.yml</q></a><pre class="src src-conf"><code>---
 - name: Include public variables.
   include_vars: ../public/vars.yml
-  tags: accounts
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org154651f" class="outline-3">
-<h3 id="org154651f"><span class="section-number-3">6.2.</span> Enable Systemd Resolved</h3>
+<div id="outline-container-org0235386" class="outline-3">
+<h3 id="org0235386"><span class="section-number-3">6.2.</span> Enable Systemd Resolved</h3>
 <div class="outline-text-3" id="text-6-2">
 <p>
 The <code>systemd-networkd</code> and <code>systemd-resolved</code> service units are not
@@ -1631,14 +1659,14 @@ follows these recommendations (and <i>not</i> the suggestion to enable
 </div>
 </div>
 </div>
-<div id="outline-container-org5b29186" class="outline-3">
-<h3 id="org5b29186"><span class="section-number-3">6.3.</span> Trust Institute Certificate Authority</h3>
+<div id="outline-container-org8dfa00f" class="outline-3">
+<h3 id="org8dfa00f"><span class="section-number-3">6.3.</span> Trust Institute Certificate Authority</h3>
 <div class="outline-text-3" id="text-6-3">
 <p>
 All servers should recognize the institute's Certificate Authority as
 trustworthy, so its certificate is added to the set of trusted CAs on
 each host.  More information about how the small institute manages its
-X.509 certificates is available in <a href="#orgaf187bb">Keys</a>.
+X.509 certificates is available in <a href="#orga69013c">Keys</a>.
 </p>
 
 <div class="org-src-container">
@@ -1665,15 +1693,15 @@ X.509 certificates is available in <a href="#orgaf187bb">Keys</a>.
 </div>
 </div>
 </div>
-<div id="outline-container-org3fbcb30" class="outline-2">
-<h2 id="org3fbcb30"><span class="section-number-2">7.</span> The Front Role</h2>
+<div id="outline-container-org3b1843c" class="outline-2">
+<h2 id="org3b1843c"><span class="section-number-2">7.</span> The Front Role</h2>
 <div class="outline-text-2" id="text-7">
 <p>
 The <code>front</code> role installs and configures the services expected on the
 institute's publicly accessible "front door": email, web, VPN.  The
 virtual machine is prepared with an Ubuntu Server install and remote
 access to a privileged, administrator's account.  (For details, see
-<a href="#orge899b2b">The Front Machine</a>.)
+<a href="#orga8651ea">The Front Machine</a>.)
 </p>
 
 <p>
@@ -1688,24 +1716,45 @@ perhaps with symbolic links to, for example,
 <q>/etc/letsencrypt/live/small.example.org/fullchain.pem</q>.
 </p>
 </div>
-<div id="outline-container-orgfc95abc" class="outline-3">
-<h3 id="orgfc95abc"><span class="section-number-3">7.1.</span> Include Particulars</h3>
+<div id="outline-container-org1c2baf1" class="outline-3">
+<h3 id="org1c2baf1"><span class="section-number-3">7.1.</span> Role Defaults</h3>
 <div class="outline-text-3" id="text-7-1">
 <p>
-The first task, as in <a href="#orgad416df">The All Role</a>, is to include the institute
+The <code>front</code> role sets a number of variables to default values in its
+<q>defaults/main.yml</q> file.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/front/defaults/main.yml"><q>roles_t/front/defaults/main.yml</q></a><pre class="src src-conf"><code>---
+&lt;&lt;network-vars&gt;&gt;
+&lt;&lt;address-vars&gt;&gt;
+&lt;&lt;membership-rolls&gt;&gt;
+</code></pre>
+</div>
+
+<p>
+The <code>membership-rolls</code> reference defines <code>membership_rolls</code> which is
+used to select an empty membership roll if one has not been written
+yet.  (See section <a href="#org32de0c9">12.7</a>.)
+</p>
+</div>
+</div>
+<div id="outline-container-orgf05c057" class="outline-3">
+<h3 id="orgf05c057"><span class="section-number-3">7.2.</span> Include Particulars</h3>
+<div class="outline-text-3" id="text-7-2">
+<p>
+The first task, as in <a href="#orgb0b1e53">The All Role</a>, is to include the institute
 particulars.  The <code>front</code> role refers to private variables and the
 membership roll, so these are included was well.
 </p>
 
 <div class="org-src-container">
-<a href="roles/front/tasks/main.yml"><q>roles/front/tasks/main.yml</q></a><pre class="src src-conf"><code>---
+<a href="roles_t/front/tasks/main.yml"><q>roles_t/front/tasks/main.yml</q></a><pre class="src src-conf"><code>---
 - name: Include public variables.
   include_vars: ../public/vars.yml
-  tags: accounts
 
 - name: Include private variables.
   include_vars: ../private/vars.yml
-  tags: accounts
 
 - name: Include members.
   include_vars: <span class="org-string">"{{ lookup('first_found', membership_rolls) }}"</span>
@@ -1714,9 +1763,9 @@ membership roll, so these are included was well.
 </div>
 </div>
 </div>
-<div id="outline-container-org90e3d3a" class="outline-3">
-<h3 id="org90e3d3a"><span class="section-number-3">7.2.</span> Configure Hostname</h3>
-<div class="outline-text-3" id="text-7-2">
+<div id="outline-container-orgd0fad6a" class="outline-3">
+<h3 id="orgd0fad6a"><span class="section-number-3">7.3.</span> Configure Hostname</h3>
+<div class="outline-text-3" id="text-7-3">
 <p>
 This task ensures that Front's <q>/etc/hostname</q> and <q>/etc/mailname</q> are
 correct.  The correct <q>/etc/mailname</q> is essential to proper email
@@ -1724,7 +1773,8 @@ delivery.
 </p>
 
 <div class="org-src-container">
-<a href="roles_t/front/tasks/main.yml"><q>roles_t/front/tasks/main.yml</q></a><pre class="src src-conf"><code>- name: Configure hostname.
+<a href="roles_t/front/tasks/main.yml"><q>roles_t/front/tasks/main.yml</q></a><pre class="src src-conf"><code>
+- name: Configure hostname.
   become: yes
   copy:
     content: <span class="org-string">"{{ domain_name }}\n"</span>
@@ -1736,15 +1786,15 @@ delivery.
 - name: Update hostname.
   become: yes
   command: hostname -F /etc/hostname
-  <span class="org-variable-name">when: domain_name !</span>= ansible_hostname
+  <span class="org-variable-name">when: domain_name !</span>= ansible_fqdn
   tags: actualizer
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-orgcfa05f6" class="outline-3">
-<h3 id="orgcfa05f6"><span class="section-number-3">7.3.</span> Add Administrator to System Groups</h3>
-<div class="outline-text-3" id="text-7-3">
+<div id="outline-container-org63fd7d3" class="outline-3">
+<h3 id="org63fd7d3"><span class="section-number-3">7.4.</span> Add Administrator to System Groups</h3>
+<div class="outline-text-3" id="text-7-4">
 <p>
 The administrator often needs to read (directories of) log files owned
 by groups <code>root</code> and <code>adm</code>.  Adding the administrator's account to
@@ -1763,9 +1813,9 @@ these groups speeds up debugging.
 </div>
 </div>
 </div>
-<div id="outline-container-org0f5fd96" class="outline-3">
-<h3 id="org0f5fd96"><span class="section-number-3">7.4.</span> Configure SSH</h3>
-<div class="outline-text-3" id="text-7-4">
+<div id="outline-container-org197d82c" class="outline-3">
+<h3 id="org197d82c"><span class="section-number-3">7.5.</span> Configure SSH</h3>
+<div class="outline-text-3" id="text-7-5">
 <p>
 The SSH service on Front needs to be known to Monkey.  The following
 tasks ensure this by replacing the automatically generated keys with
@@ -1803,16 +1853,16 @@ those stored in <a href="Secret/ssh_front/etc/ssh/"><q>Secret/ssh_front/etc/ssh/
 </div>
 </div>
 </div>
-<div id="outline-container-orgac996c6" class="outline-3">
-<h3 id="orgac996c6"><span class="section-number-3">7.5.</span> Configure Monkey</h3>
-<div class="outline-text-3" id="text-7-5">
+<div id="outline-container-org214c40a" class="outline-3">
+<h3 id="org214c40a"><span class="section-number-3">7.6.</span> Configure Monkey</h3>
+<div class="outline-text-3" id="text-7-6">
 <p>
 The small institute runs cron jobs and web scripts that generate
 reports and perform checks.  The un-privileged jobs are run by a
 system account named <code>monkey</code>.  One of Monkey's more important jobs on
 Core is to run <code>rsync</code> to update the public web site on Front.  Monkey
 on Core will login as <code>monkey</code> on Front to synchronize the files (as
-described in <a href="#org15438ec">*Configure Apache2</a>).  To do that without needing a
+described in <a href="#org17c6332">*Configure Apache2</a>).  To do that without needing a
 password, the <code>monkey</code> account on Front should authorize Monkey's SSH
 key on Core.
 </p>
@@ -1844,9 +1894,9 @@ key on Core.
 </div>
 </div>
 </div>
-<div id="outline-container-orgd5828db" class="outline-3">
-<h3 id="orgd5828db"><span class="section-number-3">7.6.</span> Install Rsync</h3>
-<div class="outline-text-3" id="text-7-6">
+<div id="outline-container-orgc334c8d" class="outline-3">
+<h3 id="orgc334c8d"><span class="section-number-3">7.7.</span> Install Rsync</h3>
+<div class="outline-text-3" id="text-7-7">
 <p>
 Monkey uses Rsync to keep the institute's public web site up-to-date.
 </p>
@@ -1860,9 +1910,9 @@ Monkey uses Rsync to keep the institute's public web site up-to-date.
 </div>
 </div>
 </div>
-<div id="outline-container-org2d6b73b" class="outline-3">
-<h3 id="org2d6b73b"><span class="section-number-3">7.7.</span> Install Unattended Upgrades</h3>
-<div class="outline-text-3" id="text-7-7">
+<div id="outline-container-org5f21bb4" class="outline-3">
+<h3 id="org5f21bb4"><span class="section-number-3">7.8.</span> Install Unattended Upgrades</h3>
+<div class="outline-text-3" id="text-7-8">
 <p>
 The institute prefers to install security updates as soon as possible.
 </p>
@@ -1876,13 +1926,13 @@ The institute prefers to install security updates as soon as possible.
 </div>
 </div>
 </div>
-<div id="outline-container-org261fe5f" class="outline-3">
-<h3 id="org261fe5f"><span class="section-number-3">7.8.</span> Configure User Accounts</h3>
-<div class="outline-text-3" id="text-7-8">
+<div id="outline-container-org5050d3e" class="outline-3">
+<h3 id="org5050d3e"><span class="section-number-3">7.9.</span> Configure User Accounts</h3>
+<div class="outline-text-3" id="text-7-9">
 <p>
 User accounts are created immediately so that Postfix and Dovecot can
 start delivering email immediately, <i>without</i> returning "no such
-recipient" replies.  The <a href="#org80f2e68">Account Management</a> chapter describes the
+recipient" replies.  The <a href="#org32de0c9">Account Management</a> chapter describes the
 <code>members</code> and <code>usernames</code> variables used below.
 </p>
 
@@ -1920,9 +1970,9 @@ recipient" replies.  The <a href="#org80f2e68">Account Management</a> chapter de
 </div>
 </div>
 </div>
-<div id="outline-container-org78b6acc" class="outline-3">
-<h3 id="org78b6acc"><span class="section-number-3">7.9.</span> Install Server Certificate</h3>
-<div class="outline-text-3" id="text-7-9">
+<div id="outline-container-org4744289" class="outline-3">
+<h3 id="org4744289"><span class="section-number-3">7.10.</span> Install Server Certificate</h3>
+<div class="outline-text-3" id="text-7-10">
 <p>
 The servers on Front use the same certificate (and key) to
 authenticate themselves to institute clients.  They share the
@@ -1951,9 +2001,9 @@ readable by <code>root</code>.
 </div>
 </div>
 </div>
-<div id="outline-container-org806dee8" class="outline-3">
-<h3 id="org806dee8"><span class="section-number-3">7.10.</span> Configure Postfix on Front</h3>
-<div class="outline-text-3" id="text-7-10">
+<div id="outline-container-orge9dd786" class="outline-3">
+<h3 id="orge9dd786"><span class="section-number-3">7.11.</span> Configure Postfix on Front</h3>
+<div class="outline-text-3" id="text-7-11">
 <p>
 Front uses Postfix to provide the institute's public SMTP service, and
 uses the institute's domain name for its host name.  The default
@@ -1969,7 +2019,7 @@ The appropriate answers are listed here but will be checked
 </ul>
 
 <p>
-As discussed in <a href="#orgff9aad1">The Email Service</a> above, Front's Postfix configuration
+As discussed in <a href="#org9ef70e4">The Email Service</a> above, Front's Postfix configuration
 includes site-wide support for larger message sizes, shorter queue
 times, the relaying configuration, and the common path to incoming
 emails.  These and a few Front-specific Postfix configurations
@@ -1982,7 +2032,7 @@ via which Core relays messages from the campus.
 </p>
 
 <div class="org-src-container">
-<code>postfix-front-networks</code><pre class="src src-conf" id="org45d47d8"><code>- p: mynetworks
+<code>postfix-front-networks</code><pre class="src src-conf" id="org90d4910"><code>- p: mynetworks
   v: &gt;-
      {{ public_wg_net_cidr }}
      127.0.0.0/8
@@ -1998,7 +2048,7 @@ difficult for internal hosts, who do <i>not</i> have (public) domain names.
 </p>
 
 <div class="org-src-container">
-<code>postfix-front-restrictions</code><pre class="src src-conf" id="org35e980f"><code>- p: smtpd_recipient_restrictions
+<code>postfix-front-restrictions</code><pre class="src src-conf" id="orgf77c22d"><code>- p: smtpd_recipient_restrictions
   v: &gt;-
      permit_mynetworks
      reject_unauth_pipelining
@@ -2019,13 +2069,13 @@ messages; incoming messages are delivered locally, without
 </p>
 
 <div class="org-src-container">
-<code>postfix-header-checks</code><pre class="src src-conf" id="orged3c81a"><code>- p: smtp_header_checks
+<code>postfix-header-checks</code><pre class="src src-conf" id="org6d1245c"><code>- p: smtp_header_checks
   v: regexp:/etc/postfix/header_checks.cf
 </code></pre>
 </div>
 
 <div class="org-src-container">
-<code>postfix-header-checks-content</code><pre class="src src-conf" id="orga3a4c3c"><code>/^Received:/    IGNORE
+<code>postfix-header-checks-content</code><pre class="src src-conf" id="org47743f8"><code>/^Received:/    IGNORE
 /^User-Agent:/  IGNORE
 </code></pre>
 </div>
@@ -2037,7 +2087,7 @@ Debian default for <code>inet_interfaces</code>.
 </p>
 
 <div class="org-src-container">
-<code>postfix-front</code><pre class="src src-conf" id="orgba02da7"><code>- { p: smtpd_tls_cert_file, v: /etc/server.crt }
+<code>postfix-front</code><pre class="src src-conf" id="orge72f626"><code>- { p: smtpd_tls_cert_file, v: /etc/server.crt }
 - { p: smtpd_tls_key_file, v: /etc/server.key }
 &lt;&lt;postfix-front-networks&gt;&gt;
 &lt;&lt;postfix-front-restrictions&gt;&gt;
@@ -2113,9 +2163,9 @@ start and enable the service.
 </div>
 </div>
 </div>
-<div id="outline-container-org4e1c5cb" class="outline-3">
-<h3 id="org4e1c5cb"><span class="section-number-3">7.11.</span> Configure Public Email Aliases</h3>
-<div class="outline-text-3" id="text-7-11">
+<div id="outline-container-org644ed17" class="outline-3">
+<h3 id="org644ed17"><span class="section-number-3">7.12.</span> Configure Public Email Aliases</h3>
+<div class="outline-text-3" id="text-7-12">
 <p>
 The institute's Front needs to deliver email addressed to a number of
 common aliases as well as those advertised on the web site.  System
@@ -2156,9 +2206,9 @@ created by a more specialized role.
 </div>
 </div>
 </div>
-<div id="outline-container-orgdc3c7f0" class="outline-3">
-<h3 id="orgdc3c7f0"><span class="section-number-3">7.12.</span> Configure Dovecot IMAPd</h3>
-<div class="outline-text-3" id="text-7-12">
+<div id="outline-container-org058e2f8" class="outline-3">
+<h3 id="org058e2f8"><span class="section-number-3">7.13.</span> Configure Dovecot IMAPd</h3>
+<div class="outline-text-3" id="text-7-13">
 <p>
 Front uses Dovecot's IMAPd to allow user Fetchmail jobs on Core to
 pick up messages.  Front's Dovecot configuration is largely the Debian
@@ -2166,7 +2216,7 @@ default with POP and IMAP (without TLS) support disabled.  This is a
 bit "over the top" given that Core accesses Front via VPN, but helps
 to ensure privacy even when members must, in extremis, access recent
 email directly from their accounts on Front.  For more information
-about Front's role in the institute's email services, see <a href="#orgff9aad1">The Email
+about Front's role in the institute's email services, see <a href="#org9ef70e4">The Email
 Service</a>.
 </p>
 
@@ -2229,9 +2279,9 @@ and enables it to start at every reboot.
 </div>
 </div>
 </div>
-<div id="outline-container-orgde141cc" class="outline-3">
-<h3 id="orgde141cc"><span class="section-number-3">7.13.</span> Configure Apache2 <a id="org15438ec"></a></h3>
-<div class="outline-text-3" id="text-7-13">
+<div id="outline-container-org54f3189" class="outline-3">
+<h3 id="org54f3189"><span class="section-number-3">7.14.</span> Configure Apache2 <a id="org17c6332"></a></h3>
+<div class="outline-text-3" id="text-7-14">
 <p>
 This is the small institute's public web site.  It is simple, static,
 and thus (hopefully) difficult to subvert.  There are no server-side
@@ -2266,7 +2316,7 @@ taken from <a href="https://www.ssllabs.com/projects/best-practices">https://www
 </p>
 
 <div class="org-src-container">
-<code>apache-ciphers</code><pre class="src src-conf" id="org4b1ab7c"><code>SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
+<code>apache-ciphers</code><pre class="src src-conf" id="orgf582e9d"><code>SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
 SSLHonorCipherOrder on
 <span class="org-type">SSLCipherSuite {</span>{ [ <span class="org-string">'ECDHE-ECDSA-AES128-GCM-SHA256'</span>,
                     <span class="org-string">'ECDHE-ECDSA-AES256-GCM-SHA384'</span>,
@@ -2321,7 +2371,7 @@ used on all of the institute's web sites.
 </p>
 
 <div class="org-src-container">
-<code>apache-userdir-front</code><pre class="src src-conf" id="org7da24fd"><code>UserDir /home/www-users
+<code>apache-userdir-front</code><pre class="src src-conf" id="org98986e5"><code>UserDir /home/www-users
 &lt;Directory /home/www-users/&gt;
         Require all granted
         AllowOverride None
@@ -2336,7 +2386,7 @@ HTTPS URLs.
 </p>
 
 <div class="org-src-container">
-<code>apache-redirect-front</code><pre class="src src-conf" id="org3b99fd9"><code>&lt;VirtualHost *:80&gt;
+<code>apache-redirect-front</code><pre class="src src-conf" id="orgba99002"><code>&lt;VirtualHost *:80&gt;
         Redirect permanent / https://{{ domain_name }}/
 &lt;/VirtualHost&gt;
 </code></pre>
@@ -2361,7 +2411,7 @@ the inside of a <code>VirtualHost</code> block.  They should apply globally.
 </p>
 
 <div class="org-src-container">
-<code>apache-front</code><pre class="src src-conf" id="org7f3bc31"><code>ServerName {{ domain_name }}
+<code>apache-front</code><pre class="src src-conf" id="org3ebb2cb"><code>ServerName {{ domain_name }}
 ServerAdmin webmaster@{{ domain_name }}
 
 DocumentRoot /home/www
@@ -2511,6 +2561,7 @@ the users' <q>~/Public/HTML/</q> directories.
     src: /home/{{ item }}/Public/HTML
     state: link
     force: yes
+    follow: false
   loop: <span class="org-string">"{{ usernames }}"</span>
   <span class="org-variable-name">when: members[item].status</span> == <span class="org-string">'current'</span>
   tags: accounts
@@ -2527,9 +2578,9 @@ the users' <q>~/Public/HTML/</q> directories.
 </div>
 </div>
 </div>
-<div id="outline-container-org25a022b" class="outline-3">
-<h3 id="org25a022b"><span class="section-number-3">7.14.</span> Configure Public WireGuard™ Subnet</h3>
-<div class="outline-text-3" id="text-7-14">
+<div id="outline-container-org8c0e7a6" class="outline-3">
+<h3 id="org8c0e7a6"><span class="section-number-3">7.15.</span> Configure Public WireGuard™ Subnet</h3>
+<div class="outline-text-3" id="text-7-15">
 <p>
 Front uses WireGuard™ to provide a public (Internet accessible) VPN
 service.  Core has an interface on this VPN and is expected to forward
@@ -2537,14 +2588,98 @@ packets between it and the institute's other private networks.
 </p>
 
 <p>
-The following example <a href="#org30319d3"><q>private/front-wg0.conf</q></a> configuration recognizes
-Core by its public key and routes the institute's private networks to
-it.  It also recognizes Dick's notebook and his (replacement) phone,
-assigning them host numbers 4 and 6 on the VPN.
+The following tasks install WireGuard™, configure it with
+<q>private/front-wg0.conf</q> (or <a href="private/front-wg0-empty.conf"><q>private/front-wg0-empty.conf</q></a> if it does
+not exist), and enable the service.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/front/tasks/main.yml"><q>roles_t/front/tasks/main.yml</q></a><pre class="src src-conf"><code>
+- name: Enable IP forwarding.
+  become: yes
+  sysctl:
+    name: net.ipv4.ip_forward
+    value: <span class="org-string">"1"</span>
+    state: present
+
+- name: Install WireGuard&#8482;.
+  become: yes
+  <span class="org-variable-name">apt: pkg</span>=wireguard
+
+- name: Configure WireGuard&#8482;.
+  become: yes
+  vars:
+    srcs:
+      - ../private/front-wg0.conf
+      - ../private/front-wg0-empty.conf
+  copy:
+    src: <span class="org-string">"{{ lookup('first_found', srcs) }}"</span>
+    dest: /etc/wireguard/wg0.conf
+    <span class="org-variable-name">mode: u</span>=r,g=,o=
+    owner: root
+    group: root
+  notify: Restart WireGuard&#8482;.
+  tags: accounts
+
+- name: Start WireGuard&#8482;.
+  become: yes
+  systemd:
+    service: wg-quick@wg0
+    state: started
+  tags: actualizer
+
+- name: Enable WireGuard&#8482;.
+  become: yes
+  systemd:
+    service: wg-quick@wg0
+    enabled: yes
+</code></pre>
+</div>
+
+<div class="org-src-container">
+<a href="roles_t/front/handlers/main.yml"><q>roles_t/front/handlers/main.yml</q></a><pre class="src src-conf"><code>
+- name: Restart WireGuard&#8482;.
+  become: yes
+  systemd:
+    service: wg-quick@wg0
+    state: restarted
+  tags: actualizer
+</code></pre>
+</div>
+
+<p>
+The "empty" WireGuard™ configuration file (below) is used until the
+<code>./inst client</code> command adds the first client, and generates an actual
+<q>private/front-wg0.conf</q>.
+</p>
+
+<div class="org-src-container">
+<a href="private/front-wg0-empty.conf"><q>private/front-wg0-empty.conf</q></a><pre class="src src-conf"><code>[<span class="org-type">Interface</span>]
+<span class="org-variable-name">Address</span> = 10.177.87.1/24
+<span class="org-variable-name">ListenPort</span> = 39608
+<span class="org-variable-name">PostUp</span> = wg set %i private-key /etc/wireguard/private-key
+<span class="org-variable-name">PostUp</span> = resolvectl dns %i 192.168.56.1
+<span class="org-variable-name">PostUp</span> = resolvectl domain %i small.private
+</code></pre>
+</div>
+</div>
+<div id="outline-container-org5e18b7c" class="outline-4">
+<h4 id="org5e18b7c"><span class="section-number-4">7.15.1.</span> Example <q>private/front-wg0.conf</q></h4>
+<div class="outline-text-4" id="text-7-15-1">
+<p>
+The example <q>private/front-wg0.conf</q> below recognizes Core by its
+public key and routes the institute's private networks to it.  It also
+recognizes Dick's notebook and his (replacement) phone, assigning them
+host numbers 4 and 6 on the VPN.
+</p>
+
+<p>
+This is just an example.  The actual file is edited by the <code>./inst
+client</code> command and so is not tangled from the following block.
 </p>
 
 <div class="org-src-container">
-<q>private/front-wg0.conf</q><pre class="src src-conf" id="org30319d3"><code>[<span class="org-type">Interface</span>]
+Example <q>private/front-wg0.conf</q><pre class="src src-conf"><code>[<span class="org-type">Interface</span>]
 <span class="org-variable-name">Address</span> = 10.177.87.1/24
 <span class="org-variable-name">ListenPort</span> = 39608
 <span class="org-variable-name">PostUp</span> = wg set %i private-key /etc/wireguard/private-key
@@ -2584,7 +2719,7 @@ WireGuard™ tunnel on Dick's notebook, used abroad<pre class="src src-conf"><co
 <span class="org-variable-name">PostUp</span> = resolvectl domain %i small.private
 
 [<span class="org-type">Peer</span>]
-<span class="org-variable-name">EndPoint</span> = 192.168.15.5:39608
+<span class="org-variable-name">EndPoint</span> = 192.168.15.4:39608
 <span class="org-variable-name">PublicKey</span> = S+6HaTnOwwhWgUGXjSBcPAvifKw+j8BDTRfq534gNW4=
 <span class="org-variable-name">AllowedIPs</span> = 10.177.87.1
 <span class="org-variable-name">AllowedIPs</span> = 192.168.56.0/24
@@ -2593,65 +2728,12 @@ WireGuard™ tunnel on Dick's notebook, used abroad<pre class="src src-conf"><co
 <span class="org-variable-name">AllowedIPs</span> = 10.84.139.0/24
 </code></pre>
 </div>
-
-<p>
-The following tasks install WireGuard™, configure it with
-<a href="#org30319d3"><q>private/front-wg0.conf</q></a>, and enable the service.
-</p>
-
-<div class="org-src-container">
-<a href="roles_t/front/tasks/main.yml"><q>roles_t/front/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Enable IP forwarding.
-  become: yes
-  sysctl:
-    name: net.ipv4.ip_forward
-    value: <span class="org-string">"1"</span>
-    state: present
-
-- name: Install WireGuard&#8482;.
-  become: yes
-  <span class="org-variable-name">apt: pkg</span>=wireguard
-
-- name: Configure WireGuard&#8482;.
-  become: yes
-  copy:
-    src: ../private/front-wg0.conf
-    dest: /etc/wireguard/wg0.conf
-    <span class="org-variable-name">mode: u</span>=r,g=,o=
-    owner: root
-    group: root
-  notify: Restart WireGuard&#8482;.
-
-- name: Start WireGuard&#8482;.
-  become: yes
-  systemd:
-    service: wg-quick@wg0
-    state: started
-  tags: actualizer
-
-- name: Enable WireGuard&#8482;.
-  become: yes
-  systemd:
-    service: wg-quick@wg0
-    enabled: yes
-</code></pre>
-</div>
-
-<div class="org-src-container">
-<a href="roles_t/front/handlers/main.yml"><q>roles_t/front/handlers/main.yml</q></a><pre class="src src-conf"><code>
-- name: Restart WireGuard&#8482;.
-  become: yes
-  systemd:
-    service: wg-quick@wg0
-    state: restarted
-  tags: actualizer
-</code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org31a52ac" class="outline-3">
-<h3 id="org31a52ac"><span class="section-number-3">7.15.</span> Configure Kamailio</h3>
-<div class="outline-text-3" id="text-7-15">
+<div id="outline-container-orgb880ef7" class="outline-3">
+<h3 id="orgb880ef7"><span class="section-number-3">7.16.</span> Configure Kamailio</h3>
+<div class="outline-text-3" id="text-7-16">
 <p>
 Front uses Kamailio to provide a SIP service on the public VPN so that
 members abroad can chat privately.  This is a connection-less UDP
@@ -2672,7 +2754,7 @@ specifies the actual IP, known here as <code>front_wg_addr</code>.
 </p>
 
 <div class="org-src-container">
-<code>kamailio</code><pre class="src src-conf" id="orgd080cca"><code><span class="org-variable-name">listen</span>=udp:{{ front_wg_addr }}:5060
+<code>kamailio</code><pre class="src src-conf" id="org138467a"><code><span class="org-variable-name">listen</span>=udp:{{ front_wg_addr }}:5060
 </code></pre>
 </div>
 
@@ -2715,8 +2797,8 @@ not be started before the <code>wg0</code> device has appeared.
   copy:
     content: |
       [<span class="org-type">Unit</span>]
+      <span class="org-variable-name">After</span>=wg-quick@wg0.service
       <span class="org-variable-name">Requires</span>=sys-devices-virtual-net-wg0.device
-      <span class="org-variable-name">After</span>=sys-devices-virtual-net-wg0.device
     dest: /etc/systemd/system/kamailio.service.d/depend.conf
   notify: Reload Systemd.
 </code></pre>
@@ -2774,22 +2856,39 @@ Finally, Kamailio can be configured and started.
 </div>
 </div>
 </div>
-<div id="outline-container-orgc29541b" class="outline-2">
-<h2 id="orgc29541b"><span class="section-number-2">8.</span> The Core Role</h2>
+<div id="outline-container-org1673460" class="outline-2">
+<h2 id="org1673460"><span class="section-number-2">8.</span> The Core Role</h2>
 <div class="outline-text-2" id="text-8">
 <p>
 The <code>core</code> role configures many essential campus network services as
 well as the institute's private cloud, so the core machine has
 horsepower (CPUs and RAM) and large disks and is prepared with a
 Debian install and remote access to a privileged, administrator's
-account.  (For details, see <a href="#org4d61626">The Core Machine</a>.)
+account.  (For details, see <a href="#org641fa41">The Core Machine</a>.)
 </p>
 </div>
-<div id="outline-container-org7bf5f3f" class="outline-3">
-<h3 id="org7bf5f3f"><span class="section-number-3">8.1.</span> Include Particulars</h3>
+<div id="outline-container-org3506d71" class="outline-3">
+<h3 id="org3506d71"><span class="section-number-3">8.1.</span> Role Defaults</h3>
 <div class="outline-text-3" id="text-8-1">
 <p>
-The first task, as in <a href="#org3fbcb30">The Front Role</a>, is to include the institute
+As in <a href="#org3b1843c">The Front Role</a>, the <code>core</code> role sets a number of variables to
+default values in its <q>defaults/main.yml</q> file.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/core/defaults/main.yml"><q>roles_t/core/defaults/main.yml</q></a><pre class="src src-conf"><code>---
+&lt;&lt;network-vars&gt;&gt;
+&lt;&lt;address-vars&gt;&gt;
+&lt;&lt;membership-rolls&gt;&gt;
+</code></pre>
+</div>
+</div>
+</div>
+<div id="outline-container-org38e89d0" class="outline-3">
+<h3 id="org38e89d0"><span class="section-number-3">8.2.</span> Include Particulars</h3>
+<div class="outline-text-3" id="text-8-2">
+<p>
+The first task, as in <a href="#org3b1843c">The Front Role</a>, is to include the institute
 particulars and membership roll.
 </p>
 
@@ -2798,9 +2897,11 @@ particulars and membership roll.
 - name: Include public variables.
   include_vars: ../public/vars.yml
   tags: accounts
+
 - name: Include private variables.
   include_vars: ../private/vars.yml
   tags: accounts
+
 - name: Include members.
   include_vars: <span class="org-string">"{{ lookup('first_found', membership_rolls) }}"</span>
   tags: accounts
@@ -2808,9 +2909,9 @@ particulars and membership roll.
 </div>
 </div>
 </div>
-<div id="outline-container-org02ca68d" class="outline-3">
-<h3 id="org02ca68d"><span class="section-number-3">8.2.</span> Configure Hostname</h3>
-<div class="outline-text-3" id="text-8-2">
+<div id="outline-container-orgd3f3b7b" class="outline-3">
+<h3 id="orgd3f3b7b"><span class="section-number-3">8.3.</span> Configure Hostname</h3>
+<div class="outline-text-3" id="text-8-3">
 <p>
 This task ensures that Core's <q>/etc/hostname</q> and <q>/etc/mailname</q> are
 correct.  Core accepts email addressed to the institute's public or
@@ -2839,9 +2940,9 @@ proper email delivery.
 </div>
 </div>
 </div>
-<div id="outline-container-org22c7c7f" class="outline-3">
-<h3 id="org22c7c7f"><span class="section-number-3">8.3.</span> Configure Systemd Resolved</h3>
-<div class="outline-text-3" id="text-8-3">
+<div id="outline-container-orgfa963c5" class="outline-3">
+<h3 id="orgfa963c5"><span class="section-number-3">8.4.</span> Configure Systemd Resolved</h3>
+<div class="outline-text-3" id="text-8-4">
 <p>
 Core runs the campus name server, so Resolved is configured to use it
 (or <code>dns.google</code>), to include the institute's domain in its search
@@ -2886,70 +2987,83 @@ list, and to disable its cache and stub listener.
 </div>
 </div>
 </div>
-<div id="outline-container-org7efe399" class="outline-3">
-<h3 id="org7efe399"><span class="section-number-3">8.4.</span> Configure Netplan</h3>
-<div class="outline-text-3" id="text-8-4">
+<div id="outline-container-orgcf5cb88" class="outline-3">
+<h3 id="orgcf5cb88"><span class="section-number-3">8.5.</span> Configure Core NetworkD</h3>
+<div class="outline-text-3" id="text-8-5">
+<p>
+Core's network interface is statically configured using the
+<code>systemd-networkd</code> configuration files <q>10-lan.link</q> and
+<q>10-lan.network</q> installed in <q>/etc/systemd/network/</q>.  Those files
+statically assign Core's IP address (as well as the campus name server
+and search domain), and its default route through Gate.  A second
+route, through Core itself to Front, is advertised to other hosts, and
+is routed through a WireGuard™ interface connected to Front's public
+WireGuard™ VPN.
+</p>
+
 <p>
-Core's network interface is statically configured using Netplan and an
-<q>/etc/netplan/60-core.yaml</q> file.  That file provides Core's address
-on the private Ethernet, the campus name server and search domain, and
-the default route through Gate to the campus ISP.  A second route,
-through Core itself to Front, is advertised to other hosts.
+Note that the <code>[Match]</code> sections of the <q>.network</q> files should
+specify only a <code>MACAddress</code>.  Getting <code>systemd-udevd</code> to rename
+interfaces has thusfar been futile (short of a reboot), so specifying
+a <code>Name</code> means the interface does not match, leaving it un-configured
+(until the next reboot).
 </p>
 
 <p>
-Core's Netplan needs the name of its main (only) Ethernet interface,
-an example of which is given here.  (A clever way to extract that name
+The configuration needs the MAC address of the primary (only) NIC, an
+example of which is given here.  (A clever way to extract that name
 from <code>ansible_facts</code> would be appreciated.  The <code>ansible_default_ipv4</code>
 fact was an empty hash at first boot on a simulated campus Ethernet.)
 </p>
 
 <div class="org-src-container">
-<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>core_ethernet:              enp0s3
+<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>core_lan_mac:               08:00:27:b3:e5:5f
 </code></pre>
 </div>
 
 <div class="org-src-container">
 <a href="roles_t/core/tasks/main.yml"><q>roles_t/core/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Install netplan.
+- name: Install 10-lan.link.
   become: yes
-  <span class="org-variable-name">apt: pkg</span>=netplan.io
+  copy:
+    content: |
+      [<span class="org-type">Match</span>]
+      <span class="org-variable-name">MACAddress</span>={{ core_lan_mac }}
 
-- name: Configure netplan.
+      [<span class="org-type">Link</span>]
+      <span class="org-variable-name">Name</span>=lan
+    dest: /etc/systemd/network/10-lan.link
+
+- name: Install 10-lan.network.
   become: yes
   copy:
     content: |
-      network:
-        renderer: networkd
-        ethernets:
-          {{ core_ethernet }}:
-            dhcp4: false
-            addresses: [ {{ core_addr_cidr }} ]
-            nameservers:
-              search: [ {{ domain_priv }} ]
-              addresses: [ {{ core_addr }} ]
-            routes:
-            - to: default
-              via: {{ gate_addr }}
-    dest: /etc/netplan/60-core.yaml
-    <span class="org-variable-name">mode: u</span>=rw,g=r,o=
-  notify: Apply netplan.
+      [<span class="org-type">Match</span>]
+      <span class="org-variable-name">MACAddress</span>={{ core_lan_mac }}
+
+      [<span class="org-type">Network</span>]
+      <span class="org-variable-name">Address</span>={{ core_addr_cidr }}
+      <span class="org-variable-name">Gateway</span>={{ gate_addr }}
+      <span class="org-variable-name">DNS</span>={{ core_addr }}
+      <span class="org-variable-name">Domains</span>={{ domain_priv }}
+    dest: /etc/systemd/network/10-lan.network
+  notify: Reload networkd.
 </code></pre>
 </div>
 
 <div class="org-src-container">
 <a href="roles_t/core/handlers/main.yml"><q>roles_t/core/handlers/main.yml</q></a><pre class="src src-conf"><code>
-- name: Apply netplan.
+- name: Reload networkd.
   become: yes
-  command: netplan apply
+  command: networkctl reload
   tags: actualizer
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org4098289" class="outline-3">
-<h3 id="org4098289"><span class="section-number-3">8.5.</span> Configure DHCP For the Private Ethernet</h3>
-<div class="outline-text-3" id="text-8-5">
+<div id="outline-container-orgf3ad234" class="outline-3">
+<h3 id="orgf3ad234"><span class="section-number-3">8.6.</span> Configure DHCP For the Private Ethernet</h3>
+<div class="outline-text-3" id="text-8-6">
 <p>
 Core speaks DHCP (Dynamic Host Configuration Protocol) using the
 Internet Software Consortium's DHCP server.  The server assigns unique
@@ -2963,9 +3077,9 @@ The example configuration file, <a href="private/core-dhcpd.conf"><q>private/cor
 RFC3442's extension to encode a second (non-default) static route.
 The default route is through the campus ISP at Gate.  A second route
 directs campus traffic to the Front VPN through Core.  This is just an
-example file, with MAC addresses chosen to (probably?) match
-VirtualBox test machines.  In actual use <q>private/core-dhcpd.conf</q>
-refers to a replacement file.
+example file, with MAC addresses chosen to match VirtualBox test
+machines.  In actual use <q>private/core-dhcpd.conf</q> refers to a
+replacement file.
 </p>
 
 <div class="org-src-container">
@@ -2992,12 +3106,8 @@ log-facility daemon;
                         0,             192,168,56,2;
 }
 
-<span class="org-type">host core</span> {
-  hardware ethernet 08:00:27:45:3b:a2; fixed-address 192.168.56.1; }
-<span class="org-type">host gate</span> {
-  hardware ethernet 08:00:27:e0:79:ab; fixed-address 192.168.56.2; }
-<span class="org-type">host server</span> {
-  hardware ethernet 08:00:27:f3:41:66; fixed-address 192.168.56.3; }
+<span class="org-type">host dick</span> {
+  hardware ethernet 08:00:27:dc:54:b5; fixed-address 192.168.56.4; }
 </code></pre>
 </div>
 
@@ -3016,8 +3126,8 @@ the real <a href="private/core-dhcpd.conf"><q>private/core-dhcpd.conf</q></a> (<
   become: yes
   lineinfile:
     path: /etc/default/isc-dhcp-server
-    <span class="org-variable-name">line: INTERFACESv4</span>=<span class="org-string">"{{ core_ethernet }}"</span>
-    <span class="org-variable-name">regexp: ^INTERFACESv4</span>=
+    regexp: <span class="org-string">"^INTERFACESv4="</span>
+    line: <span class="org-string">"INTERFACESv4=\"lan\""</span>
   notify: Restart DHCP server.
 
 - name: Configure DHCP subnet.
@@ -3054,12 +3164,12 @@ the real <a href="private/core-dhcpd.conf"><q>private/core-dhcpd.conf</q></a> (<
 </div>
 </div>
 </div>
-<div id="outline-container-org7875b3a" class="outline-3">
-<h3 id="org7875b3a"><span class="section-number-3">8.6.</span> Configure BIND9</h3>
-<div class="outline-text-3" id="text-8-6">
+<div id="outline-container-org2f805ca" class="outline-3">
+<h3 id="org2f805ca"><span class="section-number-3">8.7.</span> Configure BIND9</h3>
+<div class="outline-text-3" id="text-8-7">
 <p>
 Core uses BIND9 to provide name service for the institute as described
-in <a href="#orgda548cf">The Name Service</a>.  The configuration supports reverse name lookups,
+in <a href="#org2ae4fc9">The Name Service</a>.  The configuration supports reverse name lookups,
 resolving many private network addresses to private domain names.
 </p>
 
@@ -3131,7 +3241,7 @@ probably be used as forwarders rather than Google.
 </p>
 
 <div class="org-src-container">
-<code>bind-options</code><pre class="src src-conf" id="org132eef4"><code><span class="org-type">acl </span><span class="org-string"><span class="org-type">"trusted"</span></span> {
+<code>bind-options</code><pre class="src src-conf" id="org8064ec7"><code><span class="org-type">acl </span><span class="org-string"><span class="org-type">"trusted"</span></span> {
         {{ private_net_cidr }};
         {{ wild_net_cidr }};
         {{ public_wg_net_cidr }};
@@ -3151,6 +3261,8 @@ probably be used as forwarders rather than Google.
         allow-recursion { trusted; };
         allow-query-cache { trusted; };
 
+        dnssec-validation yes;
+
         <span class="org-type">listen-on</span> {
                 {{ core_addr }};
                 localhost;
@@ -3160,7 +3272,7 @@ probably be used as forwarders rather than Google.
 </div>
 
 <div class="org-src-container">
-<code>bind-local</code><pre class="src src-conf" id="orgd106c27"><code>include <span class="org-string">"/etc/bind/zones.rfc1918"</span>;
+<code>bind-local</code><pre class="src src-conf" id="org8eca961"><code>include <span class="org-string">"/etc/bind/zones.rfc1918"</span>;
 
 <span class="org-type">zone </span><span class="org-string"><span class="org-type">"{{ domain_priv }}."</span></span> {
         type master;
@@ -3272,9 +3384,9 @@ $TTL    7200
 </div>
 </div>
 </div>
-<div id="outline-container-org0b05646" class="outline-3">
-<h3 id="org0b05646"><span class="section-number-3">8.7.</span> Add Administrator to System Groups</h3>
-<div class="outline-text-3" id="text-8-7">
+<div id="outline-container-orgba4b848" class="outline-3">
+<h3 id="orgba4b848"><span class="section-number-3">8.8.</span> Add Administrator to System Groups</h3>
+<div class="outline-text-3" id="text-8-8">
 <p>
 The administrator often needs to read (directories of) log files owned
 by groups <code>root</code> and <code>adm</code>.  Adding the administrator's account to
@@ -3293,15 +3405,15 @@ these groups speeds up debugging.
 </div>
 </div>
 </div>
-<div id="outline-container-org701f5c9" class="outline-3">
-<h3 id="org701f5c9"><span class="section-number-3">8.8.</span> Configure Monkey</h3>
-<div class="outline-text-3" id="text-8-8">
+<div id="outline-container-orgda6ed34" class="outline-3">
+<h3 id="orgda6ed34"><span class="section-number-3">8.9.</span> Configure Monkey</h3>
+<div class="outline-text-3" id="text-8-9">
 <p>
 The small institute runs cron jobs and web scripts that generate
 reports and perform checks.  The un-privileged jobs are run by a
 system account named <code>monkey</code>.  One of Monkey's more important jobs on
 Core is to run <code>rsync</code> to update the public web site on Front (as
-described in <a href="#org017921e">*Configure Apache2</a>).
+described in <a href="#orgb32e0d5">*Configure Apache2</a>).
 </p>
 
 <div class="org-src-container">
@@ -3361,9 +3473,9 @@ described in <a href="#org017921e">*Configure Apache2</a>).
 </div>
 </div>
 </div>
-<div id="outline-container-orgbcfd5c1" class="outline-3">
-<h3 id="orgbcfd5c1"><span class="section-number-3">8.9.</span> Install Unattended Upgrades</h3>
-<div class="outline-text-3" id="text-8-9">
+<div id="outline-container-org682ab44" class="outline-3">
+<h3 id="org682ab44"><span class="section-number-3">8.10.</span> Install Unattended Upgrades</h3>
+<div class="outline-text-3" id="text-8-10">
 <p>
 The institute prefers to install security updates as soon as possible.
 </p>
@@ -3377,29 +3489,12 @@ The institute prefers to install security updates as soon as possible.
 </div>
 </div>
 </div>
-<div id="outline-container-org21821d0" class="outline-3">
-<h3 id="org21821d0"><span class="section-number-3">8.10.</span> Install Expect</h3>
-<div class="outline-text-3" id="text-8-10">
-<p>
-The <code>expect</code> program is used by <a href="#org79b145a">The Institute Commands</a> to interact
-with Nextcloud on the command line.
-</p>
-
-<div class="org-src-container">
-<a href="roles_t/core/tasks/main.yml"><q>roles_t/core/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Install expect.
-  become: yes
-  <span class="org-variable-name">apt: pkg</span>=expect
-</code></pre>
-</div>
-</div>
-</div>
-<div id="outline-container-orgaacebdb" class="outline-3">
-<h3 id="orgaacebdb"><span class="section-number-3">8.11.</span> Configure User Accounts</h3>
+<div id="outline-container-org7a7c789" class="outline-3">
+<h3 id="org7a7c789"><span class="section-number-3">8.11.</span> Configure User Accounts</h3>
 <div class="outline-text-3" id="text-8-11">
 <p>
 User accounts are created immediately so that backups can begin
-restoring as soon as possible.  The <a href="#org80f2e68">Account Management</a> chapter
+restoring as soon as possible.  The <a href="#org32de0c9">Account Management</a> chapter
 describes the <code>members</code> and <code>usernames</code> variables.
 </p>
 
@@ -3437,8 +3532,8 @@ describes the <code>members</code> and <code>usernames</code> variables.
 </div>
 </div>
 </div>
-<div id="outline-container-org146701c" class="outline-3">
-<h3 id="org146701c"><span class="section-number-3">8.12.</span> Install Server Certificate</h3>
+<div id="outline-container-org4c31c2e" class="outline-3">
+<h3 id="org4c31c2e"><span class="section-number-3">8.12.</span> Install Server Certificate</h3>
 <div class="outline-text-3" id="text-8-12">
 <p>
 The servers on Core use the same certificate (and key) to authenticate
@@ -3466,8 +3561,8 @@ themselves to institute clients.  They share the <q>/etc/server.crt</q> and
 </div>
 </div>
 </div>
-<div id="outline-container-orga13509e" class="outline-3">
-<h3 id="orga13509e"><span class="section-number-3">8.13.</span> Install Chrony</h3>
+<div id="outline-container-org2dc70d9" class="outline-3">
+<h3 id="org2dc70d9"><span class="section-number-3">8.13.</span> Install Chrony</h3>
 <div class="outline-text-3" id="text-8-13">
 <p>
 Core uses Chrony to provide a time synchronization service to the campus.
@@ -3495,6 +3590,7 @@ The default daemon's default configuration is fine.
 <div class="org-src-container">
 <a href="roles_t/core/handlers/main.yml"><q>roles_t/core/handlers/main.yml</q></a><pre class="src src-conf"><code>
 - name: Restart Chrony.
+  become: yes
   systemd:
     service: chrony
     state: restarted
@@ -3502,8 +3598,8 @@ The default daemon's default configuration is fine.
 </div>
 </div>
 </div>
-<div id="outline-container-orgf2d58ba" class="outline-3">
-<h3 id="orgf2d58ba"><span class="section-number-3">8.14.</span> Configure Postfix on Core</h3>
+<div id="outline-container-orgbc498dc" class="outline-3">
+<h3 id="orgbc498dc"><span class="section-number-3">8.14.</span> Configure Postfix on Core</h3>
 <div class="outline-text-3" id="text-8-14">
 <p>
 Core uses Postfix to provide SMTP service to the campus.  The default
@@ -3519,7 +3615,7 @@ The appropriate answers are listed here but will be checked
 </ul>
 
 <p>
-As discussed in <a href="#orgff9aad1">The Email Service</a> above, Core delivers email addressed
+As discussed in <a href="#org9ef70e4">The Email Service</a> above, Core delivers email addressed
 to any internal domain name locally, and uses its smarthost Front to
 relay the rest.  Core is reachable only on institute networks, so
 there is little benefit in enabling TLS, but it does need to handle
@@ -3532,7 +3628,7 @@ Core relays messages from any institute network.
 </p>
 
 <div class="org-src-container">
-<code>postfix-core-networks</code><pre class="src src-conf" id="org4247f6c"><code>- p: mynetworks
+<code>postfix-core-networks</code><pre class="src src-conf" id="org92d2e20"><code>- p: mynetworks
   v: &gt;-
      {{ private_net_cidr }}
      {{ public_wg_net_cidr }}
@@ -3548,7 +3644,7 @@ Core uses Front to relay messages to the Internet.
 </p>
 
 <div class="org-src-container">
-<code>postfix-core-relayhost</code><pre class="src src-conf" id="org4eade8e"><code>- { p: relayhost, v: <span class="org-string">"[{{ front_wg_addr }}]"</span> }
+<code>postfix-core-relayhost</code><pre class="src src-conf" id="org2a18b37"><code>- { p: relayhost, v: <span class="org-string">"[{{ front_wg_addr }}]"</span> }
 </code></pre>
 </div>
 
@@ -3560,7 +3656,7 @@ file.
 </p>
 
 <div class="org-src-container">
-<code>postfix-transport</code><pre class="src src-conf" id="orgeb4b125"><code>.{{ domain_name }}      local:$myhostname
+<code>postfix-transport</code><pre class="src src-conf" id="orgd818239"><code>.{{ domain_name }}      local:$myhostname
 .{{ domain_priv }}      local:$myhostname
 </code></pre>
 </div>
@@ -3571,7 +3667,7 @@ The complete list of Core's Postfix settings for
 </p>
 
 <div class="org-src-container">
-<code>postfix-core</code><pre class="src src-conf" id="orgcd46829"><code>&lt;&lt;postfix-relaying&gt;&gt;
+<code>postfix-core</code><pre class="src src-conf" id="org8b5959e"><code>&lt;&lt;postfix-relaying&gt;&gt;
 - { p: smtpd_tls_security_level, v: none }
 - { p: smtp_tls_security_level, v: none }
 &lt;&lt;postfix-message-size&gt;&gt;
@@ -3649,8 +3745,8 @@ enable the service.  Whenever <q>/etc/postfix/transport</q> is changed, the
 </div>
 </div>
 </div>
-<div id="outline-container-org22528f7" class="outline-3">
-<h3 id="org22528f7"><span class="section-number-3">8.15.</span> Configure Private Email Aliases</h3>
+<div id="outline-container-org3c74833" class="outline-3">
+<h3 id="org3c74833"><span class="section-number-3">8.15.</span> Configure Private Email Aliases</h3>
 <div class="outline-text-3" id="text-8-15">
 <p>
 The institute's Core needs to deliver email addressed to institute
@@ -3670,6 +3766,7 @@ installed by more specialized roles.
         admin:          root
         www-data:       root
         monkey:         root
+        root:           {{ ansible_user }}
     path: /etc/aliases
     marker: <span class="org-string">"# {mark} INSTITUTE MANAGED BLOCK"</span>
   notify: New aliases.
@@ -3686,8 +3783,8 @@ installed by more specialized roles.
 </div>
 </div>
 </div>
-<div id="outline-container-org8e119ad" class="outline-3">
-<h3 id="org8e119ad"><span class="section-number-3">8.16.</span> Configure Dovecot IMAPd</h3>
+<div id="outline-container-org40730fb" class="outline-3">
+<h3 id="org40730fb"><span class="section-number-3">8.16.</span> Configure Dovecot IMAPd</h3>
 <div class="outline-text-3" id="text-8-16">
 <p>
 Core uses Dovecot's IMAPd to store and serve member emails.  As on
@@ -3697,7 +3794,7 @@ top" given that Core is only accessed from private (encrypted)
 networks, but helps to ensure privacy even when members accidentally
 attempt connections from outside the private networks.  For more
 information about Core's role in the institute's email services, see
-<a href="#orgff9aad1">The Email Service</a>.
+<a href="#org9ef70e4">The Email Service</a>.
 </p>
 
 <p>
@@ -3705,7 +3802,7 @@ The institute follows the recommendation in the package
 <q>README.Debian</q> (in <q>/usr/share/dovecot-core/</q>) but replaces the
 default "snake oil" certificate with another, signed by the institute.
 (For more information about the institute's X.509 certificates, see
-<a href="#orgaf187bb">Keys</a>.)
+<a href="#orga69013c">Keys</a>.)
 </p>
 
 <p>
@@ -3758,8 +3855,8 @@ and enables it to start at every reboot.
 </div>
 </div>
 </div>
-<div id="outline-container-orgb01b9ad" class="outline-3">
-<h3 id="orgb01b9ad"><span class="section-number-3">8.17.</span> Configure Fetchmail</h3>
+<div id="outline-container-orgd1ac791" class="outline-3">
+<h3 id="orgd1ac791"><span class="section-number-3">8.17.</span> Configure Fetchmail</h3>
 <div class="outline-text-3" id="text-8-17">
 <p>
 Core runs a <code>fetchmail</code> for each member of the institute.  Individual
@@ -3776,7 +3873,7 @@ the username.  The template is only used when the record has a
 </p>
 
 <div class="org-src-container">
-<code>fetchmail-config</code><pre class="src src-conf" id="org306ddb3"><code><span class="org-comment-delimiter"># </span><span class="org-comment">Permissions on this file may be no greater than 0600.
+<code>fetchmail-config</code><pre class="src src-conf" id="org9923336"><code><span class="org-comment-delimiter"># </span><span class="org-comment">Permissions on this file may be no greater than 0600.
 </span>
 set no bouncemail
 set no spambounce
@@ -3795,7 +3892,7 @@ The Systemd service description.
 </p>
 
 <div class="org-src-container">
-<code>fetchmail-service</code><pre class="src src-conf" id="orgd9f1e00"><code>[<span class="org-type">Unit</span>]
+<code>fetchmail-service</code><pre class="src src-conf" id="org5b02326"><code>[<span class="org-type">Unit</span>]
 <span class="org-variable-name">Description</span>=Fetchmail --idle task for {{ item }}.
 <span class="org-variable-name">AssertPathExists</span>=/home/{{ item }}/.fetchmailrc
 <span class="org-variable-name">After</span>=wg-quick@wg0.service
@@ -3912,12 +4009,12 @@ Otherwise the following task might be appropriate.
 </div>
 </div>
 </div>
-<div id="outline-container-org0584f40" class="outline-3">
-<h3 id="org0584f40"><span class="section-number-3">8.18.</span> Configure Apache2 <a id="org017921e"></a></h3>
+<div id="outline-container-org2156eba" class="outline-3">
+<h3 id="org2156eba"><span class="section-number-3">8.18.</span> Configure Apache2 <a id="orgb32e0d5"></a></h3>
 <div class="outline-text-3" id="text-8-18">
 <p>
 This is the small institute's campus web server.  It hosts several web
-sites as described in <a href="#org5da08ea">The Web Services</a>.
+sites as described in <a href="#org7434abb">The Web Services</a>.
 </p>
 
 <table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
@@ -3988,7 +4085,7 @@ naming a sub-directory in the member's home directory on Core.  The
 </p>
 
 <div class="org-src-container">
-<code>apache-userdir-core</code><pre class="src src-conf" id="org6ad7a77"><code>UserDir Public/HTML
+<code>apache-userdir-core</code><pre class="src src-conf" id="orgdf45518"><code>UserDir Public/HTML
 &lt;Directory /home/*/Public/HTML/&gt;
         Require all granted
         AllowOverride None
@@ -4003,7 +4100,7 @@ redirect, the encryption ciphers and certificates.
 </p>
 
 <div class="org-src-container">
-<code>apache-live</code><pre class="src src-conf" id="orgc1eb3c8"><code>&lt;VirtualHost *:80&gt;
+<code>apache-live</code><pre class="src src-conf" id="org4c9ef97"><code>&lt;VirtualHost *:80&gt;
         ServerName live
         ServerAlias live.{{ domain_priv }}
         ServerAdmin webmaster@core.{{ domain_priv }}
@@ -4030,7 +4127,7 @@ familiar.
 </p>
 
 <div class="org-src-container">
-<code>apache-test</code><pre class="src src-conf" id="org2d1ec07"><code>&lt;VirtualHost *:80&gt;
+<code>apache-test</code><pre class="src src-conf" id="orgf726f7a"><code>&lt;VirtualHost *:80&gt;
         ServerName test
         ServerAlias test.{{ domain_priv }}
         ServerAdmin webmaster@core.{{ domain_priv }}
@@ -4059,7 +4156,7 @@ trained staffers, monitored by a revision control system, etc.
 </p>
 
 <div class="org-src-container">
-<code>apache-campus</code><pre class="src src-conf" id="org5104462"><code>&lt;VirtualHost *:80&gt;
+<code>apache-campus</code><pre class="src src-conf" id="org50392c1"><code>&lt;VirtualHost *:80&gt;
         ServerName www
         ServerAlias www.{{ domain_priv }}
         ServerAdmin webmaster@core.{{ domain_priv }}
@@ -4183,8 +4280,8 @@ The <code>a2ensite</code> command enables them.
 </div>
 </div>
 </div>
-<div id="outline-container-orgf96e151" class="outline-3">
-<h3 id="orgf96e151"><span class="section-number-3">8.19.</span> Configure Website Updates</h3>
+<div id="outline-container-org29c7e39" class="outline-3">
+<h3 id="org29c7e39"><span class="section-number-3">8.19.</span> Configure Website Updates</h3>
 <div class="outline-text-3" id="text-8-19">
 <p>
 Monkey on Core runs <q>/usr/local/sbin/webupdate</q> every 15 minutes via a
@@ -4193,7 +4290,7 @@ Monkey on Core runs <q>/usr/local/sbin/webupdate</q> every 15 minutes via a
 </p>
 
 <div class="org-src-container">
-<a href="private/webupdate"><q>private/webupdate</q></a><pre class="src src-sh" id="org408edb7"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
+<a href="private/webupdate"><q>private/webupdate</q></a><pre class="src src-sh" id="orgecee2dc"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
 </span><span class="org-comment-delimiter">#</span><span class="org-comment">
 </span><span class="org-comment-delimiter"># </span><span class="org-comment">DO NOT EDIT.
 </span><span class="org-comment-delimiter">#</span><span class="org-comment">
@@ -4204,14 +4301,14 @@ Monkey on Core runs <q>/usr/local/sbin/webupdate</q> every 15 minutes via a
 rsync -avz --delete --chmod=g-w         <span class="org-sh-escaped-newline">\</span>
         --filter=<span class="org-string">'exclude *~'</span>           <span class="org-sh-escaped-newline">\</span>
         --filter=<span class="org-string">'exclude .git*'</span>        <span class="org-sh-escaped-newline">\</span>
-        ./ {{ domain_name }}:/home/www/
+        ./ 192.168.15.4:/home/www/
 </code></pre>
 </div>
 
 <p>
 The following tasks install the <q>webupdate</q> script from <a href="private/"><q>private/</q></a>,
 and create Monkey's <code>cron</code> job.  An example <q>webupdate</q> script is
-provided <a href="#org408edb7">here</a>.
+provided <a href="#orgecee2dc">here</a>.
 </p>
 
 <div class="org-src-container">
@@ -4236,39 +4333,19 @@ provided <a href="#org408edb7">here</a>.
 </div>
 </div>
 </div>
-<div id="outline-container-orge87b2b4" class="outline-3">
-<h3 id="orge87b2b4"><span class="section-number-3">8.20.</span> Configure Core WireGuard™ Interface</h3>
+<div id="outline-container-org09d114e" class="outline-3">
+<h3 id="org09d114e"><span class="section-number-3">8.20.</span> Configure Core WireGuard™ Interface</h3>
 <div class="outline-text-3" id="text-8-20">
 <p>
 Core connects to Front's WireGuard™ service to provide members abroad
-with a route to the campus networks.  As described in <a href="#org25a022b">Configure Public
+with a route to the campus networks.  As described in <a href="#org8c0e7a6">Configure Public
 WireGuard™ Subnet</a> for Front, Core is expected to forward packets from/to the
 private networks.
 </p>
 
 <p>
-The following example <a href="private/core-wg0.conf"><q>private/core-wg0.conf</q></a> configuration recognizes
-Front by its public key, <code>S+6HaT</code>, looking for it at the institute's
-public IP address and a special port.
-</p>
-
-<div class="org-src-container">
-<a href="private/core-wg0.conf"><q>private/core-wg0.conf</q></a><pre class="src src-conf"><code>[<span class="org-type">Interface</span>]
-<span class="org-variable-name">Address</span> = 10.177.87.2
-<span class="org-variable-name">PostUp</span> = wg set %i private-key /etc/wireguard/private-key
-
-<span class="org-comment-delimiter"># </span><span class="org-comment">Front
-</span>[<span class="org-type">Peer</span>]
-<span class="org-variable-name">EndPoint</span> = 192.168.15.5:39608
-<span class="org-variable-name">PublicKey</span> = S+6HaTnOwwhWgUGXjSBcPAvifKw+j8BDTRfq534gNW4=
-<span class="org-variable-name">AllowedIPs</span> = 10.177.87.1
-<span class="org-variable-name">AllowedIPs</span> = 10.177.87.0/24
-</code></pre>
-</div>
-
-<p>
-The following tasks install WireGuard™, configure it with
-<a href="private/core-wg0.conf"><q>private/core-wg0.conf</q></a>, and enable the service.
+The following tasks install WireGuard™, configure it and enable the
+service.
 </p>
 
 <div class="org-src-container">
@@ -4287,7 +4364,17 @@ The following tasks install WireGuard™, configure it with
 - name: Configure WireGuard&#8482;.
   become: yes
   copy:
-    src: ../private/core-wg0.conf
+    content: |
+      [<span class="org-type">Interface</span>]
+      <span class="org-variable-name">Address</span> = {{ core_wg_addr }}
+      <span class="org-variable-name">PostUp</span> = wg set %i private-key /etc/wireguard/private-key
+
+      <span class="org-comment-delimiter"># </span><span class="org-comment">Front
+</span>      [<span class="org-type">Peer</span>]
+      <span class="org-variable-name">EndPoint</span> = {{ front_addr }}:{{ public_wg_port }}
+      <span class="org-variable-name">PublicKey</span> = {{ front_wg_pubkey }}
+      <span class="org-variable-name">AllowedIPs</span> = {{ front_wg_addr }}
+      <span class="org-variable-name">AllowedIPs</span> = {{ public_wg_net_cidr }}
     dest: /etc/wireguard/wg0.conf
     <span class="org-variable-name">mode: u</span>=r,g=,o=
     owner: root
@@ -4321,16 +4408,15 @@ The following tasks install WireGuard™, configure it with
 </div>
 </div>
 </div>
-<div id="outline-container-org0a9da4e" class="outline-3">
-<h3 id="org0a9da4e"><span class="section-number-3">8.21.</span> Configure NAGIOS</h3>
+<div id="outline-container-org91d41ee" class="outline-3">
+<h3 id="org91d41ee"><span class="section-number-3">8.21.</span> Configure NAGIOS</h3>
 <div class="outline-text-3" id="text-8-21">
 <p>
 Core runs a <code>nagios4</code> server to monitor "services" on institute hosts.
 The following tasks install the necessary packages and configure the
-server.  The last task installs the monitoring configuration in
-<q>/etc/nagios4/conf.d/institute.cfg</q>.  This configuration file,
-<q>nagios.cfg</q>, is tangled from code blocks described in subsequent
-subsections.
+server via edits to <q>/etc/nagios4/nagios.cfg</q>.  The monitors are
+installed in <q>/etc/nagios4/conf.d/institute.cfg</q> which is tangled from
+code blocks described in the following subsections.
 </p>
 
 <p>
@@ -4364,9 +4450,10 @@ Core and Campus (and thus Gate) machines.
     line: <span class="org-string">"{{ item.line }}"</span>
     backrefs: yes
   loop:
-  - { regexp: <span class="org-string">"^( *cfg_file *= *localhost.cfg)"</span>, line: <span class="org-string">"# \\1"</span> }
-  <span class="org-type">-</span> { regexp: <span class="org-string">"^( *admin_email *= *)"</span>,
-      line: <span class="org-string">"\\1{{ ansible_user }}@localhost"</span> }
+  - regexp: <span class="org-string">"^( *cfg_file *=.*/localhost.cfg)"</span>
+    line: <span class="org-string">"#\\1"</span>
+  - regexp: <span class="org-string">"^( *admin_email *= *)"</span>
+    line: <span class="org-string">"\\1{{ ansible_user }}@localhost"</span>
   notify: Reload NAGIOS4.
 
 - name: Configure NAGIOS4 contacts.
@@ -4411,8 +4498,8 @@ Core and Campus (and thus Gate) machines.
 </code></pre>
 </div>
 </div>
-<div id="outline-container-orgfa72bea" class="outline-4">
-<h4 id="orgfa72bea"><span class="section-number-4">8.21.1.</span> Configure NAGIOS Monitors for Core</h4>
+<div id="outline-container-org7b5f025" class="outline-4">
+<h4 id="org7b5f025"><span class="section-number-4">8.21.1.</span> Configure NAGIOS Monitors for Core</h4>
 <div class="outline-text-4" id="text-8-21-1">
 <p>
 The first block in <q>nagios.cfg</q> specifies monitors for services on
@@ -4487,8 +4574,8 @@ used here <i>may</i> specify plugin arguments.
 </div>
 </div>
 </div>
-<div id="outline-container-org5b3e3c7" class="outline-4">
-<h4 id="org5b3e3c7"><span class="section-number-4">8.21.2.</span> Custom NAGIOS Monitor <code>inst_sensors</code></h4>
+<div id="outline-container-orgd066fba" class="outline-4">
+<h4 id="orgd066fba"><span class="section-number-4">8.21.2.</span> Custom NAGIOS Monitor <code>inst_sensors</code></h4>
 <div class="outline-text-4" id="text-8-21-2">
 <p>
 The <code>check_sensors</code> plugin is included in the package
@@ -4600,8 +4687,8 @@ Core.
 </div>
 </div>
 </div>
-<div id="outline-container-orgb3ae2fd" class="outline-4">
-<h4 id="orgb3ae2fd"><span class="section-number-4">8.21.3.</span> Configure NAGIOS Monitors for Remote Hosts</h4>
+<div id="outline-container-orgf5aab83" class="outline-4">
+<h4 id="orgf5aab83"><span class="section-number-4">8.21.3.</span> Configure NAGIOS Monitors for Remote Hosts</h4>
 <div class="outline-text-4" id="text-8-21-3">
 <p>
 The following sections contain code blocks specifying monitors for
@@ -4618,12 +4705,12 @@ plugin with pre-defined arguments appropriate for the institute.  The
 commands are defined in code blocks interleaved with the blocks that
 monitor them.  The command blocks are appended to <q>nrpe.cfg</q> and the
 monitoring blocks to <q>nagios.cfg</q>.  The <q>nrpe.cfg</q> file is installed
-on each campus host by the campus role's <a href="#org8fbeb2b">Configure NRPE</a> tasks.
+on each campus host by the campus role's <a href="#orge6dfd20">Configure NRPE</a> tasks.
 </p>
 </div>
 </div>
-<div id="outline-container-orgfb7572d" class="outline-4">
-<h4 id="orgfb7572d"><span class="section-number-4">8.21.4.</span> Configure NAGIOS Monitors for Gate</h4>
+<div id="outline-container-org4ce57c6" class="outline-4">
+<h4 id="org4ce57c6"><span class="section-number-4">8.21.4.</span> Configure NAGIOS Monitors for Gate</h4>
 <div class="outline-text-4" id="text-8-21-4">
 <p>
 Define the monitored host, <code>gate</code>.  Monitor its response to network
@@ -4754,12 +4841,12 @@ Monitor <code>inst_sensors</code> on Gate.
 </div>
 </div>
 </div>
-<div id="outline-container-orgb17f0e2" class="outline-3">
-<h3 id="orgb17f0e2"><span class="section-number-3">8.22.</span> Configure Backups</h3>
+<div id="outline-container-org995c1a0" class="outline-3">
+<h3 id="org995c1a0"><span class="section-number-3">8.22.</span> Configure Backups</h3>
 <div class="outline-text-3" id="text-8-22">
 <p>
 The following task installs the <q>backup</q> script from <a href="private/"><q>private/</q></a>.  An
-example script is provided in <a href="#org2d7d21c">here</a>.
+example script is provided in <a href="#orgdb96821">here</a>.
 </p>
 
 <div class="org-src-container">
@@ -4774,20 +4861,20 @@ example script is provided in <a href="#org2d7d21c">here</a>.
 </div>
 </div>
 </div>
-<div id="outline-container-orga909c3b" class="outline-3">
-<h3 id="orga909c3b"><span class="section-number-3">8.23.</span> Configure Nextcloud</h3>
+<div id="outline-container-org1714fe9" class="outline-3">
+<h3 id="org1714fe9"><span class="section-number-3">8.23.</span> Configure Nextcloud</h3>
 <div class="outline-text-3" id="text-8-23">
 <p>
 Core runs Nextcloud to provide a private institute cloud, as described
-in <a href="#orgdabefd7">The Cloud Service</a>.  Installing, restoring (from backup), and
-upgrading Nextcloud are manual processes documented in <a href="https://docs.nextcloud.com/server/latest/admin_manual/maintenance/">The Nextcloud
+in <a href="#org44dc60e">The Cloud Service</a>.  Installing, restoring (from backup), and
+upgrading Nextcloud are manual processes documented in <a href="https://docs.nextcloud.com/server/stable/admin_manual/maintenance/">The Nextcloud
 Admin Manual, Maintenance</a>.  However Ansible can help prepare Core
 before an install or restore, and perform basic security checks
 afterwards.
 </p>
 </div>
-<div id="outline-container-org8276fb0" class="outline-4">
-<h4 id="org8276fb0"><span class="section-number-4">8.23.1.</span> Prepare Core For Nextcloud</h4>
+<div id="outline-container-orgea52e00" class="outline-4">
+<h4 id="orgea52e00"><span class="section-number-4">8.23.1.</span> Prepare Core For Nextcloud</h4>
 <div class="outline-text-4" id="text-8-23-1">
 <p>
 The Ansible code contained herein prepares Core to run Nextcloud by
@@ -4803,7 +4890,7 @@ installing a cron job.
     pkg: [ apache2, mariadb-server, php, php-apcu, php-bcmath,
            php-curl, php-gd, php-gmp, php-json, php-mysql,
            php-mbstring, php-intl, php-imagick, php-xml, php-zip,
-           libapache2-mod-php ]
+           imagemagick, libapache2-mod-php ]
 </code></pre>
 </div>
 
@@ -4826,7 +4913,7 @@ The Apache2 configuration is then extended with the following
 <q>/etc/apache2/sites-available/nextcloud.conf</q> file, which is installed
 and enabled with <code>a2ensite</code>.  The same configuration lines are given
 in the "Installation on Linux" section of the Nextcloud Server
-Administration Guide (sub-section <a href="https://docs.nextcloud.com/server/latest/admin_manual/installation/source_installation.html">Apache Web server configuration</a>).
+Administration Guide (sub-section <a href="https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html">Apache Web server configuration</a>).
 </p>
 
 <div class="org-src-container">
@@ -4949,7 +5036,7 @@ the <code>apg -n 1 -x 12 -m 12</code> command.
 </p>
 
 <div class="org-src-container">
-<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>nextcloud_dbpass:           ippAgmaygyob
+<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>nextcloud_dbpass:           ippAgmaygyobwyt5
 </code></pre>
 </div>
 
@@ -5029,8 +5116,8 @@ its document root.
 </div>
 </div>
 </div>
-<div id="outline-container-org827a6e0" class="outline-4">
-<h4 id="org827a6e0"><span class="section-number-4">8.23.2.</span> Configure PHP</h4>
+<div id="outline-container-org431a32a" class="outline-4">
+<h4 id="org431a32a"><span class="section-number-4">8.23.2.</span> Configure PHP</h4>
 <div class="outline-text-4" id="text-8-23-2">
 <p>
 The following tasks set a number of PHP parameters for better
@@ -5043,8 +5130,8 @@ performance, as recommended by Nextcloud.
   become: yes
   lineinfile:
     path: /etc/php/8.2/apache2/php.ini
-    <span class="org-variable-name">regexp: memory_limit *</span>=
-    <span class="org-variable-name">line: memory_limit</span> = 768M
+    regexp: <span class="org-string">"memory_limit *="</span>
+    line: <span class="org-string">"memory_limit = 768M"</span>
 
 - name: Include PHP parameters for Nextcloud.
   become: yes
@@ -5073,8 +5160,8 @@ performance, as recommended by Nextcloud.
 </div>
 </div>
 </div>
-<div id="outline-container-orgd428b0f" class="outline-4">
-<h4 id="orgd428b0f"><span class="section-number-4">8.23.3.</span> Create <q>/Nextcloud/</q></h4>
+<div id="outline-container-org814b85d" class="outline-4">
+<h4 id="org814b85d"><span class="section-number-4">8.23.3.</span> Create <q>/Nextcloud/</q></h4>
 <div class="outline-text-4" id="text-8-23-3">
 <p>
 The Ansible tasks up to this point have completed Core's LAMP stack
@@ -5132,8 +5219,8 @@ sudo mount /Nextcloud
 </div>
 </div>
 </div>
-<div id="outline-container-org0c72e46" class="outline-4">
-<h4 id="org0c72e46"><span class="section-number-4">8.23.4.</span> Restore Nextcloud</h4>
+<div id="outline-container-org7b57c4e" class="outline-4">
+<h4 id="org7b57c4e"><span class="section-number-4">8.23.4.</span> Restore Nextcloud</h4>
 <div class="outline-text-4" id="text-8-23-4">
 <p>
 Restoring Nextcloud in the newly created <q>/Nextcloud/</q> presumably
@@ -5157,7 +5244,7 @@ make it so.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>sudo chown -R www-data.www-data /Nextcloud/nextcloud/
+<pre class="src src-sh"><code>sudo chown -R www-data:www-data /Nextcloud/nextcloud/
 </code></pre>
 </div>
 
@@ -5192,34 +5279,35 @@ Overview web page.
 </p>
 </div>
 </div>
-<div id="outline-container-org0727aa7" class="outline-4">
-<h4 id="org0727aa7"><span class="section-number-4">8.23.5.</span> Install Nextcloud</h4>
+<div id="outline-container-orgdd6d845" class="outline-4">
+<h4 id="orgdd6d845"><span class="section-number-4">8.23.5.</span> Install Nextcloud</h4>
 <div class="outline-text-4" id="text-8-23-5">
 <p>
 Installing Nextcloud in the newly created <q>/Nextcloud/</q> starts with
 downloading and verifying a recent release tarball.  The following
-example command lines unpacked Nextcloud 23 in <q>nextcloud/</q> in
+example command lines unpacked Nextcloud 31 in <q>nextcloud/</q> in
 <q>/Nextcloud/</q> and set the ownerships and permissions of the new
 directories and files.
 </p>
 
 <div class="org-src-container">
 <pre class="src src-sh"><code><span class="org-builtin">cd</span> /Nextcloud/
-tar xzf ~/Downloads/nextcloud-23.0.0.tar.bz2
-sudo chown -R www-data.www-data nextcloud
+tar xjf ~/Downloads/nextcloud-31.0.2.tar.bz2
+sudo chown -R www-data:www-data nextcloud
 sudo find nextcloud -type d -exec chmod 750 {} <span class="org-string">\;</span>
 sudo find nextcloud -type f -exec chmod 640 {} <span class="org-string">\;</span>
 </code></pre>
 </div>
 
 <p>
-According to the latest installation instructions in version 24's
-administration guide, after unpacking and setting file permissions,
-the following <code>occ</code> command takes care of everything.  This command
-currently expects Nextcloud's database and user to exist.  The
-following SQL commands create the database and user (entered at the
-SQL prompt of the <code>sudo mysql</code> command).  The shell command then runs
-<code>occ</code>.
+According to the latest installation instructions in the Admin Manual
+for version 31 (section "Installation and server configuration",
+subsection "Installing from command line", <a href="https://docs.nextcloud.com/server/stable/admin_manual/installation/command_line_installation.html">here</a>), after unpacking and
+setting file permissions, the following <code>occ</code> command takes care of
+everything.  This command currently expects Nextcloud's database and
+user to exist.  The following SQL commands create the database and
+user (entered at the SQL prompt of the <code>sudo mysql</code> command).  The
+shell command then runs <code>occ</code>.
 </p>
 
 <div class="org-src-container">
@@ -5236,11 +5324,9 @@ flush <span class="org-keyword">privileges</span>;
 <div class="org-src-container">
 <pre class="src src-sh"><code><span class="org-builtin">cd</span> /var/www/nextcloud/
 sudo -u www-data php occ maintenance:install <span class="org-sh-escaped-newline">\</span>
-     --data-dir=/var/www/nextcloud/data <span class="org-sh-escaped-newline">\</span>
-     --database=mysql --database-name=nextcloud <span class="org-sh-escaped-newline">\</span>
-     --database-user=nextclouduser <span class="org-sh-escaped-newline">\</span>
-     --database-pass=ippAgmaygyobwyt5 <span class="org-sh-escaped-newline">\</span>
-     --admin-user=sysadm --admin-pass=PASSWORD
+--database=<span class="org-string">'mysql'</span> --database-name=<span class="org-string">'nextcloud'</span> <span class="org-sh-escaped-newline">\</span>
+--database-user=<span class="org-string">'nextclouduser'</span> --database-pass=<span class="org-string">'ippAgmaygyobwyt5'</span> <span class="org-sh-escaped-newline">\</span>
+--admin-user=<span class="org-string">'sysadm'</span> --admin-pass=<span class="org-string">'fubar'</span>
 </code></pre>
 </div>
 
@@ -5263,8 +5349,8 @@ Administration &gt; Overview page.
 </p>
 </div>
 </div>
-<div id="outline-container-orgdd1a3b6" class="outline-4">
-<h4 id="orgdd1a3b6"><span class="section-number-4">8.23.6.</span> Afterwards</h4>
+<div id="outline-container-orgb9ad7d5" class="outline-4">
+<h4 id="orgb9ad7d5"><span class="section-number-4">8.23.6.</span> Afterwards</h4>
 <div class="outline-text-4" id="text-8-23-6">
 <p>
 Whether Nextcloud was restored or installed, there are a few things
@@ -5354,7 +5440,7 @@ enables it.
 </div>
 
 <p>
-The institute implements Pretty URLs as described in the <a href="https://docs.nextcloud.com/server/22/admin_manual/installation/source_installation.html#pretty-urls">Pretty URLs</a>
+The institute implements Pretty URLs as described in the <a href="https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#pretty-urls">Pretty URLs</a>
 subsection of the "Installation on Linux" section of the "Installation
 and server configuration" chapter in the Nextcloud 22 Server
 Administration Guide.  Two settings are updated: <code>overwrite.cli.url</code>
@@ -5392,16 +5478,46 @@ a complaint on the Settings &gt; Administration &gt; Overview web page.
 </code></pre>
 </div>
 
+<p>
+It sets Nextcloud's "maintenance window" to start at 02:00MST
+(09:00UTC).  The interval is 4 hours, so ends at 06:00MST.  The
+documentation for the setting was found <a href="https://docs.nextcloud.com/server/31/admin_manual/configuration_server/background_jobs_configuration.html">here</a>.
+</p>
+
+<p>
+It also configures Nextcloud to send email with <q>/usr/sbin/sendmail</q>
+<code>From: webmaster@core.small.private</code>.  The documentation for the
+settings was found <a href="https://docs.nextcloud.com/server/31/admin_manual/configuration_server/email_configuration.html">here</a> though just two parameters are set here, not
+the 9 suggested in sub-sub-subsection "Sendmail", of sub-subsection
+"Setting mail server parameters in config.php", seemed a simple,
+unedited copy of the parameters SMTP and <i>not</i> by Sendmail nor Qmail.
+</p>
+
 <div class="org-src-container">
 <a href="roles_t/core/tasks/main.yml"><q>roles_t/core/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Configure Nextcloud phone region.
+- name: Configure Nextcloud settings.
   become: yes
   lineinfile:
     path: /var/www/nextcloud/config/config.php
-    regexp: <span class="org-string">"^ *'default_phone_region' *=&gt; *'.*', *$"</span>
-    line: <span class="org-string">"  'default_phone_region' =&gt; '{{ nextcloud_region }}',"</span>
+    regexp: <span class="org-string">"{{ item.regexp }}"</span>
+    line: <span class="org-string">"{{ item.line }}"</span>
     insertbefore: <span class="org-string">"^[)];"</span>
     firstmatch: yes
+  loop:
+  - regexp: <span class="org-string">"^ *'default_phone_region' *=&gt; *'.*', *$"</span>
+    line: <span class="org-string">"  'default_phone_region' =&gt; '{{ nextcloud_region }}',"</span>
+
+  - regexp: <span class="org-string">"^ *'maintenance_window_start' *=&gt; "</span>
+    line: <span class="org-string">"  'maintenance_window_start' =&gt; 9,"</span>
+
+  - regexp: <span class="org-string">"^ *'mail_smtpmode' *=&gt;"</span>
+    line: <span class="org-string">"  'mail_smtpmode' =&gt; 'sendmail',"</span>
+  - regexp: <span class="org-string">"^ *'mail_sendmailmode' *=&gt;"</span>
+    line: <span class="org-string">"  'mail_sendmailmode' =&gt; 'pipe',"</span>
+  - regexp: <span class="org-string">"^ *'mail_from_address' *=&gt;"</span>
+    line: <span class="org-string">"  'mail_from_address' =&gt; 'webmaster',"</span>
+  - regexp: <span class="org-string">"^ *'mail_domain' *=&gt;"</span>
+    line: <span class="org-string">"  'mail_domain' =&gt; 'core.small.private',"</span>
   when: nextcloud.stat.exists
 </code></pre>
 </div>
@@ -5446,14 +5562,14 @@ run before the next backup.
 </div>
 </div>
 </div>
-<div id="outline-container-org14a3ff4" class="outline-2">
-<h2 id="org14a3ff4"><span class="section-number-2">9.</span> The Gate Role</h2>
+<div id="outline-container-orge739301" class="outline-2">
+<h2 id="orge739301"><span class="section-number-2">9.</span> The Gate Role</h2>
 <div class="outline-text-2" id="text-9">
 <p>
 The <code>gate</code> role configures the services expected at the campus gate:
 access to the private Ethernet from the untrusted Ethernet (e.g. a
 campus Wi-Fi AP) via VPN, and access to the Internet via NAT.  The
-gate machine uses three network interfaces (see <a href="#orgd15e60d">The Gate Machine</a>)
+gate machine uses three network interfaces (see <a href="#orgcbbedd8">The Gate Machine</a>)
 configured with persistent names used in its firewall rules.
 </p>
 
@@ -5475,42 +5591,48 @@ applied first, by which Gate gets a campus machine's DNS and Postfix
 configurations, etc.
 </p>
 </div>
-<div id="outline-container-orgc86b73c" class="outline-3">
-<h3 id="orgc86b73c"><span class="section-number-3">9.1.</span> Include Particulars</h3>
+<div id="outline-container-orgbce30f7" class="outline-3">
+<h3 id="orgbce30f7"><span class="section-number-3">9.1.</span> Role Defaults</h3>
 <div class="outline-text-3" id="text-9-1">
 <p>
-The following should be familiar boilerplate by now.
+As in <a href="#org1673460">The Core Role</a>, the <code>gate</code> role sets a number of variables to
+default values in its <q>defaults/main.yml</q> file.
 </p>
 
 <div class="org-src-container">
-<a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>---
-- name: Include public variables.
-  include_vars: ../public/vars.yml
-  tags: accounts
-- name: Include private variables.
-  include_vars: ../private/vars.yml
-  tags: accounts
-- name: Include members.
-  include_vars: <span class="org-string">"{{ lookup('first_found', membership_rolls) }}"</span>
-  tags: accounts
+<a href="roles_t/gate/defaults/main.yml"><q>roles_t/gate/defaults/main.yml</q></a><pre class="src src-conf"><code>---
+&lt;&lt;network-vars&gt;&gt;
+&lt;&lt;address-vars&gt;&gt;
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-orgd31a78c" class="outline-3">
-<h3 id="orgd31a78c"><span class="section-number-3">9.2.</span> Configure Netplan <a id="orgc350fb4"></a></h3>
+<div id="outline-container-org4fa95ea" class="outline-3">
+<h3 id="org4fa95ea"><span class="section-number-3">9.2.</span> Include Particulars</h3>
 <div class="outline-text-3" id="text-9-2">
 <p>
-Gate's network interfaces are configured using Netplan and two files.
-<q>/etc/netplan/60-gate.yaml</q> describes the static interfaces, to the
-campus Ethernet and Wi-Fi.  <q>/etc/netplan/60-isp.yaml</q> is expected to
-be revised more frequently as the campus ISP changes.
+The following should be familiar boilerplate by now.
 </p>
 
+<div class="org-src-container">
+<a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>---
+- name: Include public variables.
+  include_vars: ../public/vars.yml
+
+- name: Include private variables.
+  include_vars: ../private/vars.yml
+</code></pre>
+</div>
+</div>
+</div>
+<div id="outline-container-orgdc2e00c" class="outline-3">
+<h3 id="orgdc2e00c"><span class="section-number-3">9.3.</span> Configure Gate NetworkD</h3>
+<div class="outline-text-3" id="text-9-3">
 <p>
-Netplan is configured to identify the interfaces by their MAC
-addresses, which must be provided in <a href="private/vars.yml"><q>private/vars.yml</q></a>, as in the
-example code here.
+Gate's network interfaces are configured using SystemD NetworkD
+configuration files that specify their MAC addresses.  (One or more
+might be plug-and-play USB dongles.)  These addresses are provided by
+the <a href="private/vars.yml"><q>private/vars.yml</q></a> file as in the example code here.
 </p>
 
 <div class="org-src-container">
@@ -5521,83 +5643,255 @@ gate_isp_mac:               08:00:27:3d:42:e5
 </div>
 
 <p>
-The following tasks install the two configuration files and apply the
-new network plan.
+The tasks in the following sections install the necessary
+configuration files.
+</p>
+</div>
+<div id="outline-container-org499093e" class="outline-4">
+<h4 id="org499093e"><span class="section-number-4">9.3.1.</span> Gate's <code>lan</code> Interface</h4>
+<div class="outline-text-4" id="text-9-3-1">
+<p>
+The campus Ethernet interface is named <code>lan</code> and configured by
+<q>10-lan.link</q> and <q>10-lan.network</q> files in <q>/etc/systemd/network/</q>.
 </p>
 
 <div class="org-src-container">
 <a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Install netplan (gate).
-  become: yes
-  <span class="org-variable-name">apt: pkg</span>=netplan.io
-
-- name: Configure netplan (gate).
+- name: Install 10-lan.link.
   become: yes
   copy:
     content: |
-      network:
-        ethernets:
-          lan:
-            match:
-              macaddress: {{ gate_lan_mac }}
-            addresses: [ {{ gate_addr_cidr }} ]
-            set-name: lan
-            dhcp4: false
-            nameservers:
-              addresses: [ {{ core_addr }} ]
-              search: [ {{ domain_priv }} ]
-            routes:
-              - to: {{ public_wg_net_cidr }}
-                via: {{ core_addr }}
-          wild:
-            match:
-              macaddress: {{ gate_wild_mac }}
-            addresses: [ {{ gate_wild_addr_cidr }} ]
-            set-name: wild
-            dhcp4: false
-    dest: /etc/netplan/60-gate.yaml
-    <span class="org-variable-name">mode: u</span>=rw,g=r,o=
-  notify: Apply netplan.
-
-- name: Install netplan (ISP).
+      [<span class="org-type">Match</span>]
+      <span class="org-variable-name">MACAddress</span>={{ gate_lan_mac }}
+
+      [<span class="org-type">Link</span>]
+      <span class="org-variable-name">Name</span>=lan
+    dest: /etc/systemd/network/10-lan.link
+  notify: Reload networkd.
+
+- name: Install 10-lan.network.
   become: yes
   copy:
     content: |
-      network:
-        ethernets:
-          isp:
-            match:
-              macaddress: {{ gate_isp_mac }}
-            set-name: isp
-            dhcp4: true
-            dhcp4-overrides:
-              use-dns: false
-    dest: /etc/netplan/60-isp.yaml
-    <span class="org-variable-name">mode: u</span>=rw,g=r,o=
-    force: no
-  notify: Apply netplan.
+      [<span class="org-type">Match</span>]
+      <span class="org-variable-name">MACAddress</span>={{ gate_lan_mac }}
+
+      [<span class="org-type">Network</span>]
+      <span class="org-variable-name">Address</span>={{ gate_addr_cidr }}
+      <span class="org-variable-name">DNS</span>={{ core_addr }}
+      <span class="org-variable-name">Domains</span>={{ domain_priv }}
+
+      [<span class="org-type">Route</span>]
+      <span class="org-variable-name">Destination</span>={{ public_wg_net_cidr }}
+      <span class="org-variable-name">Gateway</span>={{ core_addr }}
+    dest: /etc/systemd/network/10-lan.network
+  notify: Reload networkd.
 </code></pre>
 </div>
 
 <div class="org-src-container">
 <a href="roles_t/gate/handlers/main.yml"><q>roles_t/gate/handlers/main.yml</q></a><pre class="src src-conf"><code>---
-- name: Apply netplan.
+- name: Reload networkd.
   become: yes
-  command: netplan apply
+  command: networkctl reload
   tags: actualizer
 </code></pre>
 </div>
+</div>
+</div>
+<div id="outline-container-org87ac933" class="outline-4">
+<h4 id="org87ac933"><span class="section-number-4">9.3.2.</span> Gate's <code>wild</code> Interface</h4>
+<div class="outline-text-4" id="text-9-3-2">
+<p>
+The institute keeps the wild ones off the campus Ethernet.  Its wild
+subnet is connected to Gate via a separate physical interface.  To
+accommodate the wild ones without re-configuring them, the institute
+attempts to look like an up-link, e.g. a cable modem.  A wild one is
+expected to chirp for DHCP service and use the private subnet address
+in its lease.  Thus Gate's <code>wild</code> interface configuration enables the
+built-in DHCP server and lists the authorized lessees.
+</p>
+
+<p>
+The wild ones are not expected to number in the dozens, so they are
+simply a list of hashes in <a href="private/vars.yml"><q>private/vars.yml</q></a>, as in the example code
+here.  Note that host number 1 is Gate.  Wild ones are assigned unique
+host numbers greater than 1.
+</p>
+
+<div class="org-src-container">
+<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>wild_ones:
+- { MAC: <span class="org-string">"08:00:27:dc:54:b5"</span>, num: 2, name: wifi-ap }
+- { MAC: <span class="org-string">"94:83:c4:19:7d:58"</span>, num: 3, name: appliance }
+</code></pre>
+</div>
+
+<p>
+As with the <code>lan</code> interface, this interface is named <code>wild</code> and
+configured by <q>10-wild.link</q> and <q>10-wild.network</q> files in
+<q>/etc/systemd/network/</q>.  The latter is generated from the hashes in
+<code>wild_ones</code> and the <q>wild.network</q> template file.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
+- name: Install 10-wild.link.
+  become: yes
+  copy:
+    content: |
+      [<span class="org-type">Match</span>]
+      <span class="org-variable-name">MACAddress</span>={{ gate_wild_mac }}
+
+      [<span class="org-type">Link</span>]
+      <span class="org-variable-name">Name</span>=wild
+    dest: /etc/systemd/network/10-wild.link
+  notify: Reload networkd.
+
+- name: Install 10-wild.network.
+  become: yes
+  template:
+    src: wild.network
+    dest: /etc/systemd/network/10-wild.network
+  notify: Reload networkd.
+</code></pre>
+</div>
+
+<div class="org-src-container">
+<a href="roles_t/gate/templates/wild.network"><q>roles_t/gate/templates/wild.network</q></a><pre class="src src-conf"><code>[<span class="org-type">Match</span>]
+<span class="org-variable-name">MACAddress</span>={{ gate_wild_mac }}
+
+[<span class="org-type">Network</span>]
+<span class="org-variable-name">Address</span>={{ gate_wild_addr_cidr }}
+<span class="org-variable-name">DHCPServer</span>=yes
+
+[<span class="org-type">DHCPServer</span>]
+<span class="org-variable-name">EmitDNS</span>=yes
+<span class="org-variable-name">EmitNTP</span>=yes
+<span class="org-variable-name">NTP</span>={{ core_addr }}
+<span class="org-variable-name">EmitSMTP</span>=yes
+<span class="org-variable-name">SMTP</span>={{ core_addr }}
+{% for wild in wild_ones %}
+
+<span class="org-comment-delimiter"># </span><span class="org-comment">{{ wild.name }}
+</span>[<span class="org-type">DHCPServerStaticLease</span>]
+<span class="org-variable-name">MACAddress</span>={{ wild.MAC }}
+<span class="org-variable-name">Address</span>={{ wild_net_cidr |ansible.utils.ipaddr(wild.num) }}
+{% endfor %}
+</code></pre>
+</div>
+</div>
+</div>
+<div id="outline-container-org1329352" class="outline-4">
+<h4 id="org1329352"><span class="section-number-4">9.3.3.</span> Gate's <code>isp</code> Interface</h4>
+<div class="outline-text-4" id="text-9-3-3">
+<p>
+The interface to the campus ISP is named <code>isp</code> and configured by
+<q>10-isp.link</q> and <q>10-isp.network</q> files in <q>/etc/systemd/network/</q>.
+The latter is not automatically generated, as it varies quite a bit
+depending on the connection to the ISP: Ethernet interface, USB
+tether, Wi-Fi connection, etc.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
+- name: Install 10-isp.link.
+  become: yes
+  copy:
+    content: |
+      [<span class="org-type">Match</span>]
+      <span class="org-variable-name">MACAddress</span>={{ gate_isp_mac }}
+
+      [<span class="org-type">Link</span>]
+      <span class="org-variable-name">Name</span>=isp
+    dest: /etc/systemd/network/10-isp.link
+  notify: Reload networkd.
+
+- name: Install 10-isp.network.
+  become: yes
+  copy:
+    src: ../private/gate-isp.network
+    dest: /etc/systemd/network/10-isp.network
+    force: no
+  notify: Reload networkd.
+</code></pre>
+</div>
 
 <p>
 Note that the <q>60-isp.yaml</q> file is only updated (created) if it does
 not already exist so that it can be easily modified to debug a new
 campus ISP without interference from Ansible.
 </p>
+
+<p>
+The following example <q>gate-isp.network</q> file recognizes an Ethernet
+interface by its MAC address.
+</p>
+
+<div class="org-src-container">
+<a href="private/gate-isp.network"><q>private/gate-isp.network</q></a><pre class="src src-conf"><code>[<span class="org-type">Match</span>]
+<span class="org-variable-name">MACAddress</span>=08:00:27:3d:42:e5
+
+[<span class="org-type">Network</span>]
+<span class="org-variable-name">DHCP</span>=ipv4
+
+[<span class="org-type">DHCP</span>]
+<span class="org-variable-name">RouteMetric</span>=100
+<span class="org-variable-name">UseMTU</span>=true
+<span class="org-variable-name">UseDNS</span>=false
+</code></pre>
 </div>
 </div>
-<div id="outline-container-orgfca1bef" class="outline-3">
-<h3 id="orgfca1bef"><span class="section-number-3">9.3.</span> UFW Rules</h3>
-<div class="outline-text-3" id="text-9-3">
+</div>
+</div>
+<div id="outline-container-org407cd67" class="outline-3">
+<h3 id="org407cd67"><span class="section-number-3">9.4.</span> Configure Gate ResolveD</h3>
+<div class="outline-text-3" id="text-9-4">
+<p>
+Gate provides name service on the wild Ethernet by having its "stub
+listener" listen there.  That stub should not read <q>/etc/hosts</q> lest
+<code>gate</code> resolve to <code>127.0.1.1</code>, nonsense to the wild.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
+- name: Configure resolved.
+  become: yes
+  lineinfile:
+    path: /etc/systemd/resolved.conf
+    regexp: <span class="org-string">"{{ item.regexp }}"</span>
+    line: <span class="org-string">"{{ item.line }}"</span>
+  loop:
+  - regexp: <span class="org-string">'^ *DNSStubListenerExtra *='</span>
+    line: <span class="org-string">"DNSStubListenerExtra={{ gate_wild_addr }}"</span>
+  - regexp: <span class="org-string">'^ *ReadEtcHosts *='</span>
+    line: <span class="org-string">"ReadEtcHosts=no"</span>
+  notify:
+  - Reload Systemd.
+  - Restart Systemd resolved.
+</code></pre>
+</div>
+
+<div class="org-src-container">
+<a href="roles_t/gate/handlers/main.yml"><q>roles_t/gate/handlers/main.yml</q></a><pre class="src src-conf"><code>
+- name: Reload Systemd.
+  become: yes
+  systemd:
+    daemon-reload: yes
+  tags: actualizer
+
+- name: Restart Systemd resolved.
+  become: yes
+  systemd:
+    service: systemd-resolved
+    state: restarted
+  tags: actualizer
+</code></pre>
+</div>
+</div>
+</div>
+<div id="outline-container-orge6db498" class="outline-3">
+<h3 id="orge6db498"><span class="section-number-3">9.5.</span> UFW Rules</h3>
+<div class="outline-text-3" id="text-9-5">
 <p>
 Gate uses the Uncomplicated FireWall (UFW) to install its packet
 filters at boot-time.  The institute does not use a firewall except to
@@ -5620,7 +5914,7 @@ should not be routing their Internet traffic through their VPN.
 </p>
 
 <div class="org-src-container">
-<code>ufw-nat</code><pre class="src src-conf" id="orgb27bd6b"><code>-A POSTROUTING -s {{ private_net_cidr }} -o isp -j MASQUERADE
+<code>ufw-nat</code><pre class="src src-conf" id="orgb0e11fe"><code>-A POSTROUTING -s {{ private_net_cidr }} -o isp -j MASQUERADE
 -A POSTROUTING -s {{    wild_net_cidr }} -o isp -j MASQUERADE
 </code></pre>
 </div>
@@ -5636,17 +5930,11 @@ connection tracking).
 </p>
 
 <div class="org-src-container">
-<code>ufw-forward-nat</code><pre class="src src-conf" id="org2e50ced"><code>-A ufw-user-forward -i lan  -o isp -j ACCEPT
--A ufw-user-forward -i wild -o isp -j ACCEPT
+<code>ufw-forward-nat</code><pre class="src src-conf" id="org81b40ba"><code>-A ufw-before-forward -i lan  -o isp -j ACCEPT
+-A ufw-before-forward -i wild -o isp -j ACCEPT
 </code></pre>
 </div>
 
-<p>
-If "the standard <code>iptables-restore</code> syntax" as it is described in the
-<code>ufw-framework</code> manual page, allows continuation lines, please let us
-know!
-</p>
-
 <p>
 Forwarding rules are also needed to route packets from the campus VPN
 (the <code>wg0</code> WireGuard™ tunnel device) to the institute's LAN and back.
@@ -5656,9 +5944,9 @@ public and campus VPNs is also allowed.
 </p>
 
 <div class="org-src-container">
-<code>ufw-forward-private</code><pre class="src src-conf" id="orge0c167b"><code>-A ufw-user-forward -i lan  -o wg0 -j ACCEPT
--A ufw-user-forward -i wg0  -o lan -j ACCEPT
--A ufw-user-forward -i wg0  -o wg0 -j ACCEPT
+<code>ufw-forward-private</code><pre class="src src-conf" id="orgd7e1898"><code>-A ufw-before-forward -i lan  -o wg0 -j ACCEPT
+-A ufw-before-forward -i wg0  -o lan -j ACCEPT
+-A ufw-before-forward -i wg0  -o wg0 -j ACCEPT
 </code></pre>
 </div>
 
@@ -5675,35 +5963,15 @@ the <code>wild</code> device to the <code>lan</code> device, just the <code>wg0<
 </p>
 </div>
 </div>
-<div id="outline-container-orga4b4df7" class="outline-3">
-<h3 id="orga4b4df7"><span class="section-number-3">9.4.</span> Configure UFW</h3>
-<div class="outline-text-3" id="text-9-4">
+<div id="outline-container-orge5162a0" class="outline-3">
+<h3 id="orge5162a0"><span class="section-number-3">9.6.</span> Configure UFW</h3>
+<div class="outline-text-3" id="text-9-6">
 <p>
 The following tasks install the Uncomplicated Firewall (UFW), set its
-policy in <q>/etc/default/ufw</q>, install the NAT rules in
-<q>/etc/ufw/before.rules</q>, and the Forward rules in
-<q>/etc/ufw/user.rules</q> (where the <code>ufw-user-forward</code> chain
-is&hellip; mentioned?).
+policy in <q>/etc/default/ufw</q>, and install the institute's rules in
+<q>/etc/ufw/before.rules</q>.
 </p>
 
-<p>
-When Gate is configured by <code>./abbey config gate</code> as in the example
-bootstrap, enabling the firewall should not be a problem.  But when
-configuring a new gate with <code>./abbey config new-gate</code>, enabling the
-firewall could break Ansible's current and future ssh sessions.  For
-this reason, Ansible <i>does not</i> enable the firewall.
-</p>
-
-<p>
-The administrator must login and execute the following command after
-Gate is configured or new gate is "in position" (connected to old
-Gate's <code>wild</code> and <code>isp</code> networks).
-</p>
-
-<pre class="example">
-sudo ufw enable
-</pre>
-
 <div class="org-src-container">
 <a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
 - name: Install UFW.
@@ -5717,14 +5985,14 @@ sudo ufw enable
     line: <span class="org-string">"{{ item.line }}"</span>
     regexp: <span class="org-string">"{{ item.regexp }}"</span>
   loop:
-  <span class="org-type">-</span> { line: <span class="org-string">"DEFAULT_INPUT_POLICY=\"ACCEPT\""</span>,
-      regexp: <span class="org-string">"^DEFAULT_INPUT_POLICY="</span> }
-  <span class="org-type">-</span> { line: <span class="org-string">"DEFAULT_OUTPUT_POLICY=\"ACCEPT\""</span>,
-      regexp: <span class="org-string">"^DEFAULT_OUTPUT_POLICY="</span> }
-  <span class="org-type">-</span> { line: <span class="org-string">"DEFAULT_FORWARD_POLICY=\"DROP\""</span>,
-      regexp: <span class="org-string">"^DEFAULT_FORWARD_POLICY="</span> }
+  - line: <span class="org-string">"DEFAULT_INPUT_POLICY=\"ACCEPT\""</span>
+    regexp: <span class="org-string">"^DEFAULT_INPUT_POLICY="</span>
+  - line: <span class="org-string">"DEFAULT_OUTPUT_POLICY=\"ACCEPT\""</span>
+    regexp: <span class="org-string">"^DEFAULT_OUTPUT_POLICY="</span>
+  - line: <span class="org-string">"DEFAULT_FORWARD_POLICY=\"DROP\""</span>
+    regexp: <span class="org-string">"^DEFAULT_FORWARD_POLICY="</span>
 
-- name: Configure UFW NAT rules.
+- name: Configure UFW rules.
   become: yes
   blockinfile:
     block: |
@@ -5732,180 +6000,124 @@ sudo ufw enable
       :POSTROUTING ACCEPT [0:0]
       &lt;&lt;ufw-nat&gt;&gt;
       COMMIT
-    dest: /etc/ufw/before.rules
-    insertafter: EOF
-    prepend_newline: yes
-
-- name: Configure UFW FORWARD rules.
-  become: yes
-  blockinfile:
-    block: |
       *filter
       &lt;&lt;ufw-forward-nat&gt;&gt;
       &lt;&lt;ufw-forward-private&gt;&gt;
       COMMIT
-    dest: /etc/ufw/user.rules
+    dest: /etc/ufw/before.rules
     insertafter: EOF
     prepend_newline: yes
+
+- name: Enable UFW.
+  become: yes
+  <span class="org-variable-name">ufw: state</span>=enabled
+  tags: actualizer
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org15b077f" class="outline-3">
-<h3 id="org15b077f"><span class="section-number-3">9.5.</span> Configure DHCP For The Wild Ethernet</h3>
-<div class="outline-text-3" id="text-9-5">
-<p>
-To accommodate commodity Wi-Fi access points, as well as wired IoT
-appliances, without re-configuring them, the institute attempts to
-look like an up-link, an ISP, e.g. a cable modem (aka "router").  It
-expects a wireless AP (or IoT appliance) to route non-local traffic
-out its WAN (or only) Ethernet port, and to get an IP address for that
-port using DHCP.  Thus Gate runs ISC's DHCP daemon configured to
-listen on one network interface, recognize a specific list of clients,
-and provide each with an IP address and customary network parameters
-(default route, time server, etc.), much as was done on Core for the
-private Ethernet.
-</p>
-
+<div id="outline-container-org5aa09a4" class="outline-3">
+<h3 id="org5aa09a4"><span class="section-number-3">9.7.</span> Configure Campus WireGuard™ Subnet</h3>
+<div class="outline-text-3" id="text-9-7">
 <p>
-The example configuration file, <a href="private/gate-dhcpd.conf"><q>private/gate-dhcpd.conf</q></a>, unlike
-<a href="private/core-dhcpd.conf"><q>private/core-dhcpd.conf</q></a>, does not need RFC3442 (Classless static
-routes).  The wild, wired or wireless IoT need know nothing about the
-private network(s).  This is just an example file, with a MAC address
-chosen to (probably?) match a VirtualBox test machine.  In actual use
-<q>private/core-dhcpd.conf</q> refers to a replacement file.
+Gate uses WireGuard™ to provide a campus VPN service.  Gate's routes
+and firewall rules allow packets to be forwarded to/from the
+institute's private networks: the private Ethernet and the public VPN.
+(It should <i>not</i> forward packets to/from the wild Ethernet.)  The only
+additional route Gate needs is to the public VPN via Core.  The rest
+(private Ethernet and campus VPN) are directly connected.
 </p>
 
-<div class="org-src-container">
-<a href="private/gate-dhcpd.conf"><q>private/gate-dhcpd.conf</q></a><pre class="src src-conf"><code>default-lease-time 3600;
-max-lease-time 7200;
-
-ddns-update-style none;
-
-authoritative;
-
-log-facility daemon;
-
-<span class="org-type">subnet 192.168.57.0 netmask 255.255.255.0</span> {
-  option subnet-mask 255.255.255.0;
-  option broadcast-address 192.168.57.255;
-  option routers 192.168.57.1;
-}
-
-<span class="org-type">host campus-wifi-ap</span> {
-  hardware ethernet 94:83:c4:19:7d:57;
-  fixed-address 192.168.57.2;
-}
-</code></pre>
-</div>
-
 <p>
-Installation and configuration of the DHCP daemon follows.  Note that
-the daemon listens <i>only</i> on the <code>wild</code> network interface.  Also note
-the drop-in <code>Requires</code> dependency, without which the DHCP server
-intermittently fails, finding the <code>wild</code> interface has no IPv4
-addresses (or perhaps finding no <code>wild</code> interface at all?).
+The following tasks install WireGuard™, configure it with
+<q>private/gate-wg0.conf</q> (or <a href="private/gate-wg0-empty.conf"><q>private/gate-wg0-empty.conf</q></a> if it does
+not exist), and enable the service.
 </p>
 
 <div class="org-src-container">
 <a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Install DHCP server.
-  become: yes
-  <span class="org-variable-name">apt: pkg</span>=isc-dhcp-server
-
-- name: Configure DHCP interface.
+- name: Enable IP forwarding.
   become: yes
-  lineinfile:
-    path: /etc/default/isc-dhcp-server
-    <span class="org-variable-name">line: INTERFACESv4</span>=<span class="org-string">"wild"</span>
-    <span class="org-variable-name">regexp: ^INTERFACESv4</span>=
-  notify: Restart DHCP server.
+  sysctl:
+    name: net.ipv4.ip_forward
+    value: <span class="org-string">"1"</span>
+    state: present
 
-- name: Configure DHCP subnet.
+- name: Install WireGuard&#8482;.
   become: yes
-  copy:
-    src: ../private/gate-dhcpd.conf
-    dest: /etc/dhcp/dhcpd.conf
-  notify: Restart DHCP server.
+  <span class="org-variable-name">apt: pkg</span>=wireguard
 
-- name: Configure DHCP server dependence on interface.
+- name: Configure WireGuard&#8482;.
   become: yes
+  vars:
+    srcs:
+      - ../private/gate-wg0.conf
+      - ../private/gate-wg0-empty.conf
   copy:
-    content: |
-      [<span class="org-type">Unit</span>]
-      <span class="org-variable-name">Requires</span>=network-online.target
-    dest: /etc/systemd/system/isc-dhcp-server.service.d/depend.conf
-  notify: Reload Systemd.
+    src: <span class="org-string">"{{ lookup('first_found', srcs) }}"</span>
+    dest: /etc/wireguard/wg0.conf
+    <span class="org-variable-name">mode: u</span>=r,g=,o=
+    owner: root
+    group: root
+  notify: Restart WireGuard&#8482;.
+  tags: accounts
 
-- name: Start DHCP server.
+- name: Start WireGuard&#8482;.
   become: yes
   systemd:
-    service: isc-dhcp-server
+    service: wg-quick@wg0
     state: started
   tags: actualizer
 
-- name: Enable DHCP server.
+- name: Enable WireGuard&#8482;.
   become: yes
   systemd:
-    service: isc-dhcp-server
+    service: wg-quick@wg0
     enabled: yes
 </code></pre>
 </div>
 
 <div class="org-src-container">
 <a href="roles_t/gate/handlers/main.yml"><q>roles_t/gate/handlers/main.yml</q></a><pre class="src src-conf"><code>
-- name: Restart DHCP server.
+- name: Restart WireGuard&#8482;.
   become: yes
   systemd:
-    service: isc-dhcp-server
+    service: wg-quick@wg0
     state: restarted
   tags: actualizer
-
-- name: Reload Systemd.
-  become: yes
-  systemd:
-    daemon-reload: yes
-  tags: actualizer
 </code></pre>
 </div>
 
 <p>
-If Gate is configured with <code>./abbey config gate</code> and then connected to
-actual networks (i.e. <i>not</i> rebooted), the following command is
-executed.  If a new gate was configured with <code>./abbey config new-gate</code>
-and not rebooted, the following command would also be executed.
+The "empty" WireGuard™ configuration file (below) is used until the
+<code>./inst client</code> command adds the first client, and generates an actual
+<q>private/gate-wg0.conf</q>.
 </p>
 
-<pre class="example">
-sudo systemctl start isc-dhcp-server
-</pre>
-
-<p>
-If physically moved or rebooted for some other reason, the above
-command would not be necessary.
-</p>
+<div class="org-src-container">
+<a href="private/gate-wg0-empty.conf"><q>private/gate-wg0-empty.conf</q></a><pre class="src src-conf"><code>[<span class="org-type">Interface</span>]
+<span class="org-variable-name">Address</span> = 10.84.139.1/24
+<span class="org-variable-name">ListenPort</span> = 51820
+<span class="org-variable-name">PostUp</span> = wg set %i private-key /etc/wireguard/private-key
+</code></pre>
 </div>
 </div>
-<div id="outline-container-orgaf8450e" class="outline-3">
-<h3 id="orgaf8450e"><span class="section-number-3">9.6.</span> Configure Campus WireGuard™ Subnet</h3>
-<div class="outline-text-3" id="text-9-6">
+<div id="outline-container-org494b3fc" class="outline-4">
+<h4 id="org494b3fc"><span class="section-number-4">9.7.1.</span> Example <q>private/gate-wg0.conf</q></h4>
+<div class="outline-text-4" id="text-9-7-1">
 <p>
-Gate uses WireGuard™ to provide a campus VPN service.  Gate's routes
-and firewall rules allow packets to be forwarded to/from the
-institute's private networks: the private Ethernet and the public VPN.
-(It should <i>not</i> forward packets to/from the wild Ethernet.)  The only
-additional route Gate needs is to the public VPN via Core.  The rest
-(private Ethernet and campus VPN) are directly connected.
+The example <q>private/gate-wg0.conf</q> below recognizes a wired IoT
+appliance, Dick's notebook and his replacement phone, assigning them
+the host numbers 3, 4 and 6 respectively.
 </p>
 
 <p>
-The following example <a href="#org4542d41"><q>private/gate-wg0.conf</q></a> configuration recognizes
-a wired IoT appliance, Dick's notebook and his replacement phone,
-assigning them the host numbers 3, 4 and 6 respectively.
+This is just an example.  The actual file is edited by the <code>./inst
+client</code> command and so should not be tangled from the following block.
 </p>
 
 <div class="org-src-container">
-<q>private/gate-wg0.conf</q><pre class="src src-conf" id="org4542d41"><code>[<span class="org-type">Interface</span>]
+Example <q>private/gate-wg0.conf</q><pre class="src src-conf"><code>[<span class="org-type">Interface</span>]
 <span class="org-variable-name">Address</span> = 10.84.139.1/24
 <span class="org-variable-name">ListenPort</span> = 51820
 <span class="org-variable-name">PostUp</span> = wg set %i private-key /etc/wireguard/private-key
@@ -5966,71 +6178,18 @@ WireGuard™ tunnel on Dick's notebook, used on campus<pre class="src src-conf">
 </span>[<span class="org-type">Peer</span>]
 <span class="org-variable-name">EndPoint</span> = 192.168.57.1:51820
 <span class="org-variable-name">PublicKey</span> = y3cjFnvQbylmH4lGTujpqc8rusIElmJ4Gu9hh6iR7QI=
-<span class="org-variable-name">AllowedIPs</span> = 10.84.139.1
-<span class="org-variable-name">AllowedIPs</span> = 192.168.56.0/24
-<span class="org-variable-name">AllowedIPs</span> = 10.177.87.0/24
-<span class="org-variable-name">AllowedIPs</span> = 10.84.139.0/24
-</code></pre>
-</div>
-
-<p>
-The following tasks install WireGuard™, configure it with
-<a href="#org4542d41"><q>private/gate-wg0.conf</q></a>, and enable the service.
-</p>
-
-<div class="org-src-container">
-<a href="roles_t/gate/tasks/main.yml"><q>roles_t/gate/tasks/main.yml</q></a><pre class="src src-conf"><code>
-- name: Enable IP forwarding.
-  become: yes
-  sysctl:
-    name: net.ipv4.ip_forward
-    value: <span class="org-string">"1"</span>
-    state: present
-
-- name: Install WireGuard&#8482;.
-  become: yes
-  <span class="org-variable-name">apt: pkg</span>=wireguard
-
-- name: Configure WireGuard&#8482;.
-  become: yes
-  copy:
-    src: ../private/gate-wg0.conf
-    dest: /etc/wireguard/wg0.conf
-    <span class="org-variable-name">mode: u</span>=r,g=,o=
-    owner: root
-    group: root
-  notify: Restart WireGuard&#8482;.
-
-- name: Start WireGuard&#8482;.
-  become: yes
-  systemd:
-    service: wg-quick@wg0
-    state: started
-  tags: actualizer
-
-- name: Enable WireGuard&#8482;.
-  become: yes
-  systemd:
-    service: wg-quick@wg0
-    enabled: yes
-</code></pre>
-</div>
-
-<div class="org-src-container">
-<a href="roles_t/gate/handlers/main.yml"><q>roles_t/gate/handlers/main.yml</q></a><pre class="src src-conf"><code>
-- name: Restart WireGuard&#8482;.
-  become: yes
-  systemd:
-    service: wg-quick@wg0
-    state: restarted
-  tags: actualizer
+<span class="org-variable-name">AllowedIPs</span> = 10.84.139.1
+<span class="org-variable-name">AllowedIPs</span> = 192.168.56.0/24
+<span class="org-variable-name">AllowedIPs</span> = 10.177.87.0/24
+<span class="org-variable-name">AllowedIPs</span> = 10.84.139.0/24
 </code></pre>
 </div>
 </div>
 </div>
 </div>
-<div id="outline-container-orga5f3889" class="outline-2">
-<h2 id="orga5f3889"><span class="section-number-2">10.</span> The Campus Role</h2>
+</div>
+<div id="outline-container-org2ca91ec" class="outline-2">
+<h2 id="org2ca91ec"><span class="section-number-2">10.</span> The Campus Role</h2>
 <div class="outline-text-2" id="text-10">
 <p>
 The <code>campus</code> role configures generic campus server machines: network
@@ -6046,10 +6205,26 @@ Wireless campus devices register their public keys using the <code>./inst
 client</code> command which updates the WireGuard™ configuration on Gate.
 </p>
 </div>
-<div id="outline-container-orgd055671" class="outline-3">
-<h3 id="orgd055671"><span class="section-number-3">10.1.</span> Include Particulars</h3>
+<div id="outline-container-org71ac4e7" class="outline-3">
+<h3 id="org71ac4e7"><span class="section-number-3">10.1.</span> Role Defaults</h3>
 <div class="outline-text-3" id="text-10-1">
 <p>
+As in <a href="#orge739301">The Gate Role</a>, the <code>campus</code> role sets a number of variables to
+default values in its <q>defaults/main.yml</q> file.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/campus/defaults/main.yml"><q>roles_t/campus/defaults/main.yml</q></a><pre class="src src-conf"><code>---
+&lt;&lt;network-vars&gt;&gt;
+&lt;&lt;address-vars&gt;&gt;
+</code></pre>
+</div>
+</div>
+</div>
+<div id="outline-container-orgcafaf4a" class="outline-3">
+<h3 id="orgcafaf4a"><span class="section-number-3">10.2.</span> Include Particulars</h3>
+<div class="outline-text-3" id="text-10-2">
+<p>
 The following should be familiar boilerplate by now.
 </p>
 
@@ -6057,15 +6232,16 @@ The following should be familiar boilerplate by now.
 <a href="roles_t/campus/tasks/main.yml"><q>roles_t/campus/tasks/main.yml</q></a><pre class="src src-conf"><code>---
 - name: Include public variables.
   include_vars: ../public/vars.yml
+
 - name: Include private variables.
   include_vars: ../private/vars.yml
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org5d48bf9" class="outline-3">
-<h3 id="org5d48bf9"><span class="section-number-3">10.2.</span> Configure Hostname</h3>
-<div class="outline-text-3" id="text-10-2">
+<div id="outline-container-org2d65270" class="outline-3">
+<h3 id="org2d65270"><span class="section-number-3">10.3.</span> Configure Hostname</h3>
+<div class="outline-text-3" id="text-10-3">
 <p>
 Clients should be using the expected host name.
 </p>
@@ -6092,9 +6268,9 @@ Clients should be using the expected host name.
 </div>
 </div>
 </div>
-<div id="outline-container-org4f4ef3b" class="outline-3">
-<h3 id="org4f4ef3b"><span class="section-number-3">10.3.</span> Configure Systemd Timesyncd</h3>
-<div class="outline-text-3" id="text-10-3">
+<div id="outline-container-orgb564205" class="outline-3">
+<h3 id="orgb564205"><span class="section-number-3">10.4.</span> Configure Systemd Timesyncd</h3>
+<div class="outline-text-3" id="text-10-4">
 <p>
 The institute uses a common time reference throughout the campus.
 This is essential to campus security, improving the accuracy of log
@@ -6124,9 +6300,9 @@ and file timestamps.
 </div>
 </div>
 </div>
-<div id="outline-container-org272888b" class="outline-3">
-<h3 id="org272888b"><span class="section-number-3">10.4.</span> Add Administrator to System Groups</h3>
-<div class="outline-text-3" id="text-10-4">
+<div id="outline-container-orgf39f12b" class="outline-3">
+<h3 id="orgf39f12b"><span class="section-number-3">10.5.</span> Add Administrator to System Groups</h3>
+<div class="outline-text-3" id="text-10-5">
 <p>
 The administrator often needs to read (directories of) log files owned
 by groups <code>root</code> and <code>adm</code>.  Adding the administrator's account to
@@ -6145,9 +6321,9 @@ these groups speeds up debugging.
 </div>
 </div>
 </div>
-<div id="outline-container-org271c98d" class="outline-3">
-<h3 id="org271c98d"><span class="section-number-3">10.5.</span> Install Unattended Upgrades</h3>
-<div class="outline-text-3" id="text-10-5">
+<div id="outline-container-orgecaa5dd" class="outline-3">
+<h3 id="orgecaa5dd"><span class="section-number-3">10.6.</span> Install Unattended Upgrades</h3>
+<div class="outline-text-3" id="text-10-6">
 <p>
 The institute prefers to install security updates as soon as possible.
 </p>
@@ -6161,9 +6337,9 @@ The institute prefers to install security updates as soon as possible.
 </div>
 </div>
 </div>
-<div id="outline-container-org8b747af" class="outline-3">
-<h3 id="org8b747af"><span class="section-number-3">10.6.</span> Configure Postfix on Campus</h3>
-<div class="outline-text-3" id="text-10-6">
+<div id="outline-container-org345dc1b" class="outline-3">
+<h3 id="org345dc1b"><span class="section-number-3">10.7.</span> Configure Postfix on Campus</h3>
+<div class="outline-text-3" id="text-10-7">
 <p>
 The Postfix settings used by the campus include message size, queue
 times, and the <code>relayhost</code> Core.  The default Debian configuration
@@ -6230,9 +6406,9 @@ tasks below.
 </div>
 </div>
 </div>
-<div id="outline-container-orgc829e66" class="outline-3">
-<h3 id="orgc829e66"><span class="section-number-3">10.7.</span> Set Domain Name</h3>
-<div class="outline-text-3" id="text-10-7">
+<div id="outline-container-org7ccc976" class="outline-3">
+<h3 id="org7ccc976"><span class="section-number-3">10.8.</span> Set Domain Name</h3>
+<div class="outline-text-3" id="text-10-8">
 <p>
 The host's fully qualified (private) domain name (FQDN) is set by an
 alias in its <q>/etc/hosts</q> file, as is customary on Debian.  (See "The
@@ -6254,13 +6430,13 @@ manpage.)
 </div>
 </div>
 </div>
-<div id="outline-container-org8fbeb2b" class="outline-3">
-<h3 id="org8fbeb2b"><span class="section-number-3">10.8.</span> Configure NRPE</h3>
-<div class="outline-text-3" id="text-10-8">
+<div id="outline-container-orge6dfd20" class="outline-3">
+<h3 id="orge6dfd20"><span class="section-number-3">10.9.</span> Configure NRPE</h3>
+<div class="outline-text-3" id="text-10-9">
 <p>
 Each campus host runs an NRPE (a NAGIOS Remote Plugin Executor)
 server so that the NAGIOS4 server on Core can collect statistics.  The
-NAGIOS service is discussed in the <a href="#org8fbeb2b">Configure NRPE</a> section of <a href="#orgc29541b">The Core
+NAGIOS service is discussed in the <a href="#orge6dfd20">Configure NRPE</a> section of <a href="#org1673460">The Core
 Role</a>.
 </p>
 
@@ -6321,8 +6497,8 @@ Role</a>.
 </div>
 </div>
 </div>
-<div id="outline-container-orgb216592" class="outline-2">
-<h2 id="orgb216592"><span class="section-number-2">11.</span> The Ansible Configuration</h2>
+<div id="outline-container-orgc259203" class="outline-2">
+<h2 id="orgc259203"><span class="section-number-2">11.</span> The Ansible Configuration</h2>
 <div class="outline-text-2" id="text-11">
 <p>
 The small institute uses Ansible to maintain the configuration of its
@@ -6331,7 +6507,7 @@ runs playbook <a href="playbook/site.yml"><q>site.yml</q></a> to apply the appro
 role(s) to each host.  Examples of these files are included here, and
 are used to test the roles.  The example configuration applies the
 institutional roles to VirtualBox machines prepared according to
-chapter <a href="#org903c8fd">Testing</a>.
+chapter <a href="#org4ca9f6d">Testing</a>.
 </p>
 
 <p>
@@ -6344,13 +6520,13 @@ while changes to the institute's particulars are committed to a
 separate revision history.
 </p>
 </div>
-<div id="outline-container-orgfb79873" class="outline-3">
-<h3 id="orgfb79873"><span class="section-number-3">11.1.</span> <q>ansible.cfg</q></h3>
+<div id="outline-container-org750c054" class="outline-3">
+<h3 id="org750c054"><span class="section-number-3">11.1.</span> <q>ansible.cfg</q></h3>
 <div class="outline-text-3" id="text-11-1">
 <p>
 The Ansible configuration file <a href="ansible.cfg"><q>ansible.cfg</q></a> contains just a handful
 of settings, some included just to create a test jig as described in
-<a href="#org903c8fd">Testing</a>.
+<a href="#org4ca9f6d">Testing</a>.
 </p>
 
 <ul class="org-ul">
@@ -6359,7 +6535,7 @@ of settings, some included just to create a test jig as described in
 that Python 3 can be expected on all institute hosts.</li>
 <li><code>vault_password_file</code> is set to suppress prompts for the vault
 password.  The institute keeps its vault password in <a href="Secret/"><q>Secret/</q></a> (as
-described in <a href="#orgaf187bb">Keys</a>) and thus sets this parameter to
+described in <a href="#orga69013c">Keys</a>) and thus sets this parameter to
 <a href="Secret/vault-password"><q>Secret/vault-password</q></a>.</li>
 <li><code>inventory</code> is set to avoid specifying it on the command line.</li>
 <li><code>roles_path</code> is set to the recently tangled roles files in
@@ -6376,8 +6552,8 @@ described in <a href="#orgaf187bb">Keys</a>) and thus sets this parameter to
 </div>
 </div>
 </div>
-<div id="outline-container-orgd3555e3" class="outline-3">
-<h3 id="orgd3555e3"><span class="section-number-3">11.2.</span> <q>hosts</q></h3>
+<div id="outline-container-org6cfddde" class="outline-3">
+<h3 id="org6cfddde"><span class="section-number-3">11.2.</span> <q>hosts</q></h3>
 <div class="outline-text-3" id="text-11-2">
 <p>
 The Ansible inventory file <a href="hosts"><q>hosts</q></a> describes all of the institute's
@@ -6389,13 +6565,13 @@ describes three test servers named <code>front</code>, <code>core</code> and <co
 </p>
 
 <div class="org-src-container">
-<a href="hosts"><q>hosts</q></a><pre class="src src-conf" id="orgac2939a"><code>all:
+<a href="hosts"><q>hosts</q></a><pre class="src src-conf" id="orgec15b37"><code>all:
   vars:
     ansible_user: sysadm
     ansible_ssh_extra_args: -i Secret/ssh_admin/id_rsa
   hosts:
     front:
-      ansible_host: 192.168.57.3
+      ansible_host: 192.168.58.3
       ansible_become_password: <span class="org-string">"{{ become_front }}"</span>
     core:
       ansible_host: 192.168.56.1
@@ -6454,8 +6630,8 @@ the <a href="Secret/vault-password"><q>Secret/vault-password</q></a> file.
 </p>
 </div>
 </div>
-<div id="outline-container-orgaa4f421" class="outline-3">
-<h3 id="orgaa4f421"><span class="section-number-3">11.3.</span> <q>playbooks/site.yml</q></h3>
+<div id="outline-container-orgc0b18e4" class="outline-3">
+<h3 id="orgc0b18e4"><span class="section-number-3">11.3.</span> <q>playbooks/site.yml</q></h3>
 <div class="outline-text-3" id="text-11-3">
 <p>
 The example <a href="playbooks/site.yml"><q>playbooks/site.yml</q></a> playbook (below) applies the
@@ -6488,8 +6664,8 @@ the example inventory: <a href="hosts"><q>hosts</q></a>.
 </div>
 </div>
 </div>
-<div id="outline-container-orgd48be68" class="outline-3">
-<h3 id="orgd48be68"><span class="section-number-3">11.4.</span> <q>Secret/vault-password</q></h3>
+<div id="outline-container-org8f8b89a" class="outline-3">
+<h3 id="org8f8b89a"><span class="section-number-3">11.4.</span> <q>Secret/vault-password</q></h3>
 <div class="outline-text-3" id="text-11-4">
 <p>
 As already mentioned, the small institute keeps its Ansible vault
@@ -6501,17 +6677,17 @@ example password matches the example encryptions above.
 </p>
 
 <div class="org-src-container">
-<a href="Secret/vault-password"><q>Secret/vault-password</q></a><pre class="src src-conf" id="org861cd41"><code>alitysortstagess
+<a href="Secret/vault-password"><q>Secret/vault-password</q></a><pre class="src src-conf" id="org6e5f508"><code>alitysortstagess
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org10e919d" class="outline-3">
-<h3 id="org10e919d"><span class="section-number-3">11.5.</span> Creating A Working Ansible Configuration</h3>
+<div id="outline-container-orgb0b19c8" class="outline-3">
+<h3 id="orgb0b19c8"><span class="section-number-3">11.5.</span> Creating A Working Ansible Configuration</h3>
 <div class="outline-text-3" id="text-11-5">
 <p>
 A working Ansible configuration can be "tangled" from this document to
-produce the test configuration described in the <a href="#org903c8fd">Testing</a> chapter.  The
+produce the test configuration described in the <a href="#org4ca9f6d">Testing</a> chapter.  The
 tangling is done by Emacs's <code>org-babel-tangle</code> function and has
 already been performed with the resulting tangle included in the
 distribution with this document.
@@ -6522,8 +6698,8 @@ An institution using the Ansible configuration herein can include this
 document and its tangle as a Git submodule, e.g. in <q>institute/</q>, and
 thus safely merge updates while keeping public and private particulars
 separate, in sibling subdirectories <q>public/</q> and <q>private/</q>.
-The following example commands create a new Git repo in <q>~/net/</q>
-and add an <q>Institute/</q> submodule.
+The following example commands create a new Git repo in <q>~/network/</q>
+and add an <q>institute/</q> submodule.
 </p>
 
 <div class="org-src-container">
@@ -6531,8 +6707,8 @@ and add an <q>Institute/</q> submodule.
 mkdir network
 <span class="org-builtin">cd</span> network
 git init
-git submodule add git://birchwood-abbey.net/~puck/Institute
-git add Institute
+git submodule add git://birchwood-abbey.net/~puck/institute
+git add institute
 </code></pre>
 </div>
 
@@ -6542,24 +6718,24 @@ An institute administrator would then need to add several more files.
 
 <ul class="org-ul">
 <li>A top-level Ansible configuration file, <q>ansible.cfg</q>, would be
-created by copying <q>Institute/ansible.cfg</q> and changing the
-<code>roles_path</code> to <code>roles:Institute/roles</code>.</li>
+created by copying <q>institute/ansible.cfg</q> and changing the
+<code>roles_path</code> to <code>roles:institute/roles</code>.</li>
 <li>A host inventory, <q>hosts</q>, would be created, perhaps by copying
-<q>Institute/hosts</q> and changing its IP addresses.</li>
+<q>institute/hosts</q> and changing its IP addresses.</li>
 <li>A site playbook, <q>site.yml</q>, would be created in a new <q>playbooks/</q>
-subdirectory by copying <q>Institute/playbooks/site.yml</q> with
+subdirectory by copying <q>institute/playbooks/site.yml</q> with
 appropriate changes.</li>
-<li>All of the files in <q>Institute/public/</q> and <q>Institute/private/</q>
+<li>All of the files in <q>institute/public/</q> and <q>institute/private/</q>
 would be copied, with appropriate changes, into new subdirectories
 <q>public/</q> and <q>private/</q>.</li>
-<li><q>~/net/Secret</q> would be a symbolic link to the (auto-mounted?)
+<li><q>~/network/Secret</q> would be a symbolic link to the (auto-mounted?)
 location of the administrator's encrypted USB drive, as described in
-section <a href="#orgaf187bb">Keys</a>.</li>
+section <a href="#orga69013c">Keys</a>.</li>
 </ul>
 
 <p>
-The files in <q>Institute/roles_t/</q> were "tangled" from this document
-and must be copied to <q>Institute/roles/</q> for reasons discussed in the
+The files in <q>institute/roles_t/</q> were "tangled" from this document
+and must be copied to <q>institute/roles/</q> for reasons discussed in the
 next section.  This document does not "tangle" <i>directly</i> into
 <q>roles/</q> to avoid clobbering changes to a working (debugged!)
 configuration.
@@ -6569,13 +6745,13 @@ configuration.
 The <q>playbooks/</q> directory must include the institutional playbooks,
 which find their settings and templates relative to this directory,
 e.g. in <q>../private/vars.yml</q>.  Running institutional playbooks from
-<q>~/net/playbooks/</q> means they will use <q>~/net/private/</q> rather than
-the example <q>~/net/Institute/private/</q>.
+<q>~/network/playbooks/</q> means they will use <q>~/network/private/</q> rather
+than the example <q>~/network/institute/private/</q>.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>cp -r Institute/roles_t Institute/roles
-( <span class="org-builtin">cd</span> playbooks; ln -s ../Institute/playbooks/* . )
+<pre class="src src-sh"><code>cp -r institute/roles_t institute/roles
+( <span class="org-builtin">cd</span> playbooks; ln -s ../institute/playbooks/* . )
 </code></pre>
 </div>
 
@@ -6585,13 +6761,13 @@ super-project's directory.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>./Institute/inst config -n
+<pre class="src src-sh"><code>./institute/inst config -n
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org9fb522d" class="outline-3">
-<h3 id="org9fb522d"><span class="section-number-3">11.6.</span> Maintaining A Working Ansible Configuration</h3>
+<div id="outline-container-org518b730" class="outline-3">
+<h3 id="org518b730"><span class="section-number-3">11.6.</span> Maintaining A Working Ansible Configuration</h3>
 <div class="outline-text-3" id="text-11-6">
 <p>
 The Ansible roles currently tangle into the <a href="roles_t/"><q>roles_t/</q></a> directory to
@@ -6610,8 +6786,8 @@ their way back to the code block in this document.
 </div>
 </div>
 </div>
-<div id="outline-container-org79b145a" class="outline-2">
-<h2 id="org79b145a"><span class="section-number-2">12.</span> The Institute Commands</h2>
+<div id="outline-container-org85c4d59" class="outline-2">
+<h2 id="org85c4d59"><span class="section-number-2">12.</span> The Institute Commands</h2>
 <div class="outline-text-2" id="text-12">
 <p>
 The institute's administrator uses a convenience script to reliably
@@ -6621,8 +6797,8 @@ Ansible configuration.  The Ansible commands it executes are expected
 to get their defaults from <q>./ansible.cfg</q>.
 </p>
 </div>
-<div id="outline-container-orgcd24db3" class="outline-3">
-<h3 id="orgcd24db3"><span class="section-number-3">12.1.</span> Sub-command Blocks</h3>
+<div id="outline-container-orgb9bc6f2" class="outline-3">
+<h3 id="orgb9bc6f2"><span class="section-number-3">12.1.</span> Sub-command Blocks</h3>
 <div class="outline-text-3" id="text-12-1">
 <p>
 The code blocks in this chapter tangle into the <a href="inst"><q>inst</q></a> script.  Each
@@ -6648,8 +6824,8 @@ The first code block is the header of the <code>./inst</code> script.
 </div>
 </div>
 </div>
-<div id="outline-container-orgecb1e8e" class="outline-3">
-<h3 id="orgecb1e8e"><span class="section-number-3">12.2.</span> Sanity Check</h3>
+<div id="outline-container-org902857a" class="outline-3">
+<h3 id="org902857a"><span class="section-number-3">12.2.</span> Sanity Check</h3>
 <div class="outline-text-3" id="text-12-2">
 <p>
 The next code block does not implement a sub-command; it implements
@@ -6709,8 +6885,8 @@ permissions.  It probes past the <a href="Secret/"><q>Secret/</q></a> mount poin
 </div>
 </div>
 </div>
-<div id="outline-container-orge20f4ad" class="outline-3">
-<h3 id="orge20f4ad"><span class="section-number-3">12.3.</span> Importing Ansible Variables</h3>
+<div id="outline-container-orgdf74f7d" class="outline-3">
+<h3 id="orgdf74f7d"><span class="section-number-3">12.3.</span> Importing Ansible Variables</h3>
 <div class="outline-text-3" id="text-12-3">
 <p>
 To ensure that Ansible and <code>./inst</code> are sympatico vis-a-vi certain
@@ -6750,33 +6926,54 @@ The playbook that updates <a href="private/vars.pl"><q>private/vars.pl</q></a>:
 <div class="org-src-container">
 <a href="playbooks/check-inst-vars.yml"><q>playbooks/check-inst-vars.yml</q></a><pre class="src src-conf"><code>- hosts: localhost
   gather_facts: no
-  tasks:
-  - include_vars: ../public/vars.yml
-  - include_vars: ../private/vars.yml
-  - copy:
-      content: |
-        <span class="org-variable-name">$domain_name</span> = <span class="org-string">"{{ domain_name }}"</span>;
-        <span class="org-variable-name">$domain_priv</span> = <span class="org-string">"{{ domain_priv }}"</span>;
+  roles: [ check-inst-vars ]
+</code></pre>
+</div>
+</div>
+</div>
+<div id="outline-container-orgc203671" class="outline-3">
+<h3 id="orgc203671"><span class="section-number-3">12.4.</span> The check-inst-vars Role</h3>
+<div class="outline-text-3" id="text-12-4">
+<p>
+This role is executed by <q>playbooks/check-inst-vars.yml</q> and is not
+just a playbook because it needs a copy of the role defaults.
+</p>
+
+<div class="org-src-container">
+<a href="roles_t/check-inst-vars/defaults/main.yml"><q>roles_t/check-inst-vars/defaults/main.yml</q></a><pre class="src src-conf"><code>---
+&lt;&lt;network-vars&gt;&gt;
+&lt;&lt;address-vars&gt;&gt;
+</code></pre>
+</div>
 
-        <span class="org-variable-name">$front_addr</span> = <span class="org-string">"{{ front_addr }}"</span>;
-        <span class="org-variable-name">$front_wg_pubkey</span> = <span class="org-string">"{{ front_wg_pubkey }}"</span>;
+<div class="org-src-container">
+<a href="roles_t/check-inst-vars/tasks/main.yml"><q>roles_t/check-inst-vars/tasks/main.yml</q></a><pre class="src src-conf"><code>---
+- include_vars: ../public/vars.yml
+- include_vars: ../private/vars.yml
+- copy:
+    content: |
+      <span class="org-variable-name">$domain_name</span> = <span class="org-string">"{{ domain_name }}"</span>;
+      <span class="org-variable-name">$domain_priv</span> = <span class="org-string">"{{ domain_priv }}"</span>;
 
-        <span class="org-variable-name">$public_wg_net_cidr</span> = <span class="org-string">"{{ public_wg_net_cidr }}"</span>;
-        <span class="org-variable-name">$public_wg_port</span> = <span class="org-string">"{{ public_wg_port }}"</span>;
+      <span class="org-variable-name">$front_addr</span> = <span class="org-string">"{{ front_addr }}"</span>;
+      <span class="org-variable-name">$front_wg_pubkey</span> = <span class="org-string">"{{ front_wg_pubkey }}"</span>;
 
-        <span class="org-variable-name">$private_net_cidr</span> = <span class="org-string">"{{ private_net_cidr }}"</span>;
-        <span class="org-variable-name">$wild_net_cidr</span> = <span class="org-string">"{{ wild_net_cidr }}"</span>;
+      <span class="org-variable-name">$public_wg_net_cidr</span> = <span class="org-string">"{{ public_wg_net_cidr }}"</span>;
+      <span class="org-variable-name">$public_wg_port</span> = <span class="org-string">"{{ public_wg_port }}"</span>;
 
-        <span class="org-variable-name">$gate_wild_addr</span> = <span class="org-string">"{{ gate_wild_addr }}"</span>;
-        <span class="org-variable-name">$gate_wg_pubkey</span> = <span class="org-string">"{{ gate_wg_pubkey }}"</span>;
+      <span class="org-variable-name">$private_net_cidr</span> = <span class="org-string">"{{ private_net_cidr }}"</span>;
+      <span class="org-variable-name">$wild_net_cidr</span> = <span class="org-string">"{{ wild_net_cidr }}"</span>;
 
-        <span class="org-variable-name">$campus_wg_net_cidr</span> = <span class="org-string">"{{ campus_wg_net_cidr }}"</span>;
-        <span class="org-variable-name">$campus_wg_port</span> = <span class="org-string">"{{ campus_wg_port }}"</span>;
+      <span class="org-variable-name">$gate_wild_addr</span> = <span class="org-string">"{{ gate_wild_addr }}"</span>;
+      <span class="org-variable-name">$gate_wg_pubkey</span> = <span class="org-string">"{{ gate_wg_pubkey }}"</span>;
 
-        <span class="org-variable-name">$core_addr</span> = <span class="org-string">"{{ core_addr }}"</span>;
-        <span class="org-variable-name">$core_wg_pubkey</span> = <span class="org-string">"{{ core_wg_pubkey }}"</span>;
-      dest: ../private/vars.pl
-      <span class="org-variable-name">mode: u</span>=rw,g=,o=
+      <span class="org-variable-name">$campus_wg_net_cidr</span> = <span class="org-string">"{{ campus_wg_net_cidr }}"</span>;
+      <span class="org-variable-name">$campus_wg_port</span> = <span class="org-string">"{{ campus_wg_port }}"</span>;
+
+      <span class="org-variable-name">$core_addr</span> = <span class="org-string">"{{ core_addr }}"</span>;
+      <span class="org-variable-name">$core_wg_pubkey</span> = <span class="org-string">"{{ core_wg_pubkey }}"</span>;
+    dest: ../private/vars.pl
+    <span class="org-variable-name">mode: u</span>=rw,g=,o=
 </code></pre>
 </div>
 
@@ -6786,7 +6983,7 @@ following few provide the servers' public keys and ports.
 </p>
 
 <div class="org-src-container">
-<a href="private/vars.yml">=private/vars.yml</a><pre class="src src-conf"><code><span class="org-variable-name">front_wg_pubkey: S+6HaTnOwwhWgUGXjSBcPAvifKw+j8BDTRfq534gNW4</span>=
+<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf" id="org4c3c04f"><code><span class="org-variable-name">front_wg_pubkey: S+6HaTnOwwhWgUGXjSBcPAvifKw+j8BDTRfq534gNW4</span>=
 public_wg_port:  39608
 
 <span class="org-variable-name">gate_wg_pubkey:  y3cjFnvQbylmH4lGTujpqc8rusIElmJ4Gu9hh6iR7QI</span>=
@@ -6795,71 +6992,11 @@ campus_wg_port:  51820
 <span class="org-variable-name">core_wg_pubkey:  lGhC51IBgZtlq4H2bsYFuKvPtV0VAEwUvVIn5fW7D0c</span>=
 </code></pre>
 </div>
-
-<p>
-All of the private keys used in the example/test configuration are
-listed in the following table.  The first three are copied to
-<q>/etc/wireguard/private-key</q> on each of the corresponding test
-machines: <code>front</code>, <code>gate</code> and <code>core</code>.  The rest are installed on
-the test client to give it different personae.
-</p>
-
-<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
-
-
-<colgroup>
-<col  class="org-left" />
-
-<col  class="org-left" />
-</colgroup>
-<thead>
-<tr>
-<th scope="col" class="org-left">Test Host</th>
-<th scope="col" class="org-left">WireGuard™ Private Key</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td class="org-left"><code>front</code></td>
-<td class="org-left">AJkzVxfTm/KvRjzTN/9X2jYy+CAugiwZfN5F3JTegms=</td>
-</tr>
-
-<tr>
-<td class="org-left"><code>gate</code></td>
-<td class="org-left">yOBdLbXh6KBwYQvvb5mhiku8Fxkqc5Cdyz6gNgjc/2U=</td>
-</tr>
-
-<tr>
-<td class="org-left"><code>core</code></td>
-<td class="org-left">AI+KhwnsHzSPqyIyAObx7EBBTBXFZPiXb2/Qcts8zEI=</td>
-</tr>
-
-<tr>
-<td class="org-left"><code>thing</code></td>
-<td class="org-left">KIwQT5eGOl9w1qOa5I+2xx5kJH3z4xdpmirS/eGdsXY=</td>
-</tr>
-
-<tr>
-<td class="org-left"><code>dick</code></td>
-<td class="org-left">WAhrlGccPf/BaFS5bRtBE4hEyt3kDxCavmwZfVTsfGs=</td>
-</tr>
-
-<tr>
-<td class="org-left"><code>dicks-phone</code></td>
-<td class="org-left">oG/Kou9HOBCBwHAZGypPA1cZWUL6nR6WoxBiXc/OQWQ=</td>
-</tr>
-
-<tr>
-<td class="org-left"><code>dicks-razr</code></td>
-<td class="org-left">IGNcF0VpkIBcJQAcLZ9jgRmk0SYyUr/WwSNXZoXXUWQ=</td>
-</tr>
-</tbody>
-</table>
 </div>
 </div>
-<div id="outline-container-org274b741" class="outline-3">
-<h3 id="org274b741"><span class="section-number-3">12.4.</span> The CA Command</h3>
-<div class="outline-text-3" id="text-12-4">
+<div id="outline-container-org49e2120" class="outline-3">
+<h3 id="org49e2120"><span class="section-number-3">12.5.</span> The CA Command</h3>
+<div class="outline-text-3" id="text-12-5">
 <p>
 The next code block implements the <code>CA</code> sub-command, which creates a
 new CA (certificate authority) in <a href="Secret/CA/"><q>Secret/CA/</q></a> as well as SSH and PGP
@@ -6926,28 +7063,28 @@ config</code>.
 
   mysystem <span class="org-string">"mkdir --mode=700 Secret/root.gnupg"</span>;
   mysystem (<span class="org-string">"gpg --homedir Secret/root.gnupg"</span>,
-            <span class="org-string">" --batch --quick-generate-key --passphrase ''"</span>,
-            <span class="org-string">" root\@core.$pvt"</span>);
+            <span class="org-string">"--batch --quick-generate-key --passphrase ''"</span>,
+            <span class="org-string">"root\@core.$pvt"</span>);
   mysystem (<span class="org-string">"gpg --homedir Secret/root.gnupg"</span>,
-            <span class="org-string">" --export --armor --output Secret/root-pub.pem"</span>,
-            <span class="org-string">" root\@core.$pvt"</span>);
+            <span class="org-string">"--export --armor --output Secret/root-pub.pem"</span>,
+            <span class="org-string">"root\@core.$pvt"</span>);
   chmod 0440, <span class="org-string">"root-pub.pem"</span>;
   mysystem (<span class="org-string">"gpg --homedir Secret/root.gnupg"</span>,
-            <span class="org-string">" --export-secret-key --armor --output Secret/root-sec.pem"</span>,
-            <span class="org-string">" root\@core.$pvt"</span>);
+            <span class="org-string">"--export-secret-key --armor --output Secret/root-sec.pem"</span>,
+            <span class="org-string">"root\@core.$pvt"</span>);
   chmod 0400, <span class="org-string">"root-sec.pem"</span>;
 
   mysystem <span class="org-string">"mkdir Secret/ssh_admin"</span>;
   chmod 0700, <span class="org-string">"Secret/ssh_admin"</span>;
-  mysystem (<span class="org-string">"ssh-keygen -q -t rsa"</span>
-            .<span class="org-string">" -C A\\ Small\\ Institute\\ Administrator"</span>,
-            <span class="org-string">" -N '' -f Secret/ssh_admin/id_rsa"</span>);
+  mysystem (<span class="org-string">"ssh-keygen -q -t rsa"</span>,
+            <span class="org-string">"-C A\\ Small\\ Institute\\ Administrator"</span>,
+            <span class="org-string">"-N '' -f Secret/ssh_admin/id_rsa"</span>);
 
   mysystem <span class="org-string">"mkdir Secret/ssh_monkey"</span>;
   chmod 0700, <span class="org-string">"Secret/ssh_monkey"</span>;
   mysystem <span class="org-string">"echo 'HashKnownHosts  no' &gt;Secret/ssh_monkey/config"</span>;
   mysystem (<span class="org-string">"ssh-keygen -q -t rsa -C monkey\@core"</span>,
-            <span class="org-string">" -N '' -f Secret/ssh_monkey/id_rsa"</span>);
+            <span class="org-string">"-N '' -f Secret/ssh_monkey/id_rsa"</span>);
 
   mysystem <span class="org-string">"mkdir Secret/ssh_front"</span>;
   chmod 0700, <span class="org-string">"Secret/ssh_front"</span>;
@@ -6958,9 +7095,9 @@ config</code>.
 </div>
 </div>
 </div>
-<div id="outline-container-org2b03642" class="outline-3">
-<h3 id="org2b03642"><span class="section-number-3">12.5.</span> The Config Command</h3>
-<div class="outline-text-3" id="text-12-5">
+<div id="outline-container-org94ac93c" class="outline-3">
+<h3 id="org94ac93c"><span class="section-number-3">12.6.</span> The Config Command</h3>
+<div class="outline-text-3" id="text-12-6">
 <p>
 The next code block implements the <code>config</code> sub-command, which
 provisions network services by running the <q>site.yml</q> playbook
@@ -7009,12 +7146,12 @@ Example command lines:
 </div>
 </div>
 </div>
-<div id="outline-container-org80f2e68" class="outline-3">
-<h3 id="org80f2e68"><span class="section-number-3">12.6.</span> Account Management</h3>
-<div class="outline-text-3" id="text-12-6">
+<div id="outline-container-org32de0c9" class="outline-3">
+<h3 id="org32de0c9"><span class="section-number-3">12.7.</span> Account Management</h3>
+<div class="outline-text-3" id="text-12-7">
 <p>
 For general information about members and their Unix accounts, see
-<a href="#org56d4f14">Accounts</a>.  The account management sub-commands maintain a mapping
+<a href="#org6cfe96b">Accounts</a>.  The account management sub-commands maintain a mapping
 associating member "usernames" (Unix account names) with their
 records.  The mapping is stored among other things in
 <q>private/members.yml</q> as the value associated with the key <code>members</code>.
@@ -7068,30 +7205,30 @@ clients:
 </div>
 
 <p>
-The test campus starts with the empty membership roll found in
-<a href="private/members-empty.yml"><q>private/members-empty.yml</q></a> and saved in <q>private/members.yml</q>
-(which is <i>not</i> tangled from this document, thus <i>not</i> over-written
-during testing).  If <q>members.yml</q> is not found, <q>members-empty.yml</q>
-is used instead.
+The <q>members.yml</q> file will be modified during testing, and should not
+be overwritten by a re-tangle during testing, so it not tangled from
+this file.  Thus in the fresh built (e.g. test) system
+<q>private/members.yml</q> does not exist, not until a <code>./inst new</code> command
+creates the first member.  Until then, Ansible includes the
+<q>private/members-empty.yml</q> file.  It does that using the
+<code>first_found</code> lookup plugin and a list of the two files with
+<q>members.yml</q> first and <q>members-empty.yml</q> last.  That list is the
+value of <code>membership_rolls</code>.
 </p>
 
 <div class="org-src-container">
-<a href="private/members-empty.yml"><q>private/members-empty.yml</q></a><pre class="src src-conf"><code>---
-members:
-usernames: []
-clients: []
+<code>membership-rolls</code><pre class="src src-conf" id="orga2701b3"><code>
+membership_rolls:
+- <span class="org-string">"../private/members.yml"</span>
+- <span class="org-string">"../private/members-empty.yml"</span>
 </code></pre>
 </div>
 
-<p>
-Both locations go on the <code>membership_rolls</code> variable used by the
-<code>include_vars</code> tasks.
-</p>
-
 <div class="org-src-container">
-<a href="private/vars.yml"><q>private/vars.yml</q></a><pre class="src src-conf"><code>membership_rolls:
-- <span class="org-string">"../private/members.yml"</span>
-- <span class="org-string">"../private/members-empty.yml"</span>
+<a href="private/members-empty.yml"><q>private/members-empty.yml</q></a><pre class="src src-conf"><code>---
+members: {}
+usernames: []
+clients: []
 </code></pre>
 </div>
 
@@ -7148,7 +7285,7 @@ read from the file.  The dump subroutine is another story (below).
       print $<span class="org-variable-name">O</span> <span class="org-string">"- $user\n"</span>;
     }
   } <span class="org-keyword">else</span> {
-    print $<span class="org-variable-name">O</span> <span class="org-string">"members:\n"</span>;
+    print $<span class="org-variable-name">O</span> <span class="org-string">"members: {}\n"</span>;
     print $<span class="org-variable-name">O</span> <span class="org-string">"usernames: []\n"</span>;
   }
   <span class="org-keyword">if</span> (@{$<span class="org-variable-name">yaml</span>-&gt;{<span class="org-string">"clients"</span>}}) {
@@ -7214,9 +7351,9 @@ each record.
 </div>
 </div>
 </div>
-<div id="outline-container-org3b09346" class="outline-3">
-<h3 id="org3b09346"><span class="section-number-3">12.7.</span> The New Command</h3>
-<div class="outline-text-3" id="text-12-7">
+<div id="outline-container-orgfa8f534" class="outline-3">
+<h3 id="orgfa8f534"><span class="section-number-3">12.8.</span> The New Command</h3>
+<div class="outline-text-3" id="text-12-8">
 <p>
 The next code block implements the <code>new</code> sub-command.  It adds a new
 member to the institute's membership roll.  It runs an Ansible
@@ -7247,15 +7384,17 @@ initial, generated password.
   <span class="org-keyword">my</span> $<span class="org-variable-name">core</span> = <span class="org-string">`mkpasswd -m sha-512 "$epass"`</span>; chomp $<span class="org-variable-name">core</span>;
   <span class="org-keyword">my</span> $<span class="org-variable-name">vault</span> = strip_vault <span class="org-string">`ansible-vault encrypt_string "$epass"`</span>;
   mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
-            <span class="org-string">" playbooks/nextcloud-new.yml"</span>,
-            <span class="org-string">" -e user=$user"</span>, <span class="org-string">" -e pass=\"$epass\""</span>);
+            <span class="org-string">"playbooks/nextcloud-new.yml"</span>,
+            <span class="org-string">"-e user=$user"</span>, <span class="org-string">"-e pass=\"$epass\""</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
   $<span class="org-variable-name">members</span>-&gt;{$<span class="org-variable-name">user</span>} = { <span class="org-string">"status"</span> =&gt; <span class="org-string">"current"</span>,
                         <span class="org-string">"password_front"</span> =&gt; $<span class="org-variable-name">front</span>,
                         <span class="org-string">"password_core"</span> =&gt; $<span class="org-variable-name">core</span>,
                         <span class="org-string">"password_fetchmail"</span> =&gt; $<span class="org-variable-name">vault</span> };
   write_members_yaml $<span class="org-variable-name">yaml</span>;
   mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
-             <span class="org-string">" -t accounts -l core,front playbooks/site.yml"</span>);
+            <span class="org-string">"-t accounts -l core,front playbooks/site.yml"</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
   <span class="org-keyword">exit</span>;
 }
 
@@ -7290,35 +7429,22 @@ initial, generated password.
 
 <div class="org-src-container">
 <a href="playbooks/nextcloud-new.yml"><q>playbooks/nextcloud-new.yml</q></a><pre class="src src-conf"><code>- hosts: core
-  no_log: yes
   tasks:
   - name: Run occ user:add.
-    shell: |
-      spawn sudo -u www-data /usr/bin/php occ user:add {{ user }}
-      <span class="org-type">expect</span> {
-        <span class="org-string">"Enter password:"</span> {}
-        timeout { exit 1 }
-      }
-      send <span class="org-string">"{{ pass|quote }}\n"</span>;
-      <span class="org-type">expect</span> {
-        <span class="org-string">"Confirm password:"</span> {}
-        timeout { exit 2 }
-      }
-      send <span class="org-string">"{{ pass|quote }}\n"</span>;
-      <span class="org-type">expect</span> {
-        <span class="org-string">"The user \"{{ user }}\" was created successfully"</span> {}
-        timeout { exit 3 }
-      }
-    args:
+    become: yes
+    shell:
       chdir: /var/www/nextcloud/
-      executable: /usr/bin/expect
+      cmd: &gt;
+        sudo -u www-data sh -c
+        <span class="org-string">"OC_PASS={{ pass }}
+        php occ user:add {{ user }} --password-from-env"</span>
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org951ebee" class="outline-3">
-<h3 id="org951ebee"><span class="section-number-3">12.8.</span> The Pass Command</h3>
-<div class="outline-text-3" id="text-12-8">
+<div id="outline-container-org41f43c7" class="outline-3">
+<h3 id="org41f43c7"><span class="section-number-3">12.9.</span> The Pass Command</h3>
+<div class="outline-text-3" id="text-12-9">
 <p>
 The institute's <code>passwd</code> command on Core securely emails <code>root</code> with a
 member's desired password (hashed).  The command may update the
@@ -7331,9 +7457,9 @@ Ansible <a href="playbooks/site.yml"><q>site.yml</q></a> playbook to update the
 message is sent to <code>member@core</code>.
 </p>
 </div>
-<div id="outline-container-org7a29d69" class="outline-4">
-<h4 id="org7a29d69"><span class="section-number-4">12.8.1.</span> Less Aggressive passwd.</h4>
-<div class="outline-text-4" id="text-12-8-1">
+<div id="outline-container-orga51d20f" class="outline-4">
+<h4 id="orga51d20f"><span class="section-number-4">12.9.1.</span> Less Aggressive passwd.</h4>
+<div class="outline-text-4" id="text-12-9-1">
 <p>
 The next code block implements the less aggressive <code>passwd</code> command.
 It is less aggressive because it just emails <code>root</code>.  It does not
@@ -7399,7 +7525,7 @@ close $<span class="org-variable-name">TMP</span>;
 
 <span class="org-keyword">my</span> $<span class="org-variable-name">O</span> = new IO::File;
 open $<span class="org-variable-name">O</span>, (<span class="org-string">"| gpg --encrypt --armor"</span>
-          .<span class="org-string">" --trust-model always --recipient root\@core"</span>
+          .<span class="org-string">" --recipient-file /etc/root-pub.pem"</span>
           .<span class="org-string">" &gt; $tmp"</span>) or <span class="org-keyword">die</span> <span class="org-string">"Error running gpg &gt; $tmp: $!\n"</span>;
 print $<span class="org-variable-name">O</span> &lt;&lt;EOD;<span class="org-perl-heredoc">
 username: $username
@@ -7428,9 +7554,9 @@ that the change was completed.\n"</span>;
 </div>
 </div>
 </div>
-<div id="outline-container-orga0fd5ab" class="outline-4">
-<h4 id="orga0fd5ab"><span class="section-number-4">12.8.2.</span> Less Aggressive Pass Command</h4>
-<div class="outline-text-4" id="text-12-8-2">
+<div id="outline-container-org3f1fb76" class="outline-4">
+<h4 id="org3f1fb76"><span class="section-number-4">12.9.2.</span> Less Aggressive Pass Command</h4>
+<div class="outline-text-4" id="text-12-9-2">
 <p>
 The following code block implements the <code>./inst pass</code> command, used by
 the administrator to update <q>private/members.yml</q> before running
@@ -7440,6 +7566,7 @@ the administrator to update <q>private/members.yml</q> before running
 <div class="org-src-container">
 <a href="inst"><q>inst</q></a><pre class="src src-perl"><code>
 <span class="org-keyword">use</span> <span class="org-constant">MIME::Base64</span>;
+<span class="org-keyword">sub</span> <span class="org-function-name">write_wireguard</span> ($);
 
 <span class="org-keyword">if</span> (defined $<span class="org-variable-name">ARGV</span>[0] &amp;&amp; $<span class="org-variable-name">ARGV</span>[0] eq <span class="org-string">"pass"</span>) {
   <span class="org-keyword">my</span> $<span class="org-variable-name">I</span> = new IO::File;
@@ -7458,7 +7585,8 @@ the administrator to update <q>private/members.yml</q> before running
   <span class="org-keyword">my</span> $<span class="org-variable-name">mem_yaml</span> = read_members_yaml ();
   <span class="org-keyword">my</span> $<span class="org-variable-name">members</span> = $<span class="org-variable-name">mem_yaml</span>-&gt;{<span class="org-string">"members"</span>};
   <span class="org-keyword">my</span> $<span class="org-variable-name">member</span> = $<span class="org-variable-name">members</span>-&gt;{$<span class="org-variable-name">user</span>};
-  <span class="org-keyword">die</span> <span class="org-string">"No such member: $user\n"</span> <span class="org-keyword">if</span> ! defined $<span class="org-variable-name">member</span>;
+  <span class="org-keyword">die</span> <span class="org-string">"$user: does not exist\n"</span> <span class="org-keyword">if</span> ! defined $<span class="org-variable-name">member</span>;
+  <span class="org-keyword">die</span> <span class="org-string">"$user: no longer current\n"</span> <span class="org-keyword">if</span> $<span class="org-variable-name">member</span>-&gt;{<span class="org-string">"status"</span>} ne <span class="org-string">"current"</span>;
 
   <span class="org-keyword">my</span> $<span class="org-variable-name">pass</span> = decode_base64 $<span class="org-variable-name">pass64</span>;
   <span class="org-keyword">my</span> $<span class="org-variable-name">epass</span> = shell_escape $<span class="org-variable-name">pass</span>;
@@ -7471,10 +7599,12 @@ the administrator to update <q>private/members.yml</q> before running
 
   mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
             <span class="org-string">"playbooks/nextcloud-pass.yml"</span>,
-            <span class="org-string">"-e user=$user"</span>, <span class="org-string">"-e \"pass=$epass\""</span>);
+            <span class="org-string">"-e user=$user"</span>, <span class="org-string">"-e \"pass=$epass\""</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
   write_members_yaml $<span class="org-variable-name">mem_yaml</span>;
   mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
-            <span class="org-string">"-t accounts playbooks/site.yml"</span>);
+            <span class="org-string">"-t accounts playbooks/site.yml"</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
   <span class="org-keyword">my</span> $<span class="org-variable-name">O</span> = new IO::File;
   open ($<span class="org-variable-name">O</span>, <span class="org-string">"| sendmail $user\@$domain_priv"</span>)
     or <span class="org-keyword">die</span> <span class="org-string">"Could not pipe to sendmail: $!\n"</span>;
@@ -7492,8 +7622,8 @@ As always: please email root with any questions or concerns.\n"</span>;
 </div>
 
 <p>
-And here is the playbook that interacts with Nextcloud's <code>occ
-users:resetpassword</code> command using <code>expect(1)</code>.
+And here is the playbook that runs Nextcloud's <code>occ
+users:resetpassword</code> command.
 </p>
 
 <div class="org-src-container">
@@ -7501,34 +7631,20 @@ users:resetpassword</code> command using <code>expect(1)</code>.
   no_log: yes
   tasks:
   - name: Run occ user:resetpassword.
-    shell: |
-      spawn sudo -u www-data \
-            /usr/bin/php occ user:resetpassword {{ user }}
-      <span class="org-type">expect</span> {
-        <span class="org-string">"Enter a new password:"</span> {}
-        timeout { exit 1 }
-      }
-      send <span class="org-string">"{{ pass|quote }}\n"</span>
-      <span class="org-type">expect</span> {
-        <span class="org-string">"Confirm the new password:"</span> {}
-        timeout { exit 2 }
-      }
-      send <span class="org-string">"{{ pass|quote }}\n"</span>
-      <span class="org-type">expect</span> {
-        <span class="org-string">"Successfully reset password for {{ user }}"</span> {}
-        <span class="org-string">"Please choose a different password."</span> { exit 3 }
-        timeout { exit 4 }
-      }
-    args:
+    become: yes
+    shell:
       chdir: /var/www/nextcloud/
-      executable: /usr/bin/expect
+      cmd: &gt;
+        sudo -u www-data sh -c
+        <span class="org-string">"OC_PASS={{ pass }}
+        php occ user:resetpassword {{ user }} --password-from-env"</span>
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-orgf5775d1" class="outline-4">
-<h4 id="orgf5775d1"><span class="section-number-4">12.8.3.</span> Installing the Less Aggressive passwd</h4>
-<div class="outline-text-4" id="text-12-8-3">
+<div id="outline-container-orgc8087e2" class="outline-4">
+<h4 id="orgc8087e2"><span class="section-number-4">12.9.3.</span> Installing the Less Aggressive passwd</h4>
+<div class="outline-text-4" id="text-12-9-3">
 <p>
 The following Ansible tasks install the less aggressive <code>passwd</code>
 script in <q>/usr/local/bin/passwd</q> on Core, and a <code>sudo</code> policy file
@@ -7576,10 +7692,10 @@ configuration so that the email to root can be encrypted.
     group: root
 
 - name: Install root PGP key file.
-  become: no
+  become: yes
   copy:
     src: ../Secret/root-pub.pem
-    dest: ~/.gnupg-root-pub.pem
+    dest: /etc/root-pub.pem
     <span class="org-variable-name">mode: u</span>=r,g=r,o=r
   notify: Import root PGP key.
 </code></pre>
@@ -7589,15 +7705,15 @@ configuration so that the email to root can be encrypted.
 <a href="roles_t/core/handlers/main.yml"><q>roles_t/core/handlers/main.yml</q></a><pre class="src src-conf"><code>
 - name: Import root PGP key.
   become: no
-  command: gpg --import ~/.gnupg-root-pub.pem
+  command: gpg --import /etc/root-pub.pem
 </code></pre>
 </div>
 </div>
 </div>
 </div>
-<div id="outline-container-orgb3a237e" class="outline-3">
-<h3 id="orgb3a237e"><span class="section-number-3">12.9.</span> The Old Command</h3>
-<div class="outline-text-3" id="text-12-9">
+<div id="outline-container-org8f0c523" class="outline-3">
+<h3 id="org8f0c523"><span class="section-number-3">12.10.</span> The Old Command</h3>
+<div class="outline-text-3" id="text-12-10">
 <p>
 The <code>old</code> command disables a member's account (and thus their clients).
 </p>
@@ -7612,11 +7728,15 @@ The <code>old</code> command disables a member's account (and thus their clients
   <span class="org-keyword">die</span> <span class="org-string">"$user: does not exist\n"</span> <span class="org-keyword">if</span> ! defined $<span class="org-variable-name">member</span>;
 
   mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
-            <span class="org-string">"playbooks/nextcloud-old.yml -e user=$user"</span>);
+            <span class="org-string">"playbooks/nextcloud-old.yml -e user=$user"</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
   $<span class="org-variable-name">member</span>-&gt;{<span class="org-string">"status"</span>} = <span class="org-string">"former"</span>;
+  umask 077;
   write_members_yaml $<span class="org-variable-name">yaml</span>;
+  write_wireguard $<span class="org-variable-name">yaml</span>;
   mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
-            <span class="org-string">"-t accounts playbooks/site.yml"</span>);
+            <span class="org-string">"-t accounts playbooks/site.yml"</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
   <span class="org-keyword">exit</span>;
 }
 </code></pre>
@@ -7626,22 +7746,19 @@ The <code>old</code> command disables a member's account (and thus their clients
 <a href="playbooks/nextcloud-old.yml"><q>playbooks/nextcloud-old.yml</q></a><pre class="src src-conf"><code>- hosts: core
   tasks:
   - name: Run occ user:disable.
-    shell: |
-      spawn sudo -u www-data /usr/bin/php occ user:disable {{ user }}
-      <span class="org-type">expect</span> {
-        <span class="org-string">"The specified user is disabled"</span> {}
-        timeout { exit 1 }
-      }
-    args:
+    become: yes
+    shell:
       chdir: /var/www/nextcloud/
-      executable: /usr/bin/expect
+      cmd: &gt;
+        sudo -u www-data sh -c
+        <span class="org-string">"php occ user:disable {{ user }}"</span>
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org6edab5b" class="outline-3">
-<h3 id="org6edab5b"><span class="section-number-3">12.10.</span> The Client Command</h3>
-<div class="outline-text-3" id="text-12-10">
+<div id="outline-container-org219495e" class="outline-3">
+<h3 id="org219495e"><span class="section-number-3">12.11.</span> The Client Command</h3>
+<div class="outline-text-3" id="text-12-11">
 <p>
 The <code>client</code> command registers the public key of a client wishing to
 connect to the institute's WireGuard™ subnets.  The command allocates
@@ -7663,7 +7780,7 @@ the client (i.e. by the WireGuard for Android™ app) and never revealed
 </p>
 
 <p>
-The generated configuration vary depending on the type of client,
+The generated configurations vary depending on the type of client,
 which must be given as the first argument to the command.  For most
 types, two configuration files are generated.  <q>campus.conf</q> contains
 the client's campus VPN configuration, and <q>public.conf</q> the client's
@@ -7718,12 +7835,13 @@ better support in NetworkManager soon.)
   } <span class="org-keyword">else</span> {
     <span class="org-keyword">die</span> <span class="org-string">"usage: $0 client [debian|android|campus]\n"</span>;
   }
-  <span class="org-keyword">my</span> $<span class="org-variable-name">yaml</span>;
-  $<span class="org-variable-name">yaml</span> = read_members_yaml;
+  <span class="org-keyword">my</span> $<span class="org-variable-name">yaml</span> = read_members_yaml;
   <span class="org-keyword">my</span> $<span class="org-variable-name">members</span> = $<span class="org-variable-name">yaml</span>-&gt;{<span class="org-string">"members"</span>};
   <span class="org-keyword">my</span> $<span class="org-variable-name">member</span> = $<span class="org-variable-name">members</span>-&gt;{$<span class="org-variable-name">user</span>};
   <span class="org-keyword">die</span> <span class="org-string">"$user: does not exist\n"</span>
     <span class="org-keyword">if</span> !defined $<span class="org-variable-name">member</span> &amp;&amp; $<span class="org-variable-name">type</span> ne <span class="org-string">"campus"</span>;
+  <span class="org-keyword">die</span> <span class="org-string">"$user: no longer current\n"</span>
+    <span class="org-keyword">if</span> defined $<span class="org-variable-name">member</span> &amp;&amp; $<span class="org-variable-name">member</span>-&gt;{<span class="org-string">"status"</span>} ne <span class="org-string">"current"</span>;
 
   <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">campus_peers</span> <span class="org-comment"># [ name, hostnum, type, pubkey, user|"" ]
 </span>     = map { [ (split <span class="org-string">/ /</span>), <span class="org-string">""</span> ] } @{$<span class="org-variable-name">yaml</span>-&gt;{<span class="org-string">"clients"</span>}};
@@ -7756,14 +7874,47 @@ better support in NetworkManager soon.)
 
   umask 077;
   write_members_yaml $<span class="org-variable-name">yaml</span>;
+  write_wireguard $<span class="org-variable-name">yaml</span>;
 
-  <span class="org-keyword">if</span> ($<span class="org-variable-name">type</span> eq <span class="org-string">"campus"</span>) {
-    push @<span class="org-perl-non-scalar-variable">all_peers</span>, [ $<span class="org-variable-name">name</span>, $<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">type</span>, $<span class="org-variable-name">pubkey</span>, <span class="org-string">""</span> ];
-  } <span class="org-keyword">else</span> {
-    push @<span class="org-perl-non-scalar-variable">member_peers</span>, [ $<span class="org-variable-name">name</span>, $<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">type</span>, $<span class="org-variable-name">pubkey</span>, $<span class="org-variable-name">user</span> ];
-    push @<span class="org-perl-non-scalar-variable">all_peers</span>, [ $<span class="org-variable-name">name</span>, $<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">type</span>, $<span class="org-variable-name">pubkey</span>, $<span class="org-variable-name">user</span> ];
+  umask 033;
+  write_wg_client (<span class="org-string">"public.conf"</span>,
+                   hostnum_to_ipaddr ($<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">public_wg_net_cidr</span>),
+                   $<span class="org-variable-name">type</span>,
+                   $<span class="org-variable-name">front_wg_pubkey</span>,
+                   <span class="org-string">"$front_addr:$public_wg_port"</span>,
+                   hostnum_to_ipaddr (1, $<span class="org-variable-name">public_wg_net_cidr</span>))
+    <span class="org-keyword">if</span> $<span class="org-variable-name">type</span> ne <span class="org-string">"campus"</span>;
+  write_wg_client (<span class="org-string">"campus.conf"</span>,
+                   hostnum_to_ipaddr ($<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">campus_wg_net_cidr</span>),
+                   $<span class="org-variable-name">type</span>,
+                   $<span class="org-variable-name">gate_wg_pubkey</span>,
+                   <span class="org-string">"$gate_wild_addr:$campus_wg_port"</span>,
+                   hostnum_to_ipaddr (1, $<span class="org-variable-name">campus_wg_net_cidr</span>));
+
+  mysystem (<span class="org-string">"ansible-playbook -e \@Secret/become.yml"</span>,
+            <span class="org-string">"-l gate,front"</span>,
+            <span class="org-string">"-t accounts playbooks/site.yml"</span>,
+            <span class="org-string">"&gt;/dev/null"</span>);
+  <span class="org-keyword">exit</span>;
+}
+
+<span class="org-keyword">sub</span> <span class="org-function-name">write_wireguard</span> ($) {
+  <span class="org-keyword">my</span> ($<span class="org-variable-name">yaml</span>) = @<span class="org-perl-non-scalar-variable">_</span>;
+
+  <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">campus_peers</span> <span class="org-comment"># [ name, hostnum, type, pubkey, user|"" ]
+</span>     = map { [ (split <span class="org-string">/ /</span>), <span class="org-string">""</span> ] } @{$<span class="org-variable-name">yaml</span>-&gt;{<span class="org-string">"clients"</span>}};
+
+  <span class="org-keyword">my</span> $<span class="org-variable-name">members</span> = $<span class="org-variable-name">yaml</span>-&gt;{<span class="org-string">"members"</span>};
+  <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">member_peers</span> = ();
+  <span class="org-keyword">for</span> <span class="org-keyword">my</span> $<span class="org-variable-name">u</span> (sort keys %$<span class="org-variable-name">members</span>) {
+    <span class="org-keyword">next</span> <span class="org-keyword">if</span> $<span class="org-variable-name">members</span>-&gt;{$<span class="org-variable-name">u</span>}-&gt;{<span class="org-string">"status"</span>} ne <span class="org-string">"current"</span>;
+    push @<span class="org-perl-non-scalar-variable">member_peers</span>,
+         map { [ (split <span class="org-string">/ /</span>), $<span class="org-variable-name">u</span> ] } @{$<span class="org-variable-name">members</span>-&gt;{$<span class="org-variable-name">u</span>}-&gt;{<span class="org-string">"clients"</span>}};
   }
 
+  <span class="org-keyword">my</span> @<span class="org-perl-non-scalar-variable">all_peers</span> = sort { $<span class="org-variable-name">a</span>-&gt;[1] &lt;=&gt; $<span class="org-variable-name">b</span>-&gt;[1] }
+                       (@<span class="org-perl-non-scalar-variable">campus_peers</span>, @<span class="org-perl-non-scalar-variable">member_peers</span>);
+
   <span class="org-keyword">my</span> $<span class="org-variable-name">core_wg_addr</span> = hostnum_to_ipaddr (2, $<span class="org-variable-name">public_wg_net_cidr</span>);
   <span class="org-keyword">my</span> $<span class="org-variable-name">extra_front_config</span> = <span class="org-string">"
 PostUp = resolvectl dns %i $core_addr
@@ -7779,28 +7930,10 @@ AllowedIPs = $campus_wg_net_cidr\n"</span>;
 
   write_wg_server (<span class="org-string">"private/front-wg0.conf"</span>, \@<span class="org-perl-non-scalar-variable">member_peers</span>,
                    hostnum_to_ipaddr_cidr (1, $<span class="org-variable-name">public_wg_net_cidr</span>),
-                   $<span class="org-variable-name">public_wg_port</span>, $<span class="org-variable-name">extra_front_config</span>)
-    <span class="org-keyword">if</span> $<span class="org-variable-name">type</span> ne <span class="org-string">"campus"</span>;
+                   $<span class="org-variable-name">public_wg_port</span>, $<span class="org-variable-name">extra_front_config</span>);
   write_wg_server (<span class="org-string">"private/gate-wg0.conf"</span>, \@<span class="org-perl-non-scalar-variable">all_peers</span>,
                    hostnum_to_ipaddr_cidr (1, $<span class="org-variable-name">campus_wg_net_cidr</span>),
                    $<span class="org-variable-name">campus_wg_port</span>, <span class="org-string">"\n"</span>);
-
-  umask 033;
-  write_wg_client (<span class="org-string">"public.conf"</span>,
-                   hostnum_to_ipaddr ($<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">public_wg_net_cidr</span>),
-                   $<span class="org-variable-name">type</span>,
-                   $<span class="org-variable-name">front_wg_pubkey</span>,
-                   <span class="org-string">"$front_addr:$public_wg_port"</span>,
-                   hostnum_to_ipaddr (1, $<span class="org-variable-name">public_wg_net_cidr</span>))
-    <span class="org-keyword">if</span> $<span class="org-variable-name">type</span> ne <span class="org-string">"campus"</span>;
-  write_wg_client (<span class="org-string">"campus.conf"</span>,
-                   hostnum_to_ipaddr ($<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">campus_wg_net_cidr</span>),
-                   $<span class="org-variable-name">type</span>,
-                   $<span class="org-variable-name">gate_wg_pubkey</span>,
-                   <span class="org-string">"$gate_wild_addr:$campus_wg_port"</span>,
-                   hostnum_to_ipaddr (1, $<span class="org-variable-name">campus_wg_net_cidr</span>));
-
-  <span class="org-keyword">exit</span>;
 }
 
 <span class="org-keyword">sub</span> <span class="org-function-name">write_wg_server</span> ($$$$$) {
@@ -7868,29 +8001,29 @@ AllowedIPs = $campus_wg_net_cidr\n"</span>;
   <span class="org-comment"># Assume 24bit subnet, 8bit hostnum.
 </span>  <span class="org-comment"># Find a Perl library for more generality?
 </span>  <span class="org-keyword">die</span> <span class="org-string">"$hostnum: hostnum too large\n"</span> <span class="org-keyword">if</span> $<span class="org-variable-name">hostnum</span> &gt; 255;
-  <span class="org-keyword">my</span> ($<span class="org-variable-name">prefix</span>) = $<span class="org-variable-name">net_cidr</span> =~ m<span class="org-string">"^(\d+\.\d+\.\d+)\.\d+/24$";
-  die if !$prefix;
-  return "</span>$<span class="org-variable-name">prefix</span>.$<span class="org-variable-name">hostnum</span><span class="org-string">";
+  <span class="org-keyword">my</span> ($<span class="org-variable-name">prefix</span>) = $<span class="org-variable-name">net_cidr</span> =~ m<span class="org-string">"^(\d+\.\d+\.\d+)\.\d+/24$"</span>;
+  <span class="org-keyword">die</span> <span class="org-keyword">if</span> !$<span class="org-variable-name">prefix</span>;
+  <span class="org-keyword">return</span> <span class="org-string">"$prefix.$hostnum"</span>;
 }
 
-sub hostnum_to_ipaddr_cidr ($$)
+<span class="org-keyword">sub</span> <span class="org-function-name">hostnum_to_ipaddr_cidr</span> ($$)
 {
-  my ($hostnum, $net_cidr) = @_;
+  <span class="org-keyword">my</span> ($<span class="org-variable-name">hostnum</span>, $<span class="org-variable-name">net_cidr</span>) = @<span class="org-perl-non-scalar-variable">_</span>;
 
-  # Assume 24bit subnet, 8bit hostnum.
-  # Find a Perl library for more generality?
-  die "</span>$<span class="org-variable-name">hostnum</span>: hostnum too large\n<span class="org-string">" if $hostnum &gt; 255;
-  my ($prefix) = $net_cidr =~ m"</span>^(\d+\.\d+\.\d+)\.\d+<span class="org-string">/24$";
-  die if !$prefix;
-  return "$prefix.$hostnum/</span>24<span class="org-string">";
-}</span>
+  <span class="org-comment"># Assume 24bit subnet, 8bit hostnum.
+</span>  <span class="org-comment"># Find a Perl library for more generality?
+</span>  <span class="org-keyword">die</span> <span class="org-string">"$hostnum: hostnum too large\n"</span> <span class="org-keyword">if</span> $<span class="org-variable-name">hostnum</span> &gt; 255;
+  <span class="org-keyword">my</span> ($<span class="org-variable-name">prefix</span>) = $<span class="org-variable-name">net_cidr</span> =~ m<span class="org-string">"^(\d+\.\d+\.\d+)\.\d+/24$"</span>;
+  <span class="org-keyword">die</span> <span class="org-keyword">if</span> !$<span class="org-variable-name">prefix</span>;
+  <span class="org-keyword">return</span> <span class="org-string">"$prefix.$hostnum/24"</span>;
+}
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org91159fb" class="outline-3">
-<h3 id="org91159fb"><span class="section-number-3">12.11.</span> Institute Command Help</h3>
-<div class="outline-text-3" id="text-12-11">
+<div id="outline-container-org2abd587" class="outline-3">
+<h3 id="org2abd587"><span class="section-number-3">12.12.</span> Institute Command Help</h3>
+<div class="outline-text-3" id="text-12-12">
 <p>
 This should be the last block tangled into the <a href="inst"><q>inst</q></a> script.  It
 catches any command lines that were not handled by a sub-command
@@ -7905,8 +8038,8 @@ above.
 </div>
 </div>
 </div>
-<div id="outline-container-org903c8fd" class="outline-2">
-<h2 id="org903c8fd"><span class="section-number-2">13.</span> Testing</h2>
+<div id="outline-container-org4ca9f6d" class="outline-2">
+<h2 id="org4ca9f6d"><span class="section-number-2">13.</span> Testing</h2>
 <div class="outline-text-2" id="text-13">
 <p>
 The example files in this document, <a href="ansible.cfg"><q>ansible.cfg</q></a> and <a href="hosts"><q>hosts</q></a> as well
@@ -7914,9 +8047,9 @@ as those in <a href="public/"><q>public/</q></a> and <a href="private/"><q>priva
 certificate authority and GnuPG key-ring in <a href="Secret/"><q>Secret/</q></a> (included in the
 distribution), can be used to configure three VirtualBox VMs
 simulating Core, Gate and Front in test networks simulating a private
-Ethernet, an untrusted Ethernet, the campus ISP, and a commercial
-cloud.  With the test networks up and running, a simulated member's
-notebook can be created and alternately attached to the untrusted
+Ethernet, a wild (untrusted) Ethernet, the campus ISP, and a
+commercial cloud.  With the test networks up and running, a simulated
+member's notebook can be created and alternately attached to the wild
 Ethernet (as though it were on the campus Wi-Fi) or the Internet (as
 though it were abroad).  The administrator's notebook in this
 simulation is the VirtualBox host.
@@ -7925,7 +8058,7 @@ simulation is the VirtualBox host.
 <p>
 The next two sections list the steps taken to create the simulated
 Core, Gate and Front machines, and connect them to their networks.
-The process is similar to that described in <a href="#org6a2440c">The (Actual) Hardware</a>, but
+The process is similar to that described in <a href="#orgef4d876">The (Actual) Hardware</a>, but
 is covered in detail here where the VirtualBox hypervisor can be
 assumed and exact command lines can be given (and copied during
 re-testing).  The remaining sections describe the manual testing
@@ -7941,15 +8074,15 @@ HTML version of the latest revision can be found on the official web
 site at <a href="https://www.virtualbox.org/manual/UserManual.html">https://www.virtualbox.org/manual/UserManual.html</a>.
 </p>
 </div>
-<div id="outline-container-orgb37f468" class="outline-3">
-<h3 id="orgb37f468"><span class="section-number-3">13.1.</span> The Test Networks</h3>
+<div id="outline-container-org1bea466" class="outline-3">
+<h3 id="org1bea466"><span class="section-number-3">13.1.</span> The Test Networks</h3>
 <div class="outline-text-3" id="text-13-1">
 <p>
 The networks used in the test:
 </p>
 
 <dl class="org-dl">
-<dt><code>premises</code></dt><dd>A NAT Network, simulating the cloud provider's and
+<dt><code>public</code></dt><dd>A NAT Network, simulating the cloud provider's and
 campus ISP's networks.  This is the only network with DHCP and DNS
 services provided by the hypervisor.  It is not the default NAT
 network because <code>gate</code> and <code>front</code> need to communicate.</dd>
@@ -7959,76 +8092,184 @@ private Ethernet switch.  It has no services, no DHCP, just the host
 machine at <code>192.168.56.10</code> pretending to be the administrator's
 notebook.</dd>
 
-<dt><code>vboxnet1</code></dt><dd>Another Host-only network, simulating the untrusted
-Ethernet between Gate and the campus IoT (and Wi-Fi APs).  It has no
-services, no DHCP, just the host at <code>192.168.57.2</code>, simulating the
-NATed Wi-Fi network.</dd>
-</dl>
+<dt><code>vboxnet1</code></dt><dd>Another Host-only network, simulating the wild
+Ethernet between Gate and the campus IoT (and Wi-Fi APs).  It has no
+services, no DHCP, just the host at <code>192.168.57.2</code>.</dd>
+
+<dt><code>vboxnet2</code></dt><dd>A third Host-only network, used only to directly
+connect the host to <code>front</code>.</dd>
+</dl>
+
+<p>
+In this simulation the IP address for <code>front</code> is not a public address
+but a private address on the NAT network <code>public</code>.  Thus <code>front</code> is
+not accessible by the host, by Ansible on the administrator's
+notebook.  To work around this restriction, <code>front</code> gets a second
+network interface connected to the <code>vboxnet2</code> network.  The address of
+this second interface is used by Ansible to access <code>front</code>.<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>
+</p>
+
+<p>
+The networks described above are created and "started" with the
+following <code>VBoxManage</code> commands.
+</p>
+
+<div class="org-src-container">
+<pre class="src src-sh"><code>VBoxManage natnetwork add --netname public <span class="org-sh-escaped-newline">\</span>
+                          --network 192.168.15.0/24 <span class="org-sh-escaped-newline">\</span>
+                          --enable --dhcp on --ipv6 off
+VBoxManage natnetwork start --netname public
+VBoxManage dhcpserver modify --network=public --lower-ip=192.168.15.5
+VBoxManage hostonlyif create <span class="org-comment-delimiter"># </span><span class="org-comment">vboxnet0
+</span>VBoxManage hostonlyif ipconfig vboxnet0 --ip=192.168.56.10
+VBoxManage hostonlyif create <span class="org-comment-delimiter"># </span><span class="org-comment">vboxnet1
+</span>VBoxManage hostonlyif ipconfig vboxnet1 --ip=192.168.57.2
+VBoxManage hostonlyif create <span class="org-comment-delimiter"># </span><span class="org-comment">vboxnet2
+</span>VBoxManage hostonlyif ipconfig vboxnet2 --ip=192.168.58.1
+</code></pre>
+</div>
+
+<p>
+Note that only the NAT network <code>public</code> should have a DHCP server
+enabled (to simulate an ISP and cloud for <code>gate</code> and <code>front</code>
+respectively).  Yet <code>front</code> is statically assigned an IP address
+outside the DHCP server's pool.  This ensures it gets <code>front_addr</code>
+without more server configuration.
+</p>
+
+<p>
+Note also that actual ISPs and clouds will provide Gate and Front with
+public network addresses.  In this simulation "they" provide addresses
+in <code>192.168.15.0/24</code>, on the NAT network <code>public</code>.
+</p>
+</div>
+</div>
+<div id="outline-container-orgdf43453" class="outline-3">
+<h3 id="orgdf43453"><span class="section-number-3">13.2.</span> The Test Machines</h3>
+<div class="outline-text-3" id="text-13-2">
+<p>
+The virtual machines are created by <code>VBoxManage</code> command lines in the
+following sub-sections.  They each start with a recent Debian release
+(e.g. <q>debian-12.5.0-amd64-netinst.iso</q>) in their simulated DVD
+drives.  Preparation of <a href="#orgef4d876">The Hardware</a> installed additional software
+packages and keys while the machines had Internet access.  They were
+then moved to the new campus network where Ansible completed the
+configuration without Internet access.
+</p>
+
+<p>
+Preparation of the test machines is automated by "preparatory scripts"
+that install the same "additional software packages" and the same test
+keys given in the examples.  The scripts are run on each VM while they
+are still attached to the host's NAT network and have Internet access.
+They prepare the machine to reboot on the simulated campus network
+without Internet access, ready for final configuration by Ansible and
+the first launch of services.  The "move to campus" is simulated by
+shutting each VM down, executing a <code>VBoxManage</code> command line or two,
+and restarting.
+</p>
+</div>
+<div id="outline-container-org6642f76" class="outline-4">
+<h4 id="org6642f76"><span class="section-number-4">13.2.1.</span> The Test Wireguard™ Keys <a id="org1b5dca8"></a></h4>
+<div class="outline-text-4" id="text-13-2-1">
+<p>
+All of the private keys used in the example/test configuration are
+listed here.  The first three are copied to
+<q>/etc/wireguard/private-key</q> on the servers: <code>front</code>, <code>gate</code> and
+<code>core</code>.  The rest are installed on the test client to give it
+different personae.  In actual use, private keys are generated on the
+servers and clients, and stay there.  Only the public keys are
+collected (and registered with the <code>./inst client</code> command).
+</p>
+
+<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
+
+
+<colgroup>
+<col  class="org-left" />
+
+<col  class="org-left" />
+</colgroup>
+<thead>
+<tr>
+<th scope="col" class="org-left">Test Host</th>
+<th scope="col" class="org-left">WireGuard™ Private Key</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="org-left"><code>front</code></td>
+<td class="org-left">AJkzVxfTm/KvRjzTN/9X2jYy+CAugiwZfN5F3JTegms=</td>
+</tr>
+
+<tr>
+<td class="org-left"><code>gate</code></td>
+<td class="org-left">yOBdLbXh6KBwYQvvb5mhiku8Fxkqc5Cdyz6gNgjc/2U=</td>
+</tr>
 
-<p>
-In this simulation the IP address for <code>front</code> is not a public address
-but a private address on the NAT network <code>premises</code>.  Thus <code>front</code> is
-not accessible to the administrator's notebook (the host).  To work
-around this restriction, <code>front</code> gets a second network interface
-connected to the <code>vboxnet1</code> network and used only for ssh access from
-the host.<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>
-</p>
+<tr>
+<td class="org-left"><code>core</code></td>
+<td class="org-left">AI+KhwnsHzSPqyIyAObx7EBBTBXFZPiXb2/Qcts8zEI=</td>
+</tr>
 
-<p>
-The networks described above are created and "started" with the
-following <code>VBoxManage</code> commands.
-</p>
+<tr>
+<td class="org-left"><code>thing</code></td>
+<td class="org-left">KIwQT5eGOl9w1qOa5I+2xx5kJH3z4xdpmirS/eGdsXY=</td>
+</tr>
 
-<div class="org-src-container">
-<pre class="src src-sh"><code>VBoxManage natnetwork add --netname premises <span class="org-sh-escaped-newline">\</span>
-                          --network 192.168.15.0/24 <span class="org-sh-escaped-newline">\</span>
-                          --enable --dhcp on --ipv6 off
-VBoxManage natnetwork start --netname premises
-VBoxManage hostonlyif create <span class="org-comment-delimiter"># </span><span class="org-comment">vboxnet0
-</span>VBoxManage hostonlyif ipconfig vboxnet0 --ip=192.168.56.10
-VBoxManage dhcpserver modify --interface=vboxnet0 --disable
-VBoxManage hostonlyif create <span class="org-comment-delimiter"># </span><span class="org-comment">vboxnet1
-</span>VBoxManage hostonlyif ipconfig vboxnet1 --ip=192.168.57.2
-</code></pre>
-</div>
+<tr>
+<td class="org-left"><code>dick</code></td>
+<td class="org-left">WAhrlGccPf/BaFS5bRtBE4hEyt3kDxCavmwZfVTsfGs=</td>
+</tr>
 
-<p>
-Note that the first host-only network, <code>vboxnet0</code>, gets DHCP service
-by default, but that will interfere with the service being tested on
-<code>core</code>, so it must be explicitly disabled.  Only the NAT network
-<code>premises</code> should have a DHCP server enabled.
-</p>
+<tr>
+<td class="org-left"><code>dicks-phone</code></td>
+<td class="org-left">oG/Kou9HOBCBwHAZGypPA1cZWUL6nR6WoxBiXc/OQWQ=</td>
+</tr>
 
-<p>
-Note also that actual ISPs and clouds will provide Gate and Front with
-public network addresses.  In this simulation "they" provide addresses
-on the private <code>192.168.15.0/24</code> network.
-</p>
+<tr>
+<td class="org-left"><code>dicks-razr</code></td>
+<td class="org-left">IGNcF0VpkIBcJQAcLZ9jgRmk0SYyUr/WwSNXZoXXUWQ=</td>
+</tr>
+</tbody>
+</table>
 </div>
 </div>
-<div id="outline-container-org69aa497" class="outline-3">
-<h3 id="org69aa497"><span class="section-number-3">13.2.</span> The Test Machines</h3>
-<div class="outline-text-3" id="text-13-2">
+<div id="outline-container-org618e3d5" class="outline-4">
+<h4 id="org618e3d5"><span class="section-number-4">13.2.2.</span> Ansible Test Authorization</h4>
+<div class="outline-text-4" id="text-13-2-2">
 <p>
-The virtual machines are created by <code>VBoxManage</code> command lines in the
-following sub-sections.  They each start with a recent Debian release
-(e.g. <q>debian-12.5.0-amd64-netinst.iso</q>) in their simulated DVD
-drives.  As in <a href="#org6a2440c">The Hardware</a> preparation process being simulated, a few
-additional software packages are installed.  Unlike in <a href="#org6a2440c">The Hardware</a>
-preparation, machines are moved to their final networks and <i>then</i>
-remote access is authorized.  (They are not accessible via <code>ssh</code> on
-the VirtualBox NAT network where they first boot.)
+Part of each machine's preparation is to authorize password-less SSH
+connections from Ansible, which will be using the public key in
+<q>Secret/ssh_admin/</q>.  This is common to all machines and so is
+provided here tagged with <code>test-auth</code> and used via noweb reference
+<code>&lt;&lt;test-auth&gt;&gt;</code> in each machine's preparatory script.
 </p>
 
-<p>
-Once the administrator's notebook is authorized to access the
-privileged accounts on the virtual machines, they are prepared for
-configuration by Ansible.
-</p>
+<div class="org-src-container">
+<code>test-auth</code><pre class="src src-sh" id="orgdb94eb2"><code>( <span class="org-builtin">cd</span>
+  <span class="org-builtin">umask</span> 077
+  <span class="org-keyword">if</span> [ <span class="org-negation-char">!</span> -d .ssh ]; <span class="org-keyword">then</span> mkdir .ssh; <span class="org-keyword">fi</span>
+  ( <span class="org-builtin">echo</span> -n <span class="org-string">"ssh-rsa"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">" AAAAB3NzaC1yc2EAAAADAQABAAABgQDXxXnqFaUq3WAmmW/P8OMm3cf"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"AGJoL1UC8yjbsRzt63RmusID2CvPTJfO/sbNAxDKHPBvYJqiwBY8Wh2V"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"BDXoO2lWAK9JOSvXMZZRmBh7Yk6+NsPSbeZ6H3DgzdmKubs4E5XEdkmO"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"iivyiGBWiwzDKAOqWvb60yWDDNEuHyGNznKjyL+nAOzul1hP5f23vX3e"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"VhTxV0zdClksvIppGsYY3EvhMxasnjvGOhECz1Pq/9PPxakY1kBKMFj8"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"yh75UfYJyRiUcFUVZD/dQyDMj7gtihv4ANiUAIgn94I4Gt9t8a2OiLyr"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"KhJAwTQrs4CA+suY+3uDcp2FuQAvuzpa2moUufNetQn9YYCpCQaio8I3"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"N9N5POqPGtNT/8Fv1wwWsl/T363NJma7lrtQXKgq52YYmaUNnHxPFqLP"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"/9ELaAKbKrXTel0ew/LyVEO6QJ6fU7lE3LYMF5DngleOpuOHyQdIJKvS"</span>
+    <span class="org-builtin">echo</span> -n <span class="org-string">"oCb7ilDuG8ekZd3ZEROhtyHlr7UcHrtmZMYjhlRc="</span>
+    <span class="org-builtin">echo</span> <span class="org-string">" A Small Institute Administrator"</span> ) <span class="org-sh-escaped-newline">\</span>
+  &gt;&gt;.ssh/authorized_keys )
+</code></pre>
 </div>
-<div id="outline-container-orgc49f179" class="outline-4">
-<h4 id="orgc49f179"><span class="section-number-4">13.2.1.</span> A Test Machine</h4>
-<div class="outline-text-4" id="text-13-2-1">
+</div>
+</div>
+<div id="outline-container-org8bbff7e" class="outline-4">
+<h4 id="org8bbff7e"><span class="section-number-4">13.2.3.</span> A Test Machine</h4>
+<div class="outline-text-4" id="text-13-2-3">
 <p>
 The following shell function contains most of the <code>VBoxManage</code>
 commands needed to create the test machines.  The name of the machine
@@ -8081,12 +8322,13 @@ create_vm
 </div>
 
 <p>
-Soon after starting, the machine console should show the installer's
-first prompt: to choose a system language.  Installation on the small
-machines, <code>front</code> and <code>gate</code>, may put the installation into "low
-memory mode", in which case the installation is textual, the system
-language is English, and the first prompt is for location.  The
-appropriate responses to the prompts are given in the list below.
+Soon after starting, the machine console shows the Debian GNU/Linux
+installer menu and the default "Graphical Install" is chosen.  On the
+machines with only 512MB of RAM, <code>front</code> and <code>gate</code>, the installer
+switches to a text screen and warns it is using a "Low memory mode".
+The installation proceeds in English and its first prompt is for a
+location.  The appropriate responses to this and subsequent prompts
+are given in the list below.
 </p>
 
 <ul class="org-ul">
@@ -8096,16 +8338,17 @@ appropriate responses to the prompts are given in the list below.
 </ul></li>
 <li>Select your location
 <ul class="org-ul">
-<li>Country, territory or area:  United States</li>
+<li>Continent or region:  9 (North America, if in low memory mode!)</li>
+<li>Country, territory or area:  4 (United States)</li>
 </ul></li>
 <li>Configure the keyboard
 <ul class="org-ul">
-<li>Keymap to use:  American English</li>
+<li>Keymap to use:  1 (American English)</li>
 </ul></li>
 <li>Configure the network
 <ul class="org-ul">
-<li>Hostname:  front (gate, core, etc.)</li>
-<li>Domain name:  small.example.org (small.private)</li>
+<li>Hostname:  small (gate, core, etc.)</li>
+<li>Domain name:  example.org (small.private)</li>
 </ul></li>
 <li>Set up users and passwords.
 <ul class="org-ul">
@@ -8116,23 +8359,23 @@ appropriate responses to the prompts are given in the list below.
 </ul></li>
 <li>Configure the clock
 <ul class="org-ul">
-<li>Select your time zone:  Eastern</li>
+<li>Select your time zone:  3 (Mountain)</li>
 </ul></li>
 <li>Partition disks
 <ul class="org-ul">
-<li>Partitioning method:  Guided - use entire disk</li>
-<li>Select disk to partition:  SCSI3 (0,0,0) (sda) - &hellip;</li>
-<li>Partitioning scheme:  All files in one partition</li>
-<li>Finish partitioning and write changes to disk:  Continue</li>
-<li>Write the changes to disks?  Yes</li>
+<li>Partitioning method:  1 (Guided - use entire disk)</li>
+<li>Select disk to partition:  1 (SCSI2 (0,0,0) (sda) - &hellip;)</li>
+<li>Partitioning scheme:  1 (All files in one partition)</li>
+<li>12 (Finish partitioning and write changes to disk &hellip;)</li>
+<li>Write the changes to disks?  1 (Yes)</li>
 </ul></li>
-<li>Install the base system</li>
+<li>Installing the base system</li>
 <li>Configure the package manager
 <ul class="org-ul">
-<li>Scan extra installation media?  No</li>
-<li>Debian archive mirror country:  United States</li>
-<li>Debian archive mirror:  deb.debian.org</li>
-<li>HTTP proxy information (blank for none):  &lt;blank&gt;</li>
+<li>Scan extra installation media?  2 (No)</li>
+<li>Debian archive mirror country:  62 (United States)</li>
+<li>Debian archive mirror:  1 (deb.debian.org)</li>
+<li>HTTP proxy information (blank for none):  &lt;localnet apt cache&gt;</li>
 </ul></li>
 <li>Configure popularity-contest
 <ul class="org-ul">
@@ -8140,8 +8383,7 @@ appropriate responses to the prompts are given in the list below.
 </ul></li>
 <li>Software selection
 <ul class="org-ul">
-<li>SSH server</li>
-<li>standard system utilities</li>
+<li>Choose software to install: SSH server, standard system utilities</li>
 </ul></li>
 <li>Install the GRUB boot loader
 <ul class="org-ul">
@@ -8151,16 +8393,16 @@ appropriate responses to the prompts are given in the list below.
 </ul>
 
 <p>
-After the reboot, the machine's console should produce a <code>login:</code>
-prompt.  The administrator logs in here, with username <code>sysadm</code> and
-password <code>fubar</code>, before continuing with the specific machine's
-preparation (below).
+After the reboot, the machine's console produces a <code>login:</code> prompt.
+The administrator logs in here, with username <code>sysadm</code> and password
+<code>fubar</code>, before continuing with the specific machine's preparation
+(below).
 </p>
 </div>
 </div>
-<div id="outline-container-org34fc9dd" class="outline-4">
-<h4 id="org34fc9dd"><span class="section-number-4">13.2.2.</span> The Test Front Machine</h4>
-<div class="outline-text-4" id="text-13-2-2">
+<div id="outline-container-org675a9cc" class="outline-4">
+<h4 id="org675a9cc"><span class="section-number-4">13.2.4.</span> The Test Front Machine</h4>
+<div class="outline-text-4" id="text-13-2-4">
 <p>
 The <code>front</code> machine is created with 512MiB of RAM, 4GiB of disk, and
 Debian 12.5.0 (recently downloaded) in its CDROM drive.  The exact
@@ -8168,77 +8410,198 @@ command lines were given in the previous section.
 </p>
 
 <p>
-After Debian is installed (as detailed above) <code>front</code> is shut down and
-its primary network interface moved to the simulated Internet, the NAT
-network <code>premises</code>.  <code>front</code> also gets a second network interface, on
-the host-only network <code>vboxnet1</code>, to make it directly accessible to
-the administrator's notebook (as described in <a href="#orgb37f468">The Test Networks</a>).
+After Debian is installed (as detailed above) and the machine
+rebooted, the administrator copies the following script to the machine
+and executes it.
+</p>
+
+<p>
+The script is copied through an intermediary, an account on the local
+network thus accessible to both the host and guests on the host's NAT
+networks.  If <code>USER@SERVER</code> is such an account, the script would be
+copied and executed thusly:
+</p>
+
+<pre class="example">
+notebook$ scp private/test-front-prep USER@SERVER:
+notebook$ scp -r Secret/ssh_front/ USER@SERVER:
+</pre>
+
+<pre class="example">
+sysadm@front$ scp USER@SERVER:test-front-prep ./
+sysadm@front$ scp -r USER@SERVER:ssh_front/ ./
+sysadm@front$ ./test-front-prep
+</pre>
+
+<p>
+The script starts by installing additional software packages.  The
+<code>wireguard</code> package is installed so that <q>/etc/wireguard/</q> is created.
+The <code>systemd-resolved</code> package is installed because a reboot seems the
+only way to get name service working afterwards.  As <code>front</code> will
+always have Internet access in the cloud, the rest of the packages are
+installed just to shorten Ansible's work later.
+</p>
+
+<div class="org-src-container">
+<a href="private/test-front-prep"><q>private/test-front-prep</q></a><pre class="src src-sh"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
+</span>
+sudo apt install wireguard systemd-resolved <span class="org-sh-escaped-newline">\</span>
+    unattended-upgrades postfix dovecot-imapd rsync apache2 kamailio
+</code></pre>
+</div>
+
+<p>
+The Postfix installation prompts for a couple settings.  The defaults,
+listed below, are fine.
+</p>
+
+<ul class="org-ul">
+<li>General type of mail configuration: Internet Site</li>
+<li>System mail name: small.example.org</li>
+</ul>
+
+<p>
+The script can now install the private WireGuard™ key, as well as
+Ansible's public SSH key.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>VBoxManage modifyvm front --nic1 natnetwork --natnetwork1 premises
-VBoxManage modifyvm front --nic2 hostonly --hostonlyadapter2 vboxnet1
+<a href="private/test-front-prep"><q>private/test-front-prep</q></a><pre class="src src-sh"><code>
+( <span class="org-builtin">umask</span> 377
+  <span class="org-builtin">echo</span> <span class="org-string">"AJkzVxfTm/KvRjzTN/9X2jYy+CAugiwZfN5F3JTegms="</span> <span class="org-sh-escaped-newline">\</span>
+  | sudo tee /etc/wireguard/private-key &gt;/dev/null )
+
+&lt;&lt;test-auth&gt;&gt;
 </code></pre>
 </div>
 
 <p>
-After Debian is installed and the machine rebooted, the administrator
-logs in and configures the "extra" network interface with a static IP
-address using a drop-in configuration file:
-<q>/etc/network/interfaces.d/eth1</q>.
+Next, the network interfaces are configured with static IP addresses.
+In actuality, Front gets no network configuration tweaks.  The Debian
+12 default is to broadcast for a DHCP lease on the primary NIC.  This
+works in the cloud, which should respond with an offer, though it must
+offer the public, DNS-registered, hard-coded <code>front_addr</code>.
+</p>
+
+<p>
+For testing purposes, the preparation of <code>front</code> replaces the default
+<q>/etc/network/interfaces</q> with a new configuration that statically
+assigns <code>front_addr</code> to the primary NIC and a testing subnet address
+to the second NIC.
 </p>
 
 <div class="org-src-container">
-<q>eth1</q><pre class="src src-conf"><code>auto enp0s8
+<a href="private/test-front-prep"><q>private/test-front-prep</q></a><pre class="src src-sh"><code>
+( <span class="org-builtin">cd</span> /etc/network/; <span class="org-sh-escaped-newline">\</span>
+  [ -f interfaces~ ] || sudo mv interfaces interfaces~ )
+cat &lt;&lt;EOF | sudo tee /etc/network/interfaces &gt;/dev/null<span class="org-sh-heredoc">
+# This file describes the network interfaces available on your system
+# and how to activate them. For more information, see interfaces(5).
+
+source /etc/network/interfaces.d/*
+
+# The loopback network interface
+auto lo
+iface lo inet loopback
+
+# The primary network interface
+auto enp0s3
+iface enp0s3 inet static
+    address 192.168.15.4/24
+    gateway 192.168.15.1
+
+# Testing interface
+auto enp0s8
 iface enp0s8 inet static
-    address 192.168.57.3/24
+    address 192.168.58.3/24
+EOF</span>
+</code></pre>
+</div>
+
+<p>
+Ansible expects <code>front</code> to use the SSH host keys in
+<q>Secret/ssh_front/</q>, so it is prepared with these keys in advance.
+(If Ansible installed them, <code>front</code> would change identities while
+Ansible was configuring it.  Ansible would lose subsequent access
+until the administrator's <q>~/.ssh/known_hosts</q> was updated!)
+</p>
+
+<div class="org-src-container">
+<a href="private/test-front-prep"><q>private/test-front-prep</q></a><pre class="src src-sh"><code>
+( <span class="org-builtin">cd</span> ssh_front/etc/ssh/
+  chmod 600 ssh_host_*
+  chmod 644 ssh_host_*.pub
+  sudo cp -b ssh_host_* /etc/ssh/ )
 </code></pre>
 </div>
 
 <p>
-A <code>sudo ifup enp0s8</code> command then brings the interface up.
+With the preparatory script successfully executed, <code>front</code> is shut
+down and moved to the simulated cloud (from the default NAT network).
+</p>
+
+<p>
+The following <code>VBoxManage</code> commands effect the move, connecting the
+primary NIC to <code>public</code> and a second NIC to the host-only network
+<code>vboxnet2</code> (making it directly accessible to the administrator's
+notebook as described in <a href="#org1bea466">The Test Networks</a>).
 </p>
 
+<div class="org-src-container">
+<pre class="src src-sh"><code>VBoxManage modifyvm front --nic1 natnetwork --natnetwork1 public
+VBoxManage modifyvm front --nic2 hostonly --hostonlyadapter2 vboxnet2
+</code></pre>
+</div>
+
 <p>
-Note that there is no pre-provisioning for <code>front</code>, which is never
-deployed on a frontier, always in the cloud.  Additional Debian
-packages are assumed to be readily available.  Thus Ansible installs
-them as necessary, but first the administrator authorizes remote
-access by following the instructions in the final section: <a href="#orgf046593">Ansible
-Test Authorization</a>.
+<code>front</code> is now prepared for configuration by Ansible.
 </p>
 </div>
 </div>
-<div id="outline-container-orga769609" class="outline-4">
-<h4 id="orga769609"><span class="section-number-4">13.2.3.</span> The Test Gate Machine</h4>
-<div class="outline-text-4" id="text-13-2-3">
+<div id="outline-container-org8084b28" class="outline-4">
+<h4 id="org8084b28"><span class="section-number-4">13.2.5.</span> The Test Gate Machine</h4>
+<div class="outline-text-4" id="text-13-2-5">
 <p>
 The <code>gate</code> machine is created with the same amount of RAM and disk as
 <code>front</code>.  Assuming the <code>RAM</code>, <code>DISK</code>, and <code>ISO</code> shell variables have
-not changed, <code>gate</code> can be created with two commands.
+not changed, <code>gate</code> can be created with one command.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code><span class="org-variable-name">NAME</span>=gate
-create_vm
+<pre class="src src-sh"><code><span class="org-variable-name">NAME</span>=gate create_vm
 </code></pre>
 </div>
 
 <p>
-After Debian is installed (as detailed in <a href="#orgc49f179">A Test Machine</a>) and the
-machine rebooted, the administrator logs in and installs several
-additional software packages.
+After Debian is installed (as detailed in <a href="#org8bbff7e">A Test Machine</a>) and the
+machine rebooted, the administrator copies the following script to the
+machine and executes it.
+</p>
+
+<pre class="example">
+notebook$ scp private/test-gate-prep USER@SERVER:
+</pre>
+
+<pre class="example">
+sysadm@gate$ scp USER@SERVER:test-gate-prep ./
+sysadm@gate$ ./test-gate-prep
+</pre>
+
+<p>
+The script starts by installing additional software packages.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>sudo apt install netplan.io systemd-resolved unattended-upgrades <span class="org-sh-escaped-newline">\</span>
-                 ufw isc-dhcp-server postfix wireguard
+<a href="private/test-gate-prep"><q>private/test-gate-prep</q></a><pre class="src src-sh"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
+</span>
+sudo apt install wireguard systemd-resolved unattended-upgrades <span class="org-sh-escaped-newline">\</span>
+                 postfix ufw lm-sensors nagios-nrpe-server
 </code></pre>
 </div>
 
 <p>
-Again, the Postfix installation prompts for a couple settings.  The
-defaults, listed below, are fine.
+The Postfix installation prompts for a couple settings.  The defaults,
+listed below, are fine.
 </p>
 
 <ul class="org-ul">
@@ -8247,18 +8610,66 @@ defaults, listed below, are fine.
 </ul>
 
 <p>
-<code>gate</code> can then move to the campus.  It is shut down before the
-following <code>VBoxManage</code> commands are executed.  The commands disconnect
-the primary Ethernet interface from <code>premises</code> and connect it to
-<code>vboxnet0</code>.  They also create two new interfaces, <code>isp</code> and <code>wild</code>,
-connected to the simulated ISP and campus wireless access point.
+The script then installs the private WireGuard™ key, as well as
+Ansible's public SSH key.
+</p>
+
+<div class="org-src-container">
+<a href="private/test-gate-prep"><q>private/test-gate-prep</q></a><pre class="src src-sh"><code>( <span class="org-builtin">umask</span> 377
+  <span class="org-builtin">echo</span> <span class="org-string">"yOBdLbXh6KBwYQvvb5mhiku8Fxkqc5Cdyz6gNgjc/2U="</span> <span class="org-sh-escaped-newline">\</span>
+  | sudo tee /etc/wireguard/private-key &gt;/dev/null )
+
+&lt;&lt;test-auth&gt;&gt;
+</code></pre>
+</div>
+
+<p>
+Next, the script configures the primary NIC with <q>10-lan.link</q> and
+<q>10-lan.network</q> files installed in <q>/etc/systemd/network/</q>.  (This is
+sufficient to allow remote access by Ansible.)
+</p>
+
+<div class="org-src-container">
+<a href="private/test-gate-prep"><q>private/test-gate-prep</q></a><pre class="src src-sh"><code>
+cat &lt;&lt;EOD | sudo tee /etc/systemd/network/10-lan.link &gt;/dev/null<span class="org-sh-heredoc">
+[Match]
+MACAddress=08:00:27:f3:16:79
+
+[Link]
+Name=lan
+EOD
+</span>
+cat &lt;&lt;EOD | sudo tee /etc/systemd/network/10-lan.network &gt;/dev/null<span class="org-sh-heredoc">
+[Match]
+MACAddress=08:00:27:f3:16:79
+
+[Network]
+Address=192.168.56.2/24
+DNS=192.168.56.1
+Domains=small.private
+EOD
+</span>
+sudo systemctl --quiet enable systemd-networkd
+</code></pre>
+</div>
+
+<p>
+With the preparatory script successfully executed, <code>gate</code> is shut down
+and moved to the campus network (from the default NAT network).
+</p>
+
+<p>
+The following <code>VBoxManage</code> commands effect the move, connecting the
+primary NIC to <code>vboxnet0</code> and creating two new interfaces, <code>isp</code> and
+<code>wild</code>.  These are connected to the simulated ISP and the simulated
+wild Ethernet (e.g. campus wireless access points, IoT, whatnot).
 </p>
 
 <div class="org-src-container">
 <pre class="src src-sh"><code>VBoxManage modifyvm gate --mac-address1=080027f31679
 VBoxManage modifyvm gate --nic1 hostonly --hostonlyadapter1 vboxnet0
 VBoxManage modifyvm gate --mac-address2=0800273d42e5
-VBoxManage modifyvm gate --nic2 natnetwork --natnetwork2 premises
+VBoxManage modifyvm gate --nic2 natnetwork --natnetwork2 public
 VBoxManage modifyvm gate --mac-address3=0800274aded2
 VBoxManage modifyvm gate --nic3 hostonly --hostonlyadapter3 vboxnet1
 </code></pre>
@@ -8299,7 +8710,7 @@ values of the MAC address variables in this table.
 
 <tr>
 <td class="org-left"><code>enp0s8</code></td>
-<td class="org-left"><code>premises</code></td>
+<td class="org-left"><code>public</code></td>
 <td class="org-left">campus ISP</td>
 <td class="org-left"><code>gate_isp_mac</code></td>
 </tr>
@@ -8314,61 +8725,60 @@ values of the MAC address variables in this table.
 </table>
 
 <p>
-After <code>gate</code> boots up with its new network interfaces, the primary
-Ethernet interface is temporarily configured with an IP address.
-(Ansible will install a Netplan soon.)
-</p>
-
-<div class="org-src-container">
-<pre class="src src-sh"><code>sudo ip address add 192.168.56.2/24 dev enp0s3
-</code></pre>
-</div>
-
-<p>
-Finally, the administrator authorizes remote access by following the
-instructions in the final section: <a href="#orgf046593">Ansible Test Authorization</a>.
+<code>gate</code> is now prepared for configuration by Ansible.
 </p>
 </div>
 </div>
-<div id="outline-container-org27ade3a" class="outline-4">
-<h4 id="org27ade3a"><span class="section-number-4">13.2.4.</span> The Test Core Machine</h4>
-<div class="outline-text-4" id="text-13-2-4">
+<div id="outline-container-orgeb19a39" class="outline-4">
+<h4 id="orgeb19a39"><span class="section-number-4">13.2.6.</span> The Test Core Machine</h4>
+<div class="outline-text-4" id="text-13-2-6">
 <p>
 The <code>core</code> machine is created with 1GiB of RAM and 6GiB of disk.
 Assuming the <code>ISO</code> shell variable has not changed, <code>core</code> can be
-created with following commands.
+created with following command.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code><span class="org-variable-name">NAME</span>=core
-<span class="org-variable-name">RAM</span>=2048
-<span class="org-variable-name">DISK</span>=6144
-create_vm
+<pre class="src src-sh"><code><span class="org-variable-name">NAME</span>=core <span class="org-variable-name">RAM</span>=2048 <span class="org-variable-name">DISK</span>=6144 create_vm
 </code></pre>
 </div>
 
 <p>
-After Debian is installed (as detailed in <a href="#orgc49f179">A Test Machine</a>) and the
-machine rebooted, the administrator logs in and installs several
-additional software packages.
+After Debian is installed (as detailed in <a href="#org8bbff7e">A Test Machine</a>) and the
+machine rebooted, the administrator copies the following script to the
+machine and executes it.
+</p>
+
+<pre class="example">
+notebook$ scp private/test-core-prep USER@SERVER:
+</pre>
+
+<pre class="example">
+sysadm@core$ scp USER@SERVER:test-core-prep ./
+sysadm@core$ ./test-core-prep
+</pre>
+
+<p>
+The script starts by installing additional software packages.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>sudo apt install netplan.io systemd-resolved unattended-upgrades <span class="org-sh-escaped-newline">\</span>
-                 ntp isc-dhcp-server bind9 apache2 wireguard <span class="org-sh-escaped-newline">\</span>
-                 postfix dovecot-imapd fetchmail expect rsync <span class="org-sh-escaped-newline">\</span>
-                 gnupg
-sudo apt install mariadb-server php php-{apcu,bcmath,curl,gd,gmp}<span class="org-sh-escaped-newline">\</span>
+<a href="private/test-core-prep"><q>private/test-core-prep</q></a><pre class="src src-sh"><code><span class="org-comment-delimiter">#</span><span class="org-comment">!/bin/</span><span class="org-keyword">bash</span><span class="org-comment"> -e
+</span>
+sudo apt install wireguard systemd-resolved unattended-upgrades <span class="org-sh-escaped-newline">\</span>
+                 chrony isc-dhcp-server bind9 apache2 postfix <span class="org-sh-escaped-newline">\</span>
+                 dovecot-imapd fetchmail rsync gnupg <span class="org-sh-escaped-newline">\</span>
+                 mariadb-server php php-{apcu,bcmath,curl,gd,gmp}<span class="org-sh-escaped-newline">\</span>
                  php-{json,mysql,mbstring,intl,imagick,xml,zip} <span class="org-sh-escaped-newline">\</span>
-                 libapache2-mod-php
-sudo apt install nagios4 monitoring-plugins-basic lm-sensors <span class="org-sh-escaped-newline">\</span>
+                 imagemagick libapache2-mod-php <span class="org-sh-escaped-newline">\</span>
+                 nagios4 monitoring-plugins-basic lm-sensors <span class="org-sh-escaped-newline">\</span>
                  nagios-nrpe-plugin
 </code></pre>
 </div>
 
 <p>
-Again the Postfix installation prompts for a couple settings.  The
-defaults, listed below, are fine.
+The Postfix installation prompts for a couple settings.  The defaults,
+listed below, are fine.
 </p>
 
 <ul class="org-ul">
@@ -8377,135 +8787,109 @@ defaults, listed below, are fine.
 </ul>
 
 <p>
-And domain name resolution may be broken after installing
-<code>systemd-resolved</code>.  A reboot is often needed after the first <code>apt
-install</code> command above.
-</p>
-
-<p>
-Before shutting down, the name of the primary Ethernet interface
-should be compared to the example variable setting in
-<a href="private/vars.yml"><q>private/vars.yml</q></a>.  The value assigned to <code>core_ethernet</code> should
-match the interface name.
-</p>
-
-<p>
-<code>core</code> can now move to the campus.  It is shut down before the
-following <code>VBoxManage</code> command is executed.  The command connects the
-machine's NIC to <code>vboxnet0</code>, which simulates the campus's private
-Ethernet.
+The script can now install the private WireGuard™ key, as well as
+Ansible's public SSH key.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>VBoxManage modifyvm core --nic1 hostonly --hostonlyadapter1 vboxnet0
-</code></pre>
-</div>
-
-<p>
-After <code>core</code> boots up with its new network connection, its primary NIC
-is temporarily configured with an IP address.  (Ansible will install a
-Netplan soon.)
-</p>
+<a href="private/test-core-prep"><q>private/test-core-prep</q></a><pre class="src src-sh"><code>( <span class="org-builtin">umask</span> 377
+  <span class="org-builtin">echo</span> <span class="org-string">"AI+KhwnsHzSPqyIyAObx7EBBTBXFZPiXb2/Qcts8zEI="</span> <span class="org-sh-escaped-newline">\</span>
+  | sudo tee /etc/wireguard/private-key &gt;/dev/null )
 
-<div class="org-src-container">
-<pre class="src src-sh"><code>sudo ip address add 192.168.56.1/24 dev enp0s3
+&lt;&lt;test-auth&gt;&gt;
 </code></pre>
 </div>
 
 <p>
-Finally, the administrator authorizes remote access by following the
-instructions in the next section: <a href="#orgf046593">Ansible Test Authorization</a>.
-</p>
-</div>
-</div>
-<div id="outline-container-orgf046593" class="outline-4">
-<h4 id="orgf046593"><span class="section-number-4">13.2.5.</span> Ansible Test Authorization</h4>
-<div class="outline-text-4" id="text-13-2-5">
-<p>
-To authorize Ansible's access to the three test machines, they must
-allow remote access to their <code>sysadm</code> accounts.  In the following
-commands, the administrator must use IP addresses to copy the public
-key to each test machine.
+Next, the script configures the primary NIC with <q>10-lan.link</q> and
+<q>10-lan.network</q> files installed in <q>/etc/systemd/network/</q>.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code><span class="org-variable-name">SRC</span>=Secret/ssh_admin/id_rsa.pub
-scp $<span class="org-variable-name">SRC</span> sysadm@192.168.57.3:admin_key <span class="org-comment-delimiter"># </span><span class="org-comment">Front
-</span>scp $<span class="org-variable-name">SRC</span> sysadm@192.168.56.2:admin_key <span class="org-comment-delimiter"># </span><span class="org-comment">Gate
-</span>scp $<span class="org-variable-name">SRC</span> sysadm@192.168.56.1:admin_key <span class="org-comment-delimiter"># </span><span class="org-comment">Core</span>
-</code></pre>
-</div>
+<a href="private/test-core-prep"><q>private/test-core-prep</q></a><pre class="src src-sh"><code>
+cat &lt;&lt;EOD | sudo tee /etc/systemd/network/10-lan.link &gt;/dev/null<span class="org-sh-heredoc">
+[Match]
+MACAddress=08:00:27:b3:e5:5f
 
-<p>
-Then the key must be installed on each machine with the following
-command line (entered at each console, or in an SSH session with
-each machine).
-</p>
-
-<div class="org-src-container">
-<pre class="src src-sh"><code>( <span class="org-builtin">cd</span>; <span class="org-builtin">umask</span> 077; mkdir .ssh; cp admin_key .ssh/authorized_keys )
+[Link]
+Name=lan
+EOD
+</span>
+cat &lt;&lt;EOD | sudo tee /etc/systemd/network/10-lan.network &gt;/dev/null<span class="org-sh-heredoc">
+[Match]
+MACAddress=08:00:27:b3:e5:5f
+
+[Network]
+Address=192.168.56.1/24
+Gateway=192.168.56.2
+DNS=192.168.56.1
+Domains=small.private
+EOD
+</span>
+sudo systemctl --quiet enable systemd-networkd
 </code></pre>
 </div>
 
 <p>
-The <code>front</code> machine needs a little additional preparation.  Ansible
-will configure <code>front</code> with the host keys in <q>Secret/</q>.  These should
-be installed there now so that <code>front</code> does not appear to change
-identities while Ansible is configuring.
+With the preparatory script successfully executed, <code>core</code> is shut down
+and moved to the campus network (from the default NAT network).
 </p>
 
 <p>
-First, the host keys are securely copied to <code>front</code> with the following
-command.
+The following <code>VBoxManage</code> commands effect the move, connecting the
+primary NIC to <code>vboxnet0</code>.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>scp Secret/ssh_front/etc/ssh/ssh_host_* sysadm@192.168.57.3:
+<pre class="src src-sh"><code>VBoxManage modifyvm core --mac-address1=080027b3e55f
+VBoxManage modifyvm core --nic1 hostonly --hostonlyadapter1 vboxnet0
 </code></pre>
 </div>
 
 <p>
-Then they are installed with these commands.
+<code>core</code> is now prepared for configuration by Ansible.
 </p>
-
-<div class="org-src-container">
-<pre class="src src-sh"><code>chmod 600 ssh_host_*
-chmod 644 ssh_host_*.pub
-sudo cp -b ssh_host_* /etc/ssh/
-</code></pre>
-</div>
-
-<p>
-Finally, the system administrator removes the old identity of <code>front</code>.
-</p>
-
-<pre class="example">
-ssh-keygen -f ~/.ssh/known_hosts -R 192.168.57.3
-</pre>
 </div>
 </div>
 </div>
-<div id="outline-container-orgc94c022" class="outline-3">
-<h3 id="orgc94c022"><span class="section-number-3">13.3.</span> Configure Test Machines</h3>
+<div id="outline-container-org190d4c4" class="outline-3">
+<h3 id="org190d4c4"><span class="section-number-3">13.3.</span> Configure Test Machines</h3>
 <div class="outline-text-3" id="text-13-3">
 <p>
 At this point the three test machines <code>core</code>, <code>gate</code>, and <code>front</code> are
 running fresh Debian systems with select additional packages, on their
 final networks, with a privileged account named <code>sysadm</code> that
 authorizes password-less access from the administrator's notebook,
-ready to be configured by Ansible.
+ready to be configured by Ansible.  However the administrator's
+notebook may not recognize the test VMs or, worse yet, remember
+different public keys for them (from previous test machines).  For
+this reason, the administrator executes the following commands before
+the initial <code>./inst config</code>.
+</p>
+
+<pre class="example">
+ssh sysadm@192.168.56.1 date
+ssh sysadm@192.168.56.2 date
+ssh sysadm@192.168.58.3 date
+./inst config
+</pre>
+
+<p>
+Note that this initial run should exercise all of the handlers, <i>and</i>
+that subsequent runs probably <i>do not</i>.
 </p>
 
 <p>
-To configure the test machines, the <code>./inst config</code> command is
-executed and <code>core</code> restarted.  Note that this first run should
-exercise all of the handlers, <i>and</i> that subsequent runs probably <i>do
-not</i>.
+Presumably the <code>./inst config</code> command completed successfully, but
+before testing begins, <code>gate</code> is restarted.  Basic networking tests
+will fail unless the interfaces on <code>gate</code> are renamed, and nothing
+less than a restart will get <code>systemd-udevd</code> to rename the <code>isp</code> and
+<code>wifi</code> interfaces.
 </p>
 </div>
 </div>
-<div id="outline-container-org2f6cd0a" class="outline-3">
-<h3 id="org2f6cd0a"><span class="section-number-3">13.4.</span> Test Basics</h3>
+<div id="outline-container-org50d38f3" class="outline-3">
+<h3 id="org50d38f3"><span class="section-number-3">13.4.</span> Test Basics</h3>
 <div class="outline-text-3" id="text-13-4">
 <p>
 At this point the test institute is just <code>core</code>, <code>gate</code> and <code>front</code>,
@@ -8527,7 +8911,7 @@ forwarding (and NATing).  On <code>core</code> (and <code>gate</code>):
 
 <div class="org-src-container">
 <pre class="src src-sh"><code>ping -c 1 8.8.4.4      <span class="org-comment-delimiter"># </span><span class="org-comment">dns.google
-</span>ping -c 1 192.168.15.5 <span class="org-comment-delimiter"># </span><span class="org-comment">front_addr</span>
+</span>ping -c 1 192.168.15.4 <span class="org-comment-delimiter"># </span><span class="org-comment">front_addr</span>
 </code></pre>
 </div>
 
@@ -8567,12 +8951,12 @@ instant attention).
 </p>
 </div>
 </div>
-<div id="outline-container-orgfceae51" class="outline-3">
-<h3 id="orgfceae51"><span class="section-number-3">13.5.</span> The Test Nextcloud</h3>
+<div id="outline-container-org8d96867" class="outline-3">
+<h3 id="org8d96867"><span class="section-number-3">13.5.</span> The Test Nextcloud</h3>
 <div class="outline-text-3" id="text-13-5">
 <p>
 Further tests involve Nextcloud account management.  Nextcloud is
-installed on <code>core</code> as described in <a href="#orga909c3b">Configure Nextcloud</a>.  Once
+installed on <code>core</code> as described in <a href="#orgdd6d845">Install Nextcloud</a>.  Once
 <q>/Nextcloud/</q> is created, <code>./inst config core</code> will validate
 or update its configuration files.
 </p>
@@ -8594,8 +8978,8 @@ with the <code>./inst client</code> command.
 </p>
 </div>
 </div>
-<div id="outline-container-org751e880" class="outline-3">
-<h3 id="org751e880"><span class="section-number-3">13.6.</span> Test New Command</h3>
+<div id="outline-container-orgd84a6fc" class="outline-3">
+<h3 id="orgd84a6fc"><span class="section-number-3">13.6.</span> Test New Command</h3>
 <div class="outline-text-3" id="text-13-6">
 <p>
 A member must be enrolled so that a member's client machine can be
@@ -8615,8 +8999,8 @@ Take note of Dick's initial password.
 </p>
 </div>
 </div>
-<div id="outline-container-org904ce59" class="outline-3">
-<h3 id="org904ce59"><span class="section-number-3">13.7.</span> The Test Member Notebook</h3>
+<div id="outline-container-org75fdb08" class="outline-3">
+<h3 id="org75fdb08"><span class="section-number-3">13.7.</span> The Test Member Notebook</h3>
 <div class="outline-text-3" id="text-13-7">
 <p>
 A test member's notebook is created next, much like the servers,
@@ -8644,7 +9028,7 @@ behind) the access point.
 </p>
 
 <p>
-Debian is installed much as detailed in <a href="#orgc49f179">A Test Machine</a> <i>except</i> that
+Debian is installed much as detailed in <a href="#org8bbff7e">A Test Machine</a> <i>except</i> that
 the SSH server option is <i>not</i> needed and the GNOME desktop option
 <i>is</i>.  When the machine reboots, the administrator logs into the
 desktop and installs a couple additional software packages (which
@@ -8652,31 +9036,32 @@ require several more).
 </p>
 
 <div class="org-src-container">
-<pre class="src src-nil"><code>sudo apt install wireguard nextcloud-desktop evolution
+<pre class="src src-nil"><code>sudo apt install systemd-resolved \
+                 wireguard nextcloud-desktop evolution
 </code></pre>
 </div>
 </div>
 </div>
-<div id="outline-container-org527d5d6" class="outline-3">
-<h3 id="org527d5d6"><span class="section-number-3">13.8.</span> Test Client Command</h3>
+<div id="outline-container-orgb888947" class="outline-3">
+<h3 id="orgb888947"><span class="section-number-3">13.8.</span> Test Client Command</h3>
 <div class="outline-text-3" id="text-13-8">
 <p>
 The <code>./inst client</code> command is used to register the public key of a
 client wishing to connect to the institute's VPNs.  In this test, new
 member Dick wants to connect his notebook, <code>dick</code>, to the institute
 VPNs.  First he generates a pair of WireGuard™ keys by running the
-following commands on Dick's notebook.
+following commands on his notebook.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>( <span class="org-builtin">umask</span> 077; wg genkey &gt;private)
-wg pubkey &lt;private &gt;public
+<pre class="src src-sh"><code>( <span class="org-builtin">umask</span> 077; wg genkey <span class="org-sh-escaped-newline">\</span>
+  | sudo tee /etc/wireguard/private-key ) | wg pubkey
 </code></pre>
 </div>
 
 <p>
-The administrator uses the key in <q>public</q> to run the following
-command, generating <q>campus.conf</q> and <q>public.conf</q> files.
+Dick then sends the resulting public key to the administrator, who
+runs the following command.
 </p>
 
 <div class="org-src-container">
@@ -8684,38 +9069,25 @@ command, generating <q>campus.conf</q> and <q>public.conf</q> files.
   4qd4xdRztZBKhFrX9jI/b4fnMzpKQ5qhg691hwYSsX8=
 </code></pre>
 </div>
-</div>
-</div>
-<div id="outline-container-org4a8304f" class="outline-3">
-<h3 id="org4a8304f"><span class="section-number-3">13.9.</span> Test Campus WireGuard™ Subnet</h3>
-<div class="outline-text-3" id="text-13-9">
-<p>
-The <q>campus.conf</q> WireGuard™ configuration file (generated in <a href="#org527d5d6">Test
-Client Command</a>) is transferred to <code>dick</code>, which is at the Wi-Fi access
-point's IP address, host 2 on the wild Ethernet.
-</p>
-
-<div class="org-src-container">
-<pre class="src src-sh"><code>scp *.conf sysadm@192.168.57.2:
-</code></pre>
-</div>
-
-<p>
-Dick then pastes his notebook's private key into the template
-<q>campus.conf</q> file and installs the result in
-<q>/etc/wireguard/wg0.conf</q>, doing the same to complete <q>public.conf</q>
-and install it in <q>/etc/wireguard/wg1.conf</q>.
-</p>
 
 <p>
-To connect to the campus VPN, the following command is run.
+The command generates <q>campus.conf</q> and <q>public.conf</q> configuration
+files, which the administrator sends, openly (e.g. in email) to Dick.
+Dick then installs the configuration files in <q>/etc/wireguard/</q> and
+creates the <code>campus</code> interface.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>systemctl start wg-quick@wg0
+<pre class="src src-sh"><code>sudo cp {campus,public}.conf /etc/wireguard/
+sudo wg-quick up campus
+sudo systemctl enable wg-quick@campus
 </code></pre>
 </div>
-
+</div>
+</div>
+<div id="outline-container-org612f179" class="outline-3">
+<h3 id="org612f179"><span class="section-number-3">13.9.</span> Test Campus WireGuard™ Subnet</h3>
+<div class="outline-text-3" id="text-13-9">
 <p>
 A few basic tests are then performed in a terminal.
 </p>
@@ -8731,8 +9103,8 @@ host www
 </div>
 </div>
 </div>
-<div id="outline-container-orgc07c857" class="outline-3">
-<h3 id="orgc07c857"><span class="section-number-3">13.10.</span> Test Web Pages</h3>
+<div id="outline-container-orgdb5370a" class="outline-3">
+<h3 id="orgdb5370a"><span class="section-number-3">13.10.</span> Test Web Pages</h3>
 <div class="outline-text-3" id="text-13-10">
 <p>
 Next, the administrator copies <a href="Backup/WWW/"><q>Backup/WWW/</q></a> (included in the
@@ -8741,10 +9113,10 @@ appropriately.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>sudo chown -R sysadm.staff /WWW/campus
-sudo chown -R monkey.staff /WWW/live /WWW/test
+<pre class="src src-sh"><code>sudo chown -R monkey:staff /WWW/campus /WWW/live /WWW/test
 sudo chmod 02775 /WWW/*
 sudo chmod 664 /WWW/*/index.html
+sudo -u monkey /usr/local/sbin/webupdate
 </code></pre>
 </div>
 
@@ -8761,61 +9133,51 @@ the source file.
 <li><code>http://live.small.private/</code></li>
 <li><code>http://test/</code></li>
 <li><code>http://test.small.private/</code></li>
-<li><code>http://small.example.org/</code></li>
 </ul>
 
 <p>
-The last URL should re-direct to <code>https://small.example.org/</code>, which
-uses a certificate (self-)signed by an unknown authority.  Firefox
-will warn but allow the luser to continue.
+The first will probably be flagged as unverifiable, signed by an
+unknown issuer, etc.  Otherwise, each should be accessible, displaying
+a short description of the website that was being simulated.
 </p>
-</div>
-</div>
-<div id="outline-container-org89e041c" class="outline-3">
-<h3 id="org89e041c"><span class="section-number-3">13.11.</span> Test Web Update</h3>
-<div class="outline-text-3" id="text-13-11">
+
 <p>
-Modify <q>/WWW/live/index.html</q> on <code>core</code> and wait 15 minutes for it to
-appear as <code>https://small.example.org/</code> (and in <q>/home/www/index.html</q>
-on <code>front</code>).
+The simulated public web site at <code>http://192.168.15.4/</code> is also
+tested.  It should redirect to <code>https://small.example.org/</code>, which
+does not exist.  However, the web site at <code>https://192.168.15.4/</code>
+(with <code>httpS</code>) should exist and produce a legible page (after the
+usual warnings).
 </p>
 
 <p>
-Hack <q>/home/www/index.html</q> on <code>front</code> and observe the result at
-<code>https://small.example.org/</code>.  Wait 15 minutes for the correction.
+Next the administrator modifies <q>/WWW/live/index.html</q> on <code>core</code> and
+waits 15 minutes for the edit to appear in the web page at
+<code>https://192.168.15.4/</code> (and in the file <q>/home/www/index.html</q> on
+<code>front</code>).  The same is done to <q>/home/www/index.html</q> on <code>front</code> and
+the edit observed immediately, and its correction within 15 minutes.
 </p>
 </div>
 </div>
-<div id="outline-container-orga3da3a3" class="outline-3">
-<h3 id="orga3da3a3"><span class="section-number-3">13.12.</span> Test Nextcloud</h3>
-<div class="outline-text-3" id="text-13-12">
+<div id="outline-container-org1c9bb60" class="outline-3">
+<h3 id="org1c9bb60"><span class="section-number-3">13.11.</span> Test Nextcloud</h3>
+<div class="outline-text-3" id="text-13-11">
 <p>
-Nextcloud is typically installed and configured <i>after</i> the first
-Ansible run, when <code>core</code> has Internet access via <code>gate</code>.  Until the
-installation directory <q>/Nextcloud/nextcloud/</q> appears, the Ansible
-code skips parts of the Nextcloud configuration.  The same
-installation (or restoration) process used on Core is used on <code>core</code>
-to create <q>/Nextcloud/</q>.  The process starts with <a href="#orgd428b0f">Create
-<q>/Nextcloud/</q></a>, involves <a href="#org0c72e46">Restore Nextcloud</a> or <a href="#org0727aa7">Install Nextcloud</a>,
-and runs <code>./inst config core</code> again <a href="#orgdd1a3b6">8.23.6</a>.  When the <code>./inst
-config core</code> command is happy with the Nextcloud configuration on
-<code>core</code>, the administrator uses Dick's notebook to test it, performing
-the following tests on <code>dick</code>'s desktop.
+Using the browser on the simulated member notebook, the Nextcloud
+installation on <code>core</code> can be completed.  The following steps are
+performed on <code>dick</code>'s desktop.
 </p>
 
 <ul class="org-ul">
-<li>Use a web browser to get <code>http://core/nextcloud/</code>.  It should be a
-warning about accessing Nextcloud by an untrusted name.</li>
+<li>Get <code>http://core/nextcloud/</code>.  The attempt produces a warning about
+using Nextcloud via an untrusted name.</li>
 
-<li>Get <code>https://core.small.private/nextcloud/</code>.  It should be a
-login web page.</li>
+<li>Get <code>https://core.small.private/nextcloud/</code>.  Receive a login page.</li>
 
 <li>Login as <code>sysadm</code> with password <code>fubar</code>.</li>
 
 <li>Examine the security &amp; setup warnings in the Settings &gt;
 Administration &gt; Overview web page.  A few minor warnings are
-expected (besides the admonishment about using <code>http</code> rather than
-<code>https</code>).</li>
+expected.</li>
 
 <li>Download and enable Calendar and Contacts in the Apps &gt; Featured web
 page.</li>
@@ -8823,17 +9185,16 @@ page.</li>
 <li>Logout and login as <code>dick</code> with Dick's initial password (noted
 above).</li>
 
-<li>Use the Nextcloud app to sync <q>~/nextCloud/</q> with the cloud.  In the
-Nextcloud app's Connection Wizard (the initial dialog), choose to
-"Log in to your Nextcloud" with the URL
-<code>https://core.small.private/nextcloud</code>.  The web browser should pop
-up with a new tab: "Connect to your account".  Press "Log in" and
-"Grant access".  The Nextcloud Connection Wizard then prompts for
-sync parameters.  The defaults are fine.  Presumably the Local
-Folder is <q>/home/sysadm/Nextcloud/</q>.</li>
+<li>Use the Nextcloud app to sync <q>~/Nextcloud/</q> with the cloud.  In the
+Nextcloud Desktop app's Connection Wizard (the initial dialog),
+login with the URL <code>https://core.small.private/nextcloud</code>.  The web
+browser should pop up with a new tab: "Connect to your account".
+Press "Log in" and "Grant access".  The Nextcloud Connection Wizard
+then prompts for sync parameters.  The defaults are fine.
+Presumably the Local Folder is <q>/home/sysadm/Nextcloud/</q>.</li>
 
-<li>Drop a file in <q>~/Nextcloud/</q>, use the app to force a sync, and find
-the file in the Files web page.</li>
+<li>Drop a file in <q>~/Nextcloud/</q>, then find it in the Nextcloud Files
+web page.</li>
 
 <li><p>
 Create a Mail account in Evolution.  This step does not involve
@@ -8875,9 +9236,9 @@ the calendar.</li>
 </ul>
 </div>
 </div>
-<div id="outline-container-org2f53c67" class="outline-3">
-<h3 id="org2f53c67"><span class="section-number-3">13.13.</span> Test Email</h3>
-<div class="outline-text-3" id="text-13-13">
+<div id="outline-container-orgb1fdff3" class="outline-3">
+<h3 id="orgb1fdff3"><span class="section-number-3">13.12.</span> Test Email</h3>
+<div class="outline-text-3" id="text-13-12">
 <p>
 With Evolution running on the member notebook <code>dick</code>, one second email
 delivery can be demonstrated.  The administrator runs the following
@@ -8904,18 +9265,19 @@ Outgoing email is also tested.  A message to
 </p>
 </div>
 </div>
-<div id="outline-container-org5177fab" class="outline-3">
-<h3 id="org5177fab"><span class="section-number-3">13.14.</span> Test Public VPN</h3>
-<div class="outline-text-3" id="text-13-14">
+<div id="outline-container-orga15285e" class="outline-3">
+<h3 id="orga15285e"><span class="section-number-3">13.13.</span> Test Public VPN</h3>
+<div class="outline-text-3" id="text-13-13">
 <p>
 At this point, <code>dick</code> can move abroad, from the campus Wi-Fi
 (host-only network <code>vboxnet1</code>) to the broader Internet (the NAT
-network <code>premises</code>).  The following command makes the change.  The
-machine does not need to be shut down.
+network <code>public</code>).  The following command makes the change.  The
+machine does not need to be shut down if the GUI is used to change its
+NIC.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-s"><code>VBoxManage modifyvm dick --nic1 natnetwork --natnetwork1 premises
+<pre class="src src-s"><code>VBoxManage modifyvm dick --nic1 natnetwork --natnetwork1 public
 </code></pre>
 </div>
 
@@ -8934,7 +9296,7 @@ Again, some basics are tested in a terminal.
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>ping -c 1 8.8.4.4      <span class="org-comment-delimiter"># </span><span class="org-comment">dns.google
+<pre class="src src-sh"><code>ping -c 1 8.8.8.8      <span class="org-comment-delimiter"># </span><span class="org-comment">dns.google
 </span>ping -c 1 192.168.56.1 <span class="org-comment-delimiter"># </span><span class="org-comment">core
 </span>host dns.google
 host core.small.private
@@ -8943,17 +9305,19 @@ host www
 </div>
 
 <p>
-And these web pages are fetched with a browser.
+And, again, these web pages are fetched with a browser.
 </p>
 
 <ul class="org-ul">
-<li><a href="http://www/">http://www/</a></li>
-<li><a href="http://www.small.private/">http://www.small.private/</a></li>
-<li><a href="http://live/">http://live/</a></li>
-<li><a href="http://live.small.private/">http://live.small.private/</a></li>
-<li><a href="http://test/">http://test/</a></li>
-<li><a href="http://test.small.private/">http://test.small.private/</a></li>
-<li><a href="http://small.example.org/">http://small.example.org/</a></li>
+<li><code>http://www/</code></li>
+<li><code>http://www.small.private/</code></li>
+<li><code>http://live/</code></li>
+<li><code>http://live.small.private/</code></li>
+<li><code>http://test/</code></li>
+<li><code>http://test.small.private/</code></li>
+<li><code>http://192.168.15.4/</code></li>
+<li><code>https://192.168.15.4/</code></li>
+<li><code>http://core.small.private/nextcloud/</code></li>
 </ul>
 
 <p>
@@ -8963,19 +9327,23 @@ calendar events.
 </p>
 </div>
 </div>
-<div id="outline-container-orged86c59" class="outline-3">
-<h3 id="orged86c59"><span class="section-number-3">13.15.</span> Test Pass Command</h3>
-<div class="outline-text-3" id="text-13-15">
+<div id="outline-container-org78227a7" class="outline-3">
+<h3 id="org78227a7"><span class="section-number-3">13.14.</span> Test Pass Command</h3>
+<div class="outline-text-3" id="text-13-14">
 <p>
 To test the <code>./inst pass</code> command, the administrator logs in to <code>core</code>
 as <code>dick</code> and runs <code>passwd</code>.  A random password is entered, more
-obscure than <code>fubar</code> (else Nextcloud will reject it!).  The
-administrator then finds the password change request message in the
-most recent file in <q>/home/sysadm/Maildir/new/</q> and pipes it to the
-<code>./inst pass</code> command.  The administrator might do that by copying the
-message to a more conveniently named temporary file on <code>core</code>,
-e.g. <q>~/msg</q>, copying that to the current directory on the notebook,
-and feeding it to <code>./inst pass</code> on its standard input.
+obscure than <code>fubar</code> (else Nextcloud will reject it!).
+</p>
+
+<p>
+The administrator then finds the password change request message in
+the most recent file in <q>/home/sysadm/Maildir/new/</q> and pipes it to
+the <code>./inst pass</code> command.  The administrator might do that by copying
+the message to a more conveniently named temporary file on <code>core</code>,
+e.g. <q>~/msg</q>, copying that to the current directory on the
+administrator's notebook, and feeding it to <code>./inst pass</code> on standard
+input.
 </p>
 
 <p>
@@ -8983,7 +9351,8 @@ On <code>core</code>, logged in as <code>sysadm</code>:
 </p>
 
 <div class="org-src-container">
-<pre class="src src-sh"><code>( <span class="org-builtin">cd</span> ~/Maildir/new/
+<pre class="src src-sh"><code>sudo -u dick passwd
+( <span class="org-builtin">cd</span> ~/Maildir/new/
   cp <span class="org-sh-quoted-exec">`ls -1t | head -1`</span> ~/msg )
 grep Subject: ~/msg
 </code></pre>
@@ -9011,9 +9380,9 @@ Finally, the administrator verifies that <code>dick</code> can login on <code>co
 </p>
 </div>
 </div>
-<div id="outline-container-org3c761c1" class="outline-3">
-<h3 id="org3c761c1"><span class="section-number-3">13.16.</span> Test Old Command</h3>
-<div class="outline-text-3" id="text-13-16">
+<div id="outline-container-orgd6f02c8" class="outline-3">
+<h3 id="orgd6f02c8"><span class="section-number-3">13.15.</span> Test Old Command</h3>
+<div class="outline-text-3" id="text-13-15">
 <p>
 One more institute command is left to exercise.  The administrator
 retires <code>dick</code> and his main device <code>dick</code>.
@@ -9032,16 +9401,16 @@ fail.
 </div>
 </div>
 </div>
-<div id="outline-container-org5577075" class="outline-2">
-<h2 id="org5577075"><span class="section-number-2">14.</span> Future Work</h2>
+<div id="outline-container-org7c4f8ab" class="outline-2">
+<h2 id="org7c4f8ab"><span class="section-number-2">14.</span> Future Work</h2>
 <div class="outline-text-2" id="text-14">
 <p>
 The small institute's network, as currently defined in this doocument,
 is lacking in a number of respects.
 </p>
 </div>
-<div id="outline-container-org71b5241" class="outline-3">
-<h3 id="org71b5241"><span class="section-number-3">14.1.</span> Deficiencies</h3>
+<div id="outline-container-org5ab4b65" class="outline-3">
+<h3 id="org5ab4b65"><span class="section-number-3">14.1.</span> Deficiencies</h3>
 <div class="outline-text-3" id="text-14-1">
 <p>
 The current network monitoring is rudimentary.  It could use some
@@ -9067,16 +9436,16 @@ not available on Front, yet.
 </p>
 </div>
 </div>
-<div id="outline-container-orgbdadef9" class="outline-3">
-<h3 id="orgbdadef9"><span class="section-number-3">14.2.</span> More Tests</h3>
+<div id="outline-container-org6ca4987" class="outline-3">
+<h3 id="org6ca4987"><span class="section-number-3">14.2.</span> More Tests</h3>
 <div class="outline-text-3" id="text-14-2">
 <p>
 The testing process described in the previous chapter is far from
 complete.  Additional tests are needed.
 </p>
 </div>
-<div id="outline-container-org7f1b9ff" class="outline-4">
-<h4 id="org7f1b9ff"><span class="section-number-4">14.2.1.</span> Backup</h4>
+<div id="outline-container-orgb2b163c" class="outline-4">
+<h4 id="orgb2b163c"><span class="section-number-4">14.2.1.</span> Backup</h4>
 <div class="outline-text-4" id="text-14-2-1">
 <p>
 The <code>backup</code> command has not been tested.  It needs an encrypted
@@ -9085,8 +9454,8 @@ partition with which to sync?  And then some way to compare that to
 </p>
 </div>
 </div>
-<div id="outline-container-orgc776285" class="outline-4">
-<h4 id="orgc776285"><span class="section-number-4">14.2.2.</span> Restore</h4>
+<div id="outline-container-org7415334" class="outline-4">
+<h4 id="org7415334"><span class="section-number-4">14.2.2.</span> Restore</h4>
 <div class="outline-text-4" id="text-14-2-2">
 <p>
 The restore process has not been tested.  It might just copy <a href="Backup/"><q>Backup/</q></a>
@@ -9096,8 +9465,8 @@ perhaps permissions too.  It could also use an example
 </p>
 </div>
 </div>
-<div id="outline-container-org1f149e9" class="outline-4">
-<h4 id="org1f149e9"><span class="section-number-4">14.2.3.</span> Campus Disconnect</h4>
+<div id="outline-container-orgc833906" class="outline-4">
+<h4 id="orgc833906"><span class="section-number-4">14.2.3.</span> Campus Disconnect</h4>
 <div class="outline-text-4" id="text-14-2-3">
 <p>
 Email access (IMAPS) on <code>front</code> is&hellip; difficult to test unless
@@ -9112,7 +9481,7 @@ seen.
 <li>Find it in <q>/home/dick/Maildir/new/</q>.</li>
 <li>Re-configure Evolution on <code>dick</code>.  Edit the <code>dick@small.example.org</code>
 mail account (or create a new one?) so that the Receiving Email
-Server name is <code>192.168.15.5</code>, not <code>mail.small.private</code>.  The
+Server name is <code>192.168.15.4</code>, not <code>mail.small.private</code>.  The
 latter domain name will not work while the campus is disappeared.
 In actual use (with Front, not <code>front</code>), the institute domain name
 could be used.</li>
@@ -9121,8 +9490,8 @@ could be used.</li>
 </div>
 </div>
 </div>
-<div id="outline-container-org1b47a6b" class="outline-2">
-<h2 id="org1b47a6b"><span class="section-number-2">15.</span> Appendix: The Bootstrap</h2>
+<div id="outline-container-org813639f" class="outline-2">
+<h2 id="org813639f"><span class="section-number-2">15.</span> Appendix: The Bootstrap</h2>
 <div class="outline-text-2" id="text-15">
 <p>
 Creating the private network from whole cloth (machines with recent
@@ -9142,11 +9511,11 @@ etc.: quite a bit of temporary, manual localnet configuration just to
 get to the additional packages.
 </p>
 </div>
-<div id="outline-container-org2325810" class="outline-3">
-<h3 id="org2325810"><span class="section-number-3">15.1.</span> The Current Strategy</h3>
+<div id="outline-container-orgbd598bf" class="outline-3">
+<h3 id="orgbd598bf"><span class="section-number-3">15.1.</span> The Current Strategy</h3>
 <div class="outline-text-3" id="text-15-1">
 <p>
-The strategy pursued in <a href="#org6a2440c">The Hardware</a> is two phase: prepare the servers
+The strategy pursued in <a href="#orgef4d876">The Hardware</a> is two phase: prepare the servers
 on the Internet where additional packages are accessible, then connect
 them to the campus facilities (the private Ethernet switch, Wi-Fi AP,
 ISP), manually configure IP addresses (while the DHCP client silently
@@ -9154,8 +9523,8 @@ fails), and avoid names until BIND9 is configured.
 </p>
 </div>
 </div>
-<div id="outline-container-org235117e" class="outline-3">
-<h3 id="org235117e"><span class="section-number-3">15.2.</span> Starting With Gate</h3>
+<div id="outline-container-orgccb27a3" class="outline-3">
+<h3 id="orgccb27a3"><span class="section-number-3">15.2.</span> Starting With Gate</h3>
 <div class="outline-text-3" id="text-15-2">
 <p>
 The strategy of Starting With Gate concentrates on configuring Gate's
@@ -9199,8 +9568,8 @@ ansible-playbook -l core site.yml
 </ul>
 </div>
 </div>
-<div id="outline-container-org894c60e" class="outline-3">
-<h3 id="org894c60e"><span class="section-number-3">15.3.</span> Pre-provision With Ansible</h3>
+<div id="outline-container-org3ef3fe5" class="outline-3">
+<h3 id="org3ef3fe5"><span class="section-number-3">15.3.</span> Pre-provision With Ansible</h3>
 <div class="outline-text-3" id="text-15-3">
 <p>
 A refinement of the current strategy might avoid the need to maintain
@@ -9253,7 +9622,7 @@ routes on Front and Gate, making the simulation less&hellip; similar.
 </div></div>
 <div id="postamble" class="status">
 <p class="author">Author: Matt Birkholz</p>
-<p class="date">Created: 2025-09-18 Thu 17:59</p>
+<p class="date">Created: 2025-11-23 Sun 15:25</p>
 <p class="validation"><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
 </div>
 </body>