1 KitsNet PMG Junk-to-Bayes Training Design, Build, Deployment, and Operations Guide[edit | edit source]
Purpose: Document the final production design, build, deployment, security model, operation, and reporting for the KitsNet reviewed Bayesian spam-learning subsystem.
Production status: Deployed and operational as of 2026-08-31.
Applies to: KitsNet production mail environment; Docker Swarm mail service mail_mail-core; manager mgr1; workers wrk1 and wrk2; Proxmox Mail Gateway host dusse; Dovecot Maildir storage on shared nas.dkr storage.
Source repository: /srv/git/KNdkr/src/junk-bayes-review/
Production commits:
1ea9e144befa3ac774752ed0a8a7d5a615f608fe—mail: add reviewed Bayesian spam learning pipelinebe3fc3c46c97a900340414d07a966f9368544350—mail: add Bayesian learning activity reports
Important: This MediaWiki build/operations guide is maintained in MediaWiki, not in the Git repository. Git contains the operational source, deployment metadata, public keys, and systemd unit files.
1.1 Production workflow[edit | edit source]
The production workflow deliberately separates an end user's Junk decision from authorization to alter the system-wide PMG Bayesian classifier.
User moves unwanted mail to Junk
|
v
user .Junk/cur or .Junk/new
|
| KNBayesReview collection
| 30-day age gate
| 120-second settle period
v
reroot@kitsnet.us/Bayesian
|
| administrator reviews
|
+---- delete ----------> not trained
|
| move approved spam
v
reroot@kitsnet.us/Bayesian/Process
|
| KNBayesReview approval processing
| restricted SSH
v
dusse
|
| KNBayesSSHCommand
| KNBayesLearnSpam
v
sa-learn as pmg-smtp-filter
|
v
PMG Bayes database
|
| success recorded in SQLite
v
review copy removed from Bayesian/Process
The authorization rule is:
User Junk action = nomination for administrator review
Administrator move to Bayesian/Process = authorization for global spam training
There is no Completed folder and no Rejected folder.
1.2 Final production architecture[edit | edit source]
1.2.1 mgr1[edit | edit source]
mgr1 is the orchestration host.
Production components:
/usr/local/sbin/KNBayesReview— main Python collector, approval processor, state recorder, and report-data generator./usr/local/sbin/KNBayesLocateMailCore— locates the worker currently runningmail_mail-core./usr/local/sbin/KNBayesDailyReport— generates and sends the daily HTML activity report./var/lib/knbayes/KNBayesReview.sqlite3— persistent processing/audit database./var/lib/knbayes/.ssh/id_ed25519_mailcore— private key for restricted worker access./var/lib/knbayes/.ssh/id_ed25519_pmg— private key for restricted PMG access./var/lib/knbayes/.ssh/known_hosts— pinned SSH host keys.knbayes-review.service/knbayes-review.timer— five-minute production processor.knbayes-daily-report.service/knbayes-daily-report.timer— once-daily HTML report.
The main service runs as the dedicated unprivileged account knbayes.
The only root privilege granted on mgr1 is:
knbayes ALL=(root) NOPASSWD: /usr/local/sbin/KNBayesLocateMailCore
1.2.2 wrk1 and wrk2[edit | edit source]
Both workers carry an identical restricted helper:
/usr/local/sbin/KNBayesMailCoreExec
The helper is invoked through a forced SSH command. The worker account cannot use the key to obtain an interactive shell or execute arbitrary commands.
Worker sudo policy:
Defaults:knbayes env_keep += "SSH_ORIGINAL_COMMAND"
knbayes ALL=(root) NOPASSWD: /usr/local/sbin/KNBayesMailCoreExec
Authorized-key policy:
restrict,command="sudo /usr/local/sbin/KNBayesMailCoreExec" ssh-ed25519 ... KNBayes mail-core worker access
The helper supports only the explicitly implemented operations:
scan
stage
review-count
process-scan
process-fetch
process-complete
Any other operation is rejected.
1.2.3 dusse[edit | edit source]
PMG learning is performed locally on dusse.
Production components:
/usr/local/sbin/KNBayesSSHCommand— forced-command SSH dispatcher./usr/local/sbin/KNBayesLearnSpam— validated local SpamAssassin learning helper./var/lib/knbayes/incoming/— transient validated incoming-message area.- Dedicated account
knbayes. - PMG Bayes database owned and used by
pmg-smtp-filter.
Sudo policy:
knbayes ALL=(root) NOPASSWD: /usr/local/sbin/KNBayesLearnSpam
Authorized-key policy:
restrict,command="/usr/local/sbin/KNBayesSSHCommand" ssh-ed25519 ... KNBayes PMG learner access
The PMG forced command accepts only:
status
receive SHA256
learn SHA256
Arbitrary commands are rejected with exit status 64.
1.3 Mailbox layout[edit | edit source]
The host-side production mailbox root used by the worker helper is:
/srv/docker-nfs-mail/mailboxes
The production domain is:
kitsnet.us
Candidate source folders are restricted to:
/srv/docker-nfs-mail/mailboxes/kitsnet.us/<user>/.Junk/cur/
/srv/docker-nfs-mail/mailboxes/kitsnet.us/<user>/.Junk/new/
/srv/docker-nfs-mail/mailboxes/kitsnet.us/<user>/.Junk Email/cur/
/srv/docker-nfs-mail/mailboxes/kitsnet.us/<user>/.Junk Email/new/
The administrator review identity is:
reroot@kitsnet.us
Review folders:
Bayesian
Bayesian/Process
The corresponding host-side review roots include:
/srv/docker-nfs-mail/mailboxes/kitsnet.us/reroot/.Bayesian
/srv/docker-nfs-mail/mailboxes/kitsnet.us/reroot/.Bayesian.Process
1.4 Candidate collection[edit | edit source]
KNBayesReview run first performs candidate collection.
The worker-side scan operation:
- Searches only approved Junk Maildir paths.
- Ignores messages older than 30 days.
- Requires a 120-second settle period.
- Computes SHA-256 over the complete stored RFC822 message.
- Extracts the Message-ID when present.
- Returns candidate metadata to mgr1.
The default production values are:
max_message_age_days=30
settle_seconds=120
For an eligible candidate, mgr1 invokes the worker stage operation with the expected SHA-256 and a base64-encoded source path.
The worker then:
- Revalidates that the path is inside an approved Junk Maildir.
- Rejects symbolic links.
- Verifies that the file still exists.
- Recomputes SHA-256 and requires an exact match.
- Determines the source user and Junk mailbox.
- Requires a Message-ID.
- Locates exactly one corresponding Dovecot message.
- Uses
doveadm saveto place the exact RFC822 source inreroot@kitsnet.us/Bayesian. - Finds and verifies a byte-identical review copy by SHA-256.
- Only after the review copy is verified, expunges the exact source UID from the user's Junk mailbox.
- Confirms the original Message-ID is no longer present in that source Junk mailbox.
- Returns
result=staged_and_source_removed.
Important implementation note: The final deployed implementation therefore differs from the earlier design draft that said the user's Junk copy would remain untouched. In production, successful staging creates and verifies the administrator review copy and then removes the exact source Junk message. The verified review copy becomes the retained copy for the review workflow.
1.5 Administrator review[edit | edit source]
Candidate collection never trains PMG.
The administrator reviews:
reroot@kitsnet.us/Bayesian
Actions:
- Approve spam: move the message to
Bayesian/Process. - Decline training: delete the message from
Bayesian. - Leave undecided: leave it in
Bayesian.
Only presence in Bayesian/Process is treated as training authorization.
1.6 Approved-message processing[edit | edit source]
The processor scans Bayesian/Process through the restricted worker process-scan operation.
For every approved message:
- The worker calculates SHA-256 and returns its path, modification time, and Message-ID.
- mgr1 records the approval in SQLite.
- mgr1 requests the exact message through
process-fetch. - The worker revalidates the path and SHA-256 before returning the bytes.
- mgr1 opens restricted SSH to
knbayes@dusseand executesreceive SHA256. KNBayesSSHCommandwrites the incoming bytes to a private temporary file.- The received SHA-256 must match the requested digest.
- The verified file is atomically placed at
/var/lib/knbayes/incoming/SHA256.eml. - mgr1 invokes the restricted
learn SHA256operation. KNBayesLearnSpamlearns the message as spam under the PMG filtering identity.- On successful learning, the incoming file is removed.
- mgr1 records
learned_atin SQLite. - mgr1 invokes worker
process-complete. - The worker removes the exact approved message from
Bayesian/Process. - mgr1 records
completed_at.
The successful production log sequence is:
APPROVED sha256=<digest>
path=<Bayesian.Process path>
message-id=<message-id>
PMG=LEARNED
RESULT=COMPLETED_AND_REMOVED
1.7 PMG learning behavior[edit | edit source]
The learning operation uses the PMG SpamAssassin Bayes database associated with pmg-smtp-filter.
The production Bayes status can be queried through the restricted status operation or locally on dusse.
At final end-to-end validation on 2026-08-31:
nspam = 16
nham = 407
The successful production test increased nspam from 15 to 16.
The Bayes database remains PMG-owned; mgr1 and the workers never receive direct filesystem access to it.
1.8 SQLite state and audit history[edit | edit source]
The database is:
/var/lib/knbayes/KNBayesReview.sqlite3
The production schema contains two principal tables.
1.8.1 candidates[edit | edit source]
CREATE TABLE candidates (
sha256 TEXT PRIMARY KEY,
source_user TEXT NOT NULL,
source_mailbox TEXT NOT NULL,
source_path TEXT NOT NULL,
first_seen INTEGER NOT NULL,
staged_at INTEGER,
review_path TEXT,
source_removed_at INTEGER,
last_error TEXT
);
This records candidate discovery, successful staging, the verified review path, source removal, and errors.
1.8.2 approvals[edit | edit source]
CREATE TABLE approvals (
sha256 TEXT PRIMARY KEY,
process_path TEXT NOT NULL,
message_id TEXT,
first_seen INTEGER NOT NULL,
received_at INTEGER,
learned_at INTEGER,
completed_at INTEGER,
last_error TEXT
);
This records administrator-approved messages, receipt by PMG, successful Bayes learning, completion, and errors.
No message bodies are stored in SQLite.
1.9 Production scheduling[edit | edit source]
1.9.1 Five-minute processor[edit | edit source]
Service:
/etc/systemd/system/knbayes-review.service
Source-controlled copy:
src/junk-bayes-review/deploy/mgr1/knbayes-review.service
Unit:
[Unit]
Description=KitsNet Bayesian spam review processor
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=knbayes
Group=knbayes
ExecStart=/usr/local/sbin/KNBayesReview run
Timer:
/etc/systemd/system/knbayes-review.timer
Source-controlled copy:
src/junk-bayes-review/deploy/mgr1/knbayes-review.timer
Production timer:
[Unit]
Description=Run KitsNet Bayesian spam review processor
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
AccuracySec=30s
Persistent=true
[Install]
WantedBy=timers.target
This timer performs collection and approved-message processing. It does not send the daily report.
1.9.2 Daily report[edit | edit source]
The daily report is a separate systemd job.
Service:
/etc/systemd/system/knbayes-daily-report.service
Timer:
/etc/systemd/system/knbayes-daily-report.timer
The timer runs once per day at:
23:55 local time
The production recipient is:
postmaster@lan.kitsnet.us
Using the lan.kitsnet.us address intentionally routes the report directly through the internal mail system rather than sending it toward the public MX path.
1.10 Daily HTML reporting[edit | edit source]
KNBayesDailyReport generates a multipart HTML/text email using the persistent SQLite history and live subsystem state.
The report includes:
- today's Junk messages collected;
- today's successfully learned spam;
- today's completed/removed approvals;
- currently recorded errors;
- last 7 calendar days totals;
- last 30 calendar days totals;
- number awaiting administrator review;
- number awaiting processing;
- outstanding recorded errors;
- current PMG
nspam; - current PMG
nham; - recent successful learning events; and
- overall status.
A representative report after initial production validation showed:
TODAY
Junk messages collected: 3
Successfully learned as spam: 1
Completed and removed: 1
Errors currently recorded: 0
LAST 7 CALENDAR DAYS
Collected: 3
Learned: 1
Completed: 1
Errors currently recorded: 0
LAST 30 CALENDAR DAYS
Collected: 3
Learned: 1
Completed: 1
Errors currently recorded: 0
CURRENT STATE
Awaiting administrator review: 2
Awaiting processing: 0
Outstanding recorded errors: 0
PMG BAYES DATABASE
Spam learned total: 16
Ham learned total: 407
STATUS: OK
The HTML presentation was validated in the production mail client before the reporting code was committed.
1.11 Internal report delivery[edit | edit source]
The initial test to postmaster@kitsnet.us followed public DNS/MX routing and was deferred. Production reporting therefore uses:
postmaster@lan.kitsnet.us
Verified delivery path:
mgr1 Postfix
|
v
mailroom.lan.kitsnet.us [192.168.14.51]:25
|
v
postmaster@lan.kitsnet.us
A production test returned:
dsn=2.0.0
status=sent
The obsolete public-address test message was removed from the mgr1 queue after the internal route was validated.
1.12 Source control[edit | edit source]
All operational source is protected in the KitsNet Docker Git repository.
Production tree:
src/junk-bayes-review/
.gitignore
KNBayesDailyReport
KNBayesLearnSpam
KNBayesLocateMailCore
KNBayesMailCoreExec
KNBayesReview.py
KNBayesSSHCommand
deploy/
dusse/
authorized_keys.entry
id_ed25519_pmg.pub
knbayes-learn-spam
mgr1/
knbayes-locate-mailcore
knbayes-review.service
knbayes-review.timer
knbayes-daily-report.service
knbayes-daily-report.timer
worker/
authorized_keys.entry
id_ed25519_mailcore.pub
knbayes-mailcore
Private keys are deliberately not stored in Git.
The source tree contains the two public keys and the exact forced-command authorization entries required to reconstruct the restricted trust relationships.
The final reporting commit is:
be3fc3c46c97a900340414d07a966f9368544350
mail: add Bayesian learning activity reports
After the final push:
HEAD = be3fc3c46c97a900340414d07a966f9368544350
origin/master = be3fc3c46c97a900340414d07a966f9368544350
ahead/behind = 0 / 0
The unrelated local modification to stacks/maildb-support/stack.yml was intentionally excluded from both Bayesian commits.
1.13 Security model[edit | edit source]
1.13.1 Separate keys for separate trust paths[edit | edit source]
There are two dedicated Ed25519 key pairs:
- mgr1 → workers:
id_ed25519_mailcore - mgr1 → dusse:
id_ed25519_pmg
They are not interchangeable.
The private keys remain only in protected runtime storage beneath /var/lib/knbayes/.ssh/ on mgr1.
Only the public keys are stored in Git.
1.13.2 Forced commands[edit | edit source]
Worker access is restricted to:
sudo /usr/local/sbin/KNBayesMailCoreExec
PMG access is restricted to:
/usr/local/sbin/KNBayesSSHCommand
OpenSSH restrict prevents normal interactive/forwarding use of these authorized keys.
1.13.3 Command validation[edit | edit source]
The worker helper accepts only a tiny command protocol and validates:
- operation name;
- argument count;
- SHA-256 format;
- base64-decoded paths;
- permitted Maildir roots;
- regular-file status;
- symbolic-link rejection;
- source content hash;
- Dovecot search result count.
The PMG helper validates:
- operation name;
- argument count;
- SHA-256 format;
- incoming content SHA-256;
- duplicate incoming filenames;
- exact incoming path.
This provides defense in depth even if one orchestration component is compromised.
1.13.4 No general root SSH[edit | edit source]
The subsystem does not require root SSH between mgr1, the workers, and dusse.
The knbayes account receives only narrowly scoped sudo permissions for the exact helper required on each host.
1.14 Failure and retry behavior[edit | edit source]
1.14.1 Candidate collection failure[edit | edit source]
If scanning or staging fails:
- the run returns nonzero;
- the error is recorded where possible;
- no PMG learning occurs.
1.14.2 PMG receive failure[edit | edit source]
If message transfer or SHA-256 verification fails:
- the message remains in
Bayesian/Process; learned_atis not recorded;- a later five-minute run can retry.
1.14.3 sa-learn failure[edit | edit source]
If KNBayesLearnSpam or sa-learn fails:
- the PMG incoming file is retained when appropriate for safe diagnosis/retry;
- mgr1 does not record successful learning;
- the approved review copy is not completed/removed.
1.14.4 Completion failure[edit | edit source]
If PMG learning succeeds but worker completion fails, SQLite's learned_at prevents unnecessary relearning on the next attempt. The processor can retry the completion step.
1.14.5 No-op run[edit | edit source]
A successful run with no candidates and no approvals exits successfully. Systemd records a normal successful oneshot execution.
1.15 Operational commands[edit | edit source]
1.15.1 Check both timers[edit | edit source]
Run on mgr1:
for i in {1..10}; do echo; done
echo '===== TIMER ENABLEMENT ====='
systemctl is-enabled knbayes-review.timer
systemctl is-enabled knbayes-daily-report.timer
echo
echo '===== TIMER STATE ====='
systemctl is-active knbayes-review.timer
systemctl is-active knbayes-daily-report.timer
echo
echo '===== NEXT RUNS ====='
systemctl list-timers \
knbayes-review.timer \
knbayes-daily-report.timer \
--no-pager
1.15.2 View recent processor activity[edit | edit source]
for i in {1..10}; do echo; done
journalctl \
-u knbayes-review.service \
--since '24 hours ago' \
--no-pager
1.15.3 View recent daily-report activity[edit | edit source]
for i in {1..10}; do echo; done
journalctl \
-u knbayes-daily-report.service \
--since '7 days ago' \
--no-pager
1.15.4 Generate a report without sending email[edit | edit source]
for i in {1..10}; do echo; done
sudo -u knbayes \
/usr/local/sbin/KNBayesReview report
1.15.5 Send a report manually[edit | edit source]
for i in {1..10}; do echo; done
sudo -u knbayes \
/usr/local/sbin/KNBayesDailyReport
This sends a real report to postmaster@lan.kitsnet.us.
1.15.6 View current PMG Bayes counts from mgr1[edit | edit source]
for i in {1..10}; do echo; done
sudo -u knbayes \
ssh \
-i /var/lib/knbayes/.ssh/id_ed25519_pmg \
-o BatchMode=yes \
-o StrictHostKeyChecking=yes \
-o UserKnownHostsFile=/var/lib/knbayes/.ssh/known_hosts \
knbayes@dusse \
status
Useful values include nspam, nham, ntokens, and Bayes timestamps.
1.15.7 View current review queue count[edit | edit source]
for i in {1..10}; do echo; done
NODE="$(
sudo -u knbayes \
sudo /usr/local/sbin/KNBayesLocateMailCore
)"
echo "mail_core_node=$NODE"
sudo -u knbayes \
ssh \
-i /var/lib/knbayes/.ssh/id_ed25519_mailcore \
-o BatchMode=yes \
-o StrictHostKeyChecking=yes \
-o UserKnownHostsFile=/var/lib/knbayes/.ssh/known_hosts \
"knbayes@$NODE" \
review-count
1.15.8 Inspect processing history[edit | edit source]
for i in {1..10}; do echo; done
sqlite3 -header -column \
/var/lib/knbayes/KNBayesReview.sqlite3 \
"SELECT
datetime(learned_at,'unixepoch','localtime') AS learned,
message_id,
substr(sha256,1,16) AS sha256
FROM approvals
WHERE learned_at IS NOT NULL
ORDER BY learned_at DESC
LIMIT 25;"
1.15.9 Inspect outstanding errors[edit | edit source]
for i in {1..10}; do echo; done
sqlite3 -header -column \
/var/lib/knbayes/KNBayesReview.sqlite3 \
"SELECT 'candidate' AS type,
substr(sha256,1,16) AS sha256,
last_error
FROM candidates
WHERE last_error IS NOT NULL
UNION ALL
SELECT 'approval',
substr(sha256,1,16),
last_error
FROM approvals
WHERE last_error IS NOT NULL;"
1.16 Deployment and rebuild procedure[edit | edit source]
1.16.1 Repository source[edit | edit source]
Start on mgr1:
for i in {1..10}; do echo; done
cd /srv/git/KNdkr
git status --short
git log -2 --oneline
Expected production commits include:
be3fc3c mail: add Bayesian learning activity reports
1ea9e14 mail: add reviewed Bayesian spam learning pipeline
1.16.2 Install mgr1 programs[edit | edit source]
for i in {1..10}; do echo; done
cd /srv/git/KNdkr
sudo install -o root -g root -m 0755 \
src/junk-bayes-review/KNBayesReview.py \
/usr/local/sbin/KNBayesReview
sudo install -o root -g root -m 0755 \
src/junk-bayes-review/KNBayesLocateMailCore \
/usr/local/sbin/KNBayesLocateMailCore
sudo install -o root -g root -m 0755 \
src/junk-bayes-review/KNBayesDailyReport \
/usr/local/sbin/KNBayesDailyReport
1.16.3 Install worker helper[edit | edit source]
Install the same source-controlled helper on both wrk1 and wrk2:
for i in {1..10}; do echo; done
cd /srv/git/KNdkr
scp src/junk-bayes-review/KNBayesMailCoreExec \
wrk1:/tmp/KNBayesMailCoreExec
scp src/junk-bayes-review/KNBayesMailCoreExec \
wrk2:/tmp/KNBayesMailCoreExec
ssh wrk1 \
'sudo install -o root -g root -m 0755 /tmp/KNBayesMailCoreExec /usr/local/sbin/KNBayesMailCoreExec'
ssh wrk2 \
'sudo install -o root -g root -m 0755 /tmp/KNBayesMailCoreExec /usr/local/sbin/KNBayesMailCoreExec'
After installation, verify the source and both installed copies have identical SHA-256 checksums.
1.16.4 Install dusse programs[edit | edit source]
The source-controlled PMG programs are:
src/junk-bayes-review/KNBayesLearnSpam
src/junk-bayes-review/KNBayesSSHCommand
Install them on dusse as root-owned mode 0755 files in /usr/local/sbin/ and verify checksums against Git.
1.16.5 Install systemd units[edit | edit source]
On mgr1:
for i in {1..10}; do echo; done
cd /srv/git/KNdkr
sudo install -o root -g root -m 0644 \
src/junk-bayes-review/deploy/mgr1/knbayes-review.service \
/etc/systemd/system/knbayes-review.service
sudo install -o root -g root -m 0644 \
src/junk-bayes-review/deploy/mgr1/knbayes-review.timer \
/etc/systemd/system/knbayes-review.timer
sudo install -o root -g root -m 0644 \
src/junk-bayes-review/deploy/mgr1/knbayes-daily-report.service \
/etc/systemd/system/knbayes-daily-report.service
sudo install -o root -g root -m 0644 \
src/junk-bayes-review/deploy/mgr1/knbayes-daily-report.timer \
/etc/systemd/system/knbayes-daily-report.timer
sudo systemctl daemon-reload
1.16.6 Validate before enabling[edit | edit source]
for i in {1..10}; do echo; done
echo '===== SOURCE SYNTAX ====='
python3 -m py_compile \
/usr/local/sbin/KNBayesReview \
/usr/local/sbin/KNBayesDailyReport
echo
echo '===== PIPELINE DRY RUN ====='
sudo -u knbayes \
/usr/local/sbin/KNBayesReview run --dry-run
echo
echo '===== LIVE REPORT ====='
sudo -u knbayes \
/usr/local/sbin/KNBayesReview report
1.16.7 Enable timers[edit | edit source]
for i in {1..10}; do echo; done
sudo systemctl enable --now knbayes-review.timer
sudo systemctl enable --now knbayes-daily-report.timer
echo
systemctl list-timers \
knbayes-review.timer \
knbayes-daily-report.timer \
--no-pager
1.17 Acceptance tests completed[edit | edit source]
The production deployment was validated with the following tests:
| Test | Result |
|---|---|
| Worker forced-command status/access | Passed |
| Arbitrary worker command rejection | Passed |
| PMG forced-command status | Passed |
| Arbitrary PMG command rejection | Passed with exit status 64 |
| Candidate collection from real Junk folder | Passed |
Verified staging into Bayesian
|
Passed |
Administrator move into Bayesian/Process
|
Passed |
Restricted process-scan
|
Passed |
| Exact message fetch with SHA-256 verification | Passed |
| PMG receive with SHA-256 verification | Passed |
sa-learn under PMG identity
|
Passed |
PMG nspam increment
|
Passed, 15 → 16 |
| Automatic completion/removal from Process | Passed |
| Five-minute unattended timer processing | Passed |
| Live report generation | Passed |
| HTML report rendering | Passed |
| Internal email delivery | Passed |
| Daily report timer | Enabled and active |
| Git private-key scan | No private-key material committed |
| Git local/remote synchronization | Passed, ahead/behind 0/0 |
1.18 Production verification example[edit | edit source]
The first fully unattended administrator-approved production message completed as follows:
APPROVED sha256=8a68f0a10dcce2c69c46fbed3419acc4ae27c0af51c71a38a31dc6e1988e4a3b
message-id=<074446_250524065538ziwkcqtyezmnamapykzm@wwwcologuard.com>
PMG=LEARNED
RESULT=COMPLETED_AND_REMOVED
approved_messages=1
learned=1
completed=1
errors=0
Afterward:
Bayesian/Process messages=0
PMG nspam=16
PMG nham=407
This demonstrated the complete production path without manual execution of the learning operation.
1.19 Normal administrator procedure[edit | edit source]
Day-to-day use is intentionally minimal:
- Users move unwanted messages to their normal Junk folder.
- Within approximately five minutes, eligible candidates are moved into
reroot@kitsnet.us/Bayesian. - Review that mailbox normally.
- Delete anything that should not train the global classifier.
- Move confirmed spam into
Bayesian/Process. - Within approximately five minutes, approved messages are learned on PMG and removed from Process.
- Review the daily HTML report delivered to
postmaster@lan.kitsnet.usat 23:55.
No SSH session or manual sa-learn invocation is required for normal operation.
1.20 Rollback and emergency stop[edit | edit source]
To stop automated collection and processing immediately without altering PMG's existing Bayes database:
for i in {1..10}; do echo; done
sudo systemctl disable --now knbayes-review.timer
To stop only daily email reporting:
for i in {1..10}; do echo; done
sudo systemctl disable --now knbayes-daily-report.timer
Disabling the processor does not delete:
- SQLite history;
- review mailbox contents;
- PMG Bayes data; or
- Git source.
To revoke mgr1's PMG learning authority, remove the corresponding forced-command public-key entry from /var/lib/knbayes/.ssh/authorized_keys on dusse.
To revoke mgr1's worker authority, remove the worker forced-command public-key entry on both wrk1 and wrk2.
Do not delete the PMG Bayes database merely to disable this workflow.
1.21 Monitoring and future enhancements[edit | edit source]
The daily report now provides the primary human-readable trend view. SQLite retains the detailed event history from which daily, 7-day, and 30-day totals are calculated.
Potential future Zabbix integration can expose:
- processor timer/service health;
- last successful processor run;
- review queue count;
- Process queue count;
- outstanding SQLite errors;
- daily learned count;
- PMG
nspam; - PMG
nham; - unexpected decreases in Bayes totals.
A quiet day with zero learned spam is not itself an error.
A future ham-correction workflow should use an explicit administrator action or dedicated folder. Merely moving a message out of Junk is too ambiguous to be interpreted automatically as ham.
1.22 Design decisions[edit | edit source]
| Decision | Production selection | Reason |
|---|---|---|
| User spam action | Move to normal Junk folder | Native client behavior; no forwarding workflow |
| Meaning of Junk action | Nomination for review | Prevents a user action from directly altering global Bayes |
| Review queue | reroot@kitsnet.us/Bayesian
|
Normal IMAP administrator review |
| Approval action | Move to Bayesian/Process
|
Explicit administrator authorization |
| Decline action | Delete from Bayesian
|
No Rejected-folder maintenance |
| Completion | SQLite state plus removal from Process | No Completed-folder maintenance |
| Candidate maximum age | 30 days | Avoids bulk ingestion of uncertain historical Junk |
| Settle period | 120 seconds | Avoids racing recent mailbox changes |
| Content identity | SHA-256 | Exact end-to-end verification and deduplication |
| State store | SQLite | Durable, inspectable event history |
| Worker access | Restricted SSH forced command | No general remote shell/root trust |
| PMG access | Separate restricted SSH forced command | Separates mailbox and learner privileges |
| PMG learner identity | pmg-smtp-filter
|
Uses PMG's live Bayes ownership/context |
| Processor schedule | systemd timer every 5 minutes | Responsive unattended processing |
| Reporting schedule | Daily at 23:55 | One concise daily operational summary |
| Report destination | postmaster@lan.kitsnet.us
|
Forces internal mail routing |
| Source protection | Git under src/junk-bayes-review
|
Reproducible and auditable deployment |
| Private keys in Git | Never | Secrets remain runtime-only |
1.23 Authoritative implementation notes[edit | edit source]
The following points supersede earlier drafts of this guide:
- The subsystem is implemented on
mgr1with restricted remote helpers on the active mail-core worker anddusse; it is not a separate Swarm harvester container. - Scheduling is performed by
knbayes-review.timeron mgr1 every five minutes. - Candidate collection stages a verified copy into
Bayesianand then removes the exact source Junk message. - Only an administrator move into
Bayesian/Processauthorizes PMG learning. - PMG transport is a verified message transfer using
receive SHA256, followed by a separatelearn SHA256operation. - PMG learning is performed by
KNBayesLearnSpamunder the PMG filtering identity. - SQLite tables
candidatesandapprovalsare the authoritative processing history. - Reporting is built into the production subsystem.
- A separate daily systemd timer sends an HTML report once per day at 23:55 to
postmaster@lan.kitsnet.us. - Operational source and deployment metadata are Git-protected; private keys are not.
- There are no Completed or Rejected folders.
Any older documentation describing a direct Junk-to-PMG learner, a Swarm mail_junk-bayes-review service, a 15-minute schedule, KNBayesReceive, tar-stream submission, or preservation of the original Junk copy after successful staging is superseded by this production guide.