<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>MrBlog....</title>
    <link>https://qua.name/mrb/</link>
    <description>Dabbling in free federated writing</description>
    <pubDate>Fri, 17 Apr 2026 13:10:22 +0200</pubDate>
    <item>
      <title>Process DMARC reports with sieve</title>
      <link>https://qua.name/mrb/process-dmarc-reports-with-sieve</link>
      <description>&lt;![CDATA[I get a lot of DMARC reports because I host mail for a couple of domains. Most of these mails require no attention as they are just notifications that others use one of our domains. I want to separate these mails from my normal mail workflow and auto archive them if I haven&#39;t looked at them within, say, 2 weeks.&#xA;&#xA;Doing this with sieve server-side has my preference, but apparently it&#39;s not trivial to determine the age of a message, which is the core logic needed here. Also, the processing of sieve rules is normally only during reception of messages, not ad-hoc or on some other event, although dovecot and pigeonhole have some options for this, among others the sieve-filter tool.&#xA;&#xA;I really only found one implemenation online which roughly solves the same problem I was having, but this involved more than needed I think.&#xA;&#xA;My solution consists of 3 parts:&#xA;&#xA;the sieve script that handles DMARC reports on reception and age-ing;&#xA;use of an extension that calls an external program to evaluate expressions to determine age;&#xA;a daily job that runs the sieve script in the scope of the designated folder.&#xA;&#xA;Here&#39;s the sieve script which deals with DMARC reports both in the normal INBOX flow and a special treatment after 14 days. The latter part is not automatic by dovecot on reception of emails, but triggered by a run of the sieve-filter program.&#xA;&#xA;require [&#34;date&#34;,&#34;fileinto&#34;,&#34;relational&#34;,&#34;variables&#34;,&#34;environment&#34;,&#34;imap4flags&#34;,&#xA;         &#34;vnd.dovecot.execute&#34;, &#34;vnd.dovecot.environment&#34;];&#xA;&#xA;Parameters&#xA;set &#34;dmarcfolder&#34; &#34;Folder.for.dmarc-reports&#34;;&#xA;set &#34;purgedays&#34; &#34;14&#34;;&#xA;&#xA;Move DMARC notifications when received&#xA;if environment :is &#34;vnd.dovecot.default-mailbox&#34; &#34;INBOX&#34; {&#xA;  if anyof (&#xA;    header :contains &#34;From&#34; &#34;dmarcreport@microsoft.com&#34;,&#xA;    header :contains &#34;From&#34; &#34;noreply-dmarc-support@google.com&#34;,&#xA;    header :contains &#34;From&#34; &#34;opendmarc@mail.arctype.co&#34;,&#xA;    header :contains &#34;From&#34; &#34;opendmarc@box.euandre.org&#34;  )&#xA;  {&#xA;    addflag &#34;\\Seen&#34;;&#xA;    fileinto &#34;${dmarcfolder}&#34;;&#xA;    stop;&#xA;  }&#xA;}&#xA;&#xA;When running in the dmarcfolder, archive when age is purgedays&#xA;if environment :is &#34;vnd.dovecot.default-mailbox&#34; &#34;${dmarcfolder}&#34;&#xA;{&#xA;  if currentdate :matches &#34;julian&#34; &#34;*&#34;&#xA;  {&#xA;    # Run a simple bc expresssion to get purgedays ago from todays julian day&#xA;    execute :output &#34;fourteendaysago&#34; &#34;bc&#34; &#34;${1} - ${purgedays}&#34;;&#xA;&#xA;    # Compare this with Date header and archive when age reached&#xA;    if date :value &#34;le&#34; &#34;Date&#34; &#34;julian&#34; &#34;${fourteendaysago}&#34;&#xA;    {&#xA;      fileinto &#34;Trash&#34;;&#xA;      stop;&#xA;    }&#xA;  }&#xA;}&#xA;&#xA;The first part of the sieve script just moves the mails into the dmarc-reports folder and is a normal sieve processing rule. The second part runs if the default folder is the dmarc-reports folder. If so, it uses the extprogram extension of the sieve interpreter to let the bc program evaluate the expression for the age of the message.&#xA;&#xA;This uses a tiny script in the configured sieve execute bin directory of the extprograms extension&#xA;&#xA;!/bin/sh&#xA;echo ${1} | /usr/bin/bc&#xA;&#xA;which just pipes the input given by the sieve line into the bc program. On returning, stdout is put into the fourteendaysago variable. I&#39;m using execute because I do not need to pipe the whole message into the external program, but specify input specifically.&#xA;&#xA;With the above configuration I can set a cron job in the crontab of the vmail user to run&#xA;&#xA;sieve-filter -We -u mymailaccount \&#xA;             /path/to/vmail/mymailaccount/sieve/dmarc-archiver.sieve \&#xA;             Folder.for.dmarc-reports&#xA;&#xA;which executes the sieve script mentioned above in the IMAP folder dmarc_folder only.&#xA;&#xA;I&#39;m not sure why sieve makes it so difficult to get the age of an email (unless I&#39;m missing something). Protonmail solves this by having a custom extension &#39;vnd.proton.eval&#39; which does something similar like the above, but in the scope of the sieve language itself without having to shell out to an external program explicitly. (I think; I have not seen their implementation)&#xA;&#xA;My approach above obviously has some drawbacks:&#xA;&#xA;the bc external program is called for every mail that matches, fine for 10 or 20 I guess, but rather inefficient if the amount of matched messages is big. For now, not a problem.&#xA;unsure what sort of security consequences this has, the execution scope and environment is very limited, but we&#39;re still giving control to a script calling other programs.]]&gt;</description>
      <content:encoded><![CDATA[<p>I get a lot of DMARC reports because I host mail for a couple of domains. Most of these mails require no attention as they are just notifications that others use one of our domains. I want to separate these mails from my normal mail workflow and auto archive them if I haven&#39;t looked at them within, say, 2 weeks.</p>

<p>Doing this with sieve server-side has my preference, but apparently it&#39;s not trivial to determine the age of a message, which is the core logic needed here. Also, the processing of sieve rules is normally only during reception of messages, not ad-hoc or on some other event, although dovecot and pigeonhole have some options for this, among others the <a href="https://pigeonhole.dovecot.org/doc/man1/sieve-filter.1.html" rel="nofollow">sieve-filter</a> tool.</p>

<p>I really only found <a href="https://serverfault.com/questions/1033068/auto-clean-mail-folder-with-dovecot-sieve" rel="nofollow">one implemenation</a> online which roughly solves the same problem I was having, but this involved more than needed I think.</p>

<p>My solution consists of 3 parts:</p>
<ol><li>the sieve script that handles DMARC reports on reception and age-ing;</li>
<li>use of an extension that calls an external program to evaluate expressions to determine age;</li>
<li>a daily job that runs the sieve script in the scope of the designated folder.</li></ol>

<p>Here&#39;s the sieve script which deals with DMARC reports both in the normal INBOX flow and a special treatment after 14 days. The latter part is not automatic by dovecot on reception of emails, but triggered by a run of the <a href="https://pigeonhole.dovecot.org/doc/man1/sieve-filter.1.html" rel="nofollow">sieve-filter</a> program.</p>

<pre><code class="language-sieve">require [&#34;date&#34;,&#34;fileinto&#34;,&#34;relational&#34;,&#34;variables&#34;,&#34;environment&#34;,&#34;imap4flags&#34;,
         &#34;vnd.dovecot.execute&#34;, &#34;vnd.dovecot.environment&#34;];

# Parameters
set &#34;dmarc_folder&#34; &#34;Folder.for.dmarc-reports&#34;;
set &#34;purge_days&#34; &#34;14&#34;;

# Move DMARC notifications when received
if environment :is &#34;vnd.dovecot.default-mailbox&#34; &#34;INBOX&#34; {
  if anyof (
    header :contains &#34;From&#34; &#34;dmarcreport@microsoft.com&#34;,
    header :contains &#34;From&#34; &#34;noreply-dmarc-support@google.com&#34;,
    header :contains &#34;From&#34; &#34;opendmarc@mail.arctype.co&#34;,
    header :contains &#34;From&#34; &#34;opendmarc@box.euandre.org&#34;  )
  {
    addflag &#34;\\Seen&#34;;
    fileinto &#34;${dmarc_folder}&#34;;
    stop;
  }
}

# When running in the dmarc_folder, archive when age is &lt;purge_days&gt;
if environment :is &#34;vnd.dovecot.default-mailbox&#34; &#34;${dmarc_folder}&#34;
{
  if currentdate :matches &#34;julian&#34; &#34;*&#34;
  {
    # Run a simple bc expresssion to get &lt;purge_days&gt; ago from todays julian day
    execute :output &#34;fourteen_days_ago&#34; &#34;bc&#34; &#34;${1} - ${purge_days}&#34;;

    # Compare this with Date header and archive when age reached
    if date :value &#34;le&#34; &#34;Date&#34; &#34;julian&#34; &#34;${fourteen_days_ago}&#34;
    {
      fileinto &#34;Trash&#34;;
      stop;
    }
  }
}
</code></pre>

<p>The first part of the sieve script just moves the mails into the <code>dmarc-reports</code> folder and is a normal sieve processing rule. The second part runs if the default folder is the <code>dmarc-reports</code> folder. If so, it uses the <code>ext_program</code> extension of the sieve interpreter to let the <code>bc</code> program evaluate the expression for the age of the message.</p>

<p>This uses a tiny script in the configured sieve execute bin directory of the <code>ext_programs</code> extension</p>

<pre><code class="language-sh">#!/bin/sh
echo ${1} | /usr/bin/bc
</code></pre>

<p>which just pipes the input given by the sieve line into the <code>bc</code> program. On returning, stdout is put into the <code>fourteen_days_ago</code> variable. I&#39;m using <code>execute</code> because I do not need to pipe the whole message into the external program, but specify input specifically.</p>

<p>With the above configuration I can set a cron job in the crontab of the <code>vmail</code> user to run</p>

<pre><code class="language-sh">sieve-filter -We -u &lt;mymailaccount&gt; \
             /path/to/vmail/mymailaccount/sieve/dmarc-archiver.sieve \
             Folder.for.dmarc-reports
</code></pre>

<p>which executes the sieve script mentioned above in the IMAP folder <code>&lt;dmarc_folder&gt;</code> only.</p>

<p>I&#39;m not sure why sieve makes it so difficult to get the age of an email (unless I&#39;m missing something). Protonmail solves this by <a href="https://proton.me/support/sieve-advanced-custom-filters#transforming-variables" rel="nofollow">having a custom extension</a> &#39;vnd.proton.eval&#39; which does something similar like the above, but in the scope of the sieve language itself without having to shell out to an external program explicitly. (I think; I have not seen their implementation)</p>

<p>My approach above obviously has some drawbacks:</p>
<ul><li>the <code>bc</code> external program is called for every mail that matches, fine for 10 or 20 I guess, but rather inefficient if the amount of matched messages is big. For now, not a problem.</li>
<li>unsure what sort of security consequences this has, the execution scope and environment is very limited, but we&#39;re still giving control to a script calling other programs.</li></ul>
]]></content:encoded>
      <guid>https://qua.name/mrb/process-dmarc-reports-with-sieve</guid>
      <pubDate>Wed, 10 Aug 2022 14:51:03 +0200</pubDate>
    </item>
    <item>
      <title>Federated writing start</title>
      <link>https://qua.name/mrb/federated-writing-start</link>
      <description>&lt;![CDATA[As soon as I noticed http://write.as was published as open source I&#39;ve started playing with it.&#xA;&#xA;For the longest time I wanted a blog-like system where the comments would go on a #federated social network instead of on the blog itself. Well, it&#39;s finally here!!&#xA;&#xA;I&#39;ve installed #writefreely on the domain https://qua.name and will allow registrations if you want to play along. If things work out, this will be a lasting thing.&#xA;&#xA;For a start follow @mrb@qua.name from your favourite activitypub social network instance. I&#39;ve only tested it with #Mastodon.]]&gt;</description>
      <content:encoded><![CDATA[<p>As soon as I noticed <a href="http://write.as" rel="nofollow">http://write.as</a> was published as open source I&#39;ve started playing with it.</p>

<p>For the longest time I wanted a blog-like system where the comments would go on a <a href="/mrb/tag:federated" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">federated</span></a> social network instead of on the blog itself. Well, it&#39;s finally here!!</p>

<p>I&#39;ve installed <a href="/mrb/tag:writefreely" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">writefreely</span></a> on the domain <a href="https://qua.name" rel="nofollow">https://qua.name</a> and will allow registrations if you want to play along. If things work out, this will be a lasting thing.</p>

<p>For a start follow <a href="https://qua.name/@/mrb@qua.name" class="u-url mention" rel="nofollow">@<span>mrb@qua.name</span></a> from your favourite activitypub social network instance. I&#39;ve only tested it with <a href="/mrb/tag:Mastodon" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">Mastodon</span></a>.</p>
]]></content:encoded>
      <guid>https://qua.name/mrb/federated-writing-start</guid>
      <pubDate>Wed, 14 Nov 2018 12:46:44 +0100</pubDate>
    </item>
  </channel>
</rss>