Compare commits

..
1 Commits
Author SHA1 Message Date
daniel 448058631e Update hermit-v2. 2024-11-05 15:28:59 +01:00
72 changed files with 255 additions and 1577 deletions
-41
View File
@@ -1,41 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
{
"name": "Existing Docker Compose (Extend)",
// Update the 'dockerComposeFile' list if you have more compose files or use different names.
// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
"dockerComposeFile": [
"../docker-compose.yml",
"docker-compose.yml"
],
// The 'service' property is the name of the service for the container that VS Code should
// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
"service": "server",
// The optional 'workspaceFolder' property is the path VS Code should open by default when
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
"workspaceFolder": "/project",
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Uncomment the next line if you want start specific services in your Docker Compose config.
// "runServices": [],
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
// "shutdownAction": "none",
// Uncomment the next line to run commands after the container is created.
// "postCreateCommand": "cat /etc/os-release",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
"remoteUser": "root"
}
-25
View File
@@ -1,25 +0,0 @@
services:
# Update this to the name of the service you want to work with in your docker-compose.yml file
server:
# Uncomment if you want to override the service's Dockerfile to one in the .devcontainer
# folder. Note that the path of the Dockerfile and context is relative to the *primary*
# docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
# array). The sample below assumes your primary file is in the root of your project.
#
# build:
# context: .
# dockerfile: .devcontainer/Dockerfile
volumes:
# Update this to wherever you want VS Code to mount the folder of your project
# - ..:/project:cached
# Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
# cap_add:
# - SYS_PTRACE
# security_opt:
# - seccomp:unconfined
# Overrides default command so things don't shut down after the process ends.
# command: sleep infinity
-56
View File
@@ -1,56 +0,0 @@
name: Build and Deploy
on:
push:
branches:
- main
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: self-hosted
container:
image: ghcr.io/gohugoio/hugo:latest
options: --user root
env:
HUGO_ENVIRONMENT: production
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true # Required if your theme is a Git submodule
fetch-depth: 0 # Hugo uses full history for .GitInfo and lastmod
- name: Install Sass
run: |
npm install sass
- name: Install and update modules
run: hugo mod get -u
- name: Build site
run: hugo --minify
- name: Install rsync and openssh-client
run: |
apk add --no-cache rsync openssh-client
# Hugo's official image is Alpine-based; use apk.
# If a Debian-based Hugo image is used, swap to:
# apt-get update && apt-get install -y rsync openssh-client
- name: Set up SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
echo "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: Deploy via rsync
run: |
rsync -avz --delete \
-e "ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes" \
public/ \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/${{ secrets.DEPLOY_SUBDIR }}
-12
View File
@@ -1,12 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
# https://containers.dev/guide/dependabot
version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
+4
View File
@@ -0,0 +1,4 @@
[submodule "themes/hermit-v2"]
path = themes/hermit-v2
url = https://github.com/1bl4z3r/hermit-V2
branch = main
+1 -1
View File
@@ -25,4 +25,4 @@ docker compose exec server new content/posts/2024-07-31-my-new-blog-post.md
``` ```
[hermit-v2]: https://github.com/1bl4z3r/hermit-V2 [hermit-v2]: https://github.com/1bl4z3r/hermit-V2
[Hugo]: https:/gohugo.io [Hugo]: https:/gohugo.io
@@ -1,3 +1,25 @@
// Colors
$theme: hsl(172, 99%, 26%);
$text: hsl(204, 28%, 93%);
$light-grey: #292e32; // #494f5c;
$dark-grey: #3B3E48;
$highlight-grey: #7d828a;
$typewriter: hsl(172, 100%, 36%);
$codebackground: #1c2023 !default;
$midnightblue: $codebackground;
// Scroll to Top Default colors
$stt-stroke:#CCC;
$stt-circle:#3b3e48;
$stt-arrow:#018574;
kbd {
font-size: 0.9em !important;
color: inherit;
background-color: $midnightblue;
}
// Fonts // Fonts
$fonts: "IBM Plex Sans Light", "Segoe UI", Candara, sans-serif; $fonts: "IBM Plex Sans Light", "Segoe UI", Candara, sans-serif;
$code-fonts: "IBM Plex Mono Light", Consolas, "Andale Mono WT", "Andale Mono", Menlo, Monaco, monospace; $code-fonts: "IBM Plex Mono Light", Consolas, "Andale Mono WT", "Andale Mono", Menlo, Monaco, monospace;
@@ -74,3 +96,46 @@ $code-fonts: "IBM Plex Mono Light", Consolas, "Andale Mono WT", "Andale Mono",
url('/fonts/IBMPlexMono-MediumItalic.woff2') format('woff2'); url('/fonts/IBMPlexMono-MediumItalic.woff2') format('woff2');
} }
//Admonition
$admonition-background: (
'note': rgba(68, 138, 255, 0.1),
'info': rgba(0, 184, 212, 0.1),
'tip': rgba(0, 191, 165, 0.1),
'success': rgba(0, 200, 83, 0.1),
'warning': rgba(255, 145, 0, 0.1),
'failure': rgba(255, 82, 82, 0.1),
'danger': rgba(255, 23, 68, 0.1),
'bug': rgba(245, 0, 87, 0.1),
) !default;
$admonition-color: (
'note': #448aff,
'info': #00b8d4,
'tip': #00bfa5,
'success': #00c853,
'warning': #ff9100,
'failure': #ff5252,
'danger': #ff1744,
'bug': #f50057,
) !default;
// Mixins
@mixin dimmed {
opacity: .6;
}
@mixin aTag {
a {
word-wrap: break-word;
border: none;
box-shadow: inset 0 -4px 0 $theme;
transition-property: box-shadow;
transition-duration: .1s;
&:hover {
box-shadow: inset 0 -1em 0 $theme;
}
}
}
-11
View File
@@ -1,11 +0,0 @@
// Typography configuration for typewriter text display
$font-size-desktop: 1.6rem; // Base font size for desktop viewports
$font-size-mobile: 1.3rem; // Reduced font size for mobile devices
$line-height: 2; // Line height multiplier for optimal readability
// Responsive container width settings across device breakpoints
$width-desktop: 50vw; // Container width for desktop screens (60% viewport width)
$width-tablet: 70vw; // Container width for tablet screens (70% viewport width)
$width-mobile: 80vw; // Container width for mobile screens (80% viewport width)
@import "hugo_cookie_consent";
+141
View File
@@ -0,0 +1,141 @@
baseURL = 'https://www.bovender.de/'
languageCode = 'en-us'
timeZone = 'Europe/Berlin'
defaultContentLanguage = 'en'
title = 'bovender'
theme = "hermit-v2"
enableGitInfo = true
pygmentsCodefences = true
pygmentsUseClasses = true
#hasCJKLanguage = true
rssLimit = 10
copyright = "2024 Daniel Kraus"
enableEmoji = true
[permalinks]
[permalinks.page]
posts = '/posts/:year/:month/:slug/'
#[services]
# [services.disqus]
# shortname = ''
# [services.googleAnalytics]
# id = ''
[frontmatter]
date = ["date", "publishDate", "lastmod"]
lastmod = ["lastmod", ":git", "date", "publishDate"]
publishDate = ["publishDate", "date"]
expiryDate = ["expiryDate"]
[params.author]
name = "Daniel Kraus"
about = "/about"
[blackfriday]
hrefTargetBlank = true
# noreferrerLinks = true
# nofollowLinks = true
[taxonomies]
tag = "tags"
# Categories are disabled by default.
# category = "categories"
[markup.goldmark.renderer]
unsafe = true
# Enable to get proper Mathjax support
#[markup]
# [markup.goldmark]
# [markup.goldmark.extensions]
# [markup.goldmark.extensions.passthrough]
# enable = true
# [markup.goldmark.extensions.passthrough.delimiters]
# block = [['\[', '\]'], ['$$', '$$']]
# inline = [['\(', '\)']]
[params]
dateform = "2 Jan 2006"
dateformShort = "2 Jan"
dateformNum = "2006-01-02"
dateformNumTime = "2006-01-02 15:04 -0700"
description = "Daniel Kraus' personal homepage"
# images = [""]
themeColor = "#292e32"
homeSubtitle = "Daniel Kraus"
footerCopyright = "all rights reserved"
# bgImg = ""
ShowLastmod = true
gitUrl = "https://git.bovender.de/daniel/blog/commit/"
justifyContent = false
relatedPosts = true
code_copy_button = true
homeSubtitlePrinter = true
scrollToTop = true
global_mathjax = false
readTime = false
readTimeSeparator = "~"
# legacyLayout = false
shareSocial = true
# googleTagManager = "GTM-XXXXXXXX"
# tagSymbol = "#"
# categorySymbol = "⊲"
# pinned = "Pinned Posts"
# pinnedSVGname = "pin"
# descriptionInPosts= true
initialPublish = "Initally Posted on: "
human = ["single","posts"]
[[params.socialLinks]]
name = "mastodon"
url = "https://neph.social/@daniel_kraus"
[[params.socialLinks]]
name = "x"
url = "https://twitter.com/bovender_de"
[[params.socialLinks]]
name = "github"
url = "https://github.com/bovender"
[[params.socialLinks]]
name = "stackoverflow"
url = "https://stackoverflow.com/users/270712/bovender"
[[params.socialLinks]]
name = "linkedin"
url = "https://www.linkedin.com/in/bovender"
[menu]
[[menu.main]]
name = "Blog"
url = "posts/"
weight = 10
[[menu.main]]
name = "About bovender"
url = "about/"
weight = 20
[[menu.main]]
name = "Impressum"
url = "impressum/"
weight = 30
[[menu.main]]
name = "Cheat sheet"
url = "cheatsheet/"
weight = 15
-105
View File
@@ -1,105 +0,0 @@
baseURL: 'https://www.bovender.de/'
title: 'bovender'
language:
locale: 'en-us'
timeZone: 'Europe/Berlin'
copyright: "2024-2026 Daniel Kraus"
params:
LongDate: "2 Jan 2006"
ShortDate: "2 Jan"
NumDateShort: "2006-01-02"
NumDateLong: "2006-01-02 15:04 -0700"
description: "Daniel Kraus' personal homepage"
themeColor: "#292e32"
homeSubtitle: "Daniel Kraus' personal homepage"
footerCopyright: "all rights reserved"
ShowLastmod: true
gitUrl: "https://git.bovender.de/daniel/blog/commit/"
justifyContent: false
relatedPosts: true
code_copy_button: true
homeSubtitlePrinter: true
scrollToTop: true
global_mathjax: false
readTime: false
readTimeSeparator: "~"
shareSocial: true
initialPublish: "Initally Posted on: "
human:
- single
- posts
socialLinks:
- name: "bluesky"
url: "https://bsky.app/profile/bovender.bsky.social"
- name: "mastodon"
url: "https://neph.social/@daniel_kraus"
- name: "github"
url: "https://github.com/bovender"
- name: "stackoverflow"
url: "https://stackoverflow.com/users/270712/bovender"
- name: "linkedin"
url: "https://www.linkedin.com/in/bovender"
menu:
main:
- name: "Blog"
url: "posts/"
weight: 10
- name: "About bovender"
url: "about/"
weight: 20
- name: "Impressum"
url: "impressum/"
weight: 30
- name: "Cheat sheet"
url: "cheatsheet/"
weight: 15
enableGitInfo: true
pygmentsCodefences: true
pygmentsUseClasses: true
hasCJKLanguage: false
rssLimit: 10
enableEmoji: true
permalinks:
page:
posts: '/posts/:year/:month/:slug/'
frontmatter:
date:
- date
- publishDate
- lastmod
lastmod:
- lastmod
- ':git'
- date
- publishDate
publishDate:
- publishDate
- date
expiryDate:
- expiryDate
blackfriday:
hrefTargetBlank: true
# noreferrerLinks: true
# nofollowLinks: true
taxonomies:
tag: "tags"
markup:
goldmark:
renderer:
unsafe: true
-3
View File
@@ -1,3 +0,0 @@
imports:
- path: github.com/1bl4z3r/hermit-V2
- path: git.bovender.de/daniel/hugo_cookie_consent
-5
View File
@@ -1,5 +0,0 @@
hugo_cookie_consent:
privacy_policy_url: /impressum
enable_tracking: true
matomo_host: https://matomo.bovender.de
matomo_site_id: 1
-1
View File
@@ -1 +0,0 @@
baseURL: 'http://localhost:1313/'
-2
View File
@@ -1,2 +0,0 @@
workspace: hugo.work
-20
View File
@@ -22,31 +22,18 @@ calculator back in the 1980's.
### [ggap-ev.org](https://ggap-ev.org) ### [ggap-ev.org](https://ggap-ev.org)
GGAP is a registered charity in Germany. This NGO uses donated money to fund the
vocational training of young adults on the Philippines who would otherwise have
to "earn" their living in an unlawful way.
{{< figure src="ggap-ev.png">}} {{< figure src="ggap-ev.png">}}
### [nephrowiki.de](https://nephrowiki.de) ### [nephrowiki.de](https://nephrowiki.de)
NephroWiki is a closed wiki system that has been up and running since 2011.
{{< figure src="nephrowiki.png">}} {{< figure src="nephrowiki.png">}}
## Portfolio -- software ## Portfolio -- software
### [Daniel's XL Toolbox](https://xltoolbox.net) ### [Daniel's XL Toolbox](https://xltoolbox.net)
Daniel's XL Toolbox is an add-in for Excel that assists with data analysis
and visualization.
{{< figure src="xltoolbox.png">}} {{< figure src="xltoolbox.png">}}
Even though I haven't been able to work on this project for quite some time,
I am very happy to see that the latest release has been downloaded more than
100,000 times. :-)
### [LinkTitles](https://github.com/bovender/linktitles) ### [LinkTitles](https://github.com/bovender/linktitles)
LinkTitles is a [MediaWiki](https://mediawiki.org) extension that automatically LinkTitles is a [MediaWiki](https://mediawiki.org) extension that automatically
@@ -57,10 +44,6 @@ links terms to existing pages in a Wiki.
PubmedParser is a [MediaWiki](https://mediawiki.org) extension to include and PubmedParser is a [MediaWiki](https://mediawiki.org) extension to include and
reference articles from [Pubmed](https://pubmed.gov) by PMID. reference articles from [Pubmed](https://pubmed.gov) by PMID.
### Github Star History
[![Star History Chart][sh-img]][sh-link]
## Server administration ## Server administration
I have 10+ years experience with running and administrating my own server. I have 10+ years experience with running and administrating my own server.
@@ -68,6 +51,3 @@ This includes running a mail and an IMAP server, hosting my own
[Nextcloud](https://nextcloud.com) and [Gitea](https://gitea.io) instances, [Nextcloud](https://nextcloud.com) and [Gitea](https://gitea.io) instances,
[Mastodon for Nephrology](https://neph.social) [Mastodon for Nephrology](https://neph.social)
and more. Almost all of my services are dockerized. and more. Almost all of my services are dockerized.
[sh-img]: https://api.star-history.com/svg?repos=bovender/linktitles,bovender/pubmedparser,bovender/imapcli,bovender/xltoolbox&type=Date
[sh-link]: https://www.star-history.com/#bovender/linktitles&bovender/pubmedparser&bovender/imapcli&bovender/xltoolbox&Date
-65
View File
@@ -6,69 +6,12 @@ description: >
and maybe it is helpful for you, too, internet wanderer. and maybe it is helpful for you, too, internet wanderer.
date: 2024-11-05T08:00:00+0100 date: 2024-11-05T08:00:00+0100
draft: false draft: false
toc: true
--- ---
{{< git-info >}} {{< git-info >}}
{{< param "description" >}} {{< param "description" >}}
## R ## R
## ggplot2 elements
I can never remember which part of a [ggplot2][] graph/figure is the "plot"
and which is the "panel", so I made this little figure:
{{< figure src="/img/ggplot2-elements.png" >}}
The figure was generated with this code:
```r
library(tidyverse)
tibble(x = 1:3, y = 1:3) |>
ggplot(aes(x = x, y = y)) +
geom_col(fill = "white") +
geom_text(
label = "The green area is the PANEL BACKGROUND",
x = 0.5,
y = 3.5,
hjust = 0,
color = "dark green",
fontface = "bold"
) +
geom_text(
label = "The blue border at the top and the right is the PLOT BORDER",
x = 0.5,
y = 3.2,
hjust = 0,
color = "navy",
fontface = "bold"
) +
geom_text(
label = "The gray area is the PLOT BACKGROUND",
x = 0.5,
y = -0.5,
hjust = 0,
color = "black",
fontface = "bold"
) +
coord_cartesian(clip = "off") +
scale_x_continuous(expand = expansion(), limits = c(0, 4)) +
scale_y_continuous(expand = expansion(), limits = c(0, 4)) +
theme(
panel.border = element_rect(fill = "light blue", color = "blue"),
panel.background = element_rect(fill = "light green", color = "green"),
plot.background = element_rect(fill = "gray", color = "black"),
plot.margin = unit(rep(0.1, 4), units = "npc")
)
ggsave(
"ggplot2-elements.png",
width = 9,
height = 6,
units = "in",
dpi = 96
)
```
### Print R objects so that they can be re-generated from code ### Print R objects so that they can be re-generated from code
```r ```r
@@ -152,11 +95,3 @@ starwars |>
``` ```
<sup>Created on 2024-11-05 with [reprex v2.1.1](https://reprex.tidyverse.org)</sup> <sup>Created on 2024-11-05 with [reprex v2.1.1](https://reprex.tidyverse.org)</sup>
## Wrangling computers
### Human interaction with IMAP servers
<https://geekmungus.co.uk/?p=4358>
[ggplot2]: https://ggplot2.tidyverse.org
+13 -35
View File
@@ -1,20 +1,17 @@
--- ---
title: "Impressum und Datenschutz" title: "Impressum und Datenschutz"
date: 2025-08-02T07:00:00+0200 date: 2024-07-31T12:42:53Z
draft: false draft: false
--- ---
Dr. Daniel Kraus Dr. Daniel Kraus
H&zwnj;ö&zwnj;hen&zwnj;str&zwnj;aße 1&zwnj;5
65451 K&zwnj;e&zwnj;l&zwnj;s&zwnj;t&zwnj;e&zwnj;r&zwnj;b&zwnj;a&zwnj;c&zwnj;h
Tel. (0 61 07&zwnj;) 7 5&zwnj;6 88 4&zwnj;0 Tel. (0 61 07&zwnj;) 7 5&zwnj;6 88 4&zwnj;0
Mail: <bovender@bovender.de> <bovender@bovender.de>
{{< orcid >}} Ich bitte, von Anrufen abzusehen. Über Mail oder die verlinkten
sozialen Netzwerke (siehe Icons) bin ich i.d.R. kurzfristig
Ladungsfähige Anschrift bei meinem Arbeitgeber: Universitätsklinikum Heidelberg,
Im Neuenheimer Feld 162, 69120 Heidelberg. **Dies ist eine rein private Homepage
und in keiner Weise von meinem Arbeitgeber beworben oder unterstützt.** Meine
Privatanschrift ist zu meinem persönlichen Schutz und dem Schutz meiner Familie
hier nicht veröffentlicht. **Ich bitte, von Anrufen abzusehen.** Über Mail oder
die verlinkten sozialen Netzwerke (siehe Icons) bin ich i.d.R. kurzfristig
erreichbar. erreichbar.
## Mailserver bovender.de ## Mailserver bovender.de
@@ -32,32 +29,13 @@ Der Mailserver bovender.de ist zuständig für die Domains:
- [nierenverein.de](https://nierenverein.de) - [nierenverein.de](https://nierenverein.de)
- [xltoolbox.net](https://xltoolbox.net) - [xltoolbox.net](https://xltoolbox.net)
## Datenverarbeitung aus berechtigtem Interesse (Art. 6 Abs. 1 lit. f DSGVO) {#cookie-consent} ## Datenschutz
Die Website [bovender.de](bovender.de) erhebt anonymisierte Statistiken Diese Website erfasst keine personenbezogenen Daten, und es werden keine
über die Besuche, um die Nachfrage nach dem Web-Angebot erkennen zu können. Dienste von Drittfirmen in Anspruch genommen.
Hierbei wird ein sogenanntes **Cookie** lokal in Ihrem Webbrowser gespeichert.
Bei Seitenaufrufen wird dieses Cookie an den Server übertragen, so dass
festgestellt werden werden kann, ob Sie die Seite schon einmal besucht haben
oder nicht. Es ist uns jedoch nicht möglich, über dieses Cookie Rückschlüsse auf
Ihre Identität zu ziehen. Es findet keine Verknüpfung mit anderen Dienstleistern
statt.
Sie haben die Möglichkeit, der Speicherung eines Cookies zu widersprechen, Es findet eine anonymisierte Erfassung von Seitenbesuchen statt, die keine
indem Sie auf den entsprechenden Button unten auf der Seite klicken. Dass Rückverfolgung zu einzelnen Personen/Geräten ermöglicht.
Sie geklickt haben, wird als sog. "Local Storage" im Browser hinterlegt --
hierbei handelt es sich nicht um persönliche Daten, es wird lediglich das
Wort "accepted" oder "declined" gespeichert.
Bis zum Klicken des Buttons "Akzeptieren" oder wenn Sie der Speicherung
eines Cookies widersprochen haben, erfolgt keine anonymisierte Erfassung
Ihres Besuchs auf der Website.
{{< hugo_cookie_consent/settings >}}
Die Website und die anonymisierte statistische Erfassung
([matomo.bovender.de](https://matomo.bovender.de)) befinden sich auf ein und
demselben Server unter administrativer Kontrolle von Daniel Kraus.
## Dank ## Dank
@@ -65,4 +43,4 @@ Besonderer Dank an [Christian Heilmann][] für das Script [mastodon-share][],
um Links leicht auf Mastodon zu posten. um Links leicht auf Mastodon zu posten.
[Christian Heilmann]: https://christianheilmann.com/2023/08/18/adding-a-share-to-mastodon-link-to-any-web-site-and-here/ [Christian Heilmann]: https://christianheilmann.com/2023/08/18/adding-a-share-to-mastodon-link-to-any-web-site-and-here/
[mastodon-share]: https://github.com/codepo8/mastodon-share [mastodon-share]: https://github.com/codepo8/mastodon-share
@@ -211,37 +211,5 @@ Query TLSA records:
more useful for me than all the websites and blog and forum posts that I had more useful for me than all the websites and blog and forum posts that I had
previously read. previously read.
---
# Addendum 2026-04-22
## How to produce the certificate-associated data for the TLSA record?
The certificate associated data is a SHA-256 or SHA-512 checksum, depending
on the third number.
```plain
2 1 1 cbbc559b44d524d6a132bdac672744da3407f12aae5d5f722c5f6c7913871c75 # E7
# ^^^ this is the certificate-associated data in the TLSA record
```
```bash
openssl x509 -in E7.der -pubkey -noout | openssl ec -pubin -outform der | openssl sha256
```
### Obtain the hash for the certificate itself (DANE-EE)
Given a ECDSA key in `cert.pem`:
```bash
openssl x509 -in cert.pem -pubkey -noout | openssl ec -pubin -outform der | openssl sha256
```
Use the resulting hash in
```plain
3 1 1 HASH
```
[dane]: https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities [dane]: https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities
[isi]: https://dnssec-stats.ant.isi.edu/~viktor/x3hosts.html [isi]: https://dnssec-stats.ant.isi.edu/~viktor/x3hosts.html
-21
View File
@@ -248,27 +248,6 @@ replace `ffmpeg-free` with the non-free `ffmpeg` on F40:
After doing that and restarting Kdenlive, I was able to render my project using After doing that and restarting Kdenlive, I was able to render my project using
the H.264 and H.265 codecs as I used to be able to do with Ubuntu-based systems. the H.264 and H.265 codecs as I used to be able to do with Ubuntu-based systems.
## Update 2024-11-10
In the last couple of months, battery life has improved significantly. As
described above, in summer, battery drained almost completely (from ~90% to
~15%) within 3.5 hours even without heavy workload.
Today I spent about the same amount of time on a train, working constantly on my
laptop (browsing, downloading articles), and the battery level only dropped from
~90% to ~40% within 3 hours. Some system update, be it a kernel update or some
other software running in the background, must have reduced energy consumption
considerably :-)
{{< figure src="p14s-battery-drain-2.png">}}
(I am well aware that there are laptops whose battery run time is in an entirely
different ball park, and even some of my own Thinkpads used to do better, but
I'm overall quite happy with battery life on this machine with bright 3k display
and dedicated GPU. I _could_ have bought a machine which can run on battery like
forever, but that's not my use case and plus, I'd never buy anything else but a
Thinkpad...)
[Darktable]: https://darktable.org [Darktable]: https://darktable.org
[fancontrol]: https://ounapuu.ee/posts/2022/09/26/minimum-viable-fan-control-script/ [fancontrol]: https://ounapuu.ee/posts/2022/09/26/minimum-viable-fan-control-script/
[Fedora]: https://fedoraproject.org/ [Fedora]: https://fedoraproject.org/
Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

@@ -29,12 +29,3 @@ Link: <https://kde.org/announcements/plasma/5/5.27.0/>
I wish there were keyboard shortcuts to move windows into one of the custom tile I wish there were keyboard shortcuts to move windows into one of the custom tile
layouts as well: <https://discuss.kde.org/t/keyboard-shortcuts-for-new-tiling-feature/1843> layouts as well: <https://discuss.kde.org/t/keyboard-shortcuts-for-new-tiling-feature/1843>
---
### Added 2025-03-02
With Plasma 5.3, you can now define keyboard shortcuts to move a window to
a custom tile. On my system, there were now shortcuts defined by default, but
it is very easy to do so in `System Settings > Shortcuts > KWin`. I have now
mapped `META`+`SHIFT` and the arrow keys to move windows to custom tiles.
@@ -1,14 +0,0 @@
---
title: "Summarize Subgroups With Percentages in R"
description: I need to remember how to summarize subgroups of a dataset with percentages in R!
date: 2024-11-04T16:30:07+0100
draft: true
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- R
- Tidyverse
---
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 856 KiB

-32
View File
@@ -1,32 +0,0 @@
---
title: "Clouds"
description: Clouds over Rhön Mountains area
date: 2025-08-02T07:00:00+0200
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- photography
- landscape
- nikon
---
Cloudy skies over the [Rhön Mountains][rhoen] area in Germany.
{{< figure src="DSC_3699.jpg" caption="Nikon Z6III with Nikkor 20mm f/1.8 S" >}}
---
{{< figure src="DSC_4404.jpg" caption="Nikon Z6III with Tamron 35-150mm f/2.0-2.8">}}
---
{{< figure src="DSC_4656.jpg" caption="Nikon Z6III with Nikkor 20mm f/1.8 S" >}}
Images developed in [Darktable][]. Raw image file available
upon reasonable request. All rights reserved.
[darktable]: https://darktable.org
[rhoen]: https://en.wikipedia.org/wiki/Rh%C3%B6n_Mountains
Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

@@ -1,58 +0,0 @@
---
title: Install pynitrokey on Fedora 41
description: Here's how I installed pynitrokey on my Fedora 41 KDE laptop, resolving missing dependencies.
date: 2025-03-30T10:53:35+0100
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- security
- Linux
- Fedora
---
{{< figure src="IMGP0069.jpg"
alt="My Nitrokey 3A mini"
title="My Nitrokey 3A mini on the right side of the picture" >}}
I use [Yubikey][]s and [Nitrokey][]s as a second factor during 2FA
authentication.
My experiences with Nitrokey devices have been mixed. Currently my 3A mini
stopped working. I wanted to investigate what's going on, only to find out that
the commandline tool `nitropy` no longer worked on my machine either! :-(
Here's how I got it to work again. I'm a Python noob, so maybe there's a better
way to install the dependencies. The operating system is Fedora 41.
```bash
sudo dnf install systemd-devel python3-devel
pipx install pynitrokey
```
Afterwards I was greeted with this:
```text
$ nitropy nk3 test
Command line tool to interact with Nitrokey devices 0.8.1
Critical error:
No connected NK3 devices found
--------------------------------------------------------------------------------
Critical error occurred, exiting now
Unexpected? Is this a bug? Would you like to get support/help?
- You can report issues at: https://support.nitrokey.com/
- Writing an e-mail to support@nitrokey.com is also possible
- Please attach the log: '/tmp/nitropy.log._p6mpa3e' with any support/help request!
- Please check if you have udev rules installed: https://docs.nitrokey.com/nitrokeys/nitrokey3/firmware-update#troubleshooting-linux
```
I made sure that I had the abovementioned `udev` rules in place. The key is not
detected by any system, be it my personal Linux laptop or any Windows machine
that I could access. [D'oh][]!
[Nitrokey]: https://www.nitrokey.com
[Yubikey]: https://www.yubico.com
[d'oh]: https://en.wikipedia.org/wiki/D'oh!
Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

-40
View File
@@ -1,40 +0,0 @@
---
title: Leaving X
description: German academic institutions are leaving Twitter/X. I'm not an academic institution, but still I'm following suit.
date: 2025-01-14T21:30:00+0100
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- academia
- social media
---
Several academic institutions in Germany have decided to terminate their
presence on X, formerly known as Twitter.
Press release of the "Informationsdienst Wissenschaft" (scientific news service,
in German): <https://idw-online.de/de/news845520>
Ironically, the press release page has several buttons to share this information
on social media, right now still including a sharing button for X :smile:
Surely just a matter of time.
{{< figure src="press-release-with-x-sharing-button.png"
title="Press release (in German) informing about German academic institutions leaving X, with X sharing button." >}}
I got aware of this by reading my former university's newsletter:
<https://www.uni-wuerzburg.de/aktuelles/einblick/single/news/universitaet-wuerzburg-kehrt-plattform-x-den-ruecken-1>
I'm following suit. I've just now deleted my account on X.
{{< figure src="account-deactivated.png"
title="I did it! My account on X has been deactivated. (Screenshot in German.)" >}}
If you would like to connect with me on social media, please head over to my profiles on [Mastodon][] or [Bluesky][].
See you!
[Mastodon]: https://neph.social/@daniel_kraus
[Bluesky]: https://bsky.app/profile/bovender.bsky.social
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

@@ -1,72 +0,0 @@
---
title: "Mainz Central Station"
description: Mainz Central Station, edited to simulate a mist filter
date: 2025-09-05T18:45:00+0200
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- photography
- city
- night
- nikon
---
Mainz Central Station (Mainz Hauptbahnhof or just short Mainz Hbf) at night. I stepped
out of the [Adam & Eden][ae] restaurant the other night and I was impressed by the
illuminated station.
{{< figure src="DSC_5532-1.jpg" caption="Nikon Z6III with Tamron 35-150mm f/2.0-2.8" >}}
The above was edited as usual in [Darktable][]. I wanted to let the lights in the windows
glow a bit and have them contrast with the otherwise rather dark scene. I did not have
a mist filter on my camera, so I attempted to simulate one with Darktable's
[Diffuse or Sharpen][] module. In my opinion, the effect of mist filters can quickly
destroy an image, it's like emptying an entire bottle of ketchup all at once onto
your fries. I was wary of that and attempted to make it subtle. Don't know if this was
the way to go.
See my [Diffuse or Sharpen][] mist filter preset over at
[github.com/bovender/darktable-presets-and-styles][gh].
{{< figure src="mist-filter-in-darktable-screenshot-1.png"
caption="Screenshot of Darktable with mask overlay turned on." >}}
The screenshot above shows the mask overlay in Darktable. The screenshow below
shows the settings of the 'g' channel: I want to exclude dark image areas from
the filter effect.
{{< figure src="diffuse-or-sharpen-settings-1.png"
caption="Settings of the diffuse-or-sharpen module with 'g' channel." >}}
The next screenshot shows the 'hz' channel, which I adjusted to include warmish
colors only.
{{< figure src="diffuse-or-sharpen-settings-2.png"
caption="Settings of the diffuse-or-sharpen module with 'hz' channel." >}}
Finally, I applied a large blurring radius so that I can achieve a glowing effect.
I'm not sure yet whether to exclude the moon from the mist filter effect or not.
---
Original image developed in [Darktable][]. Raw image file available
upon reasonable request. All rights reserved.
---
Here's one of several video tutorials on the Diffuse or Sharpen module
by Boris Hajdukovic. He's a master of image editing with Darktable.
Highly recommended.
{{< youtube pAbyORw0mng >}}
---
[ae]: https://www.adamundeden.com
[darktable]: https://darktable.org
[diffuse or sharpen]: https://www.youtube.com/watch?v=pAbyORw0mng
[gh]: https://github.com/bovender/darktable-presets-and-styles
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

@@ -1,80 +0,0 @@
---
title: "[In German] USB-Sticks in den Fotostationen von dm"
description:
date: 2025-03-22T22:20:07+0100
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- German
---
(This post is in German.)
In den letzten Wochen habe ich ein paar Mal Fotoabzüge benötigt und bin mit
einem USB-Stick zur örtlichen Drogerie. Zu meiner Überraschung konnten keinerlei
Bilder auf dem Stick gefunden werden. Inzwischen habe ich das mit mehreren
Sticks und an verschiedenen Foto-Stationen (aber immer in demselben Laden)
probiert, Fehlanzeige. Interessanterweise hat es mit einer SD-Karte problemlos
geklappt.
Offenbar bin ich weder der erste noch der einzige, der dieses Problem schon
einmal hatte, es gibt zu einem ähnlichen Problem mit einer CEWE-Fotostation
einen [Forumsthread][].
In diesem Blog-Beitrag sammele ich einfach mal meine Beobachtungen, vielleicht
hilft es ja einmal jemandem. Von Mal zu Mal werde ich dies updaten.
## Beispiele für "geht" und "geht nicht"
Ich hatte zuletzt zwei Speichermedien dabei, einen USB-Stick und eine SD-Karte:
```plain
$ lsblk -f
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sda
└─sda1 exfat 1.0 [REDACTED] E7F3-FDF9 114,5G 0% /run/media/daniel/[REDACTED]
sdb
└─sdb1 exfat 1.0 7FDE-A701 119G 0% /run/media/daniel/7FDE-A701
```
`sda` ist ein USB-Stick, den ich häufig in anderen Zusammenhängen im Einsatz
habe. `sdb` ist eine ältere SD-Karte. Beide haben nominell 128 GB Kapazität, und
beide haben eine Partitionstabelle im herkömmlichen MSDOS-Format und eine
Primärpartition, die mit dem Dateisystem EXFAT formatiert ist. Das Dateisystem
auf dem USB-Stick hat ein Label, welches aus wenigen Buchstaben ohne
Sonderzeichen besteht.
Auf beiden Speichermedien hatte ich denselben Foto-Ordner kopiert, mit etwa
einer Handvoll Fotos. Die Bilder lagen also auf beiden Medien in einem
Unterorder.
**Interessanterweise wurden die Bilder auf der SD-Karte gefunden, die Bilder auf
dem USB-Stick jedoch nicht.**
Hier meine Beobachtungen im einzelnen, denn entsprechende Überlegungen findet
man auch im Netz:
### Dateisystem
Bevor ich es sowohl mit dem Stick als auch mit der SD-Karte probierte, habe ich
folgende Dateisysteme auf dem USB-Stick ausprobiert:
- EXFAT
- FAT32
Mit beiden Dateisystemen wurden keine Bilder gefunden.
### Root-Verzeichnis vs. Unterverzeichnis
Es hat keinen Unterschied gemacht, ob ich die Bilder (JPG-Dateien) im
Wurzelverzeichnis oder in einem Unterordner auf dem Stick hatte.
### Verschiedene Terminals
Ich habe meinen USB-Stick an verschiedenen Foto-Terminals ausprobiert, an keinem
von ihnen wurden die Bilder gefunden.
[Forumsthread]: https://www.cewe-community.com/forum/fb/viewtopic.php?t=13036&p=135162
Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

@@ -1,148 +0,0 @@
---
title: "Nvidia Drivers on Fedora"
description: >
My own up-to-date instructions for installing Nvidia drivers on a Fedora Linux system.
date: 2025-03-02T08:28:26Z
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- nvidia
- fedora
- linux
---
My [Thinkpad P14s]({{< relref "p14s" >}}) has a dedicated Nvidia GPU, which
requires proprietary drivers. There is a gazillion of instructions on the web
for installing theses drivers on a Fedora system. Every now and then (with
kernel updates, I guess), my drivers stop working and I have to research _again_
how to (re-)install these drivers. Usually I notice this when developing raw
images with [Darktable][]. It gets dead slow and then I find out that OpenCL is
not working.
This post mainly serves as a reminder for myself how to re-install the NVidia
drivers on a Fedora laptop. It should be fairly up to date, because as I wrote,
I keep running into this problem over and over again.
The prerequisite is to have the [RPM Fusion][] repositories enabled.
{{< figure src="discover-settings-rpm-fusion.png" >}}
## Installing the NVidia drivers
I found out that I basically only need two packages:
- `nvidia-settings` and
- `xorg-x11-drv-nvidia-cuda`
The first one suffices to pull in the actual drivers and other packages as
dependencies. The latter is required in order for Darktable to make use of OpenCL.
```fish
sudo dnf install nvidia-settings xorg-x11-drv-nvidia-cuda
```
```plain
Updating and loading repositories:
Repositories loaded.
Package Arch Version Repository Size
Installing:
nvidia-settings x86_64 3:570.86.16-1.fc41 rpmfusion-nonfree-updates 4.4 MiB
Installing dependencies:
akmod-nvidia x86_64 3:570.86.16-3.fc41 rpmfusion-nonfree-updates 92.4 KiB
egl-gbm x86_64 2:1.1.2^20240919gitb24587d-3.fc41 fedora 29.3 KiB
egl-wayland x86_64 1.1.18~20250114git26ba0e3-2.fc41 updates 80.9 KiB
egl-x11 x86_64 1.0.1~20241213git61e70b0-1.fc41 updates 161.1 KiB
nvidia-modprobe x86_64 3:570.86.16-1.fc41 rpmfusion-nonfree-updates 51.0 KiB
xorg-x11-drv-nvidia x86_64 3:570.86.16-5.fc41 rpmfusion-nonfree-updates 190.2 MiB
xorg-x11-drv-nvidia-kmodsrc x86_64 3:570.86.16-5.fc41 rpmfusion-nonfree-updates 75.4 MiB
xorg-x11-drv-nvidia-libs x86_64 3:570.86.16-5.fc41 rpmfusion-nonfree-updates 361.9 MiB
Installing weak dependencies:
xorg-x11-drv-nvidia-cuda-libs x86_64 3:570.86.16-5.fc41 rpmfusion-nonfree-updates 273.2 MiB
xorg-x11-drv-nvidia-power x86_64 3:570.86.16-5.fc41 rpmfusion-nonfree-updates 233.7 KiB
Transaction Summary:
Installing: 11 packages
Total size of inbound packages is 351 MiB. Need to download 114 KiB.
After this operation, 906 MiB extra will be used (install 906 MiB, remove 0 B).
Is this ok [y/N]:
```
## Testing the installation
```fish
inxi -G
```
This will output the following if everything is configured correctly.
```plain
Graphics:
Device-1: Intel Meteor Lake-P [Intel Arc Graphics] driver: i915 v: kernel
Device-2: NVIDIA AD107GLM [RTX 500 Ada Generation Laptop GPU]
driver: nvidia v: 570.86.16
Device-3: Syntek Integrated Camera driver: uvcvideo type: USB
Display: wayland server: Xwayland v: 24.1.6 compositor: kwin_wayland
driver: gpu: i915 resolution: 1: 3840x2160~60Hz 2: 3072x1920
API: EGL v: 1.5 drivers: iris,nvidia
platforms: gbm,wayland,x11,surfaceless,device
API: OpenGL v: 4.6.0 compat-v: 4.6 vendor: intel mesa v: 25.0.0
renderer: Mesa Intel Arc Graphics (MTL)
API: Vulkan v: 1.4.304 drivers: N/A surfaces: xcb,xlib,wayland
Info: Tools: api: clinfo, eglinfo, glxinfo, vulkaninfo
de: kscreen-console,kscreen-doctor gpu: nvidia-settings,nvidia-smi
wl: wayland-info x11: xdriinfo, xdpyinfo, xprop, xrandr```
```
If the drivers aren't installed and loaded properly, the graphics card's name
will be some generic term, not the exact name of the model.
To test if Darktable can use the CUDA driver, start if from the commandline
like so:
```fish
darktable -d opencl
```
This should output a lot of technical information about the graphics card.
If it doesn't, but instead complains along the lines of
> FINALLY: opencl is NOT AVAILABLE on this system
then there is something wrong.
---
## Addendum 2025-04-17
Yesterday I upgraded from Fedora 41 to Fedora 42 (KDE), and of course the NVIDIA
drivers were removed. Using the instructions above effortlessly installed the
driver (currently version 570). :-)
---
## Addendum 2026-01-18
This is turning into a pattern. Today I noticed while working with Darktable
that OpenCL was no longer in use. Sure enough `inxi -G` told me that no
NVIDIA driver was loaded.
When I issued
```fish
sudo dnf install nvidia-settings xorg-x11-drv-nvidia-cuda
```
dnf said that these packages were already installed, but with `sudo dnf update`
I got a whole bunch of updates for the NVIDIA-related packages.
After rebooting, my laptop would crash twice, but holding my breath I disconnected
the external screen, rebooted again, and after logging into Fedora 43 reconnected
the external screen, which finally went smoothly (and I could breathe again).
[Darktable]: https://www.darktable.org
[RPM fusion]: https://rpmfusion.org
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 MiB

@@ -1,36 +0,0 @@
---
title: "Schwanheimer Wiese"
description: Two images from Schwanheimer Wiese, Frankfurt, Germany
date: 2025-03-04T06:21:18Z
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- photography
- landscape
- pentax
---
I have decided to publish some of my photos on my blog.
Here are the first two of them, both taken on
["Schwanheimer Wiese"][wiese] a meadow in the forests
of [Frankfurt-Schwanheim][Schwanheim].
{{< figure src="20250223_152944 IMGP9786.jpg" >}}
The "Struwwelpeter" is a tree named after a figure from
a famous [children's book][struwwelpeter]:
{{< figure src="20250223_153306 IMGP9790.jpg" >}}
Camera: Pentax K-3 APS-C, 18-55 mm kit lens (crop factor 1.5),
image developed in [Darktable][]. Raw image file available
upon reasonable request. All rights reserved.
[darktable]: https://darktable.org
[Schwanheim]: https://en.wikipedia.org/wiki/Schwanheim_(Frankfurt_am_Main)
[struwwelpeter]: https://en.wikipedia.org/wiki/Struwwelpeter
[wiese]: https://de.wikipedia.org/wiki/Frankfurt-Schwanheim#Schwanheimer_Wiese
Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

-116
View File
@@ -1,116 +0,0 @@
---
title: "Soleier: Pickled eggs for Easter"
description: >
Pickled eggs "Soleier" are a favorite Easter tradition in my
family. Here's my personal recipe.
date: 2025-04-17T17:55:07+02:00
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- recipes
---
My family has a long-running tradition of producing pickled eggs for Easter
breakfast. We call these "Soleier" which is a composite of "Sole" (brine) and
"Eier" (eggs). Varous theories exist how pickled eggs came into existence, and
there are various ways to relish these eggs. As for the theories, the reader is
kindly referred to the [Wikipedia article][pickled eggs]
(also [in German][Soleier]).
As for the recipe and the way we eat pickled eggs in my family: read on.
{{< figure src="20250414_203010.jpg"
alt="My 2025 Soleier preparation."
caption="My 2025 Soleier preparation. Smartphone raw image file edited with darktable."
>}}
## How many eggs?
How many eggs you want to prepare depends on the number of people who want to
eat the pickled eggs, the number of days you want to have them for breakfast (or
any other meal to your liking) and last, but not least, what size of jar you
have to prepare them in.
My 1.5 L [Weck jar][] holds 13 medium-sized eggs.
Typically an adult will have 2 of those eggs for breakfast. With 13 eggs, this
leaves one spare egg (and toast), but there's usually a kid or two who doesn't
eat these eggs in pairs (if at all).
{{< figure src="20250414_203911.jpg"
alt="Pickled eggs with cracks."
caption="I love my pickled eggs to have cracks so that they let some of the brine in."
>}}
## Recipe
### Ingredients and materials
This recipe is for 13 eggs and a 1.5 L jar.
- 13 eggs
- 0.75 L water
- 50 g salt
- 1-2 tablespoons of caraway seeds (if you like)
- 1.5 L [Weck jar][] (doesn't need to be original Weck brand, of course, but must be sealed tight)
- 13 slices of toast
- butter
- oil, e.g. rapeseed oil, to your liking
- vinegar, e.g. any herb-flavored variant, to your liking
### Directions
You should prepare the pickled eggs a few days before you want to eat them. In
my family we typically do this 7 days before Easter, i.e. on Palm Sunday.
1. Boil the eggs so they get hard. I usually place the eggs in a pot, cover them
with water, and then slowly heat the water until it boils. Let it boil until
10 minutes have passed since the eggs were placed on the stove. Your mileage
may vary depending on your stove.
2. Pour out the hot water and shock the eggs with cold tap water.
3. Place the eggs into the jar using a clean spoon (not your hands!). I usually
let them drop into the jar so they get nice little cracks, this makes for
beautiful texture inside the eggs (see image).
4. Add the salt and caraway seeds to the jar.
5. Bring the water to boiling and pour it over the eggs in the jar.
6. Close the jar.
7. Invert the jar a couple of times (you may want to wear gloves while doing
this, the jar is hot!).
8. Store the jar in a cool place -- in our house, this is usually the basement.
## How to eat Soleier
There seem to be two fundamentally different ways to eat a _Solei_.
### Our way of eating Soleier
My family's tradition is to peel two eggs, place them on a dish, cut them into
pieces, then pour some oil and vinegar over the pieces and mash the eggs with a
fork. This leaves an ugly mess on the plate and I will update this post with a
picture of this year's appearance.
Brown two slices of toast, spread butter on them, and eat the _Solei_ mash/mess
using a fork while intermittently taking a bite from the bread.
### The more sophisticated way to eat Soleier
There is an alternative way to have your pickled eggs that supposedly is less
messy. The [German Wikipedia article][Soleier] claims it's the 'proper' or let's
say usual way to eat these eggs. I tried it once and I was totally unhappy with
the experience, so I stick with our traditional way.
Peel two eggs, cut the eggs into halves, remove the egg yolk and mash the egg
yolk with oil and vinegar. Transfer the mashed egg yolk back into the eggs. I
will look slightly less ugly, but I promise you will have a hard time getting
all the egg yolk--oil--vinegar mixture back into the eggs, the plate will look
like someone's been having _Soleier_ the traditional way, and when trying to put
the egg halves into your mouth, be prepared for everyone else who sits at the
table with you bursting into spontaneous laughter. At least that's what happened
to me one time... ;-)
[pickled eggs]: https://en.wikipedia.org/wiki/Pickled_egg
[Soleier]: https://de.wikipedia.org/wiki/Solei
[Weck jar]: https://en.wikipedia.org/wiki/Weck_jar
Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

-128
View File
@@ -1,128 +0,0 @@
---
title: "Testing CFexpress Type B card readers"
description: >
Is there a true, relevant difference in the performance of current CFexpress
Type B card readers, or is it all just marketing?
date: 2026-04-19T12:26:06+02:00
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- linux
- photography
---
{{< figure src="cfexpress_type_b_card.jpg" >}}
I've been looking for a card reader for my CFexpress Type B cards that I use in
my camera. There are not very many devices on the market, but they have a large
price span: around 10x.
Since I was wondering whether the price correlates with speed, I decided to
perform some simple tests using my existing hardware:
- a UGREEN CFexpress Type B card reader
- a PGYTECH CreateMate CFE-B/SD card reader
- a Nextorage B1 Pro CFexpress Type B card with 165 GB capacity
- the built-in NVMe SSD of my Thinkpad laptop, a Samsung SSD 990 PRO with 2 TB
storage capacity.
The two external card readers are attached via USB-C/USB 3.2 Gen 2 to my
Thinkpad Thunderbolt 3 dock. I am generally happy with both of them, I think
they have good quality.
By the way, why should I even look for a new card reader in the first place?
Well, the CreateMate card reader is a bit bulky and heavy and I once forgot to
take it with me on vacation. So I was looking for something smaller and lighter.
## Sequential read and write tests
I did not want to overcomplicate things, just get an impression if there are any
differences between card readers or not. Therefore I decided to perform
sequential read and write tests using `dd` as described at [Baeldung.com][].
These are the commands that I used in [Fish][] shell; Bash has slightly
different syntax.
```fish
#!/usr/bin/fish
echo "*** Sequential write test ***"
for i in (seq 1 5); dd if=/dev/zero of=/run/media/daniel/NIKON\ Z6_3/tempfile bs=1M count=1024 conv=fdatasync; end
echo
echo "*** Sequential read test ***"
for in in (seq 1 5); sudo sh -c "/usr/bin/echo 3 > /proc/sys/vm/drop_caches" && dd if=/run/media/daniel/NIKON\ Z6_3/tempfile of=/dev/null bs=1M count=1024; end
rm /run/media/daniel/NIKON\ Z6_3/tempfile
```
## Results
The graph below shows the means and standard deviations of 5 tests for each
device. The "direct" label denotes tests with the card reader attached directly
to a USB-C/USB 3.2 Gen 2 port of my [Thinkpad P14s][] laptop, rather than via
the Thunderbolt 3 dock.
{{< figure src = "speed_tests.svg" >}}
When attached to the Thunderbolt dock, the PGYTECH CreateMate reads from the
CFexpress card much faster than the UGREEN reader. It does also write a bit
faster than the UGREEN reader.
Attaching the card readers directly to the laptop results in increased speeds
overall, and the differences between the UGREEN and the PGYTECH readers seems
negligible.
**However, regardless of how the readers are connected to the laptop, both are
left in the dust by the internal SSD!**
Is the observed difference in read speeds between the UGREEN and the PGYTECH
card readers "clinically relevant"? Does it justify spending a lot more money
for the PGYTECH reader? Given the very large difference of both of these card
readers to the internal SSD, I don't really think so.
## What to expect from a "USB-C" or rather, USB 3.2 Gen 2 connection
It should be noted that the USB 3.2 Gen 2 standard has a nominal transfer speed
of 10 GBit/s or 1.25 GB/s. As stated in the [Wikipedia article on USB 3.2][],
actual transfer rates are round 0.8-1.0 GB/s. Still higher than what I measured.
My Thinkpad's USB-C ports a really Thunderbolt 4/USB 4 ports that should be
capable of transferring up to 40 Gbit/s or 5 GB/s. This would be in the internal
SSD's ballpark. However, the card readers have "only" USB 3.2 Gen 2. PGYTECH
claims 1000 MB/s for the CreateMate and UGREEN 1250 MB/s for their device. The
card itself claims read speeds of up to 1950 MB/s and write speeds of up to 1900
MB/s, which roughly corresponds to 1.86 and 1.90 GB/s. Obviously, the 0.3 to 0.6
GB/s that I measured are nowhere near any of these numbers.
## Using Darktable's local copies feature
Evidently, nothing is faster than the internal NVMe SSD.
This reminds me that [Darktable][] has a feature to copy image files to the
built-in SSD. This not only allows for faster read and write speeds, but also
makes it possible to work on the images without having the card reader attached
to the laptop: [Darktable's user manual on local copies][local-copies].
Local copies (or rather, the XMP sidecar files) are synchronized whenever the
external storage is attached when Darktable is starting up. I don't know if
Darktable will automatically remove synchronized local copies in case disk
space gets low.
## Conclusion
In practice, I am going to create local copies of my images and select and work
on the local copies. Yes, the initial process of copying the files will take a
few seconds less with the PGYTECH reader than with the UGREEN reader, but that
is negligible given the amount of time that I spend on selecting the images and
working on the RAW file development in total.
Therefore, if I decide to purchase a new reader, I will focus on the size and
build, and the UGREEN reader is a strong contender when it comes to small size.
[baeldung.com]: https://www.baeldung.com/linux/disk-performance-test
[darktable]: https://darktable.org
[Fish]: https://fishshell.com
[local-copies]: https://docs.darktable.org/usermanual/development/en/overview/sidecar-files/local-copies/
[Thinkpad P14s]: {{< relref "p14s" >}}
[Wikipedia article on USB 3.2]: https://en.wikipedia.org/wiki/USB_3.0#3.2
@@ -1,137 +0,0 @@
<?xml version='1.0' encoding='UTF-8' ?>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='576.00pt' height='432.00pt' viewBox='0 0 576.00 432.00'>
<g class='svglite'>
<defs>
<style type='text/css'><![CDATA[
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
fill: none;
stroke: #000000;
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 10.00;
}
.svglite text {
white-space: pre;
}
.svglite g.glyphgroup path {
fill: inherit;
stroke: none;
}
]]></style>
</defs>
<rect width='100%' height='100%' style='stroke: none; fill: #303030;'/>
<defs>
<clipPath id='cpMC4wMHw1NzYuMDB8MC4wMHw0MzIuMDA='>
<rect x='0.00' y='0.00' width='576.00' height='432.00' />
</clipPath>
</defs>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHw0MzIuMDA=)'>
<rect x='0.00' y='0.000000000000057' width='576.00' height='432.00' style='stroke-width: 1.07; stroke: none; fill: #303030;' />
</g>
<defs>
<clipPath id='cpMTUwLjk0fDU2Ni4wNHw3NS4yMXwyMTYuMDE='>
<rect x='150.94' y='75.21' width='415.10' height='140.80' />
</clipPath>
</defs>
<g clip-path='url(#cpMTUwLjk0fDU2Ni4wNHw3NS4yMXwyMTYuMDE=)'>
<polyline points='168.31,122.60 168.31,114.47 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='168.31,118.53 167.47,118.53 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='167.47,122.60 167.47,114.47 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='181.12,176.75 181.12,168.63 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='181.12,172.69 180.58,172.69 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='180.58,176.75 180.58,168.63 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='455.75,203.82 455.75,195.70 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='455.75,199.76 372.46,199.76 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='372.46,203.82 372.46,195.70 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='187.62,149.67 187.62,141.55 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='187.62,145.61 187.45,145.61 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='187.45,149.67 187.45,141.55 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='187.55,95.52 187.55,87.40 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='187.55,91.46 187.24,91.46 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='187.24,95.52 187.24,87.40 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<rect x='150.94' y='106.35' width='16.95' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='160.50' width='29.91' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='187.58' width='263.16' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='133.43' width='36.60' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='79.27' width='36.46' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<text x='180.35' y='122.84' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.307 GB/s</text>
<text x='193.16' y='176.99' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.542 GB/s</text>
<text x='467.79' y='204.07' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>4.772 GB/s</text>
<text x='199.66' y='149.92' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.664 GB/s</text>
<text x='199.59' y='95.76' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.661 GB/s</text>
</g>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHw0MzIuMDA=)'>
</g>
<defs>
<clipPath id='cpMTUwLjk0fDU2Ni4wNHwyNDEuMzd8MzgyLjE3'>
<rect x='150.94' y='241.37' width='415.10' height='140.80' />
</clipPath>
</defs>
<g clip-path='url(#cpMTUwLjk0fDU2Ni4wNHwyNDEuMzd8MzgyLjE3)'>
<polyline points='168.76,288.76 168.76,280.63 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='168.76,284.69 167.39,284.69 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='167.39,288.76 167.39,280.63 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='171.30,342.91 171.30,334.79 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='171.30,338.85 168.41,338.85 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='168.41,342.91 168.41,334.79 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='318.53,369.99 318.53,361.86 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='318.53,365.92 313.43,365.92 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='313.43,369.99 313.43,361.86 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='182.40,315.83 182.40,307.71 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='182.40,311.77 172.97,311.77 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='172.97,315.83 172.97,307.71 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='179.10,261.68 179.10,253.56 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='179.10,257.62 167.60,257.62 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<polyline points='167.60,261.68 167.60,253.56 ' style='stroke-width: 1.07; stroke: #505090; stroke-linecap: butt;' />
<rect x='150.94' y='272.51' width='17.14' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='326.66' width='18.91' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='353.74' width='165.04' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='299.59' width='26.74' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<rect x='150.94' y='245.43' width='22.41' height='24.37' style='stroke-width: 1.07; stroke: none; stroke-linecap: butt; stroke-linejoin: miter; fill: #505090;' />
<text x='180.62' y='289.00' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='59.30px' lengthAdjust='spacingAndGlyphs'>0.311 GB/s</text>
<text x='183.34' y='343.15' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.343 GB/s</text>
<text x='330.56' y='370.23' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>2.993 GB/s</text>
<text x='194.43' y='316.08' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.485 GB/s</text>
<text x='191.14' y='261.92' style='font-size: 12.04px;fill: #C0C0C0; font-family: "Arial";' textLength='60.19px' lengthAdjust='spacingAndGlyphs'>0.406 GB/s</text>
</g>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHw0MzIuMDA=)'>
</g>
<defs>
<clipPath id='cpMTUwLjk0fDU2Ni4wNHwyMjEuNDl8MjQxLjM3'>
<rect x='150.94' y='221.49' width='415.10' height='19.88' />
</clipPath>
</defs>
<g clip-path='url(#cpMTUwLjk0fDU2Ni4wNHwyMjEuNDl8MjQxLjM3)'>
<text x='358.49' y='235.73' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='25.34px' lengthAdjust='spacingAndGlyphs'>write</text>
</g>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHw0MzIuMDA=)'>
</g>
<defs>
<clipPath id='cpMTUwLjk0fDU2Ni4wNHw1NS4zM3w3NS4yMQ=='>
<rect x='150.94' y='55.33' width='415.10' height='19.88' />
</clipPath>
</defs>
<g clip-path='url(#cpMTUwLjk0fDU2Ni4wNHw1NS4zM3w3NS4yMQ==)'>
<text x='358.49' y='69.57' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='24.02px' lengthAdjust='spacingAndGlyphs'>read</text>
</g>
<g clip-path='url(#cpMC4wMHw1NzYuMDB8MC4wMHw0MzIuMDA=)'>
<text x='150.94' y='395.70' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='6.67px' lengthAdjust='spacingAndGlyphs'>0</text>
<text x='261.23' y='395.70' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='6.67px' lengthAdjust='spacingAndGlyphs'>2</text>
<text x='371.52' y='395.70' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='6.67px' lengthAdjust='spacingAndGlyphs'>4</text>
<text x='481.81' y='395.70' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='6.67px' lengthAdjust='spacingAndGlyphs'>6</text>
<text x='146.01' y='204.06' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='64.70px' lengthAdjust='spacingAndGlyphs'>internal_ssd</text>
<text x='146.01' y='176.99' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='100.03px' lengthAdjust='spacingAndGlyphs'>pgytech_cfexpress</text>
<text x='146.01' y='149.91' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='136.05px' lengthAdjust='spacingAndGlyphs'>pgytech_cfexpress_direct</text>
<text x='146.01' y='122.83' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='95.38px' lengthAdjust='spacingAndGlyphs'>ugreen_cfexpress</text>
<text x='146.01' y='95.76' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='131.39px' lengthAdjust='spacingAndGlyphs'>ugreen_cfexpress_direct</text>
<text x='146.01' y='370.22' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='64.70px' lengthAdjust='spacingAndGlyphs'>internal_ssd</text>
<text x='146.01' y='343.15' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='100.03px' lengthAdjust='spacingAndGlyphs'>pgytech_cfexpress</text>
<text x='146.01' y='316.07' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='136.05px' lengthAdjust='spacingAndGlyphs'>pgytech_cfexpress_direct</text>
<text x='146.01' y='288.99' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='95.38px' lengthAdjust='spacingAndGlyphs'>ugreen_cfexpress</text>
<text x='146.01' y='261.92' text-anchor='end' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='131.39px' lengthAdjust='spacingAndGlyphs'>ugreen_cfexpress_direct</text>
<text x='358.49' y='409.55' text-anchor='middle' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='72.67px' lengthAdjust='spacingAndGlyphs'>Speed (GB/s)</text>
<text x='150.94' y='47.33' style='font-size: 12.00px;fill: #C0C0C0; font-family: "Arial";' textLength='78.00px' lengthAdjust='spacingAndGlyphs'>File size: 1 GB</text>
<text x='150.94' y='30.23' style='font-size: 14.40px;fill: #C0C0C0; font-family: "Arial";' textLength='213.59px' lengthAdjust='spacingAndGlyphs'>Sequential read and write speeds</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -1,116 +0,0 @@
---
title: How to import emails from a .pst file into KMail
description: >
Using a tool called "readpst" you can transfer emails from an Outlook
account into any other email client, for example KMail.
date: 2026-05-15T17:30:00+02:00
draft: false
ShowLastmod: true
scrolltotop: true
images: []
tags:
- email
- KDE
- Linux
---
Here I describe how I imported an email archive from Outlook to KDE's
[KMail][]. This turned out to be harder than expected.
My machine is a Fedora 44 KDE box, but the procedure should be the same or at
least very similar on other Linux systems.
## Prerequisites
Let's assume the following:
1. An Outlook archive file named `archive.pst`.
2. Inside this archive file: An email folder structure with first-level folders
in the style of `MyFolder1`, `MyFolder2` etc. and subfolder
`MyFolder1/MyFolder1Subfolder1` and so on.
I use the venerable [readpst][] tool to extract folders and emails from the
archive file. It's included in common Linux distributions, but at least on
Fedora you don't install it directly, but rather the `libpst` package:
```fish
sudo dnf install libpst
```
```fish
# dnf info libpst
Updating and loading repositories:
Repositories loaded.
Installed packages
Name : libpst
Epoch : 0
Version : 0.6.76
Release : 28.fc44
Architecture : x86_64
Installed size : 192.7 KiB
Source : libpst-0.6.76-28.fc44.src.rpm
From repository : <unknown>
Summary : Utilities to convert Outlook .pst files to other formats
URL : http://www.five-ten-sg.com/libpst/
License : GPL-2.0-or-later
Description : The Libpst utilities include readpst which can convert email messages
: to both mbox and MH mailbox formats, pst2ldif which can convert the
: contacts to .ldif format for import into ldap databases, and pst2dii
: which can convert email messages to the DII load file format used by
: Summation.
Vendor : Fedora Project
```
## Working solution as of 2026
Modern [KMail] does not store emails in the traditional storage formats such
as [Maildir] or [Mbox]. It uses KDE's Akonadi database system. Therefore,
we need to first export all folders and emails from the Outlook data file,
then import these into KMail's database using the `File > Import Messages...`
command.
At first, I struggled a bit to find the correct output format option to make
it work. `readpst` offers a number of different formats -- basically various
flavors of the [MH][]/[maildir][] and the [mbox][] structures. It did _not_ work
with the `-k` option that is meant to produce a dedicated KMail format. I ended
up using the simple `-e` option that produces a directory structure according
to the folders in the Outlook archive, with one separate file for each email
message.
1. Export folders and emails from the Outlook data file:
```fish
readpst -e archive.pst
```
There is no need to create and specifiy a dedicated directory, because
`readpst` will create a directory structure with a generic top-level
directory (in German, this directory is called "Outlook-Datendatei",
Outlook data file).
2. Import the folder structure and emails into KMail:
a. In the `File` menu, choose `Import Messages...`.
b. In the Import dialog that appears, choose "Import KMail Maildirs and
Folder Structure".
{{< figure src="kmail-import-dialog.png" >}}
c. At the bottom of the dialog where it says "Please select the folder to
import to", click on the folder icon. Choose the existing "Local Folders".
There is no need to create an extra folder for the import because KMail
will create a folder "KMail-Import" just for you.
d. Click on "Next" and choose your `.pst` file.
e. Start the import process.
3. All imported messages are marked as unread at first, but this will go away
after a while.
[KMail]: https://apps.kde.org/kmail2
[maildir]: https://en.wikipedia.org/wiki/Maildir
[MH]: https://en.wikipedia.org/wiki/MH_Message_Handling_System
[mbox]: https://en.wikipedia.org/wiki/Mbox
[readpst]: https://www.five-ten-sg.com/libpst/rn01re01.html
Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

@@ -1,93 +0,0 @@
---
title: "Teaching an old Android new tricks"
description: >
An oldish Android phone suddenly refused to connect to an IMAP server.
The error message was not helpful at all. The solution was that the IMAP
server's SSL certificate had been issued by a different Certificate
Authority than before, and this CA's root certificate was not known to
the phone.
date: 2026-01-01T18:00:00+0100
draft: false
# ShowLastmod: true
toc: false
scrolltotop: true
images: []
tags:
- academia
- network
- phone
---
A few days ago, my smartphone suddenly stopped synchronizing my university emails.
I use [Aqua Mail][] to work with my emails on a Samsung Galaxy S20 FE. This phone
was purchased in 2021.
Aqua Mail worked perfectly well with both my personal as well as my university
email accounts. But now it refused to connect to the university's IMAP server:
{{< figure src="aqua-mail-no-peer-certificate.png"
caption="Aqua Mail complaining about the absence of a peer certificate." >}}
The German error message says (somewhat clumsily): **"Error Messages being
synchronized: Invalid security certificate (SSL): No peer certificates."**
I suspected the mail server to be at fault. After all, my setup used to work.
However, my laptop mail client was able to connect to the IMAP server without
any problem.
Web search with "Aqua Mail no peer certificates" wasn't helpful at all, either.
So I manually initiated an IMAP session with the server from the command line:
```fish
openssl s_client -connect $REDACTED:993 -crlf
```
This went well -- I simply terminated the session by issuing
```plain
a logout
```
(If you, like me, are not fluent in IMAP -- the `a` in front of the logout
command is an arbitrary, but required identifying prefix.)
The output started with these lines:
```plain
Connecting to [REDACTED]
CONNECTED(00000003)
depth=2 C=GR, O=Hellenic Academic and Research Institutions CA, CN=HARICA TLS RSA Root CA 2021
verify return:1
depth=1 C=GR, O=Hellenic Academic and Research Institutions CA, CN=GEANT TLS RSA 1
verify return:1
depth=0 C=DE, ST=[REDACTED], O=[REDACTED], CN=[REDACTED]
verify return:1
---
Certificate chain
# ...
```
This caught my eye. I remembered an announcement that the university
was going to obtain their certificates from [HARICA][] in the future,
and evidently, the future is now.
Sure enough, I did _not_ find the root certificate "HARICA TLS RSA Root CA 2021"
in the root certificate store of my phone. There were 3 HARICA certificates, but
not the required one "HARICA TLS RSA Root CA 2021".
{{< figure src="android-root-certificates.png"
caption="The root certificates of this 2021 Android phone do not include HARICA TLS RSA Root CA 2021." >}}
From there, the solution was simple. I downloaded the missing root certificate from
[HARICA's repository][] to my phone and installed it from there.
Now Aqua Mail happily syncs my uni mail account again.
Sometimes it pays off to try and teach an old dog new tricks!
Happy New Year.
[Aqua Mail]: https://aqua-mail.com/android
[HARICA]: https://www.harica.eu
[HARICA's repository]: https://repo.harica.gr/rep_dyn.php
+6 -10
View File
@@ -1,19 +1,15 @@
services: services:
build: build:
image: ghcr.io/gohugoio/hugo image: hugomods/hugo:latest
command: build command: hugo
volumes: volumes:
- ".:/project" - ".:/src"
user: 1000:1000 user: 1000:1000
server: server:
image: ghcr.io/gohugoio/hugo image: hugomods/hugo:latest
command: server --bind 0.0.0.0 --buildDrafts --buildFuture --environment development command: server --buildDrafts --buildFuture
volumes: volumes:
- ".:/project" - ".:/src"
- "../hugo_cookie_consent:/hugo_cookie_consent"
ports: ports:
- "127.0.0.1:1313:1313" - "127.0.0.1:1313:1313"
user: 1000:1000 user: 1000:1000
environment:
ENVIRONMENT: development
TZ: Europe/Berlin
-8
View File
@@ -1,8 +0,0 @@
module git.bovender.de/daniel/blog
go 1.22
require (
git.bovender.de/daniel/hugo_cookie_consent v0.0.2 // indirect
github.com/1bl4z3r/hermit-V2 v1.1.33 // indirect
)
-4
View File
@@ -1,4 +0,0 @@
git.bovender.de/daniel/hugo_cookie_consent v0.0.2 h1:GBR0K3g2QeTfiQbOrdD0KeBm8oUStBB3NIwUvtfXlo0=
git.bovender.de/daniel/hugo_cookie_consent v0.0.2/go.mod h1:XTLK60q7Zlhjtw84Toe0l8C1tgPaVKcWRluceaig0o0=
github.com/1bl4z3r/hermit-V2 v1.1.33 h1:AjYWuPMkyF5/B7KXHQyyerHb4cm/ftiH1Fdd1kvCuXY=
github.com/1bl4z3r/hermit-V2 v1.1.33/go.mod h1:jzBra4Jey2XWE2vYCkCqttBP3KxwFXpQMK0cBcsbO04=
-4
View File
@@ -1,4 +0,0 @@
go 1.22
use .
use ../hugo_cookie_consent
-3
View File
@@ -1,3 +0,0 @@
notFound: Page not found
home: Home
archives: Archives
-1
View File
@@ -1 +0,0 @@
{{ partial "hugo_cookie_consent/scripts.html" . }}
-1
View File
@@ -1 +0,0 @@
{{ partial "hugo_cookie_consent/banner.html" . }}
-12
View File
@@ -1,12 +0,0 @@
<p>
{{ partial "svg.html" (dict "context" . "name" "posts_single_date") }} {{ dateFormat .Site.Params.NumDateLong .Page.Date.Local }}
{{ if and (not (eq .Page.Params.ShowLastmod nil)) (.Page.Params.ShowLastmod) }}
{{ if and .GitInfo .Site.Params.gitUrl }}
[{{ partial "svg.html" (dict "context" . "name" "posts_single_git_commit") }}<a href="{{ .Site.Params.gitUrl -}}{{ .Page.GitInfo.Hash }}" target="_blank" rel="noopener">{{ .Page.GitInfo.AbbreviatedHash -}}</a> @ {{ dateFormat .Site.Params.dateformNum .Page.GitInfo.AuthorDate.Local }}]
{{ else if not (eq .Page.Lastmod .Page.Date ) }}
&nbsp;[{{.Site.Params.initialPublish | default "Initial Published on : "}} {{ dateFormat .Site.Params.NumDateLong .Page.Lastmod.Local }}]
{{ else }}
{{ errorf "Lastmod is not found in Page Frontmatter or Lastmod is same as Date" }}
{{ end }}
{{ end }}
</p>
-10
View File
@@ -1,10 +0,0 @@
<a id="cy-effective-orcid-url"
class="underline"
href="https://orcid.org/0000-0002-6351-3706"
target="orcid.widget"
rel="me noopener noreferrer" style="vertical-align: top">
<img src="https://orcid.org/sites/default/files/images/orcid_16x16.png"
style="width: 1em; display: inline"
alt="ORCID iD icon" />
https://orcid.org/0000-0002-6351-3706
</a>
+16
View File
@@ -0,0 +1,16 @@
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://matomo.bovender.de/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
@@ -9,12 +9,12 @@
{{- with .Params.categories }} {{- with .Params.categories }}
<p>{{- partial "svg.html" (dict "context" . "name" "posts_single_categories") -}}{{- range . -}}<span class="category"><a href="{{ "categories/" | absLangURL }}{{ . | urlize }}">{{.}}</a></span>{{- end }}</p> <p>{{- partial "svg.html" (dict "context" . "name" "posts_single_categories") -}}{{- range . -}}<span class="category"><a href="{{ "categories/" | absLangURL }}{{ . | urlize }}">{{.}}</a></span>{{- end }}</p>
{{- end }} {{- end }}
<p>{{- partial "svg.html" (dict "context" . "name" "posts_single_date") }}{{ .Date.Format .Site.Params.NumDateLong }} <p>{{- partial "svg.html" (dict "context" . "name" "posts_single_date") }}{{ .Date.Format .Site.Params.dateformNumTime }}
{{- if and (not (eq .Page.Params.ShowLastmod nil)) (.Page.Params.ShowLastmod) -}} {{- if and (not (eq .Page.Params.ShowLastmod nil)) (.Page.Params.ShowLastmod) -}}
{{ if and .GitInfo .Site.Params.gitUrl }} {{ if and .GitInfo .Site.Params.gitUrl }}
({{- partial "svg.html" (dict "context" . "name" "posts_single_git_commit") -}}<a href="{{ .Site.Params.gitUrl -}}{{ .GitInfo.Hash }}" target="_blank" rel="noopener">{{ .GitInfo.AbbreviatedHash -}}</a> @ {{ .GitInfo.AuthorDate.Local.Format .Site.Params.NumDateShort -}}) ({{- partial "svg.html" (dict "context" . "name" "posts_single_git_commit") -}}<a href="{{ .Site.Params.gitUrl -}}{{ .GitInfo.Hash }}" target="_blank" rel="noopener">{{ .GitInfo.AbbreviatedHash -}}</a> @ {{ .GitInfo.AuthorDate.Local.Format .Site.Params.dateformNum -}})
{{- else if not (eq .Lastmod .Date ) -}} {{- else if not (eq .Lastmod .Date ) -}}
&nbsp;[{{.Site.Params.initialPublish | default "Initial Published on : "}} {{ dateFormat .Site.Params.NumDateLong .Lastmod.Local -}}] &nbsp;[{{.Site.Params.initialPublish | default "Initial Published on : "}} {{ dateFormat .Site.Params.dateformNumTime .Lastmod.Local -}}]
{{- else -}} {{- else -}}
{{ errorf "Lastmod is not found in Page Frontmatter or Lastmod is same as Date" }} {{ errorf "Lastmod is not found in Page Frontmatter or Lastmod is same as Date" }}
{{- end -}} {{- end -}}
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="70mm" height="70mm" version="1.1" viewBox="0 0 70 70" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-61.818 -90.422)">
<rect x="61.818" y="90.422" width="70" height="70" color="black" fill="#fff" stroke-width="2"/>
<path d="m78.483 156.82c-2.6218-0.0558-4.788-0.11884-4.8137-0.14007-0.0257-0.0212-0.04676-1.1662-0.04676-2.5444v-2.5058l-0.62219-0.38414c-1.0414-0.64296-1.4269-2.1083-1.0708-4.0702 0.18508-1.0196 0.16835-1.2073-0.1574-1.7666-0.70394-1.2085-0.94819-4.2247-0.6102-7.5353 0.10969-1.0747 0.22404-1.397 0.64273-1.8123 0.43872-0.43508 0.50928-0.65636 0.50928-1.5972 0-0.89228 0.13688-1.3749 0.74794-2.6372 1.5915-3.2876 3.6503-5.749 5.1585-6.1673 0.40147-0.11129 1.5463-0.20294 2.5441-0.20351 2.0288-1e-3 3.6437-0.45634 5.0342-1.419 1.0923-0.75621 3.698-3.5883 4.448-4.8347l0.6408-1.0648-0.35024-0.64968c-0.19262-0.35731-0.66856-0.90152-1.0576-1.2094-1.2148-0.96111-1.4022-2.6875-0.44212-4.0715 0.2612-0.37652 0.4746-0.84815 0.47422-1.048-7.42e-4 -0.40984-0.56507-1.4016-1.1445-2.0115-0.33392-0.3515-0.37496-0.58219-0.30478-1.7134 0.0934-1.5051 0.47171-2.3639 1.2415-2.8182 0.30721-0.18128 0.71189-0.69671 0.93608-1.1922 0.5046-1.1154 3.143-3.706 4.4423-4.3618 0.59511-0.3004 1.4066-0.52332 2.1217-0.58282 1.086-0.09039 1.2247-0.158 2.2293-1.0871 1.2781-1.1821 2.8126-2.224 4.2307-2.8725 2.7426-1.2543 8.5676-0.43475 11.859 1.6685 1.4584 0.9319 4.0246 3.4938 4.2004 4.1934 0.0692 0.27543 0.33015 0.70489 0.57988 0.95437 0.24974 0.24945 0.45406 0.61947 0.45406 0.82223 0 0.20277 0.28544 0.96864 0.63432 1.7019 0.89526 1.8817 1.1891 2.7991 1.4438 4.5079 0.32272 2.165 0.2756 3.0114-0.25124 4.5123-0.47937 1.3657-1.0021 2.0954-3.2591 4.5496-1.7374 1.8891-1.9327 2.3782-1.9327 4.8389 0 1.9641-0.0203 2.0706-0.44756 2.3503-0.26617 0.17422-0.62899 0.24744-0.89525 0.18069-0.63539-0.15931-1.4381 0.19667-1.64 0.72732-0.22403 0.58862-0.51601 0.75501-1.2395 0.70633-0.85373-0.0574-1.5923 0.26091-1.4521 0.6259 0.1529 0.39806-0.57173 1.1417-1.2433 1.2759-0.37802 0.0756-0.62843 0.30247-0.85117 0.77139-0.48801 1.0273-1.1845 1.4225-2.5183 1.4289-1.3282 7e-3 -1.426-0.0484-3.501-1.9624-0.84898-0.7831-1.6061-1.4225-1.6825-1.421-0.32474 7e-3 -0.57603 0.3556-1.2085 1.6778-1.5558 3.2528-1.6577 3.5644-1.7698 5.4146l-0.10757 1.775 1.0997 0.74367c1.2809 0.86625 2.8421 2.139 3.8346 3.1263 0.54014 0.53728 0.72766 0.89812 0.8249 1.5873 0.0693 0.49058 0.33931 1.2639 0.60021 1.7186l0.47434 0.82657 0.36743-0.77988c0.20208-0.42894 0.44653-0.77989 0.54317-0.77989 0.0967 0 0.29875-0.29412 0.4491-0.6536 0.15034-0.35949 0.36134-0.65361 0.46883-0.65361 0.10757 0 0.14348-0.084 0.0799-0.18675-0.0636-0.10269 0.0156-0.18674 0.1759-0.18674s0.29144-0.083 0.29144-0.18455c0-0.1015 0.33649-0.23828 0.74776-0.30398 0.41126-0.0656 0.74776-0.18317 0.74776-0.26105 0-0.0778 0.2734-0.26955 0.60755-0.42593 1.0468-0.48989 4.8723-0.18305 5.9512 0.47733 0.61483 0.37632 1.43 1.8749 1.766 3.2467 0.35754 1.4594 0.66483 2.0353 1.4064 2.6359 0.28695 0.23237 0.58092 0.69177 0.65328 1.0209 0.0723 0.32911 0.36623 0.89531 0.65304 1.2582 0.71105 0.89969 0.92402 1.8236 0.9269 4.0212 1e-3 1.0271 0.0901 2.4066 0.19706 3.0655 0.16565 1.0198 0.15054 1.2421-0.10161 1.494-0.26633 0.26606-2.1924 0.29586-19.124 0.29586-10.355 0-20.973-0.0457-23.594-0.1015zm12.148-42.196c0.10439 0 0.18969-0.093 0.18969-0.20673 0-0.11368-0.13049-0.15667-0.29012-0.0956-0.15956 0.0611-0.24231 0.0339-0.18389-0.0605 0.17517-0.28313-0.26913-0.43381-0.56234-0.19072-0.28461 0.23596-0.35504 0.86752-0.15485 1.3887 0.09485 0.24697 0.18393 0.19621 0.4646-0.26485 0.19098-0.31371 0.43259-0.57038 0.53692-0.57038zm-0.91719 0.0778c0.0093-0.21755 0.05326-0.2618 0.113-0.11288 0.05405 0.13481 0.04743 0.29587-0.01471 0.35794-0.06213 0.0621-0.10625-0.0483-0.09817-0.24511z" fill="navy"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

+1
Submodule themes/hermit-v2 added at a01afdd69a
Executable
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
pushd /home/daniel/local/Code/blog
docker compose up build && rsync -av --delete public/* bovender.de:/var/docker-data/nginx/www/bovender/
popd