From ee75979b5d94ae18f930ff91e5b2d51cd554b60d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 9 Mar 2026 22:45:54 +0200 Subject: Update content for html --- about/dtail.html | 15 +- about/index.html | 15 +- about/novels.html | 15 +- about/resources.html | 15 +- about/showcase.html | 15 +- gemfeed/2008-06-26-perl-poetry.html | 163 +- ...12-29-using-my-nokia-n95-for-fixing-my-mta.html | 15 +- gemfeed/2009-02-13-sgi-onyx-3200.html | 15 +- gemfeed/2010-04-09-standard-ml-and-haskell.html | 181 +- ...010-05-07-lazy-evaluation-with-standard-ml.html | 15 +- .../2010-05-09-the-fype-programming-language.html | 93 +- .../2011-05-07-perl-daemon-service-framework.html | 135 +- .../2014-03-24-the-fibonacci.pl.c-polyglot.html | 203 ++- ...2-05-run-debian-on-your-phone-with-debroid.html | 199 ++- gemfeed/2016-04-03-offsite-backup-with-zfs.html | 15 +- ...04-09-jails-and-zfs-on-freebsd-with-puppet.html | 15 +- .../2016-04-16-offsite-backup-with-zfs-part2.html | 15 +- ...inning-up-my-own-authoritative-dns-servers.html | 15 +- ...20-object-oriented-programming-with-ansi-c.html | 81 +- ...alistic-load-testing-with-ioriot-for-linux.html | 15 +- ...-22-dtail-the-distributed-log-tail-program.html | 15 +- gemfeed/2021-04-24-welcome-to-the-geminispace.html | 15 +- ...021-05-16-personal-bash-coding-style-guide.html | 291 ++-- ...5-gemtexter-one-bash-script-to-rule-it-all.html | 51 +- gemfeed/2021-07-04-the-well-grounded-rubyist.html | 15 +- ...-08-01-on-being-pedantic-about-open-source.html | 15 +- gemfeed/2021-09-12-keep-it-simple-and-stupid.html | 15 +- gemfeed/2021-10-22-defensive-devops.html | 15 +- gemfeed/2021-11-29-bash-golf-part-1.html | 15 +- ...-12-26-how-to-stay-sane-as-a-devops-person.html | 15 +- gemfeed/2022-01-01-bash-golf-part-2.html | 15 +- gemfeed/2022-01-23-welcome-to-the-foo.zone.html | 15 +- ...022-02-04-computer-operating-systems-i-use.html | 15 +- gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html | 15 +- gemfeed/2022-04-10-creative-universe.html | 15 +- .../2022-05-27-perl-is-still-a-great-choice.html | 15 +- gemfeed/2022-06-15-sweating-the-small-stuff.html | 15 +- ...22-07-30-lets-encrypt-with-openbsd-and-rex.html | 15 +- ...2-08-27-gemtexter-1.1.0-lets-gemtext-again.html | 49 +- gemfeed/2022-09-30-after-a-bad-nights-sleep.html | 15 +- .../2022-10-30-installing-dtail-on-openbsd.html | 15 +- ...-tried-emacs-but-i-switched-back-to-neovim.html | 23 +- ...22-12-24-ultrarelearning-java-my-takeaways.html | 15 +- gemfeed/2023-01-23-why-grapheneos-rox.html | 15 +- .../2023-02-26-how-to-shut-down-after-work.html | 15 +- ...-03-16-the-pragmatic-programmer-book-notes.html | 15 +- ...03-25-gemtexter-2.0.0-lets-gemtext-again-2.html | 33 +- ...4-01-never-split-the-difference-book-notes.html | 15 +- ...iling-guprecords:-uptime-records-with-raku.html | 17 +- ...3-05-06-the-obstacle-is-the-way-book-notes.html | 15 +- ...3-06-01-kiss-server-monitoring-with-gogios.html | 117 +- ...17-career-guide-and-soft-skills-book-notes.html | 15 +- ...07-21-gemtexter-2.1.0-lets-gemtext-again-3.html | 25 +- ...-08-18-site-reliability-engineering-part-1.html | 15 +- gemfeed/2023-09-25-dtail-usage-examples.html | 121 +- ...static-web-photo-albums-with-photoalbum.sh.html | 133 +- gemfeed/2023-11-11-mind-management-book-notes.html | 15 +- ...-11-19-site-reliability-engineering-part-2.html | 15 +- gemfeed/2023-12-10-bash-golf-part-3.html | 253 ++- ...-01-09-site-reliability-engineering-part-3.html | 15 +- .../2024-01-13-one-reason-why-i-love-openbsd.html | 25 +- ...4-02-04-from-babylon5.buetow.org-to-.cloud.html | 15 +- ...quickly-logging-ideas-programmed-in-golang.html | 15 +- ...-04-01-KISS-high-availability-with-OpenBSD.html | 215 ++- .../2024-05-01-slow-productivity-book-notes.html | 15 +- ...03-projects-i-currently-dont-have-time-for.html | 27 +- ...2024-06-23-terminal-multiplexing-with-tmux.html | 179 +- gemfeed/2024-07-05-random-weird-things.html | 227 ++- .../2024-07-07-the-stoic-challenge-book-notes.html | 15 +- .../2024-08-05-typing-127.1-words-per-minute.html | 15 +- gemfeed/2024-09-07-projects-i-support.html | 15 +- ...-09-07-site-reliability-engineering-part-4.html | 15 +- ...10-02-gemtexter-3.0.0-lets-gemtext-again-4.html | 17 +- gemfeed/2024-10-24-staff-engineer-book-notes.html | 15 +- ...4-11-17-f3s-kubernetes-with-freebsd-part-1.html | 15 +- ...4-12-03-f3s-kubernetes-with-freebsd-part-2.html | 291 ++-- gemfeed/2024-12-15-random-helix-themes.html | 125 +- ...-01-01-posts-from-october-to-december-2024.html | 15 +- .../2025-01-15-working-with-an-sre-interview.html | 15 +- ...5-02-01-f3s-kubernetes-with-freebsd-part-3.html | 281 ++- gemfeed/2025-02-08-random-weird-things-ii.html | 143 +- ...025-03-05-sharing-on-social-media-with-gos.html | 53 +- ...5-04-05-f3s-kubernetes-with-freebsd-part-4.html | 391 ++--- gemfeed/2025-04-19-when-book-notes.html | 15 +- ...rminal-multiplexing-with-tmux-fish-edition.html | 31 +- ...5-05-11-f3s-kubernetes-with-freebsd-part-5.html | 1161 ++++++------- ...6-07-a-monks-guide-to-happiness-book-notes.html | 15 +- gemfeed/2025-06-22-task-samurai.html | 15 +- ...2025-07-01-posts-from-january-to-june-2025.html | 15 +- ...5-07-14-f3s-kubernetes-with-freebsd-part-6.html | 1837 ++++++++++---------- .../2025-08-05-local-coding-llm-with-ollama.html | 199 ++- gemfeed/2025-08-15-random-weird-things-iii.html | 15 +- gemfeed/2025-09-14-bash-golf-part-4.html | 301 ++-- ...5-10-02-f3s-kubernetes-with-freebsd-part-7.html | 1061 ++++++----- ...y-takeaways-from-the-well-grounded-rubyist.html | 179 +- .../2025-11-02-perl-new-features-and-foostats.html | 171 +- ...1-02-the-courage-to-be-disliked-book-notes.html | 15 +- ...5-12-07-f3s-kubernetes-with-freebsd-part-8.html | 403 +++-- .../2025-12-24-x-rag-observability-hackathon.html | 57 +- ...6-01-01-cloudless-kobo-forma-with-koreader.html | 15 +- ...026-01-01-posts-from-july-to-december-2025.html | 15 +- .../2026-01-01-using-supernote-nomad-offline.html | 59 +- ...tmux-popup-editor-for-cursor-agent-prompts.html | 473 +++-- gemfeed/2026-02-22-my-desk-rack.html | 15 +- gemfeed/2026-03-01-loadbars-0.13.0-released.html | 15 +- ...-03-01-site-reliability-engineering-part-5.html | 15 +- ...3-02-rcm-ruby-configuration-management-dsl.html | 223 ++- gemfeed/DRAFT-distributed-systems-simulator.html | 15 +- .../DRAFT-f3s-kubernetes-with-freebsd-part-X.html | 341 ++-- gemfeed/DRAFT-ipv6test-deployment.html | 143 +- gemfeed/index.html | 15 +- heading.ttf | Bin 1854788 -> 142040 bytes index.html | 15 +- notes/97-things-every-sre-should-know.html | 15 +- notes/a-monks-guide-to-happiness.html | 15 +- notes/career-guide-and-soft-skills.html | 15 +- notes/eat-that-frog.html | 15 +- notes/fluent-forever.html | 15 +- notes/implementing-service-level-objectives.html | 15 +- notes/index.html | 15 +- notes/influence-wihout-authority.html | 15 +- notes/joy-on-demand.html | 15 +- notes/love-people-use-things.html | 15 +- notes/meditation-for-mortals.html | 15 +- notes/mental-combat.html | 15 +- notes/mind-management.html | 15 +- notes/never-split-the-difference.html | 15 +- notes/search-inside-yourself.html | 15 +- notes/site-reliability-engineering.html | 15 +- notes/slow-productivity.html | 15 +- notes/staff-engineer.html | 15 +- notes/the-courage-to-be-disliked.html | 15 +- notes/the-obstacle-is-the-way.html | 15 +- notes/the-power-of-neuroplasticity.html | 15 +- notes/the-pragmatic-programmer.html | 15 +- notes/the-science-of-living.html | 15 +- notes/the-stoic-challenge.html | 15 +- notes/when.html | 15 +- notes/yoga-nidra-made-easy.html | 15 +- site-mirrors.html | 15 +- stats.html | 15 +- style.css | 519 +----- testpage.html | 53 +- text.ttf | Bin 18056 -> 149120 bytes typewriter.ttf | Bin 151464 -> 0 bytes uptime-stats.html | 15 +- 146 files changed, 5826 insertions(+), 6967 deletions(-) delete mode 100644 typewriter.ttf diff --git a/about/dtail.html b/about/dtail.html index a128625d..46cb3811 100644 --- a/about/dtail.html +++ b/about/dtail.html @@ -2,17 +2,12 @@ - DTail -
-
-
-

Home | Markdown | Gemini

@@ -23,11 +18,11 @@
Back to the main site
diff --git a/about/index.html b/about/index.html index 484a9cc9..3bedd4b8 100644 --- a/about/index.html +++ b/about/index.html @@ -2,17 +2,12 @@ - About -
-
-
-

Home | Markdown | Gemini

@@ -52,11 +47,11 @@
Back to the main site
diff --git a/about/novels.html b/about/novels.html index 1f1430d6..31a7d114 100644 --- a/about/novels.html +++ b/about/novels.html @@ -2,17 +2,12 @@ - Novels -
-
-
-

Home | Markdown | Gemini

@@ -161,11 +156,11 @@ _-" . ' + . . ,//////0\ | /00HHHHHHHMMMMM
Go back
diff --git a/about/resources.html b/about/resources.html index 1338f66f..6e6a088a 100644 --- a/about/resources.html +++ b/about/resources.html @@ -2,17 +2,12 @@ - Formal education -
-
-
-

Home | Markdown | Gemini

@@ -287,11 +282,11 @@
Go back
diff --git a/about/showcase.html b/about/showcase.html index 65f49fcb..ac803651 100644 --- a/about/showcase.html +++ b/about/showcase.html @@ -2,17 +2,12 @@ - Project Showcase -
-
-
-

Home | Markdown | Gemini

@@ -1613,11 +1608,11 @@ View on Codeberg
View on GitHub
diff --git a/gemfeed/2008-06-26-perl-poetry.html b/gemfeed/2008-06-26-perl-poetry.html index a193f6ff..d54b7210 100644 --- a/gemfeed/2008-06-26-perl-poetry.html +++ b/gemfeed/2008-06-26-perl-poetry.html @@ -2,17 +2,12 @@ - Perl Poetry -
-
-
-

Home | Markdown | Gemini

@@ -64,44 +59,44 @@ _~~|~/_|_|__/|~~~~~~~ | / ~~~~~ | | ~~~~~~~~ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#!/usr/bin/perl
+
#!/usr/bin/perl
 
-# (C) 2006 by Paul C. Buetow
+# (C) 2006 by Paul C. Buetow
 
-goto library for study $math;
-BEGIN { s/earching/ books/ 
-and read $them, $at, $the } library:
+goto library for study $math;
+BEGIN { s/earching/ books/ 
+and read $them, $at, $the } library:
 
-our $topics, cos and tan, 
-require strict; import { of, tied $patience };
+our $topics, cos and tan, 
+require strict; import { of, tied $patience };
 
-do { int'egrate'; sub trade; };
-do { exp'onentize' and abs'olutize' };
-study and study and study and study;
+do { int'egrate'; sub trade; };
+do { exp'onentize' and abs'olutize' };
+study and study and study and study;
 
-foreach $topic ({of, math}) {
-you, m/ay /go, to, limits }
+foreach $topic ({of, math}) {
+you, m/ay /go, to, limits }
 
-do { not qw/erk / unless $success 
-and m/ove /o;$n and study };
+do { not qw/erk / unless $success 
+and m/ove /o;$n and study };
 
-do { int'egrate'; sub trade; };
-do { exp'onentize' and abs'olutize' };
-study and study and study and study;
+do { int'egrate'; sub trade; };
+do { exp'onentize' and abs'olutize' };
+study and study and study and study;
 
-grep /all/, exp'onents' and cos'inuses';
-/seek results/ for @all, log'4rithms';
+grep /all/, exp'onents' and cos'inuses';
+/seek results/ for @all, log'4rithms';
 
-'you' =~ m/ay /go, not home 
-unless each %book ne#ars
-$completion;
+'you' =~ m/ay /go, not home 
+unless each %book ne#ars
+$completion;
 
-do { int'egrate'; sub trade; };
-do { exp'onentize' and abs'olutize' };
+do { int'egrate'; sub trade; };
+do { exp'onentize' and abs'olutize' };
 
-#at
-home: //ig,'nore', time and sleep $very =~ s/tr/on/g;
-__END__
+#at
+home: //ig,'nore', time and sleep $very =~ s/tr/on/g;
+__END__
 
 

@@ -111,47 +106,47 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#!/usr/bin/perl
+
#!/usr/bin/perl
 
-# (C) 2006 by Paul C. Buetow
+# (C) 2006 by Paul C. Buetow
 
-Christmas:{time;#!!!
+Christmas:{time;#!!!
 
-Children: do tell $wishes;
+Children: do tell $wishes;
 
-Santa: for $each (@children) { 
-BEGIN { read $each, $their, wishes and study them; use Memoize#ing
+Santa: for $each (@children) { 
+BEGIN { read $each, $their, wishes and study them; use Memoize#ing
 
-} use constant gift, 'wrapping'; 
-package Gifts; pack $each, gift and bless $each and goto deliver
-or do import if not local $available,!!! HO, HO, HO;
+} use constant gift, 'wrapping'; 
+package Gifts; pack $each, gift and bless $each and goto deliver
+or do import if not local $available,!!! HO, HO, HO;
 
-redo Santa, pipe $gifts, to_childs;
-redo Santa and do return if last one, is, delivered; 
+redo Santa, pipe $gifts, to_childs;
+redo Santa and do return if last one, is, delivered; 
 
-deliver: gift and require diagnostics if our $gifts ,not break;
-do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones;
-The_children: sleep and wait for (each %gift) and try { to => untie $gifts };
+deliver: gift and require diagnostics if our $gifts ,not break;
+do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones;
+The_children: sleep and wait for (each %gift) and try { to => untie $gifts };
 
-redo Santa, pipe $gifts, to_childs;
-redo Santa and do return if last one, is, delivered; 
+redo Santa, pipe $gifts, to_childs;
+redo Santa and do return if last one, is, delivered; 
 
-The_christmas_tree: formline s/ /childrens/, $gifts;
-alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME};  
-write <<EMail
- to the parents to buy a new christmas tree!!!!111
- and send the
-EMail
-;wait and redo deliver until defined local $tree;
+The_christmas_tree: formline s/ /childrens/, $gifts;
+alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME};  
+write <<EMail
+ to the parents to buy a new christmas tree!!!!111
+ and send the
+EMail
+;wait and redo deliver until defined local $tree;
 
-redo Santa, pipe $gifts, to_childs;
-redo Santa and do return if last one, is, delivered ;}
+redo Santa, pipe $gifts, to_childs;
+redo Santa and do return if last one, is, delivered ;}
 
-END {} our $mission and do sleep until next Christmas ;}
+END {} our $mission and do sleep until next Christmas ;}
 
-__END__
+__END__
 
-This is perl, v5.8.8 built for i386-freebsd-64int
+This is perl, v5.8.8 built for i386-freebsd-64int
 

shopping.pl


@@ -160,35 +155,35 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#!/usr/bin/perl
+
#!/usr/bin/perl
 
-# (C) 2007 by Paul C. Buetow
+# (C) 2007 by Paul C. Buetow
 
-BEGIN{} goto mall for $shopping; 
+BEGIN{} goto mall for $shopping; 
 
-m/y/; mall: seek$s, cool products(), { to => $sell };
-for $their (@business) { to:; earn:; a:; lot:; of:; money: }
+m/y/; mall: seek$s, cool products(), { to => $sell };
+for $their (@business) { to:; earn:; a:; lot:; of:; money: }
 
-do not goto home and exit mall if exists $new{product};
-foreach $of (q(uality rich products)){} package products; 
+do not goto home and exit mall if exists $new{product};
+foreach $of (q(uality rich products)){} package products; 
 
-our $news; do tell cool products() and do{ sub#tract
-cool{ $products and shift @the, @bad, @ones;
+our $news; do tell cool products() and do{ sub#tract
+cool{ $products and shift @the, @bad, @ones;
 
-do bless [q(uality)], $products 
-and return not undef $stuff if not (local $available) }};
+do bless [q(uality)], $products 
+and return not undef $stuff if not (local $available) }};
 
-do { study and study and study for cool products() }
-and do { seek $all, cool products(), { to => $buy } };
+do { study and study and study for cool products() }
+and do { seek $all, cool products(), { to => $buy } };
 
-do { write $them, $down } and do { order: foreach (@case) { package s } };
-goto home if not exists $more{money} or die q(uerying) ;for( @money){};
+do { write $them, $down } and do { order: foreach (@case) { package s } };
+goto home if not exists $more{money} or die q(uerying) ;for( @money){};
 
-at:;home: do { END{} and:; rest:; a:; bit: exit $shopping } 
-and sleep until unpack$ing, cool products();
+at:;home: do { END{} and:; rest:; a:; bit: exit $shopping } 
+and sleep until unpack$ing, cool products();
 
-__END__
-This is perl, v5.8.8 built for i386-freebsd-64int
+__END__
+This is perl, v5.8.8 built for i386-freebsd-64int
 

More...


@@ -208,11 +203,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2008-12-29-using-my-nokia-n95-for-fixing-my-mta.html b/gemfeed/2008-12-29-using-my-nokia-n95-for-fixing-my-mta.html index b26eda5c..0519118b 100644 --- a/gemfeed/2008-12-29-using-my-nokia-n95-for-fixing-my-mta.html +++ b/gemfeed/2008-12-29-using-my-nokia-n95-for-fixing-my-mta.html @@ -2,17 +2,12 @@ - Using my Nokia N95 for fixing my MTA -
-
-
-

Home | Markdown | Gemini

@@ -58,11 +53,11 @@ _jgs_\|//_\\|///_\V/_\|//__
Back to the main site
diff --git a/gemfeed/2009-02-13-sgi-onyx-3200.html b/gemfeed/2009-02-13-sgi-onyx-3200.html index 135d25e9..30be2200 100644 --- a/gemfeed/2009-02-13-sgi-onyx-3200.html +++ b/gemfeed/2009-02-13-sgi-onyx-3200.html @@ -2,17 +2,12 @@ - SGI Onyx 3200 -
-
-
-

Home | Markdown | Gemini

@@ -86,11 +81,11 @@ USB Human Interface Device: device id 0 type mouse
Back to the main site
diff --git a/gemfeed/2010-04-09-standard-ml-and-haskell.html b/gemfeed/2010-04-09-standard-ml-and-haskell.html index e098b9a4..7fc14e02 100644 --- a/gemfeed/2010-04-09-standard-ml-and-haskell.html +++ b/gemfeed/2010-04-09-standard-ml-and-haskell.html @@ -2,17 +2,12 @@ - Standard ML and Haskell -
-
-
-

Home | Markdown | Gemini

@@ -45,10 +40,10 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
datatype ’a multi
-	= EMPTY
-	| ELEM of ’a
-	| UNION of ’a multi * ’a multi
+
datatype ’a multi
+	= EMPTY
+	| ELEM of ’a
+	| UNION of ’a multi * ’a multi
 

Haskell:
@@ -57,11 +52,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
data (Eq a) => Multi a
-    = Empty
-    | Elem a
-    | Union (Multi a) (Multi a)
-    deriving Show
+
data (Eq a) => Multi a
+    = Empty
+    | Elem a
+    | Union (Multi a) (Multi a)
+    deriving Show
 

Processing a multi


@@ -72,12 +67,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
fun number (EMPTY) _ = 0
-	| number (ELEM x) w = if x = w then 1 else 0
-	| number (UNION (x,y)) w = (number x w) + (number y w)
-fun test_number w = number (UNION (EMPTY, \
-    UNION (ELEM 4, UNION (ELEM 6, \
-    UNION (UNION (ELEM 4, ELEM 4), EMPTY))))) w 
+
fun number (EMPTY) _ = 0
+	| number (ELEM x) w = if x = w then 1 else 0
+	| number (UNION (x,y)) w = (number x w) + (number y w)
+fun test_number w = number (UNION (EMPTY, \
+    UNION (ELEM 4, UNION (ELEM 6, \
+    UNION (UNION (ELEM 4, ELEM 4), EMPTY))))) w 
 

Haskell:
@@ -86,11 +81,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
number Empty _ = 0
-number (Elem x) w = if x == w then 1 else 0
-test_number w = number (Union Empty \
-    (Union (Elem 4) (Union (Elem 6) \
-    (Union (Union (Elem 4) (Elem 4)) Empty)))) w
+
number Empty _ = 0
+number (Elem x) w = if x == w then 1 else 0
+test_number w = number (Union Empty \
+    (Union (Elem 4) (Union (Elem 6) \
+    (Union (Union (Elem 4) (Elem 4)) Empty)))) w
 

Simplify function


@@ -101,19 +96,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
fun simplify (UNION (x,y)) =
-    let fun is_empty (EMPTY) = true | is_empty _ = false
-        val x’ = simplify x
-        val y’ = simplify y
-    in if (is_empty x’) andalso (is_empty y’)
-            then EMPTY
-       else if (is_empty x’)
-            then y’
-       else if (is_empty y’)
-            then x’
-       else UNION (x’, y’)
-    end
-  | simplify x = x
+
fun simplify (UNION (x,y)) =
+    let fun is_empty (EMPTY) = true | is_empty _ = false
+        val x’ = simplify x
+        val y’ = simplify y
+    in if (is_empty x’) andalso (is_empty y’)
+            then EMPTY
+       else if (is_empty x’)
+            then y’
+       else if (is_empty y’)
+            then x’
+       else UNION (x’, y’)
+    end
+  | simplify x = x
 

Haskell:
@@ -122,17 +117,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
simplify (Union x y)
-    | (isEmpty x’) && (isEmpty y’) = Empty
-    | isEmpty x’ = y’
-    | isEmpty y’ = x’
-    | otherwise = Union x’ y’
-    where
-        isEmpty Empty = True
-        isEmpty _ = False
-        x’ = simplify x
-        y’ = simplify y
-simplify x = x
+
simplify (Union x y)
+    | (isEmpty x’) && (isEmpty y’) = Empty
+    | isEmpty x’ = y’
+    | isEmpty y’ = x’
+    | otherwise = Union x’ y’
+    where
+        isEmpty Empty = True
+        isEmpty _ = False
+        x’ = simplify x
+        y’ = simplify y
+simplify x = x
 

Delete all


@@ -143,12 +138,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
fun delete_all m w =
-    let fun delete_all’ (ELEM x) = if x = w then EMPTY else ELEM x
-          | delete_all’ (UNION (x,y)) = UNION (delete_all’ x, delete_all’ y)
-          | delete_all’ x = x
-    in simplify (delete_all’ m)
-    end
+
fun delete_all m w =
+    let fun delete_all’ (ELEM x) = if x = w then EMPTY else ELEM x
+          | delete_all’ (UNION (x,y)) = UNION (delete_all’ x, delete_all’ y)
+          | delete_all’ x = x
+    in simplify (delete_all’ m)
+    end
 

Haskell:
@@ -157,11 +152,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
delete_all m w = simplify (delete_all’ m)
-    where
-        delete_all’ (Elem x) = if x == w then Empty else Elem x
-        delete_all’ (Union x y) = Union (delete_all’ x) (delete_all’ y)
-        delete_all’ x = x
+
delete_all m w = simplify (delete_all’ m)
+    where
+        delete_all’ (Elem x) = if x == w then Empty else Elem x
+        delete_all’ (Union x y) = Union (delete_all’ x) (delete_all’ y)
+        delete_all’ x = x
 

Delete one


@@ -172,21 +167,21 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
fun delete_one m w =
-    let fun delete_one’ (UNION (x,y)) =
-            let val (x’, deleted) = delete_one’ x
-                in if deleted
-                   then (UNION (x’, y), deleted)
-                   else let val (y’, deleted) = delete_one’ y
-                       in (UNION (x, y’), deleted)
-                   end
-                end
-          | delete_one’ (ELEM x) =
-            if x = w then (EMPTY, true) else (ELEM x, false)
-          | delete_one’ x = (x, false)
-            val (m’, _) = delete_one’ m
-        in simplify m’
-    end
+
fun delete_one m w =
+    let fun delete_one’ (UNION (x,y)) =
+            let val (x’, deleted) = delete_one’ x
+                in if deleted
+                   then (UNION (x’, y), deleted)
+                   else let val (y’, deleted) = delete_one’ y
+                       in (UNION (x, y’), deleted)
+                   end
+                end
+          | delete_one’ (ELEM x) =
+            if x = w then (EMPTY, true) else (ELEM x, false)
+          | delete_one’ x = (x, false)
+            val (m’, _) = delete_one’ m
+        in simplify m’
+    end
 

Haskell:
@@ -195,19 +190,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
delete_one m w = do
-    let (m’, _) = delete_one’ m
-    simplify m’
-    where
-        delete_one’ (Union x y) =
-            let (x’, deleted) = delete_one’ x
-            in if deleted
-                then (Union x’ y, deleted)
-                else let (y’, deleted) = delete_one’ y
-                    in (Union x y’, deleted)
-        delete_one’ (Elem x) =
-            if x == w then (Empty, True) else (Elem x, False)
-        delete_one’ x = (x, False)
+
delete_one m w = do
+    let (m’, _) = delete_one’ m
+    simplify m’
+    where
+        delete_one’ (Union x y) =
+            let (x’, deleted) = delete_one’ x
+            in if deleted
+                then (Union x’ y, deleted)
+                else let (y’, deleted) = delete_one’ y
+                    in (Union x y’, deleted)
+        delete_one’ (Elem x) =
+            if x == w then (Empty, True) else (Elem x, False)
+        delete_one’ x = (x, False)
 

Higher-order functions


@@ -232,11 +227,11 @@ my_filter f l = foldr (make_filter_fn f) [] l
Back to the main site
diff --git a/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html b/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html index 4e386c9a..641d8c0c 100644 --- a/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html +++ b/gemfeed/2010-05-07-lazy-evaluation-with-standard-ml.html @@ -2,17 +2,12 @@ - Lazy Evaluation with Standard ML -
-
-
-

Home | Markdown | Gemini

@@ -118,11 +113,11 @@ first 10 nat_pairs_not_null
Back to the main site
diff --git a/gemfeed/2010-05-09-the-fype-programming-language.html b/gemfeed/2010-05-09-the-fype-programming-language.html index 8b19301c..372c9d53 100644 --- a/gemfeed/2010-05-09-the-fype-programming-language.html +++ b/gemfeed/2010-05-09-the-fype-programming-language.html @@ -2,17 +2,12 @@ - The Fype Programming Language -
-
-
-

Home | Markdown | Gemini

@@ -74,12 +69,12 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
typedef struct {
-   Tupel *p_tupel_argv; // Contains command line options
-   List *p_list_token; // Initial list of token
-   Hash *p_hash_syms; // Symbol table
-   char *c_basename;
-} Fype;
+
typedef struct {
+   Tupel *p_tupel_argv; // Contains command line options
+   List *p_list_token; // Initial list of token
+   Hash *p_hash_syms; // Symbol table
+   char *c_basename;
+} Fype;
 

And here is a snippet from the primary Fype "class implementation":
@@ -88,53 +83,53 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
Fype*
-fype_new() {
-   Fype *p_fype = malloc(sizeof(Fype));
+
Fype*
+fype_new() {
+   Fype *p_fype = malloc(sizeof(Fype));
 
-   p_fype->p_hash_syms = hash_new(512);
-   p_fype->p_list_token = list_new();
-   p_fype->p_tupel_argv = tupel_new();
-   p_fype->c_basename = NULL;
+   p_fype->p_hash_syms = hash_new(512);
+   p_fype->p_list_token = list_new();
+   p_fype->p_tupel_argv = tupel_new();
+   p_fype->c_basename = NULL;
 
-   garbage_init();
+   garbage_init();
 
-   return (p_fype);
-}
+   return (p_fype);
+}
 
-void
-fype_delete(Fype *p_fype) {
-   argv_tupel_delete(p_fype->p_tupel_argv);
+void
+fype_delete(Fype *p_fype) {
+   argv_tupel_delete(p_fype->p_tupel_argv);
 
-   hash_iterate(p_fype->p_hash_syms, symbol_cleanup_hash_syms_cb);
-   hash_delete(p_fype->p_hash_syms);
+   hash_iterate(p_fype->p_hash_syms, symbol_cleanup_hash_syms_cb);
+   hash_delete(p_fype->p_hash_syms);
 
-   list_iterate(p_fype->p_list_token, token_ref_down_cb);
-   list_delete(p_fype->p_list_token);
+   list_iterate(p_fype->p_list_token, token_ref_down_cb);
+   list_delete(p_fype->p_list_token);
 
-   if (p_fype->c_basename)
-      free(p_fype->c_basename);
+   if (p_fype->c_basename)
+      free(p_fype->c_basename);
 
-   garbage_destroy();
-}
+   garbage_destroy();
+}
 
-int
-fype_run(int i_argc, char **pc_argv) {
-   Fype *p_fype = fype_new();
+int
+fype_run(int i_argc, char **pc_argv) {
+   Fype *p_fype = fype_new();
 
-   // argv: Maintains command line options
-   argv_run(p_fype, i_argc, pc_argv);
+   // argv: Maintains command line options
+   argv_run(p_fype, i_argc, pc_argv);
 
-   // scanner: Creates a list of token
-   scanner_run(p_fype);
+   // scanner: Creates a list of token
+   scanner_run(p_fype);
 
-   // interpret: Interpret the list of token
-   interpret_run(p_fype);
+   // interpret: Interpret the list of token
+   interpret_run(p_fype);
 
-   fype_delete(p_fype);
+   fype_delete(p_fype);
 
-   return (0);
-}
+   return (0);
+}
 

Data types


@@ -563,11 +558,11 @@ BB
Back to the main site
diff --git a/gemfeed/2011-05-07-perl-daemon-service-framework.html b/gemfeed/2011-05-07-perl-daemon-service-framework.html index ee91bb3a..d68f12ca 100644 --- a/gemfeed/2011-05-07-perl-daemon-service-framework.html +++ b/gemfeed/2011-05-07-perl-daemon-service-framework.html @@ -2,17 +2,12 @@ - Perl Daemon (Service Framework) -
-
-
-

Home | Markdown | Gemini

@@ -63,14 +58,14 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
# Starting
- ./bin/perldaemon start (or shortcut ./control start)
+
# Starting
+ ./bin/perldaemon start (or shortcut ./control start)
 
-# Stopping
- ./bin/perldaemon stop (or shortcut ./control stop)
+# Stopping
+ ./bin/perldaemon stop (or shortcut ./control stop)
 
-# Alternatively: Starting in foreground 
-./bin/perldaemon start daemon.daemonize=no (or shortcut ./control foreground)
+# Alternatively: Starting in foreground 
+./bin/perldaemon start daemon.daemonize=no (or shortcut ./control foreground)
 

To stop a daemon from running in foreground mode, "Ctrl+C" must be hit. To see more available startup options run "./control" without any argument.
@@ -83,30 +78,30 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
pb@titania:~/svn/utils/perldaemon/trunk$ ./control keys
-# Path to the logfile
-daemon.logfile=./log/perldaemon.log
+
pb@titania:~/svn/utils/perldaemon/trunk$ ./control keys
+# Path to the logfile
+daemon.logfile=./log/perldaemon.log
 
-# The amount of seconds until the next event look takes place
-daemon.loopinterval=1
+# The amount of seconds until the next event look takes place
+daemon.loopinterval=1
 
-# Path to the modules dir
-daemon.modules.dir=./lib/PerlDaemonModules
+# Path to the modules dir
+daemon.modules.dir=./lib/PerlDaemonModules
 
-# Specifies either the daemon should run in daemon or foreground mode
-daemon.daemonize=yes
+# Specifies either the daemon should run in daemon or foreground mode
+daemon.daemonize=yes
 
-# Path to the pidfile
-daemon.pidfile=./run/perldaemon.pid
+# Path to the pidfile
+daemon.pidfile=./run/perldaemon.pid
 
-# Each module should run every run interval seconds
-daemon.modules.runinterval=3
+# Each module should run every run interval seconds
+daemon.modules.runinterval=3
 
-# Path to the alive file (is touched every loop interval seconds, usable for monitoring)
-daemon.alivefile=./run/perldaemon.alive
+# Path to the alive file (is touched every loop interval seconds, usable for monitoring)
+daemon.alivefile=./run/perldaemon.alive
 
-# Specifies the working directory
-daemon.wd=./
+# Specifies the working directory
+daemon.wd=./
 

Example


@@ -117,17 +112,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
$ ./control keys | grep daemon.loopinterval
-daemon.loopinterval=1
-$ ./control keys daemon.loopinterval=10 | grep daemon.loopinterval
-daemon.loopinterval=10
-$ ./control start daemon.loopinterval=10; sleep 10; tail -n 2 log/perldaemon.log
-Starting daemon now...
-Mon Jun 13 11:29:27 2011 (PID 2838): Triggering PerlDaemonModules::ExampleModule 
-(last triggered before 10.002106s; carry: 7.002106s; wanted interval: 3s)
-Mon Jun 13 11:29:27 2011 (PID 2838): ExampleModule Test 2
-$ ./control stop
-Stopping daemon now...
+
$ ./control keys | grep daemon.loopinterval
+daemon.loopinterval=1
+$ ./control keys daemon.loopinterval=10 | grep daemon.loopinterval
+daemon.loopinterval=10
+$ ./control start daemon.loopinterval=10; sleep 10; tail -n 2 log/perldaemon.log
+Starting daemon now...
+Mon Jun 13 11:29:27 2011 (PID 2838): Triggering PerlDaemonModules::ExampleModule 
+(last triggered before 10.002106s; carry: 7.002106s; wanted interval: 3s)
+Mon Jun 13 11:29:27 2011 (PID 2838): ExampleModule Test 2
+$ ./control stop
+Stopping daemon now...
 

If you want to change that property forever, either edit perldaemon.conf or do this:
@@ -136,7 +131,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
$ ./control keys daemon.loopinterval=10 > new.conf; mv new.conf conf/perldaemon.conf
+
$ ./control keys daemon.loopinterval=10 > new.conf; mv new.conf conf/perldaemon.conf
 

HiRes event loop


@@ -153,35 +148,35 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
package PerlDaemonModules::ExampleModule;
+
package PerlDaemonModules::ExampleModule;
 
-use strict;
-use warnings;
+use strict;
+use warnings;
 
-sub new ($$$) {
-  my ($class, $conf) = @_;
+sub new ($$$) {
+  my ($class, $conf) = @_;
 
-  my $self = bless { conf => $conf }, $class;
+  my $self = bless { conf => $conf }, $class;
 
-  # Store some private module stuff
-  $self->{counter} = 0;
+  # Store some private module stuff
+  $self->{counter} = 0;
 
-  return $self;
-}
+  return $self;
+}
 
-# Runs periodically in a loop (set interval in perldaemon.conf)
-sub do ($) {
-  my $self = shift;
-  my $conf = $self->{conf};
-  my $logger = $conf->{logger};
+# Runs periodically in a loop (set interval in perldaemon.conf)
+sub do ($) {
+  my $self = shift;
+  my $conf = $self->{conf};
+  my $logger = $conf->{logger};
 
-  # Calculate some private module stuff
-  my $count = ++$self->{counter};
+  # Calculate some private module stuff
+  my $count = ++$self->{counter};
 
-  $logger->logmsg("ExampleModule Test $count");
-}
+  $logger->logmsg("ExampleModule Test $count");
+}
 
-1;
+1;
 

Your own module


@@ -192,11 +187,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
 cd ./lib/PerlDaemonModules/
- cp ExampleModule.pm YourModule.pm
- vi YourModule.pm
- cd -
- ./bin/perldaemon restart (or shortcurt ./control restart)
+
 cd ./lib/PerlDaemonModules/
+ cp ExampleModule.pm YourModule.pm
+ vi YourModule.pm
+ cd -
+ ./bin/perldaemon restart (or shortcurt ./control restart)
 

Now watch ./log/perldaemon.log closely. It is a good practice to test your modules in 'foreground mode' (see above how to do that).
@@ -220,11 +215,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html b/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html index 2f8d5b8c..2e6e75fd 100644 --- a/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html +++ b/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.html @@ -2,17 +2,12 @@ - The fibonacci.pl.raku.c Polyglot -
-
-
-

Home | Markdown | Gemini

@@ -32,47 +27,47 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#include <stdio.h>
+
#include <stdio.h>
 
-#define $arg function_argument
-#define my int
-#define sub int
-#define BEGIN int main(void)
+#define $arg function_argument
+#define my int
+#define sub int
+#define BEGIN int main(void)
 
-my $arg;
+my $arg;
 
-sub hello() {
-    printf("Hello, welcome to the Fibonacci Numbers!\n");
-    printf("This program is all, valid C and C++ and Perl and Raku code!\n");
-    printf("It calculates all fibonacci numbers from 0 to 9!\n\n");
-    return 0;
-}
+sub hello() {
+    printf("Hello, welcome to the Fibonacci Numbers!\n");
+    printf("This program is all, valid C and C++ and Perl and Raku code!\n");
+    printf("It calculates all fibonacci numbers from 0 to 9!\n\n");
+    return 0;
+}
 
-sub fibonacci() {
-    my $n = $arg;
+sub fibonacci() {
+    my $n = $arg;
 
-    if ($n < 2) {
-        return $n;
-    }
+    if ($n < 2) {
+        return $n;
+    }
 
-    $arg = $n - 1;
-    my $fib1 = fibonacci();
-    $arg = $n - 2;
-    my $fib2 = fibonacci();
+    $arg = $n - 1;
+    my $fib1 = fibonacci();
+    $arg = $n - 2;
+    my $fib2 = fibonacci();
 
-    return $fib1 + $fib2;
-}
+    return $fib1 + $fib2;
+}
 
-BEGIN {
-    hello();
-    my $i = 0;
+BEGIN {
+    hello();
+    my $i = 0;
 
-    while ($i <= 10) {
-        $arg = $i;
-        printf("fib(%d) = %d\n", $i, fibonacci());
-        $i++;
-    }
-}
+    while ($i <= 10) {
+        $arg = $i;
+        printf("fib(%d) = %d\n", $i, fibonacci());
+        $i++;
+    }
+}
 

You can find the full source code at GitHub:
@@ -85,41 +80,41 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
% gcc fibonacci.pl.raku.c -o fibonacci
-% ./fibonacci
-Hello, welcome to the Fibonacci Numbers!
-This program is all, valid C and C++ and Perl and Raku code!
-It calculates all fibonacci numbers from 0 to 9!
+
% gcc fibonacci.pl.raku.c -o fibonacci
+% ./fibonacci
+Hello, welcome to the Fibonacci Numbers!
+This program is all, valid C and C++ and Perl and Raku code!
+It calculates all fibonacci numbers from 0 to 9!
 
-fib(0) = 0
-fib(1) = 1
-fib(2) = 1
-fib(3) = 2
-fib(4) = 3
-fib(5) = 5
-fib(6) = 8
-fib(7) = 13
-fib(8) = 21
-fib(9) = 34
-fib(10) = 55
+fib(0) = 0
+fib(1) = 1
+fib(2) = 1
+fib(3) = 2
+fib(4) = 3
+fib(5) = 5
+fib(6) = 8
+fib(7) = 13
+fib(8) = 21
+fib(9) = 34
+fib(10) = 55
 
-% g++ fibonacci.pl.raku.c -o fibonacci
-% ./fibonacci
-Hello, welcome to the Fibonacci Numbers!
-This program is all, valid C and C++ and Perl and Raku code!
-It calculates all fibonacci numbers from 0 to 9!
+% g++ fibonacci.pl.raku.c -o fibonacci
+% ./fibonacci
+Hello, welcome to the Fibonacci Numbers!
+This program is all, valid C and C++ and Perl and Raku code!
+It calculates all fibonacci numbers from 0 to 9!
 
-fib(0) = 0
-fib(1) = 1
-fib(2) = 1
-fib(3) = 2
-fib(4) = 3
-fib(5) = 5
-fib(6) = 8
-fib(7) = 13
-fib(8) = 21
-fib(9) = 34
-fib(10) = 55
+fib(0) = 0
+fib(1) = 1
+fib(2) = 1
+fib(3) = 2
+fib(4) = 3
+fib(5) = 5
+fib(6) = 8
+fib(7) = 13
+fib(8) = 21
+fib(9) = 34
+fib(10) = 55
 

Let's run it with Perl and Raku


@@ -128,39 +123,39 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
% perl fibonacci.pl.raku.c
-Hello, welcome to the Fibonacci Numbers!
-This program is all, valid C and C++ and Perl and Raku code!
-It calculates all fibonacci numbers from 0 to 9!
+
% perl fibonacci.pl.raku.c
+Hello, welcome to the Fibonacci Numbers!
+This program is all, valid C and C++ and Perl and Raku code!
+It calculates all fibonacci numbers from 0 to 9!
 
-fib(0) = 0
-fib(1) = 1
-fib(2) = 1
-fib(3) = 2
-fib(4) = 3
-fib(5) = 5
-fib(6) = 8
-fib(7) = 13
-fib(8) = 21
-fib(9) = 34
-fib(10) = 55
+fib(0) = 0
+fib(1) = 1
+fib(2) = 1
+fib(3) = 2
+fib(4) = 3
+fib(5) = 5
+fib(6) = 8
+fib(7) = 13
+fib(8) = 21
+fib(9) = 34
+fib(10) = 55
 
-% raku fibonacci.pl.raku.c
-Hello, welcome to the Fibonacci Numbers!
-This program is all, valid C and C++ and Perl and Raku code!
-It calculates all fibonacci numbers from 0 to 9!
+% raku fibonacci.pl.raku.c
+Hello, welcome to the Fibonacci Numbers!
+This program is all, valid C and C++ and Perl and Raku code!
+It calculates all fibonacci numbers from 0 to 9!
 
-fib(0) = 0
-fib(1) = 1
-fib(2) = 1
-fib(3) = 2
-fib(4) = 3
-fib(5) = 5
-fib(6) = 8
-fib(7) = 13
-fib(8) = 21
-fib(9) = 34
-fib(10) = 55
+fib(0) = 0
+fib(1) = 1
+fib(2) = 1
+fib(3) = 2
+fib(4) = 3
+fib(5) = 5
+fib(6) = 8
+fib(7) = 13
+fib(8) = 21
+fib(9) = 34
+fib(10) = 55
 

It's entertaining to play with :-).
@@ -169,11 +164,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html b/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html index 04f3e32c..9d537d43 100644 --- a/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html +++ b/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.html @@ -2,17 +2,12 @@ - Run Debian on your phone with Debroid -
-
-
-

Home | Markdown | Gemini

@@ -61,24 +56,24 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
sudo dnf install debootstrap
-# 5g
-dd if=/dev/zero of=jessie.img bs=$[ 1024 * 1024 ] \
-  count=$[ 1024 * 5 ]
+
sudo dnf install debootstrap
+# 5g
+dd if=/dev/zero of=jessie.img bs=$[ 1024 * 1024 ] \
+  count=$[ 1024 * 5 ]
 
-# Show used loop devices
-sudo losetup -f
-# Store the next free one to $loop
-loop=loopN
-sudo losetup /dev/$loop jessie.img
+# Show used loop devices
+sudo losetup -f
+# Store the next free one to $loop
+loop=loopN
+sudo losetup /dev/$loop jessie.img
 
-mkdir jessie
-sudo mkfs.ext4 /dev/$loop
-sudo mount /dev/$loop jessie
-sudo debootstrap --foreign --variant=minbase \
-  --arch armel jessie jessie/ \
-  http://http.debian.net/debian
-sudo umount jessie
+mkdir jessie
+sudo mkfs.ext4 /dev/$loop
+sudo mount /dev/$loop jessie
+sudo debootstrap --foreign --variant=minbase \
+  --arch armel jessie jessie/ \
+  http://http.debian.net/debian
+sudo umount jessie
 

Copy Debian image to the phone


@@ -89,42 +84,42 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
adb root && adb wait-for-device && adb shell
-mkdir -p /storage/sdcard1/Linux/jessie
-exit
+
adb root && adb wait-for-device && adb shell
+mkdir -p /storage/sdcard1/Linux/jessie
+exit
 
-# Sparse image problem, may be too big for copying otherwise
-gzip jessie.img
-# Copy over
-adb push jessie.img.gz /storage/sdcard1/Linux/jessie.img.gz
-adb shell
-cd /storage/sdcard1/Linux
-gunzip jessie.img.gz
+# Sparse image problem, may be too big for copying otherwise
+gzip jessie.img
+# Copy over
+adb push jessie.img.gz /storage/sdcard1/Linux/jessie.img.gz
+adb shell
+cd /storage/sdcard1/Linux
+gunzip jessie.img.gz
 
-# Show used loop devices
-losetup -f
-# Store the next free one to $loop
-loop=loopN
+# Show used loop devices
+losetup -f
+# Store the next free one to $loop
+loop=loopN
 
-# Use the next free one (replace the loop number)
-losetup /dev/block/$loop $(pwd)/jessie.img
-mount -t ext4 /dev/block/$loop $(pwd)/jessie
+# Use the next free one (replace the loop number)
+losetup /dev/block/$loop $(pwd)/jessie.img
+mount -t ext4 /dev/block/$loop $(pwd)/jessie
 
-# Bind-Mound proc, dev, sys`
-busybox mount --bind /proc $(pwd)/jessie/proc
-busybox mount --bind /dev $(pwd)/jessie/dev
-busybox mount --bind /dev/pts $(pwd)/jessie/dev/pts
-busybox mount --bind /sys $(pwd)/jessie/sys
+# Bind-Mound proc, dev, sys`
+busybox mount --bind /proc $(pwd)/jessie/proc
+busybox mount --bind /dev $(pwd)/jessie/dev
+busybox mount --bind /dev/pts $(pwd)/jessie/dev/pts
+busybox mount --bind /sys $(pwd)/jessie/sys
 
-# Bind-Mound the rest of Android
-mkdir -p $(pwd)/jessie/storage/sdcard{0,1}
-busybox mount --bind /storage/emulated \
-  $(pwd)/jessie/storage/sdcard0
-busybox mount --bind /storage/sdcard1 \
-  $(pwd)/jessie/storage/sdcard1
+# Bind-Mound the rest of Android
+mkdir -p $(pwd)/jessie/storage/sdcard{0,1}
+busybox mount --bind /storage/emulated \
+  $(pwd)/jessie/storage/sdcard0
+busybox mount --bind /storage/sdcard1 \
+  $(pwd)/jessie/storage/sdcard1
 
-# Check mounts
-mount | grep jessie
+# Check mounts
+mount | grep jessie
 

Second debootstrap stage


@@ -135,11 +130,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
chroot $(pwd)/jessie /bin/bash -l
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
-/debootstrap/debootstrap --second-stage
-exit # Leave chroot
-exit # Leave adb shell
+
chroot $(pwd)/jessie /bin/bash -l
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
+/debootstrap/debootstrap --second-stage
+exit # Leave chroot
+exit # Leave adb shell
 

Setup of various scripts


@@ -150,36 +145,36 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
# Install script jessie.sh
-adb push storage/sdcard1/Linux/jessie.sh /storage/sdcard/Linux/jessie.sh
-adb shell
-cd /storage/sdcard1/Linux
-sh jessie.sh enter
+
# Install script jessie.sh
+adb push storage/sdcard1/Linux/jessie.sh /storage/sdcard/Linux/jessie.sh
+adb shell
+cd /storage/sdcard1/Linux
+sh jessie.sh enter
 
-# Bashrc
-cat <<END >~/.bashrc
-export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
-export EDITOR=vim
-hostname $(cat /etc/hostname)
-END
+# Bashrc
+cat <<END >~/.bashrc
+export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
+export EDITOR=vim
+hostname $(cat /etc/hostname)
+END
 
-# Fixing an error message while loading the profile
-sed -i s#id#/usr/bin/id# /etc/profile
+# Fixing an error message while loading the profile
+sed -i s#id#/usr/bin/id# /etc/profile
 
-# Setting the hostname
-echo phobos > /etc/hostname
-echo 127.0.0.1 phobos > /etc/hosts
-hostname phobos
+# Setting the hostname
+echo phobos > /etc/hostname
+echo 127.0.0.1 phobos > /etc/hosts
+hostname phobos
 
-# Apt-sources
-cat <<END > sources.list
-deb http://ftp.uk.debian.org/debian/ jessie main contrib non-free
-deb-src http://ftp.uk.debian.org/debian/ jessie main contrib non-free
-END
-apt-get update
-apt-get upgrade
-apt-get dist-upgrade
-exit # Exit chroot
+# Apt-sources
+cat <<END > sources.list
+deb http://ftp.uk.debian.org/debian/ jessie main contrib non-free
+deb-src http://ftp.uk.debian.org/debian/ jessie main contrib non-free
+END
+apt-get update
+apt-get upgrade
+apt-get dist-upgrade
+exit # Exit chroot
 

Entering Debroid and enable a service


@@ -190,19 +185,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
sh jessie.sh enter
+
sh jessie.sh enter
 
-# Setup example serice uptimed
-apt-get install uptimed
-cat <<END > /etc/rc.debroid
-export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
-service uptimed status &>/dev/null || service uptimed start
-exit 0
-END
+# Setup example serice uptimed
+apt-get install uptimed
+cat <<END > /etc/rc.debroid
+export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH
+service uptimed status &>/dev/null || service uptimed start
+exit 0
+END
 
-chmod 0755 /etc/rc.debroid
-exit # Exit chroot
-exit # Exit adb shell
+chmod 0755 /etc/rc.debroid
+exit # Exit chroot
+exit # Exit adb shell
 

Include to Android startup:


@@ -213,10 +208,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
adb push data/local/userinit.sh /data/local/userinit.sh
-adb shell
-chmod +x /data/local/userinit.sh
-exit
+
adb push data/local/userinit.sh /data/local/userinit.sh
+adb shell
+chmod +x /data/local/userinit.sh
+exit
 

Reboot & test! Enjoy!
@@ -225,11 +220,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2016-04-03-offsite-backup-with-zfs.html b/gemfeed/2016-04-03-offsite-backup-with-zfs.html index 37c5c494..39f6caf9 100644 --- a/gemfeed/2016-04-03-offsite-backup-with-zfs.html +++ b/gemfeed/2016-04-03-offsite-backup-with-zfs.html @@ -2,17 +2,12 @@ - Offsite backup with ZFS -
-
-
-

Home | Markdown | Gemini

@@ -62,11 +57,11 @@
Back to the main site
diff --git a/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html b/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html index 13428342..e26c354d 100644 --- a/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html +++ b/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.html @@ -2,17 +2,12 @@ - Jails and ZFS with Puppet on FreeBSD -
-
-
-

Home | Markdown | Gemini

@@ -434,11 +429,11 @@ Notice: Finished catalog run in 206.09 seconds
Back to the main site
diff --git a/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html b/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html index f8a00816..3aa08168 100644 --- a/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html +++ b/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.html @@ -2,17 +2,12 @@ - Offsite backup with ZFS (Part 2) -
-
-
-

Home | Markdown | Gemini

@@ -48,11 +43,11 @@
Back to the main site
diff --git a/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html b/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html index ba8076f4..3b428a68 100644 --- a/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html +++ b/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.html @@ -2,17 +2,12 @@ - Spinning up my own authoritative DNS servers -
-
-
-

Home | Markdown | Gemini

@@ -266,11 +261,11 @@ apply Service "dig6" {
Back to the main site
diff --git a/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html b/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html index 70e1c34e..bedf6692 100644 --- a/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html +++ b/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.html @@ -2,17 +2,12 @@ - Object oriented programming with ANSI C -
-
-
-

Home | Markdown | Gemini

@@ -39,37 +34,37 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#include <stdio.h>
+
#include <stdio.h>
 
-typedef struct {
-    double (*calculate)(const double, const double);
-    char *name;
-} something_s;
+typedef struct {
+    double (*calculate)(const double, const double);
+    char *name;
+} something_s;
 
-double multiplication(const double a, const double b) {
-    return a * b;
-}
+double multiplication(const double a, const double b) {
+    return a * b;
+}
 
-double division(const double a, const double b) {
-    return a / b;
-}
+double division(const double a, const double b) {
+    return a / b;
+}
 
-int main(void) {
-    something_s mult = (something_s) {
-        .calculate = multiplication,
-        .name = "Multiplication"
-    };
+int main(void) {
+    something_s mult = (something_s) {
+        .calculate = multiplication,
+        .name = "Multiplication"
+    };
 
-    something_s div = (something_s) {
-        .calculate = division,
-        .name = "Division"
-    };
+    something_s div = (something_s) {
+        .calculate = division,
+        .name = "Division"
+    };
 
-    const double a = 3, b = 2;
+    const double a = 3, b = 2;
 
-    printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b));
-    printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b));
-}
+    printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b));
+    printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b));
+}
 

As you can see, you can call the function (pointed by the function pointer) with the same syntax as in C++ or Java:
@@ -78,8 +73,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b));
-printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b));
+
printf("%s(%f, %f) => %f\n", mult.name, a, b, mult.calculate(a,b));
+printf("%s(%f, %f) => %f\n", div.name, a, b, div.calculate(a,b));
 

However, that's just syntactic sugar for:
@@ -88,8 +83,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
printf("%s(%f, %f) => %f\n", mult.name, a, b, (*mult.calculate)(a,b));
-printf("%s(%f, %f) => %f\n", div.name, a, b, (*div.calculate)(a,b));
+
printf("%s(%f, %f) => %f\n", mult.name, a, b, (*mult.calculate)(a,b));
+printf("%s(%f, %f) => %f\n", div.name, a, b, (*div.calculate)(a,b));
 

Output:
@@ -98,10 +93,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
pbuetow ~/git/blog/source [38268]% gcc oop-c-example.c -o oop-c-example
-pbuetow ~/git/blog/source [38269]% ./oop-c-example
-Multiplication(3.000000, 2.000000) => 6.000000
-Division(3.000000, 2.000000) => 1.500000
+
pbuetow ~/git/blog/source [38268]% gcc oop-c-example.c -o oop-c-example
+pbuetow ~/git/blog/source [38269]% ./oop-c-example
+Multiplication(3.000000, 2.000000) => 6.000000
+Division(3.000000, 2.000000) => 1.500000
 

Not complicated at all, but nice to know and helps to make the code easier to read!
@@ -114,7 +109,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
mult.calculate(mult,a,b));
+
mult.calculate(mult,a,b));
 

Real object oriented programming with C


@@ -135,11 +130,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html b/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html index 2f54f3cf..c4079734 100644 --- a/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html +++ b/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.html @@ -2,17 +2,12 @@ - Realistic load testing with I/O Riot for Linux -
-
-
-

Home | Markdown | Gemini

@@ -223,11 +218,11 @@ Total time: 1213.00s
Back to the main site
diff --git a/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html b/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html index d0990454..8c32a7d5 100644 --- a/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html +++ b/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.html @@ -2,17 +2,12 @@ - DTail - The distributed log tail program -
-
-
-

Home | Markdown | Gemini

@@ -149,11 +144,11 @@ dtail –servers serverlist.txt –files ‘/var/log/*.log’ –regex ‘(?i:er
Back to the main site
diff --git a/gemfeed/2021-04-24-welcome-to-the-geminispace.html b/gemfeed/2021-04-24-welcome-to-the-geminispace.html index a04cfad8..7f60b5db 100644 --- a/gemfeed/2021-04-24-welcome-to-the-geminispace.html +++ b/gemfeed/2021-04-24-welcome-to-the-geminispace.html @@ -2,17 +2,12 @@ - Welcome to the Geminispace -
-
-
-

Home | Markdown | Gemini

@@ -122,11 +117,11 @@
Back to the main site
diff --git a/gemfeed/2021-05-16-personal-bash-coding-style-guide.html b/gemfeed/2021-05-16-personal-bash-coding-style-guide.html index 2511cec9..3a832d5b 100644 --- a/gemfeed/2021-05-16-personal-bash-coding-style-guide.html +++ b/gemfeed/2021-05-16-personal-bash-coding-style-guide.html @@ -2,17 +2,12 @@ - Personal Bash coding style guide -
-
-
-

Home | Markdown | Gemini

@@ -71,7 +66,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#!/bin/bash 
+
#!/bin/bash 
 

... as the shebang line, but that does not work on all Unix and Unix-like operating systems (e.g., the *BSDs don't have Bash installed to /bin/bash). Better is:
@@ -80,7 +75,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#!/usr/bin/env bash
+
#!/usr/bin/env bash
 

Two space soft-tabs indentation


@@ -101,14 +96,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
# All fits on one line
-command1 | command2
+
# All fits on one line
+command1 | command2
 
-# Long commands
-command1 \
-  | command2 \
-  | command3 \
-  | command4
+# Long commands
+command1 \
+  | command2 \
+  | command3 \
+  | command4
 

I think there is a better way like the following, which is less noisy. The pipe | already indicates the Bash that another command is expected, thus making the explicit line breaks with \ obsolete:
@@ -117,11 +112,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
# Long commands
-command1 |
-    command2 |
-    command3 |
-    command4
+
# Long commands
+command1 |
+    command2 |
+    command3 |
+    command4
 

Update: It's 2023 now, and I have changed my mind. I think Google's way is the better one. It may be a bit more to type, but the leading | are a nice eye catcher, so you know immediately what is going on!
@@ -134,11 +129,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
greet () {
-    local -r greeting="${1}"
-    local -r name="${2}"
-    echo "${greeting} ${name}!"
-}
+
greet () {
+    local -r greeting="${1}"
+    local -r name="${2}"
+    echo "${greeting} ${name}!"
+}
 

In this particular example, I agree that you should quote them as you don't know the input (are there, for example, whitespace characters?). But if you are sure that you are only using simple bare words, then I think that the code looks much cleaner when you do this instead:
@@ -147,11 +142,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
say_hello_to_paul () {
-    local -r greeting=Hello
-    local -r name=Paul
-    echo "$greeting $name!"
-}
+
say_hello_to_paul () {
+    local -r greeting=Hello
+    local -r name=Paul
+    echo "$greeting $name!"
+}
 

You see, I also omitted the curly braces { } around the variables. I only use the curly braces around variables when it makes the code either easier/clearer to read or if it is necessary to use them:
@@ -160,9 +155,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
declare FOO=bar
-# Curly braces around FOO are necessary
-echo "foo${FOO}baz"
+
declare FOO=bar
+# Curly braces around FOO are necessary
+echo "foo${FOO}baz"
 

A few more words on always quoting the variables: For the sake of consistency (and for making ShellCheck happy), I am not against quoting everything I encounter. I also think that the larger the Bash script becomes, the more critical it becomes always to quote variables. That's because it will be more likely that you might not remember that some of the functions don't work on values with spaces in them, for example. It's just that I won't quote everything in every small script I write.
@@ -175,13 +170,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
# Prefer this:
-addition=$(( X + Y ))
-substitution="${string/#foo/bar}"
+
# Prefer this:
+addition=$(( X + Y ))
+substitution="${string/#foo/bar}"
 
-# Instead of this:
-addition="$(expr "${X}" + "${Y}")"
-substitution="$(echo "${string}" | sed -e 's/^foo/bar/')"
+# Instead of this:
+addition="$(expr "${X}" + "${Y}")"
+substitution="$(echo "${string}" | sed -e 's/^foo/bar/')"
 

I can't entirely agree here. The external commands (especially sed) are much more sophisticated and powerful than the built-in Bash versions. Sed can do much more than the Bash can ever do by itself when it comes to text manipulation (the name "sed" stands for streaming editor, after all).
@@ -202,20 +197,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
declare -r SUGAR_FREE=yes
-declare -r I_NEED_THE_BUZZ=no
+
declare -r SUGAR_FREE=yes
+declare -r I_NEED_THE_BUZZ=no
 
-buy_soda () {
-    local -r sugar_free=$1
+buy_soda () {
+    local -r sugar_free=$1
 
-    if [[ $sugar_free == yes ]]; then
-        echo 'Diet Dr. Pepper'
-    else
-        echo 'Pepsi Coke'
-    fi
-}
+    if [[ $sugar_free == yes ]]; then
+        echo 'Diet Dr. Pepper'
+    else
+        echo 'Pepsi Coke'
+    fi
+}
 
-buy_soda $I_NEED_THE_BUZZ
+buy_soda $I_NEED_THE_BUZZ
 

Non-evil alternative to variable assignments via eval


@@ -226,12 +221,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
# What does this set?
-# Did it succeed? In part or whole?
-eval $(set_my_variables)
+
# What does this set?
+# Did it succeed? In part or whole?
+eval $(set_my_variables)
 
-# What happens if one of the returned values has a space in it?
-variable="$(eval some_function)"
+# What happens if one of the returned values has a space in it?
+variable="$(eval some_function)"
 

However, if I want to read variables from another file, I don't have to use eval here. I only have to source the file:
@@ -270,39 +265,39 @@ Hello paul, it is Sat 15 May 19:21:12 BST 2021 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
filter_lines () {
-    echo 'Start filtering lines in a fancy way!' >&2
-    grep ... | sed ....
-}
+
filter_lines () {
+    echo 'Start filtering lines in a fancy way!' >&2
+    grep ... | sed ....
+}
 
-process_lines () {
-    echo 'Start processing line by line!' >&2
-    while read -r line; do
-        ... do something and produce a result...
-        echo "$result"
-    done 
-}
+process_lines () {
+    echo 'Start processing line by line!' >&2
+    while read -r line; do
+        ... do something and produce a result...
+        echo "$result"
+    done 
+}
 
-# Do some post-processing of the data
-postprocess_lines () {
-    echo 'Start removing duplicates!' >&2
-    sort -u
-}
+# Do some post-processing of the data
+postprocess_lines () {
+    echo 'Start removing duplicates!' >&2
+    sort -u
+}
 
-genreate_report () {
-    echo 'My boss wants to have a report!' >&2
-    tee outfile.txt
-    wc -l outfile.txt
-}
+genreate_report () {
+    echo 'My boss wants to have a report!' >&2
+    tee outfile.txt
+    wc -l outfile.txt
+}
 
-main () {
-    filter_lines |
-        process_lines |
-        postprocess_lines |
-        generate_report
-}
+main () {
+    filter_lines |
+        process_lines |
+        postprocess_lines |
+        generate_report
+}
 
-main
+main
 

The stdout is always passed as a pipe to the next following stage. The stderr is used for info logging.
@@ -317,13 +312,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
some_function () {
-    local -r param_foo="$1"; shift
-    local -r param_baz="$1"; shift
-    local -r param_bay="$1"; shift
+
some_function () {
+    local -r param_foo="$1"; shift
+    local -r param_baz="$1"; shift
+    local -r param_bay="$1"; shift
 
-    # ...
-}
+    # ...
+}
 

Want to add a param_baz? Just do this:
@@ -332,14 +327,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
some_function () {
-    local -r param_foo="$1"; shift
-    local -r param_bar="$1"; shift
-    local -r param_baz="$1"; shift
-    local -r param_bay="$1"; shift
+
some_function () {
+    local -r param_foo="$1"; shift
+    local -r param_bar="$1"; shift
+    local -r param_baz="$1"; shift
+    local -r param_bay="$1"; shift
 
-    # ...
-}
+    # ...
+}
 

Want to remove param_foo? Nothing easier than that:
@@ -348,13 +343,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
some_function () {
-    local -r param_bar="$1"; shift
-    local -r param_baz="$1"; shift
-    local -r param_bay="$1"; shift
-    
-    # ...
-}
+
some_function () {
+    local -r param_bar="$1"; shift
+    local -r param_baz="$1"; shift
+    local -r param_bay="$1"; shift
+    
+    # ...
+}
 

As you can see, I didn't need to change any other assignments within the function. Of course, you would also need to change the function argument lists at every occasion where the function is invoked - you would do that within the same refactoring session.
@@ -367,9 +362,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
set -e
-grep -q foo <<< bar
-echo Jo
+
set -e
+grep -q foo <<< bar
+echo Jo
 

Here 'Jo' will never be printed out as the grep didn't find any match. It's unrealistic for most scripts to run in paranoid mode purely, so there must be a way to add exceptions. Critical Bash scripts of mine tend to look like this:
@@ -378,26 +373,26 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
#!/usr/bin/env bash
+
#!/usr/bin/env bash
 
-set -e
+set -e
 
-some_function () {
-    # .. some critical code
-    # ...
+some_function () {
+    # .. some critical code
+    # ...
 
-    set +e
-    # Grep might fail, but that's OK now
-    grep ....
-    local -i ec=$?
-    set -e
+    set +e
+    # Grep might fail, but that's OK now
+    grep ....
+    local -i ec=$?
+    set -e
 
-    # .. critical code continues ...
-    if [[ $ec -ne 0 ]]; then
-        : # ...
-    fi
-    # ...
-}
+    # .. critical code continues ...
+    if [[ $ec -ne 0 ]]; then
+        : # ...
+    fi
+    # ...
+}
 

Learned


@@ -412,10 +407,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
if [[ "${my_var}" > 3 ]]; then
-    # True for 4, false for 22.
-    do_something
-fi
+
if [[ "${my_var}" > 3 ]]; then
+    # True for 4, false for 22.
+    do_something
+fi
 

... but it is probably an unintended lexicographical comparison. A correct way would be:
@@ -424,9 +419,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
if (( my_var > 3 )); then
-    do_something
-fi
+
if (( my_var > 3 )); then
+    do_something
+fi
 

or
@@ -435,9 +430,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
if [[ "${my_var}" -gt 3 ]]; then
-    do_something
-fi
+
if [[ "${my_var}" -gt 3 ]]; then
+    do_something
+fi
 

PIPESTATUS


@@ -450,10 +445,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
tar -cf - ./* | ( cd "${dir}" && tar -xf - )
-if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then
-    echo "Unable to tar files to ${dir}" >&2
-fi
+
tar -cf - ./* | ( cd "${dir}" && tar -xf - )
+if (( PIPESTATUS[0] != 0 || PIPESTATUS[1] != 0 )); then
+    echo "Unable to tar files to ${dir}" >&2
+fi
 

However, as PIPESTATUS will be overwritten as soon as you do any other command, if you need to act differently on errors based on where it happened in the pipe, you'll need to assign PIPESTATUS to another variable immediately after running the command (don't forget that [ is a command and will wipe out PIPESTATUS).
@@ -462,14 +457,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
-return_codes=( "${PIPESTATUS[@]}" )
-if (( return_codes[0] != 0 )); then
-    do_something
-fi
-if (( return_codes[1] != 0 )); then
-    do_something_else
-fi
+
tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
+return_codes=( "${PIPESTATUS[@]}" )
+if (( return_codes[0] != 0 )); then
+    do_something
+fi
+if (( return_codes[1] != 0 )); then
+    do_something_else
+fi
 

Use common sense and BE CONSISTENT.


@@ -500,11 +495,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html b/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html index c632ebff..926335cd 100644 --- a/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html +++ b/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.html @@ -2,17 +2,12 @@ - Gemtexter - One Bash script to rule it all -
-
-
-

Home | Markdown | Gemini

@@ -119,18 +114,18 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
paul in uranus in gemtexter on 🌱 main
-❯ wc -l gemtexter lib/*
-    117 gemtexter
-     59 lib/assert.source.sh
-    128 lib/atomfeed.source.sh
-     64 lib/gemfeed.source.sh
-    161 lib/generate.source.sh
-     50 lib/git.source.sh
-    162 lib/html.source.sh
-     30 lib/log.source.sh
-     63 lib/md.source.sh
-     834 total
+
paul in uranus in gemtexter on 🌱 main
+❯ wc -l gemtexter lib/*
+    117 gemtexter
+     59 lib/assert.source.sh
+    128 lib/atomfeed.source.sh
+     64 lib/gemfeed.source.sh
+    161 lib/generate.source.sh
+     50 lib/git.source.sh
+    162 lib/html.source.sh
+     30 lib/log.source.sh
+     63 lib/md.source.sh
+     834 total
 

This way, the script could grow far beyond 1000 lines of code and still be maintainable. With more features, execution speed may slowly become a problem, though. I already notice that Gemtexter doesn't produce results instantly but requires few seconds of runtime already. That's not a problem yet, though.
@@ -165,9 +160,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
gemtext='=> http://example.org Description of the link'
-assert::equals "$(generate::make_link html "$gemtext")" \
-    '<a class="textlink" href="http://example.org">Description of the link</a><br />'
+
gemtext='=> http://example.org Description of the link'
+assert::equals "$(generate::make_link html "$gemtext")" \
+    '<a class="textlink" href="http://example.org">Description of the link</a><br />'
 

Markdown unit test example


@@ -176,9 +171,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
gemtext='=> http://example.org Description of the link'
-assert::equals "$(generate::make_link md "$gemtext")" \
-    '[Description of the link](http://example.org)  '
+
gemtext='=> http://example.org Description of the link'
+assert::equals "$(generate::make_link md "$gemtext")" \
+    '[Description of the link](http://example.org)  '
 

Handcrafted HTML styles


@@ -227,11 +222,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2021-07-04-the-well-grounded-rubyist.html b/gemfeed/2021-07-04-the-well-grounded-rubyist.html index 8d8c8799..1c572a06 100644 --- a/gemfeed/2021-07-04-the-well-grounded-rubyist.html +++ b/gemfeed/2021-07-04-the-well-grounded-rubyist.html @@ -2,17 +2,12 @@ - The Well-Grounded Rubyist -
-
-
-

Home | Markdown | Gemini

@@ -146,11 +141,11 @@ Hello World
Back to the main site
diff --git a/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html b/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html index 99ca25e7..fe745a70 100644 --- a/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html +++ b/gemfeed/2021-08-01-on-being-pedantic-about-open-source.html @@ -2,17 +2,12 @@ - On being Pedantic about Open-Source -
-
-
-

Home | Markdown | Gemini

@@ -156,11 +151,11 @@
Back to the main site
diff --git a/gemfeed/2021-09-12-keep-it-simple-and-stupid.html b/gemfeed/2021-09-12-keep-it-simple-and-stupid.html index 68b22052..a11eea9a 100644 --- a/gemfeed/2021-09-12-keep-it-simple-and-stupid.html +++ b/gemfeed/2021-09-12-keep-it-simple-and-stupid.html @@ -2,17 +2,12 @@ - Keep it simple and stupid -
-
-
-

Home | Markdown | Gemini

@@ -143,11 +138,11 @@
Back to the main site
diff --git a/gemfeed/2021-10-22-defensive-devops.html b/gemfeed/2021-10-22-defensive-devops.html index aadbee04..e9689033 100644 --- a/gemfeed/2021-10-22-defensive-devops.html +++ b/gemfeed/2021-10-22-defensive-devops.html @@ -2,17 +2,12 @@ - Defensive DevOps -
-
-
-

Home | Markdown | Gemini

@@ -136,11 +131,11 @@
Back to the main site
diff --git a/gemfeed/2021-11-29-bash-golf-part-1.html b/gemfeed/2021-11-29-bash-golf-part-1.html index c879a24e..22c38b78 100644 --- a/gemfeed/2021-11-29-bash-golf-part-1.html +++ b/gemfeed/2021-11-29-bash-golf-part-1.html @@ -2,17 +2,12 @@ - Bash Golf Part 1 -
-
-
-

Home | Markdown | Gemini

@@ -509,11 +504,11 @@ bash: line 1: 1/10.0 : syntax error: invalid arithmetic operator (error token is
Back to the main site
diff --git a/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html b/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html index 6ba83e5c..05f4bad7 100644 --- a/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html +++ b/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.html @@ -2,17 +2,12 @@ - How to stay sane as a DevOps person -
-
-
-

Home | Markdown | Gemini

@@ -165,11 +160,11 @@
Back to the main site
diff --git a/gemfeed/2022-01-01-bash-golf-part-2.html b/gemfeed/2022-01-01-bash-golf-part-2.html index 781df35e..b8c77be3 100644 --- a/gemfeed/2022-01-01-bash-golf-part-2.html +++ b/gemfeed/2022-01-01-bash-golf-part-2.html @@ -2,17 +2,12 @@ - Bash Golf Part 2 -
-
-
-

Home | Markdown | Gemini

@@ -528,11 +523,11 @@ PAUL:X:1000:1000:PAUL BUETOW:/HOME/PAUL:/BIN/BASH
Back to the main site
diff --git a/gemfeed/2022-01-23-welcome-to-the-foo.zone.html b/gemfeed/2022-01-23-welcome-to-the-foo.zone.html index e35d9996..3bc42ed3 100644 --- a/gemfeed/2022-01-23-welcome-to-the-foo.zone.html +++ b/gemfeed/2022-01-23-welcome-to-the-foo.zone.html @@ -2,17 +2,12 @@ - Welcome to the foo.zone -
-
-
-

Home | Markdown | Gemini

@@ -67,11 +62,11 @@
Back to the main site
diff --git a/gemfeed/2022-02-04-computer-operating-systems-i-use.html b/gemfeed/2022-02-04-computer-operating-systems-i-use.html index 8b498243..666658e7 100644 --- a/gemfeed/2022-02-04-computer-operating-systems-i-use.html +++ b/gemfeed/2022-02-04-computer-operating-systems-i-use.html @@ -2,17 +2,12 @@ - Computer operating systems I use(d) -
-
-
-

Home | Markdown | Gemini

@@ -281,11 +276,11 @@ GNU/kFreeBSD rhea.buetow.org 8.0-RELEASE-p5 FreeBSD 8.0-RELEASE-p5 #2: Sat Nov 2
Back to the main site
diff --git a/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html b/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html index 9fd5fa04..aadc7f23 100644 --- a/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html +++ b/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.html @@ -2,17 +2,12 @@ - The release of DTail 4.0.0 -
-
-
-

Home | Markdown | Gemini

@@ -341,11 +336,11 @@ exec /usr/local/bin/dtailhealth --server localhost:2222
Back to the main site
diff --git a/gemfeed/2022-04-10-creative-universe.html b/gemfeed/2022-04-10-creative-universe.html index ce689dd3..d4a24614 100644 --- a/gemfeed/2022-04-10-creative-universe.html +++ b/gemfeed/2022-04-10-creative-universe.html @@ -2,17 +2,12 @@ - Creative universe -
-
-
-

Home | Markdown | Gemini

@@ -173,11 +168,11 @@ learn () {
Back to the main site
diff --git a/gemfeed/2022-05-27-perl-is-still-a-great-choice.html b/gemfeed/2022-05-27-perl-is-still-a-great-choice.html index 1e14e44b..a549863e 100644 --- a/gemfeed/2022-05-27-perl-is-still-a-great-choice.html +++ b/gemfeed/2022-05-27-perl-is-still-a-great-choice.html @@ -2,17 +2,12 @@ - Perl is still a great choice -
-
-
-

Home | Markdown | Gemini

@@ -179,11 +174,11 @@
Back to the main site
diff --git a/gemfeed/2022-06-15-sweating-the-small-stuff.html b/gemfeed/2022-06-15-sweating-the-small-stuff.html index 776d0105..cdb98bf7 100644 --- a/gemfeed/2022-06-15-sweating-the-small-stuff.html +++ b/gemfeed/2022-06-15-sweating-the-small-stuff.html @@ -2,17 +2,12 @@ - Sweating the small stuff - Tiny projects of mine -
-
-
-

Home | Markdown | Gemini

@@ -372,11 +367,11 @@ v = 008 [v = p*c*(s != c ? 2 : 1)] Total logical CPUs
Back to the main site
diff --git a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html index 2562aa22..a97963ee 100644 --- a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html +++ b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.html @@ -2,17 +2,12 @@ - Let's Encrypt with OpenBSD and Rex -
-
-
-

Home | Markdown | Gemini

@@ -713,11 +708,11 @@ rex commons
Back to the main site
diff --git a/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html b/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html index 4d0e9f5c..ff1c5ec4 100644 --- a/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html +++ b/gemfeed/2022-08-27-gemtexter-1.1.0-lets-gemtext-again.html @@ -2,17 +2,12 @@ - Gemtexter 1.1.0 - Let's Gemtext again -
-
-
-

Home | Markdown | Gemini

@@ -62,23 +57,23 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
check_dependencies () {
-    # At least, Bash 5 is required
-    local -i required_version=5
-    IFS=. read -ra version <<< "$BASH_VERSION"
-    if [ "${version[0]}" -lt $required_version ]; then
-        log ERROR "ERROR, \"bash\" must be at least at major version $required_version!"
-        exit 2
-    fi
+
check_dependencies () {
+    # At least, Bash 5 is required
+    local -i required_version=5
+    IFS=. read -ra version <<< "$BASH_VERSION"
+    if [ "${version[0]}" -lt $required_version ]; then
+        log ERROR "ERROR, \"bash\" must be at least at major version $required_version!"
+        exit 2
+    fi
 
-    # These must be the GNU versions of the commands
-    for tool in $DATE $SED $GREP; do
-        if ! $tool --version | grep -q GNU; then
-            log ERROR "ERROR, \"$tool\" command is not the GNU version, please install!"
-            exit 2
-        fi
-    done
-}
+    # These must be the GNU versions of the commands
+    for tool in $DATE $SED $GREP; do
+        if ! $tool --version | grep -q GNU; then
+            log ERROR "ERROR, \"$tool\" command is not the GNU version, please install!"
+            exit 2
+        fi
+    done
+}
 

Especially macOS users didn't read the README carefully enough to install GNU Grep, GNU Sed and GNU Date before using Gemtexter.
@@ -99,7 +94,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
./gemtexter --generate '.*hello.*'
+
./gemtexter --generate '.*hello.*'
 

Revamped git support


@@ -136,11 +131,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2022-09-30-after-a-bad-nights-sleep.html b/gemfeed/2022-09-30-after-a-bad-nights-sleep.html index ed9cb7d4..8c6cb3cb 100644 --- a/gemfeed/2022-09-30-after-a-bad-nights-sleep.html +++ b/gemfeed/2022-09-30-after-a-bad-nights-sleep.html @@ -2,17 +2,12 @@ - After a bad night's sleep -
-
-
-

Home | Markdown | Gemini

@@ -136,11 +131,11 @@ jgs (________\ \
Back to the main site
diff --git a/gemfeed/2022-10-30-installing-dtail-on-openbsd.html b/gemfeed/2022-10-30-installing-dtail-on-openbsd.html index 17275109..a5aaeefe 100644 --- a/gemfeed/2022-10-30-installing-dtail-on-openbsd.html +++ b/gemfeed/2022-10-30-installing-dtail-on-openbsd.html @@ -2,17 +2,12 @@ - Installing DTail on OpenBSD -
-
-
-

Home | Markdown | Gemini

@@ -383,11 +378,11 @@ REMOTE|fishfinger|100|7|fstab|093f510ec5c0f512.h /usr/local ffs rw,wxallowed,nod
Back to the main site
diff --git a/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html b/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html index 3c8b5fe1..978f6839 100644 --- a/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html +++ b/gemfeed/2022-11-24-i-tried-emacs-but-i-switched-back-to-neovim.html @@ -2,17 +2,12 @@ - I tried (Doom) Emacs, but I switched back to (Neo)Vim -
-
-
-

Home | Markdown | Gemini

@@ -99,10 +94,10 @@ Art by \ \_! / __! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
" Clipboard
-vnoremap ,y !pbcopy<CR>ugv
-vnoremap ,i !pbpaste<CR>
-nmap ,i !wpbpaste<CR>
+
" Clipboard
+vnoremap ,y !pbcopy<CR>ugv
+vnoremap ,i !pbpaste<CR>
+nmap ,i !wpbpaste<CR>
 

That's only a very few lines and does precisely what I want. It's quick and dirty but get's the job done! If VimScript becomes too cumbersome, I can use Lua for NeoVim scripting.
@@ -147,11 +142,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html b/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html index 74319830..fdb7d474 100644 --- a/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html +++ b/gemfeed/2022-12-24-ultrarelearning-java-my-takeaways.html @@ -2,17 +2,12 @@ - (Re)learning Java - My takeaways -
-
-
-

Home | Markdown | Gemini

@@ -142,11 +137,11 @@
Back to the main site
diff --git a/gemfeed/2023-01-23-why-grapheneos-rox.html b/gemfeed/2023-01-23-why-grapheneos-rox.html index 0d4f25a1..4b326b94 100644 --- a/gemfeed/2023-01-23-why-grapheneos-rox.html +++ b/gemfeed/2023-01-23-why-grapheneos-rox.html @@ -2,17 +2,12 @@ - Why GrapheneOS rox -
-
-
-

Home | Markdown | Gemini

@@ -172,11 +167,11 @@ Art by Joan Stark
Back to the main site
diff --git a/gemfeed/2023-02-26-how-to-shut-down-after-work.html b/gemfeed/2023-02-26-how-to-shut-down-after-work.html index c900fd73..0b0b8854 100644 --- a/gemfeed/2023-02-26-how-to-shut-down-after-work.html +++ b/gemfeed/2023-02-26-how-to-shut-down-after-work.html @@ -2,17 +2,12 @@ - How to shut down after work -
-
-
-

Home | Markdown | Gemini

@@ -108,11 +103,11 @@
Back to the main site
diff --git a/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html b/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html index 8d29cbb0..418ee711 100644 --- a/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html +++ b/gemfeed/2023-03-16-the-pragmatic-programmer-book-notes.html @@ -2,17 +2,12 @@ - 'The Pragmatic Programmer' book notes -
-
-
-

Home | Markdown | Gemini

@@ -117,11 +112,11 @@
Back to the main site
diff --git a/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html b/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html index 7f5c9bfd..57582782 100644 --- a/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html +++ b/gemfeed/2023-03-25-gemtexter-2.0.0-lets-gemtext-again-2.html @@ -2,17 +2,12 @@ - Gemtexter 2.0.0 - Let's Gemtext again² -
-
-
-

Home | Markdown | Gemini

@@ -128,8 +123,8 @@ Blablabla... by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
declare -xr PRE_GENERATE_HOOK=./pre_generate_hook.sh
-declare -xr POST_PUBLISH_HOOK=./post_publish_hook.sh
+
declare -xr PRE_GENERATE_HOOK=./pre_generate_hook.sh
+declare -xr POST_PUBLISH_HOOK=./post_publish_hook.sh
 

Use of safer Bash options


@@ -146,10 +141,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
% cat gemfeed/2023-02-26-title-here.gmi
-# Title here
+
% cat gemfeed/2023-02-26-title-here.gmi
+# Title here
 
-The remaining content of the Gemtext file...
+The remaining content of the Gemtext file...
 

Gemtexter will add a line starting with > Published at ... now. Any subsequent Atom feed generation will then use that date.
@@ -158,12 +153,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
% cat gemfeed/2023-02-26-title-here.gmi
-# Title here
+
% cat gemfeed/2023-02-26-title-here.gmi
+# Title here
 
-> Published at 2023-02-26T21:43:51+01:00
+> Published at 2023-02-26T21:43:51+01:00
 
-The remaining content of the Gemtext file...
+The remaining content of the Gemtext file...
 

XMLLint support


@@ -187,11 +182,11 @@ http://www.gnu.org/software/src-highlite -->
Back to the main site
diff --git a/gemfeed/2023-04-01-never-split-the-difference-book-notes.html b/gemfeed/2023-04-01-never-split-the-difference-book-notes.html index 50b1bd3b..ffe52af6 100644 --- a/gemfeed/2023-04-01-never-split-the-difference-book-notes.html +++ b/gemfeed/2023-04-01-never-split-the-difference-book-notes.html @@ -2,17 +2,12 @@ - 'Never split the difference' book notes -
-
-
-

Home | Markdown | Gemini

@@ -180,11 +175,11 @@
Back to the main site
diff --git a/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html b/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html index 71ad13b3..bedad8bf 100644 --- a/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html +++ b/gemfeed/2023-05-01-unveiling-guprecords:-uptime-records-with-raku.html @@ -2,17 +2,12 @@ - Unveiling `guprecords.raku`: Global Uptime Records with Raku -
-
-
-

Home | Markdown | Gemini

@@ -86,7 +81,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
$ raku guprecords.raku --stats=dir=$HOME/git/uprecords/stats --all
+
$ raku guprecords.raku --stats=dir=$HOME/git/uprecords/stats --all
 

This command will generate a comprehensive uptime report from the collected statistics, making it easy to review and enjoy the data.
@@ -198,11 +193,11 @@ no1 in 455 days, 18:52:44 | at Sun Jul 21 07:37:51 2024
Back to the main site
diff --git a/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html b/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html index cd6be663..d6355fc1 100644 --- a/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html +++ b/gemfeed/2023-05-06-the-obstacle-is-the-way-book-notes.html @@ -2,17 +2,12 @@ - 'The Obstacle is the Way' book notes -
-
-
-

Home | Markdown | Gemini

@@ -133,11 +128,11 @@
Back to the main site
diff --git a/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html b/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html index 228da233..b4b61489 100644 --- a/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html +++ b/gemfeed/2023-06-01-kiss-server-monitoring-with-gogios.html @@ -2,17 +2,12 @@ - KISS server monitoring with Gogios -
-
-
-

Home | Markdown | Gemini

@@ -122,11 +117,11 @@ Have a nice day! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
git clone https://codeberg.org/snonux/gogios.git
-cd gogios
-go build -o gogios cmd/gogios/main.go
-doas cp gogios /usr/local/bin/gogios
-doas chmod 755 /usr/local/bin/gogios
+
git clone https://codeberg.org/snonux/gogios.git
+cd gogios
+go build -o gogios cmd/gogios/main.go
+doas cp gogios /usr/local/bin/gogios
+doas chmod 755 /usr/local/bin/gogios
 

You can use cross-compilation if you want to compile Gogios for OpenBSD on a Linux system without installing the Go compiler on OpenBSD. Follow these steps:
@@ -135,9 +130,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
export GOOS=openbsd
-export GOARCH=amd64
-go build -o gogios cmd/gogios/main.go
+
export GOOS=openbsd
+export GOARCH=amd64
+go build -o gogios cmd/gogios/main.go
 

On your OpenBSD system, copy the binary to /usr/local/bin/gogios and set the correct permissions as described in the previous section. All steps described here you could automate with your configuration management system of choice. I use Rexify, the friendly configuration management system, to automate the installation, but that is out of the scope of this document.
@@ -152,11 +147,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
doas adduser -group _gogios -batch _gogios
-doas usermod -d /var/run/gogios _gogios
-doas mkdir -p /var/run/gogios
-doas chown _gogios:_gogios /var/run/gogios
-doas chmod 750 /var/run/gogios
+
doas adduser -group _gogios -batch _gogios
+doas usermod -d /var/run/gogios _gogios
+doas mkdir -p /var/run/gogios
+doas chown _gogios:_gogios /var/run/gogios
+doas chmod 750 /var/run/gogios
 

Please note that creating a user and group might differ depending on your operating system. For other operating systems, consult their documentation for creating system users and groups.
@@ -169,8 +164,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
doas pkg_add monitoring-plugins
-doas pkg_add nrpe # If you want to execute checks remotely via NRPE.
+
doas pkg_add monitoring-plugins
+doas pkg_add nrpe # If you want to execute checks remotely via NRPE.
 

Once the installation is complete, you can find the monitoring plugins in the /usr/local/libexec/nagios directory, which then can be configured to be used in gogios.json.
@@ -197,41 +192,41 @@ echo 'This is a test email from OpenBSD.' | mail -s 'Test Email' by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
{
-  "EmailTo": "paul@dev.buetow.org",
-  "EmailFrom": "gogios@buetow.org",
-  "CheckTimeoutS": 10,
-  "CheckConcurrency": 2,
-  "StateDir": "/var/run/gogios",
-  "Checks": {
-    "Check ICMP4 www.foo.zone": {
-      "Plugin": "/usr/local/libexec/nagios/check_ping",
-      "Args": [ "-H", "www.foo.zone", "-4", "-w", "50,10%", "-c", "100,15%" ],
-      "Retries": 3,
-      "RetryInterval": 10
-    },
-    "Check ICMP6 www.foo.zone": {
-      "Plugin": "/usr/local/libexec/nagios/check_ping",
-      "Args": [ "-H", "www.foo.zone", "-6", "-w", "50,10%", "-c", "100,15%" ],
-      "Retries": 3,
-      "RetryInterval": 10
-    },
-    "www.foo.zone HTTP IPv4": {
-      "Plugin": "/usr/local/libexec/nagios/check_http",
-      "Args": ["www.foo.zone", "-4"],
-      "DependsOn": ["Check ICMP4 www.foo.zone"]
-    },
-    "www.foo.zone HTTP IPv6": {
-      "Plugin": "/usr/local/libexec/nagios/check_http",
-      "Args": ["www.foo.zone", "-6"],
-      "DependsOn": ["Check ICMP6 www.foo.zone"]
-    }
-    "Check NRPE Disk Usage foo.zone": {
-      "Plugin": "/usr/local/libexec/nagios/check_nrpe",
-      "Args": ["-H", "foo.zone", "-c", "check_disk", "-p", "5666", "-4"]
-    }
-  }
-}
+
{
+  "EmailTo": "paul@dev.buetow.org",
+  "EmailFrom": "gogios@buetow.org",
+  "CheckTimeoutS": 10,
+  "CheckConcurrency": 2,
+  "StateDir": "/var/run/gogios",
+  "Checks": {
+    "Check ICMP4 www.foo.zone": {
+      "Plugin": "/usr/local/libexec/nagios/check_ping",
+      "Args": [ "-H", "www.foo.zone", "-4", "-w", "50,10%", "-c", "100,15%" ],
+      "Retries": 3,
+      "RetryInterval": 10
+    },
+    "Check ICMP6 www.foo.zone": {
+      "Plugin": "/usr/local/libexec/nagios/check_ping",
+      "Args": [ "-H", "www.foo.zone", "-6", "-w", "50,10%", "-c", "100,15%" ],
+      "Retries": 3,
+      "RetryInterval": 10
+    },
+    "www.foo.zone HTTP IPv4": {
+      "Plugin": "/usr/local/libexec/nagios/check_http",
+      "Args": ["www.foo.zone", "-4"],
+      "DependsOn": ["Check ICMP4 www.foo.zone"]
+    },
+    "www.foo.zone HTTP IPv6": {
+      "Plugin": "/usr/local/libexec/nagios/check_http",
+      "Args": ["www.foo.zone", "-6"],
+      "DependsOn": ["Check ICMP6 www.foo.zone"]
+    }
+    "Check NRPE Disk Usage foo.zone": {
+      "Plugin": "/usr/local/libexec/nagios/check_nrpe",
+      "Args": ["-H", "foo.zone", "-c", "check_disk", "-p", "5666", "-4"]
+    }
+  }
+}
 

    @@ -260,7 +255,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    doas -u _gogios /usr/local/bin/gogios -cfg /etc/gogios.json
    +
    doas -u _gogios /usr/local/bin/gogios -cfg /etc/gogios.json
     

    To run Gogios via CRON on OpenBSD as the gogios user and check all services once per minute, follow these steps:
    @@ -302,11 +297,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html b/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html index 8f05b03d..b0a6fbec 100644 --- a/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html +++ b/gemfeed/2023-07-17-career-guide-and-soft-skills-book-notes.html @@ -2,17 +2,12 @@ - 'Software Developers Career Guide and Soft Skills' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -365,11 +360,11 @@
    Back to the main site
    diff --git a/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html b/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html index 609652f0..7bcae7ca 100644 --- a/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html +++ b/gemfeed/2023-07-21-gemtexter-2.1.0-lets-gemtext-again-3.html @@ -2,17 +2,12 @@ - Gemtexter 2.1.0 - Let's Gemtext again³ -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -77,9 +72,9 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    if [ -n "$foo" ]; then
    -  echo "$foo"
    -fi
    +
    if [ -n "$foo" ]; then
    +  echo "$foo"
    +fi
     

    Please run source-highlight --lang-list for a list of all supported languages.
    @@ -108,7 +103,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    declare -xr MASTODON_URI='https://fosstodon.org/@snonux'
    +
    declare -xr MASTODON_URI='https://fosstodon.org/@snonux'
     

    and add the following into your index.gmi:
    @@ -123,7 +118,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    <a href='https://fosstodon.org/@snonux' rel='me'>Me at Mastodon</a>
    +
    <a href='https://fosstodon.org/@snonux' rel='me'>Me at Mastodon</a>
     

    More


    @@ -143,11 +138,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2023-08-18-site-reliability-engineering-part-1.html b/gemfeed/2023-08-18-site-reliability-engineering-part-1.html index 5ef9c19d..716dcfd5 100644 --- a/gemfeed/2023-08-18-site-reliability-engineering-part-1.html +++ b/gemfeed/2023-08-18-site-reliability-engineering-part-1.html @@ -2,17 +2,12 @@ - Site Reliability Engineering - Part 1: SRE and Organizational Culture -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -78,11 +73,11 @@ DC on fire:
    Back to the main site
    diff --git a/gemfeed/2023-09-25-dtail-usage-examples.html b/gemfeed/2023-09-25-dtail-usage-examples.html index 84513d63..6723a770 100644 --- a/gemfeed/2023-09-25-dtail-usage-examples.html +++ b/gemfeed/2023-09-25-dtail-usage-examples.html @@ -2,17 +2,12 @@ - DTail usage examples -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -75,7 +70,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail --servers serverlist.txt --grep INFO --files "/var/log/dserver/*.log"
    +
    % dtail --servers serverlist.txt --grep INFO --files "/var/log/dserver/*.log"
     

    Hint: you can also provide a comma separated server list, e.g.: servers server1.example.org,server2.example.org:PORT,...
    @@ -88,7 +83,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail --servers serverlist.txt --grep INFO "/var/log/dserver/*.log"
    +
    % dtail --servers serverlist.txt --grep INFO "/var/log/dserver/*.log"
     

    Aggregating logs


    @@ -101,10 +96,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail --servers serverlist.txt \
    -    --files '/var/log/dserver/*.log' \
    -    --query 'from STATS select sum($goroutines),sum($cgocalls),
    -             last($time),max(lifetimeConnections)'
    +
    % dtail --servers serverlist.txt \
    +    --files '/var/log/dserver/*.log' \
    +    --query 'from STATS select sum($goroutines),sum($cgocalls),
    +             last($time),max(lifetimeConnections)'
     

    Beware: For map-reduce queries to work, you have to ensure that DTail supports your log format. Check out the documentaiton of the DTail query language and the DTail log formats on the DTail homepage for more information.
    @@ -117,10 +112,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail --servers serverlist.txt \
    -    --files '/var/log/dserver/*.log' \
    -    'from STATS select sum($goroutines),sum($cgocalls),
    -     last($time),max(lifetimeConnections)'
    +
    % dtail --servers serverlist.txt \
    +    --files '/var/log/dserver/*.log' \
    +    'from STATS select sum($goroutines),sum($cgocalls),
    +     last($time),max(lifetimeConnections)'
     

    Here is another example:
    @@ -129,10 +124,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail --servers serverlist.txt \
    -    --files '/var/log/dserver/*.log' \
    -    --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    -             lifetimeConnections group by $hostname order by max($cgocalls)'
    +
    % dtail --servers serverlist.txt \
    +    --files '/var/log/dserver/*.log' \
    +    --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    +             lifetimeConnections group by $hostname order by max($cgocalls)'
     

    Tail map-reduce example 2
    @@ -143,9 +138,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail --servers serverlist.txt \
    -    --files '/var/log/dserver/*.log' \
    -    --query 'from STATS select ... outfile append result.csv'
    +
    % dtail --servers serverlist.txt \
    +    --files '/var/log/dserver/*.log' \
    +    --query 'from STATS select ... outfile append result.csv'
     

    How to use dcat


    @@ -158,7 +153,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dcat --servers serverlist.txt --files /etc/hostname
    +
    % dcat --servers serverlist.txt --files /etc/hostname
     

    Cat example
    @@ -169,7 +164,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dcat --servers serverlist.txt /etc/hostname
    +
    % dcat --servers serverlist.txt /etc/hostname
     

    How to use dgrep


    @@ -180,9 +175,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dgrep --servers server1.example.org:2223 \
    -    --files /etc/passwd \
    -    --regex nologin
    +
    % dgrep --servers server1.example.org:2223 \
    +    --files /etc/passwd \
    +    --regex nologin
     

    Generally, dgrep is also a very useful way to search historic application logs for certain content.
    @@ -199,10 +194,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dmap --servers serverlist.txt \
    -    --files '/var/log/dserver/*.log' \
    -    --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    -             lifetimeConnections group by $hostname order by max($cgocalls)'
    +
    % dmap --servers serverlist.txt \
    +    --files '/var/log/dserver/*.log' \
    +    --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    +             lifetimeConnections group by $hostname order by max($cgocalls)'
     

    Remember: For that to work, you have to make sure that DTail supports your log format. You can either use the ones already defined in internal/mapr/logformat or add an extension to support a custom log format. The example here works out of the box though, as DTail understands its own log format already.
    @@ -225,9 +220,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dmap --files /var/log/dserver/dserver.log
    -    --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    -              lifetimeConnections group by $hostname order by max($cgocalls)'
    +
    % dmap --files /var/log/dserver/dserver.log
    +    --query 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    +              lifetimeConnections group by $hostname order by max($cgocalls)'
     

    As a shorthand version the following command can be used:
    @@ -236,9 +231,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    -        lifetimeConnections group by $hostname order by max($cgocalls)' \
    -        /var/log/dsever/dserver.log
    +
    % dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    +        lifetimeConnections group by $hostname order by max($cgocalls)' \
    +        /var/log/dsever/dserver.log
     

    You can also use a file input pipe as follows:
    @@ -247,9 +242,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % cat /var/log/dserver/dserver.log | \
    -    dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    -          lifetimeConnections group by $hostname order by max($cgocalls)'
    +
    % cat /var/log/dserver/dserver.log | \
    +    dmap 'from STATS select $hostname,max($goroutines),max($cgocalls),$loadavg,
    +          lifetimeConnections group by $hostname order by max($cgocalls)'
     

    Aggregating CSV files


    @@ -260,16 +255,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % cat example.csv
    -name,lastname,age,profession
    -Michael,Jordan,40,Basketball player
    -Michael,Jackson,100,Singer
    -Albert,Einstein,200,Physician
    -% dmap --query 'select lastname,name where age > 40 logformat csv outfile result.csv' example.csv
    -% cat result.csv
    -lastname,name
    -Jackson,Michael
    -Einstein,Albert
    +
    % cat example.csv
    +name,lastname,age,profession
    +Michael,Jordan,40,Basketball player
    +Michael,Jackson,100,Singer
    +Albert,Einstein,200,Physician
    +% dmap --query 'select lastname,name where age > 40 logformat csv outfile result.csv' example.csv
    +% cat result.csv
    +lastname,name
    +Jackson,Michael
    +Einstein,Albert
     

    DMap can also be used to query and aggregate CSV files from remote servers.
    @@ -282,44 +277,44 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % dtail /var/log/dserver/dserver.log
    +
    % dtail /var/log/dserver/dserver.log
     

    -
    % dtail --logLevel trace /var/log/dserver/dserver.log
    +
    % dtail --logLevel trace /var/log/dserver/dserver.log
     

    -
    % dcat /etc/passwd
    +
    % dcat /etc/passwd
     

    -
    % dcat --plain /etc/passwd > /etc/test
    -# Should show no differences.
    -diff /etc/test /etc/passwd 
    +
    % dcat --plain /etc/passwd > /etc/test
    +# Should show no differences.
    +diff /etc/test /etc/passwd 
     

    -
    % dgrep --regex ERROR --files /var/log/dserver/dsever.log
    +
    % dgrep --regex ERROR --files /var/log/dserver/dsever.log
     

    -
    % dgrep --before 10 --after 10 --max 10 --grep ERROR /var/log/dserver/dsever.log
    +
    % dgrep --before 10 --after 10 --max 10 --grep ERROR /var/log/dserver/dsever.log
     

    Use --help for more available options. Or go to the DTail page for more information! Hope you find DTail useful!
    @@ -339,11 +334,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html b/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html index c7706082..b67d90e4 100644 --- a/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html +++ b/gemfeed/2023-10-29-kiss-static-web-photo-albums-with-photoalbum.sh.html @@ -2,17 +2,12 @@ - KISS static web photo albums with `photoalbum.sh` -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -135,42 +130,42 @@ photoalbum makemake by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % photoalbum makemake
    -You may now customize ./photoalbumrc and run make
    +
    % photoalbum makemake
    +You may now customize ./photoalbumrc and run make
     
    -% cat Makefile
    -all:
    -	photoalbum generate photoalbumrc
    -clean:
    -	photoalbum clean photoalbumrc
    +% cat Makefile
    +all:
    +	photoalbum generate photoalbumrc
    +clean:
    +	photoalbum clean photoalbumrc
     
    -% cat photoalbumrc
    -# The title of the photoalbum
    -TITLE='A simple Photoalbum'
    +% cat photoalbumrc
    +# The title of the photoalbum
    +TITLE='A simple Photoalbum'
     
    -# Thumbnail height geometry
    -THUMBHEIGHT=300
    -# Normal geometry height (when viewing photo). Uncomment, to keep original size.
    -HEIGHT=1200
    -# Max previews per page.
    -MAXPREVIEWS=40
    -# Randomly shuffle all previews.
    -# SHUFFLE=yes
    +# Thumbnail height geometry
    +THUMBHEIGHT=300
    +# Normal geometry height (when viewing photo). Uncomment, to keep original size.
    +HEIGHT=1200
    +# Max previews per page.
    +MAXPREVIEWS=40
    +# Randomly shuffle all previews.
    +# SHUFFLE=yes
     
    -# Diverse directories, need to be full paths, not relative!
    -INCOMING_DIR=$(pwd)/incoming
    -DIST_DIR=$(pwd)/dist
    -TEMPLATE_DIR=/usr/share/photoalbum/templates/default
    -#TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal
    +# Diverse directories, need to be full paths, not relative!
    +INCOMING_DIR=$(pwd)/incoming
    +DIST_DIR=$(pwd)/dist
    +TEMPLATE_DIR=/usr/share/photoalbum/templates/default
    +#TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal
     
    -# Includes a .tar of the incoming dir in the dist, can be yes or no
    -TARBALL_INCLUDE=yes
    -TARBALL_SUFFIX=.tar
    -TAR_OPTS='-c'
    +# Includes a .tar of the incoming dir in the dist, can be yes or no
    +TARBALL_INCLUDE=yes
    +TARBALL_SUFFIX=.tar
    +TAR_OPTS='-c'
     
    -# Some debugging options
    -#set -e
    -#set -x
    +# Some debugging options
    +#set -e
    +#set -x
     

    In the case for irregular.ninja, I changed the defaults to the following:
    @@ -179,38 +174,38 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    --- photoalbumrc        2023-10-29 21:42:00.894202045 +0200
    -+++ photoalbumrc.new 2023-06-04 10:40:08.030994440 +0300
    -@@ -1,23 +1,24 @@
    - # The title of the photoalbum
    --TITLE='A simple Photoalbum'
    -+TITLE='Irregular.Ninja'
    +
    --- photoalbumrc        2023-10-29 21:42:00.894202045 +0200
    ++++ photoalbumrc.new 2023-06-04 10:40:08.030994440 +0300
    +@@ -1,23 +1,24 @@
    + # The title of the photoalbum
    +-TITLE='A simple Photoalbum'
    ++TITLE='Irregular.Ninja'
     
    - # Thumbnail height geometry
    --THUMBHEIGHT=300
    -+THUMBHEIGHT=400
    - # Normal geometry height (when viewing photo). Uncomment, to keep original size.
    --HEIGHT=1200
    -+HEIGHT=1800
    - # Max previews per page.
    - MAXPREVIEWS=40
    --# Randomly shuffle all previews.
    --# SHUFFLE=yes
    -+# Randomly shuffle
    -+SHUFFLE=yes
    + # Thumbnail height geometry
    +-THUMBHEIGHT=300
    ++THUMBHEIGHT=400
    + # Normal geometry height (when viewing photo). Uncomment, to keep original size.
    +-HEIGHT=1200
    ++HEIGHT=1800
    + # Max previews per page.
    + MAXPREVIEWS=40
    +-# Randomly shuffle all previews.
    +-# SHUFFLE=yes
    ++# Randomly shuffle
    ++SHUFFLE=yes
     
    - # Diverse directories, need to be full paths, not relative!
    --INCOMING_DIR=$(pwd)/incoming
    -+INCOMING_DIR=~/Nextcloud/Photos/irregular.ninja
    - DIST_DIR=$(pwd)/dist
    - TEMPLATE_DIR=/usr/share/photoalbum/templates/default
    - #TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal
    + # Diverse directories, need to be full paths, not relative!
    +-INCOMING_DIR=$(pwd)/incoming
    ++INCOMING_DIR=~/Nextcloud/Photos/irregular.ninja
    + DIST_DIR=$(pwd)/dist
    + TEMPLATE_DIR=/usr/share/photoalbum/templates/default
    + #TEMPLATE_DIR=/usr/share/photoalbum/templates/minimal
     
    - # Includes a .tar of the incoming dir in the dist, can be yes or no
    --TARBALL_INCLUDE=yes
    -+TARBALL_INCLUDE=no
    - TARBALL_SUFFIX=.tar
    - TAR_OPTS='-c'
    + # Includes a .tar of the incoming dir in the dist, can be yes or no
    +-TARBALL_INCLUDE=yes
    ++TARBALL_INCLUDE=no
    + TARBALL_SUFFIX=.tar
    + TAR_OPTS='-c'
     

    So I changed the album title, adjusted some image and thumbnail dimensions, and I want all images to be randomly shuffled every time the album is generated! I also have all my photos in my Nextcloud Photo directory and don't want to copy them to the local incoming directory. Also, a tarball containing the whole album as a download isn't provided.
    @@ -311,11 +306,11 @@ blurs html index.html photos thumbs
    Back to the main site
    diff --git a/gemfeed/2023-11-11-mind-management-book-notes.html b/gemfeed/2023-11-11-mind-management-book-notes.html index d10ffb0c..19d30776 100644 --- a/gemfeed/2023-11-11-mind-management-book-notes.html +++ b/gemfeed/2023-11-11-mind-management-book-notes.html @@ -2,17 +2,12 @@ - 'Mind Management' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -150,11 +145,11 @@
    Back to the main site
    diff --git a/gemfeed/2023-11-19-site-reliability-engineering-part-2.html b/gemfeed/2023-11-19-site-reliability-engineering-part-2.html index 8c5220c7..6cfd8a2c 100644 --- a/gemfeed/2023-11-19-site-reliability-engineering-part-2.html +++ b/gemfeed/2023-11-19-site-reliability-engineering-part-2.html @@ -2,17 +2,12 @@ - Site Reliability Engineering - Part 2: Operational Balance -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -68,11 +63,11 @@
    Back to the main site
    diff --git a/gemfeed/2023-12-10-bash-golf-part-3.html b/gemfeed/2023-12-10-bash-golf-part-3.html index db32cfae..0b848821 100644 --- a/gemfeed/2023-12-10-bash-golf-part-3.html +++ b/gemfeed/2023-12-10-bash-golf-part-3.html @@ -2,17 +2,12 @@ - Bash Golf Part 3 -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -61,24 +56,24 @@ jgs^^^^^^^`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -log () {
    -    local -r level="$1"; shift
    -    local -r message="$1"; shift
    -    local -i pid="$$"
    +log () {
    +    local -r level="$1"; shift
    +    local -r message="$1"; shift
    +    local -i pid="$$"
     
    -    local -r callee=${FUNCNAME[1]}
    -    local -r stamp=$(date +%Y%m%d-%H%M%S)
    +    local -r callee=${FUNCNAME[1]}
    +    local -r stamp=$(date +%Y%m%d-%H%M%S)
     
    -    echo "$level|$stamp|$pid|$callee|$message" >&2
    -}
    +    echo "$level|$stamp|$pid|$callee|$message" >&2
    +}
     
    -at_home_friday_evening () {
    -    log INFO 'One Peperoni Pizza, please'
    -}
    +at_home_friday_evening () {
    +    log INFO 'One Peperoni Pizza, please'
    +}
     
    -at_home_friday_evening
    +at_home_friday_evening
     

    The output is as follows:
    @@ -87,8 +82,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    ./logexample.sh
    -INFO|20231210-082732|123002|at_home_friday_evening|One Peperoni Pizza, please
    +
    ❯ ./logexample.sh
    +INFO|20231210-082732|123002|at_home_friday_evening|One Peperoni Pizza, please
     

    :(){ :|:& };:


    @@ -122,18 +117,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    -
    -outer() {
    -  inner() {
    -    echo 'Intel inside!'
    -  }
    -  inner
    -}
    -
    -inner
    -outer
    -inner
    +
    #!/usr/bin/env bash
    +
    +outer() {
    +  inner() {
    +    echo 'Intel inside!'
    +  }
    +  inner
    +}
    +
    +inner
    +outer
    +inner
     

    And let's execute it:
    @@ -151,26 +146,26 @@ Intel inside! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    -
    -outer1() {
    -  inner() {
    -    echo 'Intel inside!'
    -  }
    -  inner
    -}
    -
    -outer2() {
    -  inner() {
    -    echo 'Wintel inside!'
    -  }
    -  inner
    -}
    -
    -outer1
    -inner
    -outer2
    -inner
    +
    #!/usr/bin/env bash
    +
    +outer1() {
    +  inner() {
    +    echo 'Intel inside!'
    +  }
    +  inner
    +}
    +
    +outer2() {
    +  inner() {
    +    echo 'Wintel inside!'
    +  }
    +  inner
    +}
    +
    +outer1
    +inner
    +outer2
    +inner
     

    And let's run it:
    @@ -191,14 +186,14 @@ Wintel inside! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -some_expensive_operations() {
    -  echo "Doing expensive operations with '$1' from pid $$"
    -}
    +some_expensive_operations() {
    +  echo "Doing expensive operations with '$1' from pid $$"
    +}
     
    -for i in {0..9}; do echo $i; done \
    -  | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
    +for i in {0..9}; do echo $i; done \
    +  | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
     

    We try here to run ten parallel processes; each of them should run the some_expensive_operations function with a different argument. The arguments are provided to xargs through STDIN one per line. When executed, we get this:
    @@ -223,15 +218,15 @@ bash: line 1: some_expensive_operations: command not found by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -some_expensive_operations() {
    -  echo "Doing expensive operations with '$1' from pid $$"
    -}
    -export -f some_expensive_operations
    +some_expensive_operations() {
    +  echo "Doing expensive operations with '$1' from pid $$"
    +}
    +export -f some_expensive_operations
     
    -for i in {0..9}; do echo $i; done \
    -  | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
    +for i in {0..9}; do echo $i; done \
    +  | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
     

    When we run this now, we get:
    @@ -256,19 +251,19 @@ Doing expensive operations with '9' from pid 132840 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -some_other_function() {
    -  echo "$1"
    -}
    +some_other_function() {
    +  echo "$1"
    +}
     
    -some_expensive_operations() {
    -  some_other_function "Doing expensive operations with '$1' from pid $$"
    -}
    -export -f some_expensive_operations
    +some_expensive_operations() {
    +  some_other_function "Doing expensive operations with '$1' from pid $$"
    +}
    +export -f some_expensive_operations
     
    -for i in {0..9}; do echo $i; done \
    -  | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
    +for i in {0..9}; do echo $i; done \
    +  | xargs -P10 -I{} bash -c 'some_expensive_operations "{}"'
     

    ... because some_other_function isn't exported! You will also need to add an export -f some_other_function!
    @@ -281,22 +276,22 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    -
    -foo() {
    -  local foo=bar # Declare local/dynamic variable
    -  bar
    -  echo "$foo"
    -}
    -
    -bar() {
    -  echo "$foo"
    -  foo=baz
    -}
    -
    -foo=foo # Declare global variable
    -foo # Call function foo
    -echo "$foo"
    +
    #!/usr/bin/env bash
    +
    +foo() {
    +  local foo=bar # Declare local/dynamic variable
    +  bar
    +  echo "$foo"
    +}
    +
    +bar() {
    +  echo "$foo"
    +  foo=baz
    +}
    +
    +foo=foo # Declare global variable
    +foo # Call function foo
    +echo "$foo"
     

    Let's pause a minute. What do you think the output would be?
    @@ -321,34 +316,34 @@ foo by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -declare -r foo=foo
    -declare -r bar=bar
    +declare -r foo=foo
    +declare -r bar=bar
     
    -if [ "$foo" = foo ]; then
    -  if [ "$bar" = bar ]; then
    -    echo ok1
    -  fi
    -fi
    +if [ "$foo" = foo ]; then
    +  if [ "$bar" = bar ]; then
    +    echo ok1
    +  fi
    +fi
     
    -if [ "$foo" = foo ] && [ "$bar" == bar ]; then
    -  echo ok2a
    -fi
    +if [ "$foo" = foo ] && [ "$bar" == bar ]; then
    +  echo ok2a
    +fi
     
    -[ "$foo" = foo ] && [ "$bar" == bar ] && echo ok2b
    +[ "$foo" = foo ] && [ "$bar" == bar ] && echo ok2b
     
    -if [[ "$foo" = foo && "$bar" == bar ]]; then
    -  echo ok3a
    -fi
    +if [[ "$foo" = foo && "$bar" == bar ]]; then
    +  echo ok3a
    +fi
     
    - [[ "$foo" = foo && "$bar" == bar ]] && echo ok3b
    + [[ "$foo" = foo && "$bar" == bar ]] && echo ok3b
     
    -if test "$foo" = foo && test "$bar" = bar; then
    -  echo ok4a
    -fi
    +if test "$foo" = foo && test "$bar" = bar; then
    +  echo ok4a
    +fi
     
    -test "$foo" = foo && test "$bar" = bar && echo ok4b
    +test "$foo" = foo && test "$bar" = bar && echo ok4b
     

    The output we get is:
    @@ -372,18 +367,18 @@ ok4b by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -# Single line comment
    +# Single line comment
     
    -# These are two single line
    -# comments one after another
    +# These are two single line
    +# comments one after another
     
    -: <<COMMENT
    -This is another way a
    -multi line comment
    -could be written!
    -COMMENT
    +: <<COMMENT
    +This is another way a
    +multi line comment
    +could be written!
    +COMMENT
     

    I will not demonstrate the execution of this script, as it won't print anything! It's obviously not the most pretty way of commenting on your code, but it could sometimes be handy!
    @@ -396,11 +391,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/env bash
    +
    #!/usr/bin/env bash
     
    -echo foo
    -echo echo baz >> $0
    -echo bar
    +echo foo
    +echo echo baz >> $0
    +echo bar
     

    When it is run, it will do:
    @@ -434,11 +429,11 @@ echo baz
    Back to the main site
    diff --git a/gemfeed/2024-01-09-site-reliability-engineering-part-3.html b/gemfeed/2024-01-09-site-reliability-engineering-part-3.html index 4dd24489..6df79f17 100644 --- a/gemfeed/2024-01-09-site-reliability-engineering-part-3.html +++ b/gemfeed/2024-01-09-site-reliability-engineering-part-3.html @@ -2,17 +2,12 @@ - Site Reliability Engineering - Part 3: On-Call Culture -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -82,11 +77,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html b/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html index a7ada5bc..0f201cc5 100644 --- a/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html +++ b/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.html @@ -2,17 +2,12 @@ - One reason why I love OpenBSD -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -44,8 +39,8 @@ SSFISHKISSFISHKISSFISHKISSFISHKIS SFIS by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    $ doas installboot sd0 # Update the bootloader (not for every upgrade required)
    -$ doas sysupgrade # Update all binaries (including Kernel)
    +
    $ doas installboot sd0 # Update the bootloader (not for every upgrade required)
    +$ doas sysupgrade # Update all binaries (including Kernel)
     

    sysupgrade downloaded and upgraded to the next release and rebooted the system. After the reboot, I run:
    @@ -56,9 +51,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    $ doas sysmerge # Update system configuration files
    -$ doas pkg_add -u # Update all packages
    -$ doas reboot # Just in case, reboot one more time
    +
    $ doas sysmerge # Update system configuration files
    +$ doas pkg_add -u # Update all packages
    +$ doas reboot # Just in case, reboot one more time
     

    That's it! Took me around 5 minutes in total! No issues, only these few comands, only 5 minutes! It just works! No problems, no conflicts, no tons (actually none) config file merge conflicts.
    @@ -93,11 +88,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html b/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html index eff38503..fc4904ab 100644 --- a/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html +++ b/gemfeed/2024-02-04-from-babylon5.buetow.org-to-.cloud.html @@ -2,17 +2,12 @@ - From `babylon5.buetow.org` to `*.buetow.cloud` -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -202,11 +197,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html b/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html index 16e9717b..29e335bc 100644 --- a/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html +++ b/gemfeed/2024-03-03-a-fine-fyne-android-app-for-quickly-logging-ideas-programmed-in-golang.html @@ -2,17 +2,12 @@ - A fine Fyne Android app for quickly logging ideas programmed in Go -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -79,11 +74,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html b/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html index 16bff225..ed9f17dd 100644 --- a/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html +++ b/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.html @@ -2,17 +2,12 @@ - KISS high-availability with OpenBSD -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -103,38 +98,38 @@ _____|_:_:_| (o)-(o) |_:_:_|--'`-. ,--. ksh under-water (((\'/ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/bin/ksh
    +
    #!/bin/ksh
     
    -ZONES_DIR=/var/nsd/zones/master/
    -DEFAULT_MASTER=fishfinger.buetow.org
    -DEFAULT_STANDBY=blowfish.buetow.org
    +ZONES_DIR=/var/nsd/zones/master/
    +DEFAULT_MASTER=fishfinger.buetow.org
    +DEFAULT_STANDBY=blowfish.buetow.org
     
    -determine_master_and_standby () {
    -    local master=$DEFAULT_MASTER
    -    local standby=$DEFAULT_STANDBY
    +determine_master_and_standby () {
    +    local master=$DEFAULT_MASTER
    +    local standby=$DEFAULT_STANDBY
     
    -    .
    -    .
    -    .
    -    
    -    local -i health_ok=1
    -    if ! ftp -4 -o - https://$master/index.txt | grep -q "Welcome to $master"; then
    -        echo "https://$master/index.txt IPv4 health check failed"
    -        health_ok=0
    -    elif ! ftp -6 -o - https://$master/index.txt | grep -q "Welcome to $master"; then
    -        echo "https://$master/index.txt IPv6 health check failed"
    -        health_ok=0
    -    fi
    -    if [ $health_ok -eq 0 ]; then
    -        local tmp=$master
    -        master=$standby
    -        standby=$tmp
    -    fi
    +    .
    +    .
    +    .
    +    
    +    local -i health_ok=1
    +    if ! ftp -4 -o - https://$master/index.txt | grep -q "Welcome to $master"; then
    +        echo "https://$master/index.txt IPv4 health check failed"
    +        health_ok=0
    +    elif ! ftp -6 -o - https://$master/index.txt | grep -q "Welcome to $master"; then
    +        echo "https://$master/index.txt IPv6 health check failed"
    +        health_ok=0
    +    fi
    +    if [ $health_ok -eq 0 ]; then
    +        local tmp=$master
    +        master=$standby
    +        standby=$tmp
    +    fi
     
    -    .
    -    .
    -    .
    -}
    +    .
    +    .
    +    .
    +}
     

    The failover scripts looks for the ; Enable failover string in the DNS zone files and swaps the A and AAAA records of the DNS entries accordingly:
    @@ -143,42 +138,42 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    fishfinger$ grep failover /var/nsd/zones/master/foo.zone.zone
    -        300 IN A 46.23.94.99 ; Enable failover
    -        300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover
    -www     300 IN A 46.23.94.99 ; Enable failover
    -www     300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover
    -standby  300 IN A 23.88.35.144 ; Enable failover
    -standby  300 IN AAAA 2a01:4f8:c17:20f1::42 ; Enable failover
    +
    fishfinger$ grep failover /var/nsd/zones/master/foo.zone.zone
    +        300 IN A 46.23.94.99 ; Enable failover
    +        300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover
    +www     300 IN A 46.23.94.99 ; Enable failover
    +www     300 IN AAAA 2a03:6000:6f67:624::99 ; Enable failover
    +standby  300 IN A 23.88.35.144 ; Enable failover
    +standby  300 IN AAAA 2a01:4f8:c17:20f1::42 ; Enable failover
     

    -
    transform () {
    -  sed -E '
    -	/IN A .*; Enable failover/ {
    -	    /^standby/! {
    -	        s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/master_a)' ; \3/;
    -	    }
    -	    /^standby/ {
    -	        s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/standby_a)' ; \3/;
    -	    }
    -	}
    -	/IN AAAA .*; Enable failover/ {
    -	    /^standby/! {
    -	        s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/master_aaaa)' ; \3/;
    -	    }
    -	    /^standby/ {
    -	        s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/standby_aaaa)' ; \3/;
    -	    }
    -	}
    -	/ ; serial/ {
    -	    s/^( +) ([0-9]+) .*; (.*)/\1 '$(date +%s)' ; \3/;
    -	}
    -  '
    -}
    +
    transform () {
    +  sed -E '
    +	/IN A .*; Enable failover/ {
    +	    /^standby/! {
    +	        s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/master_a)' ; \3/;
    +	    }
    +	    /^standby/ {
    +	        s/^(.*) 300 IN A (.*) ; (.*)/\1 300 IN A '$(cat /var/nsd/run/standby_a)' ; \3/;
    +	    }
    +	}
    +	/IN AAAA .*; Enable failover/ {
    +	    /^standby/! {
    +	        s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/master_aaaa)' ; \3/;
    +	    }
    +	    /^standby/ {
    +	        s/^(.*) 300 IN AAAA (.*) ; (.*)/\1 300 IN AAAA '$(cat /var/nsd/run/standby_aaaa)' ; \3/;
    +	    }
    +	}
    +	/ ; serial/ {
    +	    s/^( +) ([0-9]+) .*; (.*)/\1 '$(date +%s)' ; \3/;
    +	}
    +  '
    +}
     

    After the failover, the script reloads nsd and performs a sanity check to see if DNS still works. If not, a rollback will be performed:
    @@ -187,48 +182,48 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #! Race condition !#
    -   
    -if [ -f $zone_file.bak ]; then
    -    mv $zone_file.bak $zone_file
    -fi
    +
    #! Race condition !#
    +   
    +if [ -f $zone_file.bak ]; then
    +    mv $zone_file.bak $zone_file
    +fi
     
    -cat $zone_file | transform > $zone_file.new.tmp 
    +cat $zone_file | transform > $zone_file.new.tmp 
     
    -grep -v ' ; serial' $zone_file.new.tmp > $zone_file.new.noserial.tmp
    -grep -v ' ; serial' $zone_file > $zone_file.old.noserial.tmp
    +grep -v ' ; serial' $zone_file.new.tmp > $zone_file.new.noserial.tmp
    +grep -v ' ; serial' $zone_file > $zone_file.old.noserial.tmp
     
    -echo "Has zone $zone_file changed?"
    -if diff -u $zone_file.old.noserial.tmp $zone_file.new.noserial.tmp; then
    -    echo "The zone $zone_file hasn't changed"
    -    rm $zone_file.*.tmp
    -    return 0
    -fi
    +echo "Has zone $zone_file changed?"
    +if diff -u $zone_file.old.noserial.tmp $zone_file.new.noserial.tmp; then
    +    echo "The zone $zone_file hasn't changed"
    +    rm $zone_file.*.tmp
    +    return 0
    +fi
     
    -cp $zone_file $zone_file.bak
    -mv $zone_file.new.tmp $zone_file
    -rm $zone_file.*.tmp
    -echo "Reloading nsd"
    -nsd-control reload
    +cp $zone_file $zone_file.bak
    +mv $zone_file.new.tmp $zone_file
    +rm $zone_file.*.tmp
    +echo "Reloading nsd"
    +nsd-control reload
     
    -if ! zone_is_ok $zone; then
    -    echo "Rolling back $zone_file changes"
    -    cp $zone_file $zone_file.invalid
    -    mv $zone_file.bak $zone_file
    -    echo "Reloading nsd"
    -    nsd-control reload
    -    zone_is_ok $zone
    -    return 3
    -fi
    +if ! zone_is_ok $zone; then
    +    echo "Rolling back $zone_file changes"
    +    cp $zone_file $zone_file.invalid
    +    mv $zone_file.bak $zone_file
    +    echo "Reloading nsd"
    +    nsd-control reload
    +    zone_is_ok $zone
    +    return 3
    +fi
     
    -for cleanup in invalid bak; do
    -    if [ -f $zone_file.$cleanup ]; then
    -        rm $zone_file.$cleanup
    -    fi
    -done
    +for cleanup in invalid bak; do
    +    if [ -f $zone_file.$cleanup ]; then
    +        rm $zone_file.$cleanup
    +    fi
    +done
     
    -echo "Failover of zone $zone to $MASTER completed"
    -return 1
    +echo "Failover of zone $zone to $MASTER completed"
    +return 1
     

    A non-zero return code (here, 3 when a rollback and 1 when a DNS failover was performed) will cause CRON to send an E-Mail with the whole script output.
    @@ -285,13 +280,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Weekly auto-failover for Let's Encrypt automation
    -local -i -r week_of_the_year=$(date +%U)
    -if [ $(( week_of_the_year % 2 )) -eq 0 ]; then
    -    local tmp=$master
    -    master=$standby
    -    standby=$tmp
    -fi
    +
    # Weekly auto-failover for Let's Encrypt automation
    +local -i -r week_of_the_year=$(date +%U)
    +if [ $(( week_of_the_year % 2 )) -eq 0 ]; then
    +    local tmp=$master
    +    master=$standby
    +    standby=$tmp
    +fi
     

    This way, a DNS failover is performed weekly so that the ACME automation can update the Let's Encrypt certificates (for master and standby) before they expire on each VM.
    @@ -354,11 +349,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2024-05-01-slow-productivity-book-notes.html b/gemfeed/2024-05-01-slow-productivity-book-notes.html index 9ad53a95..8ab1182a 100644 --- a/gemfeed/2024-05-01-slow-productivity-book-notes.html +++ b/gemfeed/2024-05-01-slow-productivity-book-notes.html @@ -2,17 +2,12 @@ - 'Slow Productivity' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -176,11 +171,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html b/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html index 240f835e..7889e2b9 100644 --- a/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html +++ b/gemfeed/2024-05-03-projects-i-currently-dont-have-time-for.html @@ -2,17 +2,12 @@ - Projects I currently don't have time for -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -225,12 +220,12 @@ Art by Laura Brown by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    Cluster :UK, :uk01 do
    -  Customer.C1A1.segments.volumes.each do |volume|
    -    puts volume.usage_stats
    -    volume.move_off! if volume.over_subscribed?
    -  end
    -end
    +
    Cluster :UK, :uk01 do
    +  Customer.C1A1.segments.volumes.each do |volume|
    +    puts volume.usage_stats
    +    volume.move_off! if volume.over_subscribed?
    +  end
    +end
     

    I am abandoning this project because my workplace has stopped the annual pet project competition, and I have other more important projects to work on at the moment.
    @@ -332,11 +327,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html b/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html index 505e0e34..a64fd737 100644 --- a/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html +++ b/gemfeed/2024-06-23-terminal-multiplexing-with-tmux.html @@ -2,17 +2,12 @@ - Terminal multiplexing with `tmux` - Z-Shell edition -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -95,13 +90,13 @@ jgs `-=========-`() by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    alias tm=tmux
    -alias tl='tmux list-sessions'
    -alias tn=tmux::new
    -alias ta=tmux::attach
    -alias tx=tmux::remote
    -alias ts=tmux::search
    -alias tssh=tmux::cluster_ssh
    +
    alias tm=tmux
    +alias tl='tmux list-sessions'
    +alias tn=tmux::new
    +alias ta=tmux::attach
    +alias tx=tmux::remote
    +alias ts=tmux::search
    +alias tssh=tmux::cluster_ssh
     

    Note all tmux::...; those are custom shell functions doing certain things, and they aren't part of the Tmux distribution. But let's run through every alias one by one.
    @@ -116,23 +111,23 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Create new session and if already exists attach to it
    -tmux::new () {
    -    readonly session=$1
    -    local date=date
    -    if where gdate &>/dev/null; then
    -        date=gdate
    -    fi
    +
    # Create new session and if already exists attach to it
    +tmux::new () {
    +    readonly session=$1
    +    local date=date
    +    if where gdate &>/dev/null; then
    +        date=gdate
    +    fi
     
    -    tmux::cleanup_default
    -    if [ -z "$session" ]; then
    -        tmux::new T$($date +%s)
    -    else
    -        tmux new-session -d -s $session
    -        tmux -2 attach-session -t $session || tmux -2 switch-client -t $session
    -    fi
    -}
    -alias tn=tmux::new
    +    tmux::cleanup_default
    +    if [ -z "$session" ]; then
    +        tmux::new T$($date +%s)
    +    else
    +        tmux new-session -d -s $session
    +        tmux -2 attach-session -t $session || tmux -2 switch-client -t $session
    +    fi
    +}
    +alias tn=tmux::new
     

    There is a lot going on here. Let's have a detailed look at what it is doing. As a note, the function relies on GNU Date, so MacOS is looking for the gdate commands to be available. Otherwise, it will fall back to date. You need to install GNU Date for Mac, as it isn't installed by default there. As I use Fedora Linux on my personal Laptop and a MacBook for work, I have to make it work for both.
    @@ -147,14 +142,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::cleanup_default () {
    -    local s
    -    tmux list-sessions | grep '^T.*: ' | grep -F -v attached |
    -    cut -d: -f1 | while read -r s; do
    -        echo "Killing $s"
    -        tmux kill-session -t "$s"
    -    done
    -}
    +
    tmux::cleanup_default () {
    +    local s
    +    tmux list-sessions | grep '^T.*: ' | grep -F -v attached |
    +    cut -d: -f1 | while read -r s; do
    +        echo "Killing $s"
    +        tmux kill-session -t "$s"
    +    done
    +}
     

    The cleanup function kills all open Tmux sessions that haven't been renamed properly yet—but only if they aren't attached (e.g., don't run in the foreground in any terminal). Cleaning them up automatically keeps my Tmux sessions as neat and tidy as possible.
    @@ -171,16 +166,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::attach () {
    -    readonly session=$1
    +
    tmux::attach () {
    +    readonly session=$1
     
    -    if [ -z "$session" ]; then
    -        tmux attach-session || tmux::new
    -    else
    -        tmux attach-session -t $session || tmux::new $session
    -    fi
    -}
    -alias ta=tmux::attach
    +    if [ -z "$session" ]; then
    +        tmux attach-session || tmux::new
    +    else
    +        tmux attach-session -t $session || tmux::new $session
    +    fi
    +}
    +alias ta=tmux::attach
     

    If no session is specified (as the argument of the function), it will try to attach to the first open session. If no Tmux server is running, it will create a new one with tmux::new. Otherwise, with a session name given as the argument, it will attach to it. If unsuccessful (e.g., the session doesn't exist), it will be created and attached to.
    @@ -193,12 +188,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::remote () {
    -    readonly server=$1
    -    tmux new -s $server "ssh -t $server 'tmux attach-session || tmux'" || \
    -        tmux attach-session -d -t $server
    -}
    -alias tr=tmux::remote
    +
    tmux::remote () {
    +    readonly server=$1
    +    tmux new -s $server "ssh -t $server 'tmux attach-session || tmux'" || \
    +        tmux attach-session -d -t $server
    +}
    +alias tr=tmux::remote
     

    Change of the Tmux prefix for better nesting


    @@ -221,15 +216,15 @@ set-option -g prefix C-g by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::search () {
    -    local -r session=$(tmux list-sessions | fzf | cut -d: -f1)
    -    if [ -z "$TMUX" ]; then
    -        tmux attach-session -t $session
    -    else
    -        tmux switch -t $session
    -    fi
    -}
    -alias ts=tmux::search
    +
    tmux::search () {
    +    local -r session=$(tmux list-sessions | fzf | cut -d: -f1)
    +    if [ -z "$TMUX" ]; then
    +        tmux attach-session -t $session
    +    else
    +        tmux switch -t $session
    +    fi
    +}
    +alias ts=tmux::search
     

    All it does is list all currently open sessions in fzf, where one of them can be searched and selected through fuzzy find, and then either switch (if already inside a session) to the other session or attach to the other session (if not yet in Tmux).
    @@ -256,15 +251,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::cluster_ssh () {
    -    if [ -f "$1" ]; then
    -        tmux::tssh_from_file $1
    -        return
    -    fi
    +
    tmux::cluster_ssh () {
    +    if [ -f "$1" ]; then
    +        tmux::tssh_from_file $1
    +        return
    +    fi
     
    -    tmux::tssh_from_argument $@
    -}
    -alias tssh=tmux::cluster_ssh
    +    tmux::tssh_from_argument $@
    +}
    +alias tssh=tmux::cluster_ssh
     

    This function is just a wrapper around the more complex tmux::tssh_from_file and tmux::tssh_from_argument functions, as you have learned already. Most of the magic happens there.
    @@ -277,23 +272,23 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::tssh_from_argument () {
    -    local -r session=$1; shift
    -    local first_server=$1; shift
    +
    tmux::tssh_from_argument () {
    +    local -r session=$1; shift
    +    local first_server=$1; shift
     
    -    tmux new-session -d -s $session "ssh -t $first_server"
    -    if ! tmux list-session | grep "^$session:"; then
    -        echo "Could not create session $session"
    -        return 2
    -    fi
    +    tmux new-session -d -s $session "ssh -t $first_server"
    +    if ! tmux list-session | grep "^$session:"; then
    +        echo "Could not create session $session"
    +        return 2
    +    fi
     
    -    for server in "${@[@]}"; do
    -        tmux split-window -t $session "tmux select-layout tiled; ssh -t $server"
    -    done
    +    for server in "${@[@]}"; do
    +        tmux split-window -t $session "tmux select-layout tiled; ssh -t $server"
    +    done
     
    -    tmux setw -t $session synchronize-panes on
    -    tmux -2 attach-session -t $session | tmux -2 switch-client -t $session
    -}
    +    tmux setw -t $session synchronize-panes on
    +    tmux -2 attach-session -t $session | tmux -2 switch-client -t $session
    +}
     

    It expects at least two arguments. The first argument is the session name to create for the clustered SSH session. All other arguments are server hostnames or FQDNs to which to connect. The first one is used to make the initial session. All remaining ones are added to that session with tmux split-window -t $session.... At the end, we enable synchronized panes by default, so whenever you type, the commands will be sent to every SSH connection, thus allowing the neat ClusterSSH feature to run commands on multiple servers simultaneously. Once done, we attach (or switch, if already in Tmux) to it.
    @@ -313,12 +308,12 @@ bind-key P setw synchronize-panes on by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    tmux::tssh_from_file () {
    -    local -r serverlist=$1; shift
    -    local -r session=$(basename $serverlist | cut -d. -f1)
    +
    tmux::tssh_from_file () {
    +    local -r serverlist=$1; shift
    +    local -r session=$(basename $serverlist | cut -d. -f1)
     
    -    tmux::tssh_from_argument $session $(awk '{ print $1} ' $serverlist | sed 's/.lan./.lan/g')
    -}
    +    tmux::tssh_from_argument $session $(awk '{ print $1} ' $serverlist | sed 's/.lan./.lan/g')
    +}
     

    tssh examples


    @@ -449,11 +444,11 @@ bind-key r source-file ~/.config/tmux/tmux.conf \; display-message "tmux.conf re
    Back to the main site
    diff --git a/gemfeed/2024-07-05-random-weird-things.html b/gemfeed/2024-07-05-random-weird-things.html index 08899a99..9645d261 100644 --- a/gemfeed/2024-07-05-random-weird-things.html +++ b/gemfeed/2024-07-05-random-weird-things.html @@ -2,17 +2,12 @@ - Random Weird Things - Part Ⅰ -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -61,52 +56,52 @@ WHOA!! ( o.o ) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    ❯ traceroute -m 60 bad.horse
    -traceroute to bad.horse (162.252.205.157), 60 hops max, 60 byte packets
    - 1  _gateway (192.168.1.1)  5.237 ms  5.264 ms  6.009 ms
    - 2  77-85-0-2.ip.btc-net.bg (77.85.0.2)  8.753 ms  7.112 ms  8.336 ms
    - 3  212-39-69-103.ip.btc-net.bg (212.39.69.103)  9.434 ms  9.268 ms  9.986 ms
    - 4  * * *
    - 5  xe-1-2-0.mpr1.fra4.de.above.net (80.81.194.26)  39.812 ms  39.030 ms  39.772 ms
    - 6  * ae12.cs1.fra6.de.eth.zayo.com (64.125.26.172)  123.576 ms *
    - 7  * * *
    - 8  * * *
    - 9  ae10.cr1.lhr15.uk.eth.zayo.com (64.125.29.17)  119.097 ms  119.478 ms  120.767 ms
    -10  ae2.cr1.lhr11.uk.zip.zayo.com (64.125.24.140)  120.398 ms  121.147 ms  120.948 ms
    -11  * * *
    -12  ae25.mpr1.yyz1.ca.zip.zayo.com (64.125.23.117)  145.072 ms *  181.773 ms
    -13  ae5.mpr1.tor3.ca.zip.zayo.com (64.125.23.118)  168.239 ms  168.158 ms  168.137 ms
    -14  64.124.217.237.IDIA-265104-ZYO.zip.zayo.com (64.124.217.237)  168.026 ms  167.999 ms  165.451 ms
    -15  * * *
    -16  t00.toroc1.on.ca.sn11.net (162.252.204.2)  131.598 ms  131.308 ms  131.482 ms
    -17  bad.horse (162.252.205.130)  131.430 ms  145.914 ms  130.514 ms
    -18  bad.horse (162.252.205.131)  136.634 ms  145.295 ms  135.631 ms
    -19  bad.horse (162.252.205.132)  139.158 ms  148.363 ms  138.934 ms
    -20  bad.horse (162.252.205.133)  145.395 ms  148.054 ms  147.140 ms
    -21  he.rides.across.the.nation (162.252.205.134)  149.687 ms  147.731 ms  150.135 ms
    -22  the.thoroughbred.of.sin (162.252.205.135)  156.644 ms  155.155 ms  156.447 ms
    -23  he.got.the.application (162.252.205.136)  161.187 ms  162.318 ms  162.674 ms
    -24  that.you.just.sent.in (162.252.205.137)  166.763 ms  166.675 ms  164.243 ms
    -25  it.needs.evaluation (162.252.205.138)  172.073 ms  171.919 ms  171.390 ms
    -26  so.let.the.games.begin (162.252.205.139)  175.386 ms  174.180 ms  175.965 ms
    -27  a.heinous.crime (162.252.205.140)  180.857 ms  180.766 ms  180.192 ms
    -28  a.show.of.force (162.252.205.141)  187.942 ms  186.669 ms  186.986 ms
    -29  a.murder.would.be.nice.of.course (162.252.205.142)  191.349 ms  191.939 ms  190.740 ms
    -30  bad.horse (162.252.205.143)  195.425 ms  195.716 ms  196.186 ms
    -31  bad.horse (162.252.205.144)  199.238 ms  200.620 ms  200.318 ms
    -32  bad.horse (162.252.205.145)  207.554 ms  206.729 ms  205.201 ms
    -33  he-s.bad (162.252.205.146)  211.087 ms  211.649 ms  211.712 ms
    -34  the.evil.league.of.evil (162.252.205.147)  212.657 ms  216.777 ms  216.589 ms
    -35  is.watching.so.beware (162.252.205.148)  220.911 ms  220.326 ms  221.961 ms
    -36  the.grade.that.you.receive (162.252.205.149)  225.384 ms  225.696 ms  225.640 ms
    -37  will.be.your.last.we.swear (162.252.205.150)  232.312 ms  230.989 ms  230.919 ms
    -38  so.make.the.bad.horse.gleeful (162.252.205.151)  235.761 ms  235.291 ms  235.585 ms
    -39  or.he-ll.make.you.his.mare (162.252.205.152)  241.350 ms  239.407 ms  238.394 ms
    -40  o_o (162.252.205.153)  246.154 ms  247.650 ms  247.110 ms
    -41  you-re.saddled.up (162.252.205.154)  250.925 ms  250.401 ms  250.619 ms
    -42  there-s.no.recourse (162.252.205.155)  256.071 ms  251.154 ms  255.340 ms
    -43  it-s.hi-ho.silver (162.252.205.156)  260.152 ms  261.775 ms  261.544 ms
    -44  signed.bad.horse (162.252.205.157)  262.430 ms  261.410 ms  261.365 ms
    +
    ❯ traceroute -m 60 bad.horse
    +traceroute to bad.horse (162.252.205.157), 60 hops max, 60 byte packets
    + 1  _gateway (192.168.1.1)  5.237 ms  5.264 ms  6.009 ms
    + 2  77-85-0-2.ip.btc-net.bg (77.85.0.2)  8.753 ms  7.112 ms  8.336 ms
    + 3  212-39-69-103.ip.btc-net.bg (212.39.69.103)  9.434 ms  9.268 ms  9.986 ms
    + 4  * * *
    + 5  xe-1-2-0.mpr1.fra4.de.above.net (80.81.194.26)  39.812 ms  39.030 ms  39.772 ms
    + 6  * ae12.cs1.fra6.de.eth.zayo.com (64.125.26.172)  123.576 ms *
    + 7  * * *
    + 8  * * *
    + 9  ae10.cr1.lhr15.uk.eth.zayo.com (64.125.29.17)  119.097 ms  119.478 ms  120.767 ms
    +10  ae2.cr1.lhr11.uk.zip.zayo.com (64.125.24.140)  120.398 ms  121.147 ms  120.948 ms
    +11  * * *
    +12  ae25.mpr1.yyz1.ca.zip.zayo.com (64.125.23.117)  145.072 ms *  181.773 ms
    +13  ae5.mpr1.tor3.ca.zip.zayo.com (64.125.23.118)  168.239 ms  168.158 ms  168.137 ms
    +14  64.124.217.237.IDIA-265104-ZYO.zip.zayo.com (64.124.217.237)  168.026 ms  167.999 ms  165.451 ms
    +15  * * *
    +16  t00.toroc1.on.ca.sn11.net (162.252.204.2)  131.598 ms  131.308 ms  131.482 ms
    +17  bad.horse (162.252.205.130)  131.430 ms  145.914 ms  130.514 ms
    +18  bad.horse (162.252.205.131)  136.634 ms  145.295 ms  135.631 ms
    +19  bad.horse (162.252.205.132)  139.158 ms  148.363 ms  138.934 ms
    +20  bad.horse (162.252.205.133)  145.395 ms  148.054 ms  147.140 ms
    +21  he.rides.across.the.nation (162.252.205.134)  149.687 ms  147.731 ms  150.135 ms
    +22  the.thoroughbred.of.sin (162.252.205.135)  156.644 ms  155.155 ms  156.447 ms
    +23  he.got.the.application (162.252.205.136)  161.187 ms  162.318 ms  162.674 ms
    +24  that.you.just.sent.in (162.252.205.137)  166.763 ms  166.675 ms  164.243 ms
    +25  it.needs.evaluation (162.252.205.138)  172.073 ms  171.919 ms  171.390 ms
    +26  so.let.the.games.begin (162.252.205.139)  175.386 ms  174.180 ms  175.965 ms
    +27  a.heinous.crime (162.252.205.140)  180.857 ms  180.766 ms  180.192 ms
    +28  a.show.of.force (162.252.205.141)  187.942 ms  186.669 ms  186.986 ms
    +29  a.murder.would.be.nice.of.course (162.252.205.142)  191.349 ms  191.939 ms  190.740 ms
    +30  bad.horse (162.252.205.143)  195.425 ms  195.716 ms  196.186 ms
    +31  bad.horse (162.252.205.144)  199.238 ms  200.620 ms  200.318 ms
    +32  bad.horse (162.252.205.145)  207.554 ms  206.729 ms  205.201 ms
    +33  he-s.bad (162.252.205.146)  211.087 ms  211.649 ms  211.712 ms
    +34  the.evil.league.of.evil (162.252.205.147)  212.657 ms  216.777 ms  216.589 ms
    +35  is.watching.so.beware (162.252.205.148)  220.911 ms  220.326 ms  221.961 ms
    +36  the.grade.that.you.receive (162.252.205.149)  225.384 ms  225.696 ms  225.640 ms
    +37  will.be.your.last.we.swear (162.252.205.150)  232.312 ms  230.989 ms  230.919 ms
    +38  so.make.the.bad.horse.gleeful (162.252.205.151)  235.761 ms  235.291 ms  235.585 ms
    +39  or.he-ll.make.you.his.mare (162.252.205.152)  241.350 ms  239.407 ms  238.394 ms
    +40  o_o (162.252.205.153)  246.154 ms  247.650 ms  247.110 ms
    +41  you-re.saddled.up (162.252.205.154)  250.925 ms  250.401 ms  250.619 ms
    +42  there-s.no.recourse (162.252.205.155)  256.071 ms  251.154 ms  255.340 ms
    +43  it-s.hi-ho.silver (162.252.205.156)  260.152 ms  261.775 ms  261.544 ms
    +44  signed.bad.horse (162.252.205.157)  262.430 ms  261.410 ms  261.365 ms
     

    2. ASCII cinema


    @@ -134,20 +129,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #include <stdio.h>
    +
    #include <stdio.h>
     
    -int main(void) {
    -  int array[5] = { 1, 2, 3, 4, 5 };
    +int main(void) {
    +  int array[5] = { 1, 2, 3, 4, 5 };
     
    -  for (int i = 0; i < 5; i++)
    -    printf("%d\n", array[i]);
    +  for (int i = 0; i < 5; i++)
    +    printf("%d\n", array[i]);
     
    -  for (int i = 0; i < 5; i++)
    -    printf("%d\n", i[array]);
    +  for (int i = 0; i < 5; i++)
    +    printf("%d\n", i[array]);
     
    -  for (int i = 0; i < 5; i++)
    -    printf("%d\n", *(i + array));
    -}
    +  for (int i = 0; i < 5; i++)
    +    printf("%d\n", *(i + array));
    +}
     

    5. Variables with prefix $


    @@ -158,20 +153,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #include <stdio.h>
    +
    #include <stdio.h>
     
    -int main(void) {
    -  int $array[5] = { 1, 2, 3, 4, 5 };
    +int main(void) {
    +  int $array[5] = { 1, 2, 3, 4, 5 };
     
    -  for (int $i = 0; $i < 5; $i++)
    -    printf("%d\n", $array[$i]);
    +  for (int $i = 0; $i < 5; $i++)
    +    printf("%d\n", $array[$i]);
     
    -  for (int $i = 0; $i < 5; $i++)
    -    printf("%d\n", $i[$array]);
    +  for (int $i = 0; $i < 5; $i++)
    +    printf("%d\n", $i[$array]);
     
    -  for (int $i = 0; $i < 5; $i++)
    -    printf("%d\n", *($i + $array));
    -}
    +  for (int $i = 0; $i < 5; $i++)
    +    printf("%d\n", *($i + $array));
    +}
     

    6. Object oriented shell scripts using ksh


    @@ -182,40 +177,40 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/usr/bin/ksh93
    - 
    -typeset -T Point_t=(
    -    integer -h 'x coordinate' x=0
    -    integer -h 'y coordinate' y=0
    -    typeset -h 'point color'  color="red"
    +
    #!/usr/bin/ksh93
    + 
    +typeset -T Point_t=(
    +    integer -h 'x coordinate' x=0
    +    integer -h 'y coordinate' y=0
    +    typeset -h 'point color'  color="red"
     
    -    function getcolor {
    -        print -r ${_.color}
    -    }
    +    function getcolor {
    +        print -r ${_.color}
    +    }
     
    -    function setcolor {
    -        _.color=$1
    -    }
    +    function setcolor {
    +        _.color=$1
    +    }
     
    -    setxy() {
    -        _.x=$1; _.y=$2
    -    }
    +    setxy() {
    +        _.x=$1; _.y=$2
    +    }
     
    -    getxy() {
    -        print -r "(${_.x},${_.y})"
    -    }
    -)
    - 
    -Point_t point
    - 
    -echo "Initial coordinates are (${point.x},${point.y}). Color is ${point.color}"
    - 
    -point.setxy 5 6
    -point.setcolor blue
    - 
    -echo "New coordinates are ${point.getxy}. Color is ${point.getcolor}"
    - 
    -exit 0
    +    getxy() {
    +        print -r "(${_.x},${_.y})"
    +    }
    +)
    + 
    +Point_t point
    + 
    +echo "Initial coordinates are (${point.x},${point.y}). Color is ${point.color}"
    + 
    +point.setxy 5 6
    +point.setcolor blue
    + 
    +echo "New coordinates are ${point.getxy}. Color is ${point.getcolor}"
    + 
    +exit 0
     

    Using types to create object oriented Korn shell 93 scripts
    @@ -228,18 +223,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    package main
    +
    package main
     
    -import "fmt"
    +import "fmt"
     
    -func main() {
    -	var i int
    -	f := func() *int {
    -		return &i
    -	}
    -	*f()++
    -	fmt.Println(i)
    -}
    +func main() {
    +	var i int
    +	f := func() *int {
    +		return &i
    +	}
    +	*f()++
    +	fmt.Println(i)
    +}
     

    Go playground
    @@ -401,11 +396,11 @@ r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
    Back to the main site
    diff --git a/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html b/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html index ab0accd9..a13e9450 100644 --- a/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html +++ b/gemfeed/2024-07-07-the-stoic-challenge-book-notes.html @@ -2,17 +2,12 @@ - 'The Stoic Challenge' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -92,11 +87,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-08-05-typing-127.1-words-per-minute.html b/gemfeed/2024-08-05-typing-127.1-words-per-minute.html index 2f7a9532..669d37a4 100644 --- a/gemfeed/2024-08-05-typing-127.1-words-per-minute.html +++ b/gemfeed/2024-08-05-typing-127.1-words-per-minute.html @@ -2,17 +2,12 @@ - Typing `127.1` words per minute (`>100wpm average`) -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -253,11 +248,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-09-07-projects-i-support.html b/gemfeed/2024-09-07-projects-i-support.html index 6b248ff6..01cf18b0 100644 --- a/gemfeed/2024-09-07-projects-i-support.html +++ b/gemfeed/2024-09-07-projects-i-support.html @@ -2,17 +2,12 @@ - Projects I financially support -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -124,11 +119,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-09-07-site-reliability-engineering-part-4.html b/gemfeed/2024-09-07-site-reliability-engineering-part-4.html index 3b752ccf..a3980380 100644 --- a/gemfeed/2024-09-07-site-reliability-engineering-part-4.html +++ b/gemfeed/2024-09-07-site-reliability-engineering-part-4.html @@ -2,17 +2,12 @@ - Site Reliability Engineering - Part 4: Onboarding for On-Call Engineers -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -97,11 +92,11 @@ jgs \\`_..---.Y.---.._`//
    Back to the main site
    diff --git a/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html b/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html index 94a6325e..6056850c 100644 --- a/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html +++ b/gemfeed/2024-10-02-gemtexter-3.0.0-lets-gemtext-again-4.html @@ -2,17 +2,12 @@ - Gemtexter 3.0.0 - Let's Gemtext again⁴ -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -81,7 +76,7 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    declare -xr HTML_THEME_DIR=./extras/html/themes/simple
    +
    declare -xr HTML_THEME_DIR=./extras/html/themes/simple
     

    To customize the theme or create your own, simply copy the theme directory and modify it as needed. This makes it also much easier to switch between layouts.
    @@ -107,11 +102,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2024-10-24-staff-engineer-book-notes.html b/gemfeed/2024-10-24-staff-engineer-book-notes.html index c26e0330..94037125 100644 --- a/gemfeed/2024-10-24-staff-engineer-book-notes.html +++ b/gemfeed/2024-10-24-staff-engineer-book-notes.html @@ -2,17 +2,12 @@ - 'Staff Engineer' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -157,11 +152,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html b/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html index 78f39715..9fe54ab8 100644 --- a/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html +++ b/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 1: Setting the stage -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -203,11 +198,11 @@
    Back to the main site
    diff --git a/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html b/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html index b9e3c2f0..5578fbf1 100644 --- a/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html +++ b/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 2: Hardware and base installation -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -137,9 +132,9 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth]~/Downloads% sudo dd \
    -  if=FreeBSD-14.1-RELEASE-amd64-bootonly.iso \
    -  of=/dev/sda conv=sync
    +
    [paul@earth]~/Downloads% sudo dd \
    +  if=FreeBSD-14.1-RELEASE-amd64-bootonly.iso \
    +  of=/dev/sda conv=sync
     

    Next, I plugged the Beelinks (one after another) into my monitor via HDMI (the resolution of the FreeBSD text console seems strangely stretched, as I am using the LG Dual Up monitor), connected Ethernet, an external USB keyboard, and the FreeBSD USB stick, and booted the devices up. With F7, I entered the boot menu and selected the USB stick for the FreeBSD installation.
    @@ -165,9 +160,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # freebsd-update fetch
    -root@f0:~ # freebsd-update install
    -root@f0:~ # freebsd-update reboot
    +
    root@f0:~ # freebsd-update fetch
    +root@f0:~ # freebsd-update install
    +root@f0:~ # freebsd-update reboot
     

    I also added the following entries for the three FreeBSD boxes to the /etc/hosts file:
    @@ -176,11 +171,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # cat <<END >>/etc/hosts
    -192.168.1.130 f0 f0.lan f0.lan.buetow.org
    -192.168.1.131 f1 f1.lan f1.lan.buetow.org
    -192.168.1.132 f2 f2.lan f2.lan.buetow.org
    -END
    +
    root@f0:~ # cat <<END >>/etc/hosts
    +192.168.1.130 f0 f0.lan f0.lan.buetow.org
    +192.168.1.131 f1 f1.lan f1.lan.buetow.org
    +192.168.1.132 f2 f2.lan f2.lan.buetow.org
    +END
     

    You might wonder why bother using the hosts file? Why not use DNS properly? The reason is simplicity. I don't manage 100 hosts, only a few here and there. Having an OpenWRT router in my home, I could also configure everything there, but maybe I'll do that later. For now, keep it simple and straightforward.
    @@ -193,7 +188,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # pkg install helix doas zfs-periodic uptimed
    +
    root@f0:~ # pkg install helix doas zfs-periodic uptimed
     

    Helix editor


    @@ -210,7 +205,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # cp /usr/local/etc/doas.conf.sample /usr/local/etc/doas.conf
    +
    root@f0:~ # cp /usr/local/etc/doas.conf.sample /usr/local/etc/doas.conf
     

    https://man.openbsd.org/doas
    @@ -223,17 +218,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # cat <<END >>/etc/periodic.conf
    -daily_zfs_snapshot_enable="YES"
    -daily_zfs_snapshot_pools="zroot"
    -daily_zfs_snapshot_keep="7"
    -weekly_zfs_snapshot_enable="YES"
    -weekly_zfs_snapshot_pools="zroot"
    -weekly_zfs_snapshot_keep="5"
    -monthly_zfs_snapshot_enable="YES"
    -monthly_zfs_snapshot_pools="zroot"
    -monthly_zfs_snapshot_keep="6"
    -END
    +
    root@f0:~ # cat <<END >>/etc/periodic.conf
    +daily_zfs_snapshot_enable="YES"
    +daily_zfs_snapshot_pools="zroot"
    +daily_zfs_snapshot_keep="7"
    +weekly_zfs_snapshot_enable="YES"
    +weekly_zfs_snapshot_pools="zroot"
    +weekly_zfs_snapshot_keep="5"
    +monthly_zfs_snapshot_enable="YES"
    +monthly_zfs_snapshot_pools="zroot"
    +monthly_zfs_snapshot_keep="6"
    +END
     

    https://github.com/ross/zfs-periodic
    @@ -248,9 +243,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # cp /usr/local/mimecast/etc/uptimed.conf-dist \
    -  /usr/local/mimecast/etc/uptimed.conf 
    -root@f0:~ # hx /usr/local/mimecast/etc/uptimed.conf
    +
    root@f0:~ # cp /usr/local/mimecast/etc/uptimed.conf-dist \
    +  /usr/local/mimecast/etc/uptimed.conf 
    +root@f0:~ # hx /usr/local/mimecast/etc/uptimed.conf
     

    In the Helix editor session, I changed LOG_MAXIMUM_ENTRIES to 0 to keep all uptime entries forever and not cut off at 50 (the default config). After that, I enabled and started uptimed:
    @@ -259,8 +254,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@f0:~ # service uptimed enable
    -root@f0:~ # service uptimed start
    +
    root@f0:~ # service uptimed enable
    +root@f0:~ # service uptimed start
     

    To check the current uptime stats, I can now run uprecords:
    @@ -269,15 +264,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
     root@f0:~ # uprecords
    -     #               Uptime | System                                     Boot up
    -----------------------------+---------------------------------------------------
    -->   1     0 days, 00:07:34 | FreeBSD 14.1-RELEASE      Mon Dec  2 12:21:44 2024
    -----------------------------+---------------------------------------------------
    -NewRec     0 days, 00:07:33 | since                     Mon Dec  2 12:21:44 2024
    -    up     0 days, 00:07:34 | since                     Mon Dec  2 12:21:44 2024
    -  down     0 days, 00:00:00 | since                     Mon Dec  2 12:21:44 2024
    -   %up              100.000 | since                     Mon Dec  2 12:21:44 2024
    +
     root@f0:~ # uprecords
    +     #               Uptime | System                                     Boot up
    +----------------------------+---------------------------------------------------
    +->   1     0 days, 00:07:34 | FreeBSD 14.1-RELEASE      Mon Dec  2 12:21:44 2024
    +----------------------------+---------------------------------------------------
    +NewRec     0 days, 00:07:33 | since                     Mon Dec  2 12:21:44 2024
    +    up     0 days, 00:07:34 | since                     Mon Dec  2 12:21:44 2024
    +  down     0 days, 00:00:00 | since                     Mon Dec  2 12:21:44 2024
    +   %up              100.000 | since                     Mon Dec  2 12:21:44 2024
     

    This is how I track the uptimes for all of my host:
    @@ -295,17 +290,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % ifconfig re0
    -re0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
    -        options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE>
    -        ether e8:ff:1e:d7:1c:ac
    -        inet 192.168.1.130 netmask 0xffffff00 broadcast 192.168.1.255
    -        inet6 fe80::eaff:1eff:fed7:1cac%re0 prefixlen 64 scopeid 0x1
    -        inet6 fd22:c702:acb7:0:eaff:1eff:fed7:1cac prefixlen 64 detached autoconf
    -        inet6 2a01:5a8:304:1d5c:eaff:1eff:fed7:1cac prefixlen 64 autoconf pltime 10800 vltime 14400
    -        media: Ethernet autoselect (1000baseT <full-duplex>)
    -        status: active
    -        nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
    +
    paul@f0:~ % ifconfig re0
    +re0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
    +        options=8209b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE>
    +        ether e8:ff:1e:d7:1c:ac
    +        inet 192.168.1.130 netmask 0xffffff00 broadcast 192.168.1.255
    +        inet6 fe80::eaff:1eff:fed7:1cac%re0 prefixlen 64 scopeid 0x1
    +        inet6 fd22:c702:acb7:0:eaff:1eff:fed7:1cac prefixlen 64 detached autoconf
    +        inet6 2a01:5a8:304:1d5c:eaff:1eff:fed7:1cac prefixlen 64 autoconf pltime 10800 vltime 14400
    +        media: Ethernet autoselect (1000baseT <full-duplex>)
    +        status: active
    +        nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
     

    RAM


    @@ -316,8 +311,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % sysctl hw.physmem
    -hw.physmem: 16902905856
    +
    paul@f0:~ % sysctl hw.physmem
    +hw.physmem: 16902905856
     
     

    @@ -329,11 +324,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % sysctl dev.cpu | grep freq:
    -dev.cpu.3.freq: 705
    -dev.cpu.2.freq: 705
    -dev.cpu.1.freq: 604
    -dev.cpu.0.freq: 604
    +
    paul@f0:~ % sysctl dev.cpu | grep freq:
    +dev.cpu.3.freq: 705
    +dev.cpu.2.freq: 705
    +dev.cpu.1.freq: 604
    +dev.cpu.0.freq: 604
     

    CPU throttling


    @@ -344,14 +339,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas pkg install ubench
    -paul@f0:~ % rehash # For tcsh to find the newly installed command
    -paul@f0:~ % ubench &
    -paul@f0:~ % sysctl dev.cpu | grep freq:
    -dev.cpu.3.freq: 2922
    -dev.cpu.2.freq: 2922
    -dev.cpu.1.freq: 2923
    -dev.cpu.0.freq: 2922
    +
    paul@f0:~ % doas pkg install ubench
    +paul@f0:~ % rehash # For tcsh to find the newly installed command
    +paul@f0:~ % ubench &
    +paul@f0:~ % sysctl dev.cpu | grep freq:
    +dev.cpu.3.freq: 2922
    +dev.cpu.2.freq: 2922
    +dev.cpu.1.freq: 2923
    +dev.cpu.0.freq: 2922
     

    Idle, all three Beelinks plus the switch consumed 26.2W. But with ubench stressing all the CPUs, it went up to 38.8W.
    @@ -374,7 +369,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth]~% sudo dnf install -y wol
    +
    [paul@earth]~% sudo dnf install -y wol
     

    Next, I created a simple script (~/bin/wol-f3s) to wake and shutdown the machines:
    @@ -383,68 +378,68 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    #!/bin/bash
    -# Wake-on-LAN and shutdown script for f3s cluster (f0, f1, f2)
    +
    #!/bin/bash
    +# Wake-on-LAN and shutdown script for f3s cluster (f0, f1, f2)
     
    -# MAC addresses
    -F0_MAC="e8:ff:1e:d7:1c:ac"  # f0 (192.168.1.130)
    -F1_MAC="e8:ff:1e:d7:1e:44"  # f1 (192.168.1.131)
    -F2_MAC="e8:ff:1e:d7:1c:a0"  # f2 (192.168.1.132)
    +# MAC addresses
    +F0_MAC="e8:ff:1e:d7:1c:ac"  # f0 (192.168.1.130)
    +F1_MAC="e8:ff:1e:d7:1e:44"  # f1 (192.168.1.131)
    +F2_MAC="e8:ff:1e:d7:1c:a0"  # f2 (192.168.1.132)
     
    -# IP addresses
    -F0_IP="192.168.1.130"
    -F1_IP="192.168.1.131"
    -F2_IP="192.168.1.132"
    +# IP addresses
    +F0_IP="192.168.1.130"
    +F1_IP="192.168.1.131"
    +F2_IP="192.168.1.132"
     
    -# SSH user
    -SSH_USER="paul"
    +# SSH user
    +SSH_USER="paul"
     
    -# Broadcast address for your LAN
    -BROADCAST="192.168.1.255"
    +# Broadcast address for your LAN
    +BROADCAST="192.168.1.255"
     
    -wake() {
    -    local name=$1
    -    local mac=$2
    -    echo "Sending WoL packet to $name ($mac)..."
    -    wol -i "$BROADCAST" "$mac"
    -}
    +wake() {
    +    local name=$1
    +    local mac=$2
    +    echo "Sending WoL packet to $name ($mac)..."
    +    wol -i "$BROADCAST" "$mac"
    +}
     
    -shutdown_host() {
    -    local name=$1
    -    local ip=$2
    -    echo "Shutting down $name ($ip)..."
    -    ssh -o ConnectTimeout=5 "$SSH_USER@$ip" "doas poweroff" 2>/dev/null && \
    -        echo "  ✓ Shutdown command sent to $name" || \
    -        echo "  ✗ Failed to reach $name (already down?)"
    -}
    +shutdown_host() {
    +    local name=$1
    +    local ip=$2
    +    echo "Shutting down $name ($ip)..."
    +    ssh -o ConnectTimeout=5 "$SSH_USER@$ip" "doas poweroff" 2>/dev/null && \
    +        echo "  ✓ Shutdown command sent to $name" || \
    +        echo "  ✗ Failed to reach $name (already down?)"
    +}
     
    -ACTION="${1:-all}"
    +ACTION="${1:-all}"
     
    -case "$ACTION" in
    -    f0) wake "f0" "$F0_MAC" ;;
    -    f1) wake "f1" "$F1_MAC" ;;
    -    f2) wake "f2" "$F2_MAC" ;;
    -    all|"")
    -        wake "f0" "$F0_MAC"
    -        wake "f1" "$F1_MAC"
    -        wake "f2" "$F2_MAC"
    -        ;;
    -    shutdown|poweroff|down)
    -        shutdown_host "f0" "$F0_IP"
    -        shutdown_host "f1" "$F1_IP"
    -        shutdown_host "f2" "$F2_IP"
    -        echo ""
    -        echo "✓ Shutdown commands sent to all machines."
    -        exit 0
    -        ;;
    -    *)
    -        echo "Usage: $0 [f0|f1|f2|all|shutdown]"
    -        exit 1
    -        ;;
    -esac
    +case "$ACTION" in
    +    f0) wake "f0" "$F0_MAC" ;;
    +    f1) wake "f1" "$F1_MAC" ;;
    +    f2) wake "f2" "$F2_MAC" ;;
    +    all|"")
    +        wake "f0" "$F0_MAC"
    +        wake "f1" "$F1_MAC"
    +        wake "f2" "$F2_MAC"
    +        ;;
    +    shutdown|poweroff|down)
    +        shutdown_host "f0" "$F0_IP"
    +        shutdown_host "f1" "$F1_IP"
    +        shutdown_host "f2" "$F2_IP"
    +        echo ""
    +        echo "✓ Shutdown commands sent to all machines."
    +        exit 0
    +        ;;
    +    *)
    +        echo "Usage: $0 [f0|f1|f2|all|shutdown]"
    +        exit 1
    +        ;;
    +esac
     
    -echo ""
    -echo "✓ WoL packets sent. Machines should boot in a few seconds."
    +echo ""
    +echo "✓ WoL packets sent. Machines should boot in a few seconds."
     

    After making the script executable with chmod +x ~/bin/wol-f3s, I can now control the machines with simple commands:
    @@ -453,9 +448,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth]~% wol-f3s          # Wake all three
    -[paul@earth]~% wol-f3s f0       # Wake only f0
    -[paul@earth]~% wol-f3s shutdown # Shutdown all three via SSH
    +
    [paul@earth]~% wol-f3s          # Wake all three
    +[paul@earth]~% wol-f3s f0       # Wake only f0
    +[paul@earth]~% wol-f3s shutdown # Shutdown all three via SSH
     

    Testing WoL and Shutdown


    @@ -466,15 +461,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth]~% wol-f3s shutdown
    -Shutting down f0 (192.168.1.130)...
    -  ✓ Shutdown command sent to f0
    -Shutting down f1 (192.168.1.131)...
    -  ✓ Shutdown command sent to f1
    -Shutting down f2 (192.168.1.132)...
    -  ✓ Shutdown command sent to f2
    +
    [paul@earth]~% wol-f3s shutdown
    +Shutting down f0 (192.168.1.130)...
    +  ✓ Shutdown command sent to f0
    +Shutting down f1 (192.168.1.131)...
    +  ✓ Shutdown command sent to f1
    +Shutting down f2 (192.168.1.132)...
    +  ✓ Shutdown command sent to f2
     
    -✓ Shutdown commands sent to all machines.
    +✓ Shutdown commands sent to all machines.
     

    After waiting for them to fully power down (about 1 minute), I sent the WoL magic packets:
    @@ -483,15 +478,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth]~% wol-f3s
    -Sending WoL packet to f0 (e8:ff:1e:d7:1c:ac)...
    -Waking up e8:ff:1e:d7:1c:ac...
    -Sending WoL packet to f1 (e8:ff:1e:d7:1e:44)...
    -Waking up e8:ff:1e:d7:1e:44...
    -Sending WoL packet to f2 (e8:ff:1e:d7:1c:a0)...
    -Waking up e8:ff:1e:d7:1c:a0...
    +
    [paul@earth]~% wol-f3s
    +Sending WoL packet to f0 (e8:ff:1e:d7:1c:ac)...
    +Waking up e8:ff:1e:d7:1c:ac...
    +Sending WoL packet to f1 (e8:ff:1e:d7:1e:44)...
    +Waking up e8:ff:1e:d7:1e:44...
    +Sending WoL packet to f2 (e8:ff:1e:d7:1c:a0)...
    +Waking up e8:ff:1e:d7:1c:a0...
     
    -✓ WoL packets sent. Machines should boot in a few seconds.
    +✓ WoL packets sent. Machines should boot in a few seconds.
     

    Within 30-50 seconds, all three machines successfully booted up and became accessible via SSH!
    @@ -512,7 +507,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth]~% wol-f3s shutdown
    +
    [paul@earth]~% wol-f3s shutdown
     

    And all three machines will shut down cleanly. The next time I need them, a simple wol-f3s command wakes them all back up. This combination makes the cluster very energy-efficient while maintaining quick access when needed.
    @@ -560,11 +555,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2024-12-15-random-helix-themes.html b/gemfeed/2024-12-15-random-helix-themes.html index 351086a9..3b399487 100644 --- a/gemfeed/2024-12-15-random-helix-themes.html +++ b/gemfeed/2024-12-15-random-helix-themes.html @@ -2,17 +2,12 @@ - Random Helix Themes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -30,33 +25,33 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    export EDITOR=hx
    -export VISUAL=$EDITOR
    -export GIT_EDITOR=$EDITOR
    -export HELIX_CONFIG_DIR=$HOME/.config/helix
    +
    export EDITOR=hx
    +export VISUAL=$EDITOR
    +export GIT_EDITOR=$EDITOR
    +export HELIX_CONFIG_DIR=$HOME/.config/helix
     
    -editor::helix::random_theme () {
    -    # May add more theme search paths based on OS. This one is
    -    # for Fedora Linux, but there is also MacOS, etc.
    -    local -r theme_dir=/usr/share/helix/runtime/themes
    -    if [ ! -d $theme_dir ]; then
    -        echo "Helix theme dir $theme_dir doesnt exist"
    -        return 1
    -    fi
    +editor::helix::random_theme () {
    +    # May add more theme search paths based on OS. This one is
    +    # for Fedora Linux, but there is also MacOS, etc.
    +    local -r theme_dir=/usr/share/helix/runtime/themes
    +    if [ ! -d $theme_dir ]; then
    +        echo "Helix theme dir $theme_dir doesnt exist"
    +        return 1
    +    fi
     
    -    local -r config_file=$HELIX_CONFIG_DIR/config.toml
    -    local -r random_theme="$(basename "$(ls $theme_dir \
    -        | grep -v random.toml | grep .toml | sort -R \
    -        | head -n 1)" | cut -d. -f1)"
    +    local -r config_file=$HELIX_CONFIG_DIR/config.toml
    +    local -r random_theme="$(basename "$(ls $theme_dir \
    +        | grep -v random.toml | grep .toml | sort -R \
    +        | head -n 1)" | cut -d. -f1)"
     
    -    sed "/^theme =/ { s/.*/theme = \"$random_theme\"/; }" \
    -        $config_file > $config_file.tmp && 
    -        mv $config_file.tmp $config_file
    -}
    +    sed "/^theme =/ { s/.*/theme = \"$random_theme\"/; }" \
    +        $config_file > $config_file.tmp && 
    +        mv $config_file.tmp $config_file
    +}
     
    -if [ -f $HELIX_CONFIG_DIR/config.toml ]; then
    -    editor::helix::random_theme
    -fi
    +if [ -f $HELIX_CONFIG_DIR/config.toml ]; then
    +    editor::helix::random_theme
    +fi
     

    So every time I open a new terminal or shell, editor::helix::random_theme gets called, which randomly selects a theme from all installed ones and updates the helix config accordingly.
    @@ -65,16 +60,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [paul@earth] ~ % editor::helix::random_theme
    -[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml
    -theme = "jellybeans"
    -[paul@earth] ~ % editor::helix::random_theme
    -[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml
    -theme = "rose_pine"
    -[paul@earth] ~ % editor::helix::random_theme
    -[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml
    -theme = "noctis"
    -[paul@earth] ~ %
    +
    [paul@earth] ~ % editor::helix::random_theme
    +[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml
    +theme = "jellybeans"
    +[paul@earth] ~ % editor::helix::random_theme
    +[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml
    +theme = "rose_pine"
    +[paul@earth] ~ % editor::helix::random_theme
    +[paul@earth] ~ % head -n 1 ~/.config/helix/config.toml
    +theme = "noctis"
    +[paul@earth] ~ %
     

    A better version


    @@ -85,44 +80,44 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    export EDITOR=hx
    -export VISUAL=$EDITOR
    -export GIT_EDITOR=$EDITOR
    -export HELIX_CONFIG_DIR=$HOME/.config/helix
    +
    export EDITOR=hx
    +export VISUAL=$EDITOR
    +export GIT_EDITOR=$EDITOR
    +export HELIX_CONFIG_DIR=$HOME/.config/helix
     
    -editor::helix::theme::get_random () {
    -    for dir in $(hx --health \
    -        | awk '/^Runtime directories/ { print $3 }' | tr ';' ' '); do
    -        if [ -d $dir/themes ]; then
    -            ls $dir/themes
    -        fi
    -    done | grep -F .toml | sort -R | head -n 1 | cut -d. -f1
    -}
    +editor::helix::theme::get_random () {
    +    for dir in $(hx --health \
    +        | awk '/^Runtime directories/ { print $3 }' | tr ';' ' '); do
    +        if [ -d $dir/themes ]; then
    +            ls $dir/themes
    +        fi
    +    done | grep -F .toml | sort -R | head -n 1 | cut -d. -f1
    +}
     
    -editor::helix::theme::set () {
    -    local -r theme="$1"; shift
    +editor::helix::theme::set () {
    +    local -r theme="$1"; shift
     
    -    local -r config_file=$HELIX_CONFIG_DIR/config.toml
    +    local -r config_file=$HELIX_CONFIG_DIR/config.toml
     
    -    sed "/^theme =/ { s/.*/theme = \"$theme\"/; }" \
    -        $config_file > $config_file.tmp && 
    -        mv $config_file.tmp $config_file
    -}
    +    sed "/^theme =/ { s/.*/theme = \"$theme\"/; }" \
    +        $config_file > $config_file.tmp && 
    +        mv $config_file.tmp $config_file
    +}
     
    -if [ -f $HELIX_CONFIG_DIR/config.toml ]; then
    -    editor::helix::theme::set $(editor::helix::theme::get_random)
    -fi
    +if [ -f $HELIX_CONFIG_DIR/config.toml ]; then
    +    editor::helix::theme::set $(editor::helix::theme::get_random)
    +fi
     

    I hope you had some fun. E-Mail your comments to paul@nospam.buetow.org :-)

    Back to the main site
    diff --git a/gemfeed/2025-01-01-posts-from-october-to-december-2024.html b/gemfeed/2025-01-01-posts-from-october-to-december-2024.html index c79dd0f8..5c474849 100644 --- a/gemfeed/2025-01-01-posts-from-october-to-december-2024.html +++ b/gemfeed/2025-01-01-posts-from-october-to-december-2024.html @@ -2,17 +2,12 @@ - Posts from October to December 2024 -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -354,11 +349,11 @@
    Back to the main site
    diff --git a/gemfeed/2025-01-15-working-with-an-sre-interview.html b/gemfeed/2025-01-15-working-with-an-sre-interview.html index 1c328929..4658e48b 100644 --- a/gemfeed/2025-01-15-working-with-an-sre-interview.html +++ b/gemfeed/2025-01-15-working-with-an-sre-interview.html @@ -2,17 +2,12 @@ - Working with an SRE Interview -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -198,11 +193,11 @@
    Back to the main site
    diff --git a/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html b/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html index 63e092a3..30252c8d 100644 --- a/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html +++ b/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 3: Protecting from power cuts -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -67,11 +62,11 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0: ~ % doas freebsd-update fetch
    -paul@f0: ~ % doas freebsd-update install
    -paul@f0: ~ % doas freebsd-update -r 14.2-RELEASE upgrade
    -paul@f0: ~ % doas freebsd-update install
    -paul@f0: ~ % doas shutdown -r now
    +
    paul@f0: ~ % doas freebsd-update fetch
    +paul@f0: ~ % doas freebsd-update install
    +paul@f0: ~ % doas freebsd-update -r 14.2-RELEASE upgrade
    +paul@f0: ~ % doas freebsd-update install
    +paul@f0: ~ % doas shutdown -r now
     

    And after rebooting, I ran:
    @@ -80,10 +75,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0: ~ % doas freebsd-update install
    -paul@f0: ~ % doas pkg update
    -paul@f0: ~ % doas pkg upgrade
    -paul@f0: ~ % doas shutdown -r now
    +
    paul@f0: ~ % doas freebsd-update install
    +paul@f0: ~ % doas pkg update
    +paul@f0: ~ % doas pkg upgrade
    +paul@f0: ~ % doas shutdown -r now
     

    And after another reboot, I was on 14.2:
    @@ -92,9 +87,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % uname -a
    -FreeBSD f0.lan.buetow.org 14.2-RELEASE FreeBSD 14.2-RELEASE 
    - releng/14.2-n269506-c8918d6c7412 GENERIC amd64
    +
    paul@f0:~ % uname -a
    +FreeBSD f0.lan.buetow.org 14.2-RELEASE FreeBSD 14.2-RELEASE 
    + releng/14.2-n269506-c8918d6c7412 GENERIC amd64
     

    And, of course, I ran this on all 3 nodes!
    @@ -140,8 +135,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0: ~ % doas dmesg | grep UPS
    -ugen0.2: <American Power Conversion Back-UPS BX750MI> at usbus0
    +
    paul@f0: ~ % doas dmesg | grep UPS
    +ugen0.2: <American Power Conversion Back-UPS BX750MI> at usbus0
     

    apcupsd Installation


    @@ -152,7 +147,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0: ~ % doas install apcupsd
    +
    paul@f0: ~ % doas install apcupsd
     

    I have made the following modifications to the configuration file so that the UPS can be used via the USB interface:
    @@ -161,29 +156,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample  apcupsd.conf
    ---- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200
    -+++ apcupsd.conf        2024-12-03 10:58:24.009501000 +0200
    -@@ -31,7 +31,7 @@
    - #     940-1524C, 940-0024G, 940-0095A, 940-0095B,
    - #     940-0095C, 940-0625A, M-04-02-2000
    - #
    --UPSCABLE smart
    -+UPSCABLE usb
    +
    paul@f0:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample  apcupsd.conf
    +--- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200
    ++++ apcupsd.conf        2024-12-03 10:58:24.009501000 +0200
    +@@ -31,7 +31,7 @@
    + #     940-1524C, 940-0024G, 940-0095A, 940-0095B,
    + #     940-0095C, 940-0625A, M-04-02-2000
    + #
    +-UPSCABLE smart
    ++UPSCABLE usb
     
    - # To get apcupsd to work, in addition to defining the cable
    - # above, you must also define a UPSTYPE, which corresponds to
    -@@ -88,8 +88,10 @@
    - #                            that apcupsd binds to that particular unit
    - #                            (helpful if you have more than one USB UPS).
    - #
    --UPSTYPE apcsmart
    --DEVICE /dev/usv
    -+UPSTYPE usb
    -+DEVICE
    + # To get apcupsd to work, in addition to defining the cable
    + # above, you must also define a UPSTYPE, which corresponds to
    +@@ -88,8 +88,10 @@
    + #                            that apcupsd binds to that particular unit
    + #                            (helpful if you have more than one USB UPS).
    + #
    +-UPSTYPE apcsmart
    +-DEVICE /dev/usv
    ++UPSTYPE usb
    ++DEVICE
     
    - # POLLTIME <int>
    - #   Interval (in seconds) at which apcupsd polls the UPS for status. This
    + # POLLTIME <int>
    + #   Interval (in seconds) at which apcupsd polls the UPS for status. This
     

    I left the remaining settings as the default ones; for example, the following are of main interest:
    @@ -206,10 +201,10 @@ MINUTES 3 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES
    -apcupsd_enable:  -> YES
    -paul@f0:/usr/local/etc/apcupsd % doas service apcupsd start
    -Starting apcupsd.
    +
    paul@f0:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES
    +apcupsd_enable:  -> YES
    +paul@f0:/usr/local/etc/apcupsd % doas service apcupsd start
    +Starting apcupsd.
     

    UPS Connectivity Test


    @@ -220,43 +215,43 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % apcaccess
    -APC      : 001,035,0857
    -DATE     : 2025-01-26 14:43:27 +0200
    -HOSTNAME : f0.lan.buetow.org
    -VERSION  : 3.14.14 (31 May 2016) freebsd
    -UPSNAME  : f0.lan.buetow.org
    -CABLE    : USB Cable
    -DRIVER   : USB UPS Driver
    -UPSMODE  : Stand Alone
    -STARTTIME: 2025-01-26 14:43:25 +0200
    -MODEL    : Back-UPS BX750MI
    -STATUS   : ONLINE
    -LINEV    : 230.0 Volts
    -LOADPCT  : 4.0 Percent
    -BCHARGE  : 100.0 Percent
    -TIMELEFT : 65.3 Minutes
    -MBATTCHG : 5 Percent
    -MINTIMEL : 3 Minutes
    -MAXTIME  : 0 Seconds
    -SENSE    : Medium
    -LOTRANS  : 145.0 Volts
    -HITRANS  : 295.0 Volts
    -ALARMDEL : No alarm
    -BATTV    : 13.6 Volts
    -LASTXFER : Automatic or explicit self test
    -NUMXFERS : 0
    -TONBATT  : 0 Seconds
    -CUMONBATT: 0 Seconds
    -XOFFBATT : N/A
    -SELFTEST : NG
    -STATFLAG : 0x05000008
    -SERIALNO : 9B2414A03599
    -BATTDATE : 2001-01-01
    -NOMINV   : 230 Volts
    -NOMBATTV : 12.0 Volts
    -NOMPOWER : 410 Watts
    -END APC  : 2025-01-26 14:44:06 +0200
    +
    paul@f0:~ % apcaccess
    +APC      : 001,035,0857
    +DATE     : 2025-01-26 14:43:27 +0200
    +HOSTNAME : f0.lan.buetow.org
    +VERSION  : 3.14.14 (31 May 2016) freebsd
    +UPSNAME  : f0.lan.buetow.org
    +CABLE    : USB Cable
    +DRIVER   : USB UPS Driver
    +UPSMODE  : Stand Alone
    +STARTTIME: 2025-01-26 14:43:25 +0200
    +MODEL    : Back-UPS BX750MI
    +STATUS   : ONLINE
    +LINEV    : 230.0 Volts
    +LOADPCT  : 4.0 Percent
    +BCHARGE  : 100.0 Percent
    +TIMELEFT : 65.3 Minutes
    +MBATTCHG : 5 Percent
    +MINTIMEL : 3 Minutes
    +MAXTIME  : 0 Seconds
    +SENSE    : Medium
    +LOTRANS  : 145.0 Volts
    +HITRANS  : 295.0 Volts
    +ALARMDEL : No alarm
    +BATTV    : 13.6 Volts
    +LASTXFER : Automatic or explicit self test
    +NUMXFERS : 0
    +TONBATT  : 0 Seconds
    +CUMONBATT: 0 Seconds
    +XOFFBATT : N/A
    +SELFTEST : NG
    +STATFLAG : 0x05000008
    +SERIALNO : 9B2414A03599
    +BATTDATE : 2001-01-01
    +NOMINV   : 230 Volts
    +NOMBATTV : 12.0 Volts
    +NOMPOWER : 410 Watts
    +END APC  : 2025-01-26 14:44:06 +0200
     

    APC Info on Partner Nodes:


    @@ -275,10 +270,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f1:~ % apcaccess -h f0.lan.buetow.org | grep Percent
    -LOADPCT  : 12.0 Percent
    -BCHARGE  : 94.0 Percent
    -MBATTCHG : 5 Percent
    +
    paul@f1:~ % apcaccess -h f0.lan.buetow.org | grep Percent
    +LOADPCT  : 12.0 Percent
    +BCHARGE  : 94.0 Percent
    +MBATTCHG : 5 Percent
     

    But I want the daemon to be configured and enabled in such a way that it connects to the master UPS node (the one with the UPS connected via USB) so that it can also initiate a system shutdown when the UPS battery reaches low levels. For that, apcupsd itself needs to be aware of the UPS status.
    @@ -289,52 +284,52 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f2:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample apcupsd.conf
    ---- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200
    -+++ apcupsd.conf        2025-01-26 15:52:45.108469000 +0200
    -@@ -31,7 +31,7 @@
    - #     940-1524C, 940-0024G, 940-0095A, 940-0095B,
    - #     940-0095C, 940-0625A, M-04-02-2000
    - #
    --UPSCABLE smart
    -+UPSCABLE ether
    +
    paul@f2:/usr/local/etc/apcupsd % diff -u apcupsd.conf.sample apcupsd.conf
    +--- apcupsd.conf.sample 2024-11-01 16:40:42.000000000 +0200
    ++++ apcupsd.conf        2025-01-26 15:52:45.108469000 +0200
    +@@ -31,7 +31,7 @@
    + #     940-1524C, 940-0024G, 940-0095A, 940-0095B,
    + #     940-0095C, 940-0625A, M-04-02-2000
    + #
    +-UPSCABLE smart
    ++UPSCABLE ether
     
    - # To get apcupsd to work, in addition to defining the cable
    - # above, you must also define a UPSTYPE, which corresponds to
    -@@ -52,7 +52,6 @@
    - #                            Network Information Server. This is used if the
    - #                            UPS powering your computer is connected to a
    - #                            different computer for monitoring.
    --#
    - # snmp      hostname:port:vendor:community
    - #                            SNMP network link to an SNMP-enabled UPS device.
    - #                            Hostname is the ip address or hostname of the UPS
    -@@ -88,8 +87,8 @@
    - #                            that apcupsd binds to that particular unit
    - #                            (helpful if you have more than one USB UPS).
    - #
    --UPSTYPE apcsmart
    --DEVICE /dev/usv
    -+UPSTYPE net
    -+DEVICE f0.lan.buetow.org:3551
    + # To get apcupsd to work, in addition to defining the cable
    + # above, you must also define a UPSTYPE, which corresponds to
    +@@ -52,7 +52,6 @@
    + #                            Network Information Server. This is used if the
    + #                            UPS powering your computer is connected to a
    + #                            different computer for monitoring.
    +-#
    + # snmp      hostname:port:vendor:community
    + #                            SNMP network link to an SNMP-enabled UPS device.
    + #                            Hostname is the ip address or hostname of the UPS
    +@@ -88,8 +87,8 @@
    + #                            that apcupsd binds to that particular unit
    + #                            (helpful if you have more than one USB UPS).
    + #
    +-UPSTYPE apcsmart
    +-DEVICE /dev/usv
    ++UPSTYPE net
    ++DEVICE f0.lan.buetow.org:3551
     
    - # POLLTIME <int>
    - #   Interval (in seconds) at which apcupsd polls the UPS for status. This
    -@@ -147,12 +146,12 @@
    - # If during a power failure, the remaining battery percentage
    - # (as reported by the UPS) is below or equal to BATTERYLEVEL,
    - # apcupsd will initiate a system shutdown.
    --BATTERYLEVEL 5
    -+BATTERYLEVEL 10
    + # POLLTIME <int>
    + #   Interval (in seconds) at which apcupsd polls the UPS for status. This
    +@@ -147,12 +146,12 @@
    + # If during a power failure, the remaining battery percentage
    + # (as reported by the UPS) is below or equal to BATTERYLEVEL,
    + # apcupsd will initiate a system shutdown.
    +-BATTERYLEVEL 5
    ++BATTERYLEVEL 10
     
    - # If during a power failure, the remaining runtime in minutes
    - # (as calculated internally by the UPS) is below or equal to MINUTES,
    - # apcupsd, will initiate a system shutdown.
    --MINUTES 3
    -+MINUTES 6
    + # If during a power failure, the remaining runtime in minutes
    + # (as calculated internally by the UPS) is below or equal to MINUTES,
    + # apcupsd, will initiate a system shutdown.
    +-MINUTES 3
    ++MINUTES 6
     
    - # If during a power failure, the UPS has run on batteries for TIMEOUT
    - # many seconds or longer, apcupsd will initiate a system shutdown.
    + # If during a power failure, the UPS has run on batteries for TIMEOUT
    + # many seconds or longer, apcupsd will initiate a system shutdown.
     
     
    So I also ran the following commands on f1 and f2:
    @@ -343,10 +338,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f1:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES
    -apcupsd_enable:  -> YES
    -paul@f1:/usr/local/etc/apcupsd % doas service apcupsd start
    -Starting apcupsd.
    +
    paul@f1:/usr/local/etc/apcupsd % doas sysrc apcupsd_enable=YES
    +apcupsd_enable:  -> YES
    +paul@f1:/usr/local/etc/apcupsd % doas service apcupsd start
    +Starting apcupsd.
     

    And then I was able to connect to localhost via the apcaccess command:
    @@ -355,10 +350,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f1:~ % doas apcaccess | grep Percent
    -LOADPCT  : 5.0 Percent
    -BCHARGE  : 95.0 Percent
    -MBATTCHG : 5 Percent
    +
    paul@f1:~ % doas apcaccess | grep Percent
    +LOADPCT  : 5.0 Percent
    +BCHARGE  : 95.0 Percent
    +MBATTCHG : 5 Percent
     

    Power outage simulation


    @@ -380,8 +375,8 @@ Power failure. Running on UPS batteries. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/usr/local/etc/apcupsd % apcaccess -p TIMELEFT
    -63.9 Minutes
    +
    paul@f0:/usr/local/etc/apcupsd % apcaccess -p TIMELEFT
    +63.9 Minutes
     

    And after around one hour (f1 and f2 a bit earlier, f0 a bit later due to the different BATTERYLEVEL and MINUTES settings outlined earlier), the following broadcast was sent out:
    @@ -443,11 +438,11 @@ Jan 26 17:36:32 f2 apcupsd[2159]: apcupsd shutdown succeeded
    Back to the main site
    diff --git a/gemfeed/2025-02-08-random-weird-things-ii.html b/gemfeed/2025-02-08-random-weird-things-ii.html index 225c8084..64a37731 100644 --- a/gemfeed/2025-02-08-random-weird-things-ii.html +++ b/gemfeed/2025-02-08-random-weird-things-ii.html @@ -2,17 +2,12 @@ - Random Weird Things - Part Ⅱ -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -85,25 +80,25 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    package main
    +
    package main
     
    -import "log"
    +import "log"
     
    -type fun func() string
    +type fun func() string
     
    -func (f fun) Bar() string {
    -        return "Bar"
    -}
    +func (f fun) Bar() string {
    +        return "Bar"
    +}
     
    -func main() {
    -        var f fun = func() string {
    -                return "Foo"
    -        }
    -        log.Println("Example 1: ", f())
    -        log.Println("Example 2: ", f.Bar())
    -        log.Println("Example 3: ", fun(f.Bar).Bar())
    -        log.Println("Example 4: ", fun(fun(f.Bar).Bar).Bar())
    -}
    +func main() {
    +        var f fun = func() string {
    +                return "Foo"
    +        }
    +        log.Println("Example 1: ", f())
    +        log.Println("Example 2: ", f.Bar())
    +        log.Println("Example 3: ", fun(f.Bar).Bar())
    +        log.Println("Example 4: ", fun(fun(f.Bar).Bar).Bar())
    +}
     

    It runs just fine:
    @@ -112,11 +107,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    ❯ go run main.go
    -2025/02/07 22:56:14 Example 1:  Foo
    -2025/02/07 22:56:14 Example 2:  Bar
    -2025/02/07 22:56:14 Example 3:  Bar
    -2025/02/07 22:56:14 Example 4:  Bar
    +
    ❯ go run main.go
    +2025/02/07 22:56:14 Example 1:  Foo
    +2025/02/07 22:56:14 Example 2:  Bar
    +2025/02/07 22:56:14 Example 3:  Bar
    +2025/02/07 22:56:14 Example 4:  Bar
     

    macOS


    @@ -135,20 +130,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    ❯ touch Maß
    -❯ ls -l
    --rw-r--r--@ 1 paul  wheel  0 Feb  7 23:02 Maß
    -❯ touch Mass
    -❯ ls -l
    --rw-r--r--@ 1 paul  wheel  0 Feb  7 23:02 Maß
    -❯ rm Mass
    -❯ ls -l
    +
    ❯ touch Maß
    +❯ ls -l
    +-rw-r--r--@ 1 paul  wheel  0 Feb  7 23:02 Maß
    +❯ touch Mass
    +❯ ls -l
    +-rw-r--r--@ 1 paul  wheel  0 Feb  7 23:02 Maß
    +❯ rm Mass
    +❯ ls -l
     
    -❯ touch Mass
    -❯ ls -ltr
    --rw-r--r--@ 1 paul  wheel  0 Feb  7 23:02 Mass
    -❯ rm Maß
    -❯ ls -l
    +❯ touch Mass
    +❯ ls -ltr
    +-rw-r--r--@ 1 paul  wheel  0 Feb  7 23:02 Mass
    +❯ rm Maß
    +❯ ls -l
     
     

    @@ -192,16 +187,16 @@ ADFS::4.$.Documents.Techwriter.Myfile by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    arr = {10, 20, 30, 40, 50}
    -print(arr[1]) -- Accessing the first element
    +
    arr = {10, 20, 30, 40, 50}
    +print(arr[1]) -- Accessing the first element
     

    -
    ❯ lua foo.lua
    -10
    +
    ❯ lua foo.lua
    +10
     

    One-based indexing is more natural for human-readable, mathematical, and theoretical contexts, where counting traditionally starts from one.
    @@ -216,45 +211,45 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # (C) 2006 by Paul C. Buetow
    +
    # (C) 2006 by Paul C. Buetow
     
    -Christmas:{time;#!!!
    +Christmas:{time;#!!!
     
    -Children: do tell $wishes;
    +Children: do tell $wishes;
     
    -Santa: for $each (@children) { 
    -BEGIN { read $each, $their, wishes and study them; use Memoize#ing
    +Santa: for $each (@children) { 
    +BEGIN { read $each, $their, wishes and study them; use Memoize#ing
     
    -} use constant gift, 'wrapping'; 
    -package Gifts; pack $each, gift and bless $each and goto deliver
    -or do import if not local $available,!!! HO, HO, HO;
    +} use constant gift, 'wrapping'; 
    +package Gifts; pack $each, gift and bless $each and goto deliver
    +or do import if not local $available,!!! HO, HO, HO;
     
    -redo Santa, pipe $gifts, to_childs;
    -redo Santa and do return if last one, is, delivered; 
    +redo Santa, pipe $gifts, to_childs;
    +redo Santa and do return if last one, is, delivered; 
     
    -deliver: gift and require diagnostics if our $gifts ,not break;
    -do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones;
    -The_children: sleep and wait for (each %gift) and try { to => untie $gifts };
    +deliver: gift and require diagnostics if our $gifts ,not break;
    +do{ use NEXT; time; tied $gifts} if broken and dump the, broken, ones;
    +The_children: sleep and wait for (each %gift) and try { to => untie $gifts };
     
    -redo Santa, pipe $gifts, to_childs;
    -redo Santa and do return if last one, is, delivered; 
    +redo Santa, pipe $gifts, to_childs;
    +redo Santa and do return if last one, is, delivered; 
     
    -The_christmas_tree: formline s/ /childrens/, $gifts;
    -alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME};  
    -write <<EMail
    - to the parents to buy a new christmas tree!!!!111
    - and send the
    -EMail
    -;wait and redo deliver until defined local $tree;
    +The_christmas_tree: formline s/ /childrens/, $gifts;
    +alarm and warn if not exists $Christmas{ tree}, @t, $ENV{HOME};  
    +write <<EMail
    + to the parents to buy a new christmas tree!!!!111
    + and send the
    +EMail
    +;wait and redo deliver until defined local $tree;
     
    -redo Santa, pipe $gifts, to_childs;
    -redo Santa and do return if last one, is, delivered ;}
    +redo Santa, pipe $gifts, to_childs;
    +redo Santa and do return if last one, is, delivered ;}
     
    -END {} our $mission and do sleep until next Christmas ;}
    +END {} our $mission and do sleep until next Christmas ;}
     
    -__END__
    +__END__
     
    -This is perl, v5.8.8 built for i386-freebsd-64int
    +This is perl, v5.8.8 built for i386-freebsd-64int
     

    More Perl Poetry of mine
    @@ -289,11 +284,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html b/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html index 088e5159..2fe45356 100644 --- a/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html +++ b/gemfeed/2025-03-05-sharing-on-social-media-with-gos.html @@ -2,17 +2,12 @@ - Sharing on Social Media with Gos v1.0.0 -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -89,8 +84,8 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    git clone https://codeberg.org/snonux/gos.git
    -cd gos
    +
    git clone https://codeberg.org/snonux/gos.git
    +cd gos
     

    Build the binaries:
    @@ -99,10 +94,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    go build -o gos ./cmd/gos
    -go build -o gosc ./cmd/gosc
    -sudo mv gos ~/go/bin
    -sudo mv gosc ~/go/bin
    +
    go build -o gos ./cmd/gos
    +go build -o gosc ./cmd/gosc
    +sudo mv gos ~/go/bin
    +sudo mv gosc ~/go/bin
     

    Or, if you want to use the Taskfile:
    @@ -111,7 +106,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    go-task install
    +
    go-task install
     

    Configuration


    @@ -124,13 +119,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    {
    -  "MastodonURL": "https://mastodon.example.com",
    -  "MastodonAccessToken": "your-mastodon-access-token",
    -  "LinkedInClientID": "your-linkedin-client-id",
    -  "LinkedInSecret": "your-linkedin-client-secret",
    -  "LinkedInRedirectURL": "http://localhost:8080/callback",
    -}
    +
    {
    +  "MastodonURL": "https://mastodon.example.com",
    +  "MastodonAccessToken": "your-mastodon-access-token",
    +  "LinkedInClientID": "your-linkedin-client-id",
    +  "LinkedInSecret": "your-linkedin-client-secret",
    +  "LinkedInRedirectURL": "http://localhost:8080/callback",
    +}
     

    Configuration fields


    @@ -186,7 +181,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    ./gos --dry
    +
    ./gos --dry
     

    *Normal run*
    @@ -197,7 +192,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    ./gos 
    +
    ./gos 
     

    :-)
    @@ -362,7 +357,7 @@ Hello World :-) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    gos --geminiSummaryFor 202410,202411,202412
    +
    gos --geminiSummaryFor 202410,202411,202412
     

    This outputs the summary for the three specified months, as shown in the example. The summary includes posts from all social media networks but removes duplicates.
    @@ -374,7 +369,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412
    +
    gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412
     

    Gemtexter
    @@ -385,7 +380,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412 --geminiCapsules "foo.zone,paul.buetow.org"
    +
    gos --gemtexterEnable --geminiSummaryFor 202410,202411,202412 --geminiCapsules "foo.zone,paul.buetow.org"
     

    It will then also generate Gemini Gemtext links in the summary page and flag them with (Gemini).
    @@ -398,11 +393,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html b/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html index 3784d6cd..0a87081a 100644 --- a/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html +++ b/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 4: Rocky Linux Bhyve VMs -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -88,10 +83,10 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % dmesg | grep 'Features2=.*POPCNT'
    -  Features2=0x7ffafbbf<SSE3,PCLMULQDQ,DTES64,MON,DS_CPL,VMX,EST,TM2,SSSE3,SDBG,
    -	FMA,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,TSCDLT,AESNI,XSAVE,
    -	OSXSAVE,AVX,F16C,RDRAND>
    +
    paul@f0:~ % dmesg | grep 'Features2=.*POPCNT'
    +  Features2=0x7ffafbbf<SSE3,PCLMULQDQ,DTES64,MON,DS_CPL,VMX,EST,TM2,SSSE3,SDBG,
    +	FMA,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,TSCDLT,AESNI,XSAVE,
    +	OSXSAVE,AVX,F16C,RDRAND>
     

    So it's there! All good.
    @@ -108,15 +103,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas pkg install vm-bhyve bhyve-firmware
    -paul@f0:~ % doas sysrc vm_enable=YES
    -vm_enable:  -> YES
    -paul@f0:~ % doas sysrc vm_dir=zfs:zroot/bhyve
    -vm_dir:  -> zfs:zroot/bhyve
    -paul@f0:~ % doas zfs create zroot/bhyve
    -paul@f0:~ % doas vm init
    -paul@f0:~ % doas vm switch create public
    -paul@f0:~ % doas vm switch add public re0
    +
    paul@f0:~ % doas pkg install vm-bhyve bhyve-firmware
    +paul@f0:~ % doas sysrc vm_enable=YES
    +vm_enable:  -> YES
    +paul@f0:~ % doas sysrc vm_dir=zfs:zroot/bhyve
    +vm_dir:  -> zfs:zroot/bhyve
    +paul@f0:~ % doas zfs create zroot/bhyve
    +paul@f0:~ % doas vm init
    +paul@f0:~ % doas vm switch create public
    +paul@f0:~ % doas vm switch add public re0
     

    Bhyve stores all its data in the /bhyve of the zroot ZFS pool:
    @@ -125,8 +120,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % zfs list | grep bhyve
    -zroot/bhyve                                   1.74M   453G  1.74M  /zroot/bhyve
    +
    paul@f0:~ % zfs list | grep bhyve
    +zroot/bhyve                                   1.74M   453G  1.74M  /zroot/bhyve
     

    For convenience, we also create this symlink:
    @@ -135,7 +130,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas ln -s /zroot/bhyve/ /bhyve
    +
    paul@f0:~ % doas ln -s /zroot/bhyve/ /bhyve
     
     

    @@ -145,8 +140,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas vm list
    -NAME  DATASTORE  LOADER  CPU  MEMORY  VNC  AUTO  STATE
    +
    paul@f0:~ % doas vm list
    +NAME  DATASTORE  LOADER  CPU  MEMORY  VNC  AUTO  STATE
     

    Rocky Linux VMs


    @@ -167,10 +162,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas vm iso \
    - https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-minimal.iso
    -/zroot/bhyve/.iso/Rocky-9.5-x86_64-minimal.iso        1808 MB 4780 kBps 06m28s
    -paul@f0:/bhyve % doas vm create rocky
    +
    paul@f0:~ % doas vm iso \
    + https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-minimal.iso
    +/zroot/bhyve/.iso/Rocky-9.5-x86_64-minimal.iso        1808 MB 4780 kBps 06m28s
    +paul@f0:/bhyve % doas vm create rocky
     

    VM configuration


    @@ -181,16 +176,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/bhyve/rocky % cat rocky.conf
    -loader="bhyveload"
    -cpu=1
    -memory=256M
    -network0_type="virtio-net"
    -network0_switch="public"
    -disk0_type="virtio-blk"
    -disk0_name="disk0.img"
    -uuid="1c4655ac-c828-11ef-a920-e8ff1ed71ca0"
    -network0_mac="58:9c:fc:0d:13:3f"
    +
    paul@f0:/bhyve/rocky % cat rocky.conf
    +loader="bhyveload"
    +cpu=1
    +memory=256M
    +network0_type="virtio-net"
    +network0_switch="public"
    +disk0_type="virtio-blk"
    +disk0_name="disk0.img"
    +uuid="1c4655ac-c828-11ef-a920-e8ff1ed71ca0"
    +network0_mac="58:9c:fc:0d:13:3f"
     

    The uuid and the network0_mac differ for each of the three VMs (the ones being installed on f0, f1 and f2).
    @@ -221,17 +216,17 @@ network0_mac="58:9c:fc:0d:13:3f" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas vm install rocky Rocky-9.5-x86_64-minimal.iso
    -Starting rocky
    -  * found guest in /zroot/bhyve/rocky
    -  * booting...
    +
    paul@f0:~ % doas vm install rocky Rocky-9.5-x86_64-minimal.iso
    +Starting rocky
    +  * found guest in /zroot/bhyve/rocky
    +  * booting...
     
    -paul@f0:/bhyve/rocky % doas vm list
    -NAME   DATASTORE  LOADER  CPU  MEMORY  VNC           AUTO  STATE
    -rocky  default    uefi    4    14G     0.0.0.0:5900  No    Locked (f0.lan.buetow.org)
    +paul@f0:/bhyve/rocky % doas vm list
    +NAME   DATASTORE  LOADER  CPU  MEMORY  VNC           AUTO  STATE
    +rocky  default    uefi    4    14G     0.0.0.0:5900  No    Locked (f0.lan.buetow.org)
     
    -paul@f0:/bhyve/rocky % doas sockstat -4 | grep 5900
    -root     bhyve       6079 8   tcp4   *:5900                *:*
    +paul@f0:/bhyve/rocky % doas sockstat -4 | grep 5900
    +root     bhyve       6079 8   tcp4   *:5900                *:*
     

    Port 5900 now also opens for VNC connections, so I connected it with a VNC client and ran through the installation dialogues. This could be done unattended or more automated, but there are only three VMs to install, and the automation doesn't seem worth it as we do it only once a year or less often.
    @@ -244,9 +239,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/bhyve/rocky % doas vm stop rocky
    -paul@f0:/bhyve/rocky % doas truncate -s 100G disk0.img
    -paul@f0:/bhyve/rocky % doas vm install rocky Rocky-9.5-x86_64-minimal.iso
    +
    paul@f0:/bhyve/rocky % doas vm stop rocky
    +paul@f0:/bhyve/rocky % doas truncate -s 100G disk0.img
    +paul@f0:/bhyve/rocky % doas vm install rocky Rocky-9.5-x86_64-minimal.iso
     

    Connect to VNC


    @@ -275,9 +270,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/rc.conf
    -vm_list="rocky"
    -vm_delay="5"
    +
    paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/rc.conf
    +vm_list="rocky"
    +vm_delay="5"
     

    The vm_delay isn't really required. It is used to wait 5 seconds before starting each VM, but there is currently only one VM per host. Maybe later, when there are more, this will be useful. After adding, there's now a Yes indicator in the AUTO column.
    @@ -286,9 +281,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas vm list
    -NAME   DATASTORE  LOADER  CPU  MEMORY  VNC           AUTO     STATE
    -rocky  default    uefi    4    14G     0.0.0.0:5900  Yes [1]  Running (2063)
    +
    paul@f0:~ % doas vm list
    +NAME   DATASTORE  LOADER  CPU  MEMORY  VNC           AUTO     STATE
    +rocky  default    uefi    4    14G     0.0.0.0:5900  Yes [1]  Running (2063)
     

    Static IP configuration


    @@ -307,11 +302,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/hosts
    -192.168.1.120 r0 r0.lan r0.lan.buetow.org
    -192.168.1.121 r1 r1.lan r1.lan.buetow.org
    -192.168.1.122 r2 r2.lan r2.lan.buetow.org
    -END
    +
    paul@f0:/bhyve/rocky % cat <<END | doas tee -a /etc/hosts
    +192.168.1.120 r0 r0.lan r0.lan.buetow.org
    +192.168.1.121 r1 r1.lan r1.lan.buetow.org
    +192.168.1.122 r2 r2.lan r2.lan.buetow.org
    +END
     

    And we configure the IPs accordingly on the VMs themselves by opening a root shell via SSH to the VMs and entering the following commands on each of the VMs:
    @@ -320,18 +315,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~] % nmcli connection modify enp0s5 ipv4.address 192.168.1.120/24
    -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.gateway 192.168.1.1
    -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.DNS 192.168.1.1
    -[root@r0 ~] % nmcli connection modify enp0s5 ipv4.method manual
    -[root@r0 ~] % nmcli connection down enp0s5
    -[root@r0 ~] % nmcli connection up enp0s5
    -[root@r0 ~] % hostnamectl set-hostname r0.lan.buetow.org
    -[root@r0 ~] % cat <<END >>/etc/hosts
    -192.168.1.120 r0 r0.lan r0.lan.buetow.org
    -192.168.1.121 r1 r1.lan r1.lan.buetow.org
    -192.168.1.122 r2 r2.lan r2.lan.buetow.org
    -END
    +
    [root@r0 ~] % nmcli connection modify enp0s5 ipv4.address 192.168.1.120/24
    +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.gateway 192.168.1.1
    +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.DNS 192.168.1.1
    +[root@r0 ~] % nmcli connection modify enp0s5 ipv4.method manual
    +[root@r0 ~] % nmcli connection down enp0s5
    +[root@r0 ~] % nmcli connection up enp0s5
    +[root@r0 ~] % hostnamectl set-hostname r0.lan.buetow.org
    +[root@r0 ~] % cat <<END >>/etc/hosts
    +192.168.1.120 r0 r0.lan r0.lan.buetow.org
    +192.168.1.121 r1 r1.lan r1.lan.buetow.org
    +192.168.1.122 r2 r2.lan r2.lan.buetow.org
    +END
     

    Whereas:
    @@ -352,7 +347,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    % for i in 0 1 2; do ssh-copy-id root@r$i.lan.buetow.org; done
    +
    % for i in 0 1 2; do ssh-copy-id root@r$i.lan.buetow.org; done
     

    Then, we edit the /etc/ssh/sshd_config file again on all three VMs and configure PasswordAuthentication no to only allow SSH key authentication from now on.
    @@ -363,8 +358,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~] % dnf update
    -[root@r0 ~] % reboot
    +
    [root@r0 ~] % dnf update
    +[root@r0 ~] % reboot
     

    Stress testing CPU


    @@ -375,28 +370,28 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    package main
    +
    package main
     
    -import "testing"
    +import "testing"
     
    -func BenchmarkCPUSilly1(b *testing.B) {
    -	for i := 0; i < b.N; i++ {
    -		_ = i * i
    -	}
    -}
    +func BenchmarkCPUSilly1(b *testing.B) {
    +	for i := 0; i < b.N; i++ {
    +		_ = i * i
    +	}
    +}
     
    -func BenchmarkCPUSilly2(b *testing.B) {
    -	var sillyResult float64
    -	for i := 0; i < b.N; i++ {
    -		sillyResult += float64(i)
    -		sillyResult *= float64(i)
    -		divisor := float64(i) + 1
    -		if divisor > 0 {
    -			sillyResult /= divisor
    -		}
    -	}
    -	_ = sillyResult // to avoid compiler optimization
    -}
    +func BenchmarkCPUSilly2(b *testing.B) {
    +	var sillyResult float64
    +	for i := 0; i < b.N; i++ {
    +		sillyResult += float64(i)
    +		sillyResult *= float64(i)
    +		divisor := float64(i) + 1
    +		if divisor > 0 {
    +			sillyResult /= divisor
    +		}
    +	}
    +	_ = sillyResult // to avoid compiler optimization
    +}
     

    You can find the repository here:
    @@ -411,10 +406,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas pkg install git go
    -paul@f0:~ % mkdir ~/git && cd ~/git && \
    -  git clone https://codeberg.org/snonux/sillybench && \
    -  cd sillybench
    +
    paul@f0:~ % doas pkg install git go
    +paul@f0:~ % mkdir ~/git && cd ~/git && \
    +  git clone https://codeberg.org/snonux/sillybench && \
    +  cd sillybench
     

    And to run it:
    @@ -423,18 +418,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~/git/sillybench % go version
    -go version go1.24.1 freebsd/amd64
    +
    paul@f0:~/git/sillybench % go version
    +go version go1.24.1 freebsd/amd64
     
    -paul@f0:~/git/sillybench % go test -bench=.
    -goos: freebsd
    -goarch: amd64
    -pkg: codeberg.org/snonux/sillybench
    -cpu: Intel(R) N100
    -BenchmarkCPUSilly1-4    1000000000               0.4022 ns/op
    -BenchmarkCPUSilly2-4    1000000000               0.4027 ns/op
    -PASS
    -ok      codeberg.org/snonux/sillybench 0.891s
    +paul@f0:~/git/sillybench % go test -bench=.
    +goos: freebsd
    +goarch: amd64
    +pkg: codeberg.org/snonux/sillybench
    +cpu: Intel(R) N100
    +BenchmarkCPUSilly1-4    1000000000               0.4022 ns/op
    +BenchmarkCPUSilly2-4    1000000000               0.4027 ns/op
    +PASS
    +ok      codeberg.org/snonux/sillybench 0.891s
     

    Silly Rocky Linux VM @ Bhyve benchmark


    @@ -445,10 +440,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# dnf install golang git
    -[root@r0 ~]# mkdir ~/git && cd ~/git && \
    -  git clone https://codeberg.org/snonux/sillybench && \
    -  cd sillybench
    +
    [root@r0 ~]# dnf install golang git
    +[root@r0 ~]# mkdir ~/git && cd ~/git && \
    +  git clone https://codeberg.org/snonux/sillybench && \
    +  cd sillybench
     

    And to run it:
    @@ -457,15 +452,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 sillybench]# go version
    -go version go1.22.9 (Red Hat 1.22.9-2.el9_5) linux/amd64
    -[root@r0 sillybench]# go test -bench=.
    -goos: linux
    -goarch: amd64
    -pkg: codeberg.org/snonux/sillybench
    -cpu: Intel(R) N100
    -BenchmarkCPUSilly1-4    1000000000               0.4347 ns/op
    -BenchmarkCPUSilly2-4    1000000000               0.4345 ns/op
    +
    [root@r0 sillybench]# go version
    +go version go1.22.9 (Red Hat 1.22.9-2.el9_5) linux/amd64
    +[root@r0 sillybench]# go test -bench=.
    +goos: linux
    +goarch: amd64
    +pkg: codeberg.org/snonux/sillybench
    +cpu: Intel(R) N100
    +BenchmarkCPUSilly1-4    1000000000               0.4347 ns/op
    +BenchmarkCPUSilly2-4    1000000000               0.4345 ns/op
     

    The Linux benchmark is slightly slower than the FreeBSD one. The Go version is also a bit older. I tried the same with the up-to-date version of Go (1.24.x) with similar results. There could be a slight Bhyve overhead, or FreeBSD is just slightly more efficient in this benchmark. Overall, this shows that Bhyve performs excellently.
    @@ -480,15 +475,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@freebsd:~/git/sillybench # go test -bench=.
    -goos: freebsd
    -goarch: amd64
    -pkg: codeberg.org/snonux/sillybench
    -cpu: Intel(R) N100
    -BenchmarkCPUSilly1      1000000000               0.4273 ns/op
    -BenchmarkCPUSilly2      1000000000               0.4286 ns/op
    -PASS
    -ok      codeberg.org/snonux/sillybench  0.949s
    +
    root@freebsd:~/git/sillybench # go test -bench=.
    +goos: freebsd
    +goarch: amd64
    +pkg: codeberg.org/snonux/sillybench
    +cpu: Intel(R) N100
    +BenchmarkCPUSilly1      1000000000               0.4273 ns/op
    +BenchmarkCPUSilly2      1000000000               0.4286 ns/op
    +PASS
    +ok      codeberg.org/snonux/sillybench  0.949s
     

    It's a bit better than Linux! I am sure that this is not really a scientific benchmark, so take the results with a grain of salt!
    @@ -505,16 +500,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas ubench -s 1
    -Unix Benchmark Utility v.0.3
    -Copyright (C) July, 1999 PhysTech, Inc.
    -Author: Sergei Viznyuk <sv@phystech.com>
    -http://www.phystech.com/download/ubench.html
    -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    -Ubench Single CPU:   671010 (0.40s)
    -Ubench Single MEM:  1705237 (0.48s)
    ------------------------------------
    -Ubench Single AVG:  1188123
    +
    paul@f0:~ % doas ubench -s 1
    +Unix Benchmark Utility v.0.3
    +Copyright (C) July, 1999 PhysTech, Inc.
    +Author: Sergei Viznyuk <sv@phystech.com>
    +http://www.phystech.com/download/ubench.html
    +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    +Ubench Single CPU:   671010 (0.40s)
    +Ubench Single MEM:  1705237 (0.48s)
    +-----------------------------------
    +Ubench Single AVG:  1188123
     
     

    @@ -524,16 +519,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas ubench
    -Unix Benchmark Utility v.0.3
    -Copyright (C) July, 1999 PhysTech, Inc.
    -Author: Sergei Viznyuk <sv@phystech.com>
    -http://www.phystech.com/download/ubench.html
    -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    -Ubench CPU:  2660220
    -Ubench MEM:  3095182
    ---------------------
    -Ubench AVG:  2877701
    +
    paul@f0:~ % doas ubench
    +Unix Benchmark Utility v.0.3
    +Copyright (C) July, 1999 PhysTech, Inc.
    +Author: Sergei Viznyuk <sv@phystech.com>
    +http://www.phystech.com/download/ubench.html
    +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    +Ubench CPU:  2660220
    +Ubench MEM:  3095182
    +--------------------
    +Ubench AVG:  2877701
     

    FreeBSD VM @ Bhyve ubench benchmark


    @@ -544,16 +539,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@freebsd:~ # ubench -s 1
    -Unix Benchmark Utility v.0.3
    -Copyright (C) July, 1999 PhysTech, Inc.
    -Author: Sergei Viznyuk <sv@phystech.com>
    -http://www.phystech.com/download/ubench.html
    -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    -Ubench Single CPU:   672792 (0.40s)
    -Ubench Single MEM:   852757 (0.48s)
    ------------------------------------
    -Ubench Single AVG:   762774
    +
    root@freebsd:~ # ubench -s 1
    +Unix Benchmark Utility v.0.3
    +Copyright (C) July, 1999 PhysTech, Inc.
    +Author: Sergei Viznyuk <sv@phystech.com>
    +http://www.phystech.com/download/ubench.html
    +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    +Ubench Single CPU:   672792 (0.40s)
    +Ubench Single MEM:   852757 (0.48s)
    +-----------------------------------
    +Ubench Single AVG:   762774
     

    Wow, the CPU in the VM was a tiny bit faster than on the host! So this was probably just a glitch in the matrix. Memory seems slower, though.
    @@ -564,24 +559,24 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    root@freebsd:~ # ubench
    -Unix Benchmark Utility v.0.3
    -Copyright (C) July, 1999 PhysTech, Inc.
    -Author: Sergei Viznyuk <sv@phystech.com>
    -http://www.phystech.com/download/ubench.html
    -FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    -Ubench CPU:  2652857
    -swap_pager: out of swap space
    -swp_pager_getswapspace(27): failed
    -swap_pager: out of swap space
    -swp_pager_getswapspace(18): failed
    -Apr  4 23:02:43 freebsd kernel: pid 862 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    -swp_pager_getswapspace(6): failed
    -Apr  4 23:02:46 freebsd kernel: pid 863 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    -Apr  4 23:02:47 freebsd kernel: pid 864 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    -Apr  4 23:02:48 freebsd kernel: pid 865 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    -Apr  4 23:02:49 freebsd kernel: pid 861 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    -Apr  4 23:02:51 freebsd kernel: pid 839 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    +
    root@freebsd:~ # ubench
    +Unix Benchmark Utility v.0.3
    +Copyright (C) July, 1999 PhysTech, Inc.
    +Author: Sergei Viznyuk <sv@phystech.com>
    +http://www.phystech.com/download/ubench.html
    +FreeBSD 14.2-RELEASE-p1 FreeBSD 14.2-RELEASE-p1 GENERIC amd64
    +Ubench CPU:  2652857
    +swap_pager: out of swap space
    +swp_pager_getswapspace(27): failed
    +swap_pager: out of swap space
    +swp_pager_getswapspace(18): failed
    +Apr  4 23:02:43 freebsd kernel: pid 862 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    +swp_pager_getswapspace(6): failed
    +Apr  4 23:02:46 freebsd kernel: pid 863 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    +Apr  4 23:02:47 freebsd kernel: pid 864 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    +Apr  4 23:02:48 freebsd kernel: pid 865 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    +Apr  4 23:02:49 freebsd kernel: pid 861 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
    +Apr  4 23:02:51 freebsd kernel: pid 839 (ubench), jid 0, uid 0, was killed: failed to reclaim memory
     

    The multi-CPU benchmark in the Bhyve VM ran with almost identical results to the FreeBSD host system. However, the memory benchmark failed with out-of-swap space errors. I am unsure why, as the VM has 14GB RAM, but I am not investigating further.
    @@ -620,8 +615,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync
    -8192000 bytes copied, 31.7058 s, 258 kB/s
    +
    [root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync
    +8192000 bytes copied, 31.7058 s, 258 kB/s
     

    The Solution: Switch to NVMe Emulation


    @@ -636,14 +631,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# cat > /etc/dracut.conf.d/nvme.conf << EOF
    -add_drivers+=" nvme nvme_core "
    -hostonly=no
    -EOF
    +
    [root@r0 ~]# cat > /etc/dracut.conf.d/nvme.conf << EOF
    +add_drivers+=" nvme nvme_core "
    +hostonly=no
    +EOF
     
    -[root@r0 ~]# sed -i 's/# use_devicesfile = 1/use_devicesfile = 0/' /etc/lvm/lvm.conf
    -[root@r0 ~]# dracut -f
    -[root@r0 ~]# shutdown -h now
    +[root@r0 ~]# sed -i 's/# use_devicesfile = 1/use_devicesfile = 0/' /etc/lvm/lvm.conf
    +[root@r0 ~]# dracut -f
    +[root@r0 ~]# shutdown -h now
     

    The hostonly=no setting ensures the initramfs includes drivers for hardware not currently present. The use_devicesfile = 0 tells LVM to scan all block devices rather than only those recorded in /etc/lvm/devices/system.devices - this is important because the device path changes from /dev/vda to /dev/nvme0n1.
    @@ -656,8 +651,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas vm stop rocky
    -paul@f0:~ % doas vm configure rocky
    +
    paul@f0:~ % doas vm stop rocky
    +paul@f0:~ % doas vm configure rocky
     

    Change disk0_type from virtio-blk to nvme:
    @@ -672,7 +667,7 @@ disk0_type="nvme" by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas vm start rocky
    +
    paul@f0:~ % doas vm start rocky
     

    Benchmark Results


    @@ -683,8 +678,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync
    -8192000 bytes copied, 0.330718 s, 24.8 MB/s
    +
    [root@r0 ~]# dd if=/dev/zero of=/tmp/test bs=4k count=2000 oflag=dsync
    +8192000 bytes copied, 0.330718 s, 24.8 MB/s
     

    That's approximately **100x faster** than before (24.8 MB/s vs 258 kB/s).
    @@ -738,11 +733,11 @@ etcd_disk_wal_fsync_duration_seconds_bucket{le="0.004"} 408
    Back to the main site
    diff --git a/gemfeed/2025-04-19-when-book-notes.html b/gemfeed/2025-04-19-when-book-notes.html index 5bf2ac81..9fed5290 100644 --- a/gemfeed/2025-04-19-when-book-notes.html +++ b/gemfeed/2025-04-19-when-book-notes.html @@ -2,17 +2,12 @@ - 'When: The Scientific Secrets of Perfect Timing' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -138,11 +133,11 @@ __ejm\___/________dwb`---`______________________
    Back to the main site
    diff --git a/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html b/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html index 3492c050..e179bd90 100644 --- a/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html +++ b/gemfeed/2025-05-02-terminal-multiplexing-with-tmux-fish-edition.html @@ -2,17 +2,12 @@ - Terminal multiplexing with `tmux` - Fish edition -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -197,14 +192,14 @@ set-option -g prefix C-g by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    function tmux::search
    -    set -l session (tmux list-sessions | fzf | cut -d: -f1)
    -    if test -z "$TMUX"
    -        tmux attach-session -t $session
    -    else
    -        tmux switch -t $session
    -    end
    -end
    +
    function tmux::search
    +    set -l session (tmux list-sessions | fzf | cut -d: -f1)
    +    if test -z "$TMUX"
    +        tmux attach-session -t $session
    +    else
    +        tmux switch -t $session
    +    end
    +end
     

    All it does is list all currently open sessions in fzf, where one of them can be searched and selected through fuzzy find, and then either switch (if already inside a session) to the other session or attach to the other session (if not yet in Tmux).
    @@ -413,11 +408,11 @@ bind-key r source-file ~/.config/tmux/tmux.conf \; display-message "tmux.conf re
    Back to the main site
    diff --git a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html index fe444fcc..80f7c564 100644 --- a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html +++ b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -161,14 +156,14 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas freebsd-update fetch
    -paul@f0:~ % doas freebsd-update install
    -paul@f0:~ % doas shutdown -r now
    -..
    -..
    -paul@f0:~ % doas pkg update
    -paul@f0:~ % doas pkg upgrade
    -paul@f0:~ % reboot
    +
    paul@f0:~ % doas freebsd-update fetch
    +paul@f0:~ % doas freebsd-update install
    +paul@f0:~ % doas shutdown -r now
    +..
    +..
    +paul@f0:~ % doas pkg update
    +paul@f0:~ % doas pkg upgrade
    +paul@f0:~ % reboot
     

    Next, we install wireguard-tools and configure the WireGuard service:
    @@ -177,19 +172,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas pkg install wireguard-tools
    -paul@f0:~ % doas sysrc wireguard_interfaces=wg0
    -wireguard_interfaces:  -> wg0
    -paul@f0:~ % doas sysrc wireguard_enable=YES
    -wireguard_enable:  -> YES
    -paul@f0:~ % doas mkdir -p /usr/local/etc/wireguard
    -paul@f0:~ % doas touch /usr/local/etc/wireguard/wg0.conf
    -paul@f0:~ % doas service wireguard start
    -paul@f0:~ % doas wg show
    -interface: wg0
    -  public key: L+V9o0fNYkMVKNqsX7spBzD/9oSvxM/C7ZCZX1jLO3Q=
    -  private key: (hidden)
    -  listening port: 20246
    +
    paul@f0:~ % doas pkg install wireguard-tools
    +paul@f0:~ % doas sysrc wireguard_interfaces=wg0
    +wireguard_interfaces:  -> wg0
    +paul@f0:~ % doas sysrc wireguard_enable=YES
    +wireguard_enable:  -> YES
    +paul@f0:~ % doas mkdir -p /usr/local/etc/wireguard
    +paul@f0:~ % doas touch /usr/local/etc/wireguard/wg0.conf
    +paul@f0:~ % doas service wireguard start
    +paul@f0:~ % doas wg show
    +interface: wg0
    +  public key: L+V9o0fNYkMVKNqsX7spBzD/9oSvxM/C7ZCZX1jLO3Q=
    +  private key: (hidden)
    +  listening port: 20246
     

    We now have the WireGuard up and running, but it is not yet in any functional configuration. We will come back to that later.
    @@ -200,34 +195,34 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % cat <<END | doas tee -a /etc/hosts
    +
    paul@f0:~ % cat <<END | doas tee -a /etc/hosts
     
    -192.168.1.120 r0 r0.lan r0.lan.buetow.org
    -192.168.1.121 r1 r1.lan r1.lan.buetow.org
    -192.168.1.122 r2 r2.lan r2.lan.buetow.org
    +192.168.1.120 r0 r0.lan r0.lan.buetow.org
    +192.168.1.121 r1 r1.lan r1.lan.buetow.org
    +192.168.1.122 r2 r2.lan r2.lan.buetow.org
     
    -192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org
    -192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org
    -192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org
    +192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org
    +192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org
    +192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org
     
    -192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org
    -192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org
    -192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org
    +192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org
    +192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org
    +192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org
     
    -192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    -192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    +192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    +192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
     
    -fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org
    -fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org
    -fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org
    +fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org
    +fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org
    +fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org
     
    -fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org
    -fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org
    -fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org
    +fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org
    +fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org
    +fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org
     
    -fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    -fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    -END
    +fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    +fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    +END
     

    As you can see, 192.168.1.0/24 is the network used in my LAN (with the fN and rN hosts) and 192.168.2.0/24 is the network used for the WireGuard mesh network. The wg0 interface will be used for all WireGuard traffic.
    @@ -240,8 +235,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~] dnf update -y
    -[root@r0 ~] reboot
    +
    [root@r0 ~] dnf update -y
    +[root@r0 ~] reboot
     

    Next, we prepare WireGuard on them. Same as on the FreeBSD hosts, we will only prepare WireGuard without any useful configuration yet:
    @@ -250,12 +245,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~] dnf install -y wireguard-tools
    -[root@r0 ~] mkdir -p /etc/wireguard
    -[root@r0 ~] touch /etc/wireguard/wg0.conf
    -[root@r0 ~] systemctl enable wg-quick@wg0.service
    -[root@r0 ~] systemctl start wg-quick@wg0.service
    -[root@r0 ~] systemctl disable firewalld
    +
    [root@r0 ~] dnf install -y wireguard-tools
    +[root@r0 ~] mkdir -p /etc/wireguard
    +[root@r0 ~] touch /etc/wireguard/wg0.conf
    +[root@r0 ~] systemctl enable wg-quick@wg0.service
    +[root@r0 ~] systemctl start wg-quick@wg0.service
    +[root@r0 ~] systemctl disable firewalld
     

    We also update the hosts file accordingly:
    @@ -264,34 +259,34 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~] cat <<END >>/etc/hosts
    +
    [root@r0 ~] cat <<END >>/etc/hosts
     
    -192.168.1.130 f0 f0.lan f0.lan.buetow.org
    -192.168.1.131 f1 f1.lan f1.lan.buetow.org
    -192.168.1.132 f2 f2.lan f2.lan.buetow.org
    +192.168.1.130 f0 f0.lan f0.lan.buetow.org
    +192.168.1.131 f1 f1.lan f1.lan.buetow.org
    +192.168.1.132 f2 f2.lan f2.lan.buetow.org
     
    -192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org
    -192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org
    -192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org
    +192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org
    +192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org
    +192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org
     
    -192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org
    -192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org
    -192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org
    +192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org
    +192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org
    +192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org
     
    -192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    -192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    +192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    +192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
     
    -fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org
    -fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org
    -fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org
    +fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org
    +fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org
    +fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org
     
    -fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org
    -fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org
    -fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org
    +fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org
    +fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org
    +fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org
     
    -fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    -fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    -END
    +fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    +fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    +END
     

    Unfortunately, the SELinux policy on Rocky Linux blocks WireGuard's operation. By making the wireguard_t domain permissive using semanage permissive -a wireguard_t, SELinux will no longer enforce restrictions for WireGuard, allowing it to work as intended:
    @@ -300,9 +295,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~] dnf install -y policycoreutils-python-utils
    -[root@r0 ~] semanage permissive -a wireguard_t
    -[root@r0 ~] reboot
    +
    [root@r0 ~] dnf install -y policycoreutils-python-utils
    +[root@r0 ~] semanage permissive -a wireguard_t
    +[root@r0 ~] reboot
     

    https://github.com/angristan/wireguard-install/discussions/499
    @@ -315,14 +310,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    blowfish$ doas pkg_add wireguard-tools
    -blowfish$ doas mkdir /etc/wireguard
    -blowfish$ doas touch /etc/wireguard/wg0.conf
    -blowsish$ cat <<END | doas tee /etc/hostname.wg0
    -inet 192.168.2.110 255.255.255.0 NONE
    -up
    -!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf
    -END
    +
    blowfish$ doas pkg_add wireguard-tools
    +blowfish$ doas mkdir /etc/wireguard
    +blowfish$ doas touch /etc/wireguard/wg0.conf
    +blowsish$ cat <<END | doas tee /etc/hostname.wg0
    +inet 192.168.2.110 255.255.255.0 NONE
    +up
    +!/usr/local/bin/wg setconf wg0 /etc/wireguard/wg0.conf
    +END
     

    Note that on blowfish, we configure 192.168.2.110 here in the hostname.wg, and on fishfinger, we configure 192.168.2.111. Those are the IP addresses of the WireGuard interfaces on those hosts.
    @@ -333,34 +328,34 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    blowfish$ cat <<END | doas tee -a /etc/hosts
    -
    -192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org
    -192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org
    -192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org
    -
    -192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org
    -192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org
    -192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org
    -
    -192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    -192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    -192.168.2.200 earth.wg0 earth.wg0.wan.buetow.org
    -192.168.2.201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org
    -
    -fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org
    -fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org
    -fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org
    -
    -fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org
    -fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org
    -fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org
    -
    -fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    -fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    -fd42:beef:cafe:2::200 earth.wg0 earth.wg0.wan.buetow.org
    -fd42:beef:cafe:2::201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org
    -END
    +
    blowfish$ cat <<END | doas tee -a /etc/hosts
    +
    +192.168.2.130 f0.wg0 f0.wg0.wan.buetow.org
    +192.168.2.131 f1.wg0 f1.wg0.wan.buetow.org
    +192.168.2.132 f2.wg0 f2.wg0.wan.buetow.org
    +
    +192.168.2.120 r0.wg0 r0.wg0.wan.buetow.org
    +192.168.2.121 r1.wg0 r1.wg0.wan.buetow.org
    +192.168.2.122 r2.wg0 r2.wg0.wan.buetow.org
    +
    +192.168.2.110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    +192.168.2.111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    +192.168.2.200 earth.wg0 earth.wg0.wan.buetow.org
    +192.168.2.201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org
    +
    +fd42:beef:cafe:2::130 f0.wg0 f0.wg0.wan.buetow.org
    +fd42:beef:cafe:2::131 f1.wg0 f1.wg0.wan.buetow.org
    +fd42:beef:cafe:2::132 f2.wg0 f2.wg0.wan.buetow.org
    +
    +fd42:beef:cafe:2::120 r0.wg0 r0.wg0.wan.buetow.org
    +fd42:beef:cafe:2::121 r1.wg0 r1.wg0.wan.buetow.org
    +fd42:beef:cafe:2::122 r2.wg0 r2.wg0.wan.buetow.org
    +
    +fd42:beef:cafe:2::110 blowfish.wg0 blowfish.wg0.wan.buetow.org
    +fd42:beef:cafe:2::111 fishfinger.wg0 fishfinger.wg0.wan.buetow.org
    +fd42:beef:cafe:2::200 earth.wg0 earth.wg0.wan.buetow.org
    +fd42:beef:cafe:2::201 pixel7pro.wg0 pixel7pro.wg0.wan.buetow.org
    +END
     

    To enable roaming clients (like earth and pixel7pro) to access the internet through the VPN, we need to configure NAT on the OpenBSD gateways. This allows the roaming clients to use the gateway's public IP address for outbound traffic. We add the following to /etc/pf.conf on both blowfish and fishfinger:
    @@ -369,14 +364,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # NAT for WireGuard clients to access internet
    -match out on vio0 from 192.168.2.0/24 to any nat-to (vio0)
    +
    # NAT for WireGuard clients to access internet
    +match out on vio0 from 192.168.2.0/24 to any nat-to (vio0)
     
    -# Allow inbound traffic on WireGuard interface
    -pass in on wg0
    +# Allow inbound traffic on WireGuard interface
    +pass in on wg0
     
    -# Allow all UDP traffic on WireGuard port
    -pass in inet proto udp from any to any port 56709
    +# Allow all UDP traffic on WireGuard port
    +pass in inet proto udp from any to any port 56709
     

    The NAT rule translates outgoing traffic from the WireGuard network (192.168.2.0/24) to the gateway's public IP. The firewall rules permit WireGuard traffic on the wg0 interface and UDP port 56709. After updating /etc/pf.conf, reload the firewall:
    @@ -385,7 +380,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    blowfish$ doas pfctl -f /etc/pf.conf
    +
    blowfish$ doas pfctl -f /etc/pf.conf
     

    WireGuard configuration


    @@ -544,10 +539,10 @@ PersistentKeepalive = 25 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > git clone https://codeberg.org/snonux/wireguardmeshgenerator
    -> cd ./wireguardmeshgenerator
    -> bundle install
    -> sudo dnf install -y wireguard-tools
    +
    > git clone https://codeberg.org/snonux/wireguardmeshgenerator
    +> cd ./wireguardmeshgenerator
    +> bundle install
    +> sudo dnf install -y wireguard-tools
     

    This assumes that Ruby and the bundler gem are already installed. If not, refer to the docs of your distribution.
    @@ -747,39 +742,39 @@ hosts: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    begin
    -  options = { hosts: [] }
    -  OptionParser.new do |opts|
    -    opts.banner = 'Usage: wireguardmeshgenerator.rb [options]'
    -    opts.on('--generate', 'Generate Wireguard configs') do
    -      options[:generate] = true
    -    end
    -    opts.on('--install', 'Install Wireguard configs') do
    -      options[:install] = true
    -    end
    -    opts.on('--clean', 'Clean Wireguard configs') do
    -      options[:clean] = true
    -    end
    -    opts.on('--hosts=HOSTS', 'Comma separated hosts to configure') do |hosts|
    -      options[:hosts] = hosts.split(',')
    -    end
    -  end.parse!
    -
    -  conf = YAML.load_file('wireguardmeshgenerator.yaml').freeze
    -  conf['hosts'].keys.select { options[:hosts].empty? || options[:hosts].include?(_1) }
    -               .each do |host|
    -    # Generate Wireguard configuration for the host reload!
    -    WireguardConfig.new(host, conf['hosts']).generate! if options[:generate]
    -    # Install Wireguard configuration for the host.
    -    InstallConfig.new(host, conf['hosts']).upload!.install!.reload! if options[:install]
    -    # Clean Wireguard configuration for the host.
    -    WireguardConfig.new(host, conf['hosts']).clean! if options[:clean]
    -  end
    -rescue StandardError => e
    -  puts "Error: #{e.message}"
    -  puts e.backtrace.join("\n")
    -  exit 2
    -end
    +
    begin
    +  options = { hosts: [] }
    +  OptionParser.new do |opts|
    +    opts.banner = 'Usage: wireguardmeshgenerator.rb [options]'
    +    opts.on('--generate', 'Generate Wireguard configs') do
    +      options[:generate] = true
    +    end
    +    opts.on('--install', 'Install Wireguard configs') do
    +      options[:install] = true
    +    end
    +    opts.on('--clean', 'Clean Wireguard configs') do
    +      options[:clean] = true
    +    end
    +    opts.on('--hosts=HOSTS', 'Comma separated hosts to configure') do |hosts|
    +      options[:hosts] = hosts.split(',')
    +    end
    +  end.parse!
    +
    +  conf = YAML.load_file('wireguardmeshgenerator.yaml').freeze
    +  conf['hosts'].keys.select { options[:hosts].empty? || options[:hosts].include?(_1) }
    +               .each do |host|
    +    # Generate Wireguard configuration for the host reload!
    +    WireguardConfig.new(host, conf['hosts']).generate! if options[:generate]
    +    # Install Wireguard configuration for the host.
    +    InstallConfig.new(host, conf['hosts']).upload!.install!.reload! if options[:install]
    +    # Clean Wireguard configuration for the host.
    +    WireguardConfig.new(host, conf['hosts']).clean! if options[:clean]
    +  end
    +rescue StandardError => e
    +  puts "Error: #{e.message}"
    +  puts e.backtrace.join("\n")
    +  exit 2
    +end
     

    And we also have a Rakefile:
    @@ -788,19 +783,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    task :generate do
    -  ruby 'wireguardmeshgenerator.rb', '--generate'
    -end
    +
    task :generate do
    +  ruby 'wireguardmeshgenerator.rb', '--generate'
    +end
     
    -task :clean do
    -  ruby 'wireguardmeshgenerator.rb', '--clean'
    -end
    +task :clean do
    +  ruby 'wireguardmeshgenerator.rb', '--clean'
    +end
     
    -task :install do
    -  ruby 'wireguardmeshgenerator.rb', '--install'
    -end
    +task :install do
    +  ruby 'wireguardmeshgenerator.rb', '--install'
    +end
     
    -task default: :generate
    +task default: :generate
     


    @@ -814,18 +809,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > rake generate
    -/usr/bin/ruby wireguardmeshgenerator.rb --generate
    -Generating dist/f0/etc/wireguard/wg0.conf
    -Generating dist/f1/etc/wireguard/wg0.conf
    -Generating dist/f2/etc/wireguard/wg0.conf
    -Generating dist/r0/etc/wireguard/wg0.conf
    -Generating dist/r1/etc/wireguard/wg0.conf
    -Generating dist/r2/etc/wireguard/wg0.conf
    -Generating dist/blowfish/etc/wireguard/wg0.conf
    -Generating dist/fishfinger/etc/wireguard/wg0.conf
    -Generating dist/earth/etc/wireguard/wg0.conf
    -Generating dist/pixel7pro/etc/wireguard/wg0.conf
    +
    > rake generate
    +/usr/bin/ruby wireguardmeshgenerator.rb --generate
    +Generating dist/f0/etc/wireguard/wg0.conf
    +Generating dist/f1/etc/wireguard/wg0.conf
    +Generating dist/f2/etc/wireguard/wg0.conf
    +Generating dist/r0/etc/wireguard/wg0.conf
    +Generating dist/r1/etc/wireguard/wg0.conf
    +Generating dist/r2/etc/wireguard/wg0.conf
    +Generating dist/blowfish/etc/wireguard/wg0.conf
    +Generating dist/fishfinger/etc/wireguard/wg0.conf
    +Generating dist/earth/etc/wireguard/wg0.conf
    +Generating dist/pixel7pro/etc/wireguard/wg0.conf
     

    It generated all the wg0.conf files listed in the output, plus those keys:
    @@ -834,59 +829,59 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > find keys/ -type f
    -keys/f0/priv.key
    -keys/f0/pub.key
    -keys/psk/f0_f1.key
    -keys/psk/f0_f2.key
    -keys/psk/f0_r0.key
    -keys/psk/f0_r1.key
    -keys/psk/f0_r2.key
    -keys/psk/blowfish_f0.key
    -keys/psk/f0_fishfinger.key
    -keys/psk/f1_f2.key
    -keys/psk/f1_r0.key
    -keys/psk/f1_r1.key
    -keys/psk/f1_r2.key
    -keys/psk/blowfish_f1.key
    -keys/psk/f1_fishfinger.key
    -keys/psk/f2_r0.key
    -keys/psk/f2_r1.key
    -keys/psk/f2_r2.key
    -keys/psk/blowfish_f2.key
    -keys/psk/f2_fishfinger.key
    -keys/psk/r0_r1.key
    -keys/psk/r0_r2.key
    -keys/psk/blowfish_r0.key
    -keys/psk/fishfinger_r0.key
    -keys/psk/r1_r2.key
    -keys/psk/blowfish_r1.key
    -keys/psk/fishfinger_r1.key
    -keys/psk/blowfish_r2.key
    -keys/psk/fishfinger_r2.key
    -keys/psk/blowfish_fishfinger.key
    -keys/psk/blowfish_earth.key
    -keys/psk/earth_fishfinger.key
    -keys/psk/blowfish_pixel7pro.key
    -keys/psk/fishfinger_pixel7pro.key
    -keys/f1/priv.key
    -keys/f1/pub.key
    -keys/f2/priv.key
    -keys/f2/pub.key
    -keys/r0/priv.key
    -keys/r0/pub.key
    -keys/r1/priv.key
    -keys/r1/pub.key
    -keys/r2/priv.key
    -keys/r2/pub.key
    -keys/blowfish/priv.key
    -keys/blowfish/pub.key
    -keys/fishfinger/priv.key
    -keys/fishfinger/pub.key
    -keys/earth/priv.key
    -keys/earth/pub.key
    -keys/pixel7pro/priv.key
    -keys/pixel7pro/pub.key
    +
    > find keys/ -type f
    +keys/f0/priv.key
    +keys/f0/pub.key
    +keys/psk/f0_f1.key
    +keys/psk/f0_f2.key
    +keys/psk/f0_r0.key
    +keys/psk/f0_r1.key
    +keys/psk/f0_r2.key
    +keys/psk/blowfish_f0.key
    +keys/psk/f0_fishfinger.key
    +keys/psk/f1_f2.key
    +keys/psk/f1_r0.key
    +keys/psk/f1_r1.key
    +keys/psk/f1_r2.key
    +keys/psk/blowfish_f1.key
    +keys/psk/f1_fishfinger.key
    +keys/psk/f2_r0.key
    +keys/psk/f2_r1.key
    +keys/psk/f2_r2.key
    +keys/psk/blowfish_f2.key
    +keys/psk/f2_fishfinger.key
    +keys/psk/r0_r1.key
    +keys/psk/r0_r2.key
    +keys/psk/blowfish_r0.key
    +keys/psk/fishfinger_r0.key
    +keys/psk/r1_r2.key
    +keys/psk/blowfish_r1.key
    +keys/psk/fishfinger_r1.key
    +keys/psk/blowfish_r2.key
    +keys/psk/fishfinger_r2.key
    +keys/psk/blowfish_fishfinger.key
    +keys/psk/blowfish_earth.key
    +keys/psk/earth_fishfinger.key
    +keys/psk/blowfish_pixel7pro.key
    +keys/psk/fishfinger_pixel7pro.key
    +keys/f1/priv.key
    +keys/f1/pub.key
    +keys/f2/priv.key
    +keys/f2/pub.key
    +keys/r0/priv.key
    +keys/r0/pub.key
    +keys/r1/priv.key
    +keys/r1/pub.key
    +keys/r2/priv.key
    +keys/r2/pub.key
    +keys/blowfish/priv.key
    +keys/blowfish/pub.key
    +keys/fishfinger/priv.key
    +keys/fishfinger/pub.key
    +keys/earth/priv.key
    +keys/earth/pub.key
    +keys/pixel7pro/priv.key
    +keys/pixel7pro/pub.key
     

    Those keys are embedded in the resulting wg0.conf, so later, we only need to install the wg0.conf files and not all the keys individually.
    @@ -899,112 +894,112 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > rake install
    -/usr/bin/ruby wireguardmeshgenerator.rb --install
    -Uploading dist/f0/etc/wireguard/wg0.conf to f0.lan.buetow.org:.
    -Installing Wireguard config on f0
    -Uploading cmd.sh to f0.lan.buetow.org:.
    -+ [ ! -d /usr/local/etc/wireguard ]
    -+ doas chmod 700 /usr/local/etc/wireguard
    -+ doas mv -v wg0.conf /usr/local/etc/wireguard
    -wg0.conf -> /usr/local/etc/wireguard/wg0.conf
    -+ doas chmod 644 /usr/local/etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on f0
    -Uploading cmd.sh to f0.lan.buetow.org:.
    -+ doas service wireguard reload
    -+ rm cmd.sh
    -Uploading dist/f1/etc/wireguard/wg0.conf to f1.lan.buetow.org:.
    -Installing Wireguard config on f1
    -Uploading cmd.sh to f1.lan.buetow.org:.
    -+ [ ! -d /usr/local/etc/wireguard ]
    -+ doas chmod 700 /usr/local/etc/wireguard
    -+ doas mv -v wg0.conf /usr/local/etc/wireguard
    -wg0.conf -> /usr/local/etc/wireguard/wg0.conf
    -+ doas chmod 644 /usr/local/etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on f1
    -Uploading cmd.sh to f1.lan.buetow.org:.
    -+ doas service wireguard reload
    -+ rm cmd.sh
    -Uploading dist/f2/etc/wireguard/wg0.conf to f2.lan.buetow.org:.
    -Installing Wireguard config on f2
    -Uploading cmd.sh to f2.lan.buetow.org:.
    -+ [ ! -d /usr/local/etc/wireguard ]
    -+ doas chmod 700 /usr/local/etc/wireguard
    -+ doas mv -v wg0.conf /usr/local/etc/wireguard
    -wg0.conf -> /usr/local/etc/wireguard/wg0.conf
    -+ doas chmod 644 /usr/local/etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on f2
    -Uploading cmd.sh to f2.lan.buetow.org:.
    -+ doas service wireguard reload
    -+ rm cmd.sh
    -Uploading dist/r0/etc/wireguard/wg0.conf to r0.lan.buetow.org:.
    -Installing Wireguard config on r0
    -Uploading cmd.sh to r0.lan.buetow.org:.
    -+ '[' '!' -d /etc/wireguard ']'
    -+ chmod 700 /etc/wireguard
    -+ mv -v wg0.conf /etc/wireguard
    -renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf'
    -+ chmod 644 /etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on r0
    -Uploading cmd.sh to r0.lan.buetow.org:.
    -+ systemctl reload wg-quick@wg0.service
    -+ rm cmd.sh
    -Uploading dist/r1/etc/wireguard/wg0.conf to r1.lan.buetow.org:.
    -Installing Wireguard config on r1
    -Uploading cmd.sh to r1.lan.buetow.org:.
    -+ '[' '!' -d /etc/wireguard ']'
    -+ chmod 700 /etc/wireguard
    -+ mv -v wg0.conf /etc/wireguard
    -renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf'
    -+ chmod 644 /etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on r1
    -Uploading cmd.sh to r1.lan.buetow.org:.
    -+ systemctl reload wg-quick@wg0.service
    -+ rm cmd.sh
    -Uploading dist/r2/etc/wireguard/wg0.conf to r2.lan.buetow.org:.
    -Installing Wireguard config on r2
    -Uploading cmd.sh to r2.lan.buetow.org:.
    -+ '[' '!' -d /etc/wireguard ']'
    -+ chmod 700 /etc/wireguard
    -+ mv -v wg0.conf /etc/wireguard
    -renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf'
    -+ chmod 644 /etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on r2
    -Uploading cmd.sh to r2.lan.buetow.org:.
    -+ systemctl reload wg-quick@wg0.service
    -+ rm cmd.sh
    -Uploading dist/blowfish/etc/wireguard/wg0.conf to blowfish.buetow.org:.
    -Installing Wireguard config on blowfish
    -Uploading cmd.sh to blowfish.buetow.org:.
    -+ [ ! -d /etc/wireguard ]
    -+ doas chmod 700 /etc/wireguard
    -+ doas mv -v wg0.conf /etc/wireguard
    -wg0.conf -> /etc/wireguard/wg0.conf
    -+ doas chmod 644 /etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on blowfish
    -Uploading cmd.sh to blowfish.buetow.org:.
    -+ doas sh /etc/netstart wg0
    -+ rm cmd.sh
    -Uploading dist/fishfinger/etc/wireguard/wg0.conf to fishfinger.buetow.org:.
    -Installing Wireguard config on fishfinger
    -Uploading cmd.sh to fishfinger.buetow.org:.
    -+ [ ! -d /etc/wireguard ]
    -+ doas chmod 700 /etc/wireguard
    -+ doas mv -v wg0.conf /etc/wireguard
    -wg0.conf -> /etc/wireguard/wg0.conf
    -+ doas chmod 644 /etc/wireguard/wg0.conf
    -+ rm cmd.sh
    -Reloading Wireguard on fishfinger
    -Uploading cmd.sh to fishfinger.buetow.org:.
    -+ doas sh /etc/netstart wg0
    -+ rm cmd.sh
    +
    > rake install
    +/usr/bin/ruby wireguardmeshgenerator.rb --install
    +Uploading dist/f0/etc/wireguard/wg0.conf to f0.lan.buetow.org:.
    +Installing Wireguard config on f0
    +Uploading cmd.sh to f0.lan.buetow.org:.
    ++ [ ! -d /usr/local/etc/wireguard ]
    ++ doas chmod 700 /usr/local/etc/wireguard
    ++ doas mv -v wg0.conf /usr/local/etc/wireguard
    +wg0.conf -> /usr/local/etc/wireguard/wg0.conf
    ++ doas chmod 644 /usr/local/etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on f0
    +Uploading cmd.sh to f0.lan.buetow.org:.
    ++ doas service wireguard reload
    ++ rm cmd.sh
    +Uploading dist/f1/etc/wireguard/wg0.conf to f1.lan.buetow.org:.
    +Installing Wireguard config on f1
    +Uploading cmd.sh to f1.lan.buetow.org:.
    ++ [ ! -d /usr/local/etc/wireguard ]
    ++ doas chmod 700 /usr/local/etc/wireguard
    ++ doas mv -v wg0.conf /usr/local/etc/wireguard
    +wg0.conf -> /usr/local/etc/wireguard/wg0.conf
    ++ doas chmod 644 /usr/local/etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on f1
    +Uploading cmd.sh to f1.lan.buetow.org:.
    ++ doas service wireguard reload
    ++ rm cmd.sh
    +Uploading dist/f2/etc/wireguard/wg0.conf to f2.lan.buetow.org:.
    +Installing Wireguard config on f2
    +Uploading cmd.sh to f2.lan.buetow.org:.
    ++ [ ! -d /usr/local/etc/wireguard ]
    ++ doas chmod 700 /usr/local/etc/wireguard
    ++ doas mv -v wg0.conf /usr/local/etc/wireguard
    +wg0.conf -> /usr/local/etc/wireguard/wg0.conf
    ++ doas chmod 644 /usr/local/etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on f2
    +Uploading cmd.sh to f2.lan.buetow.org:.
    ++ doas service wireguard reload
    ++ rm cmd.sh
    +Uploading dist/r0/etc/wireguard/wg0.conf to r0.lan.buetow.org:.
    +Installing Wireguard config on r0
    +Uploading cmd.sh to r0.lan.buetow.org:.
    ++ '[' '!' -d /etc/wireguard ']'
    ++ chmod 700 /etc/wireguard
    ++ mv -v wg0.conf /etc/wireguard
    +renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf'
    ++ chmod 644 /etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on r0
    +Uploading cmd.sh to r0.lan.buetow.org:.
    ++ systemctl reload wg-quick@wg0.service
    ++ rm cmd.sh
    +Uploading dist/r1/etc/wireguard/wg0.conf to r1.lan.buetow.org:.
    +Installing Wireguard config on r1
    +Uploading cmd.sh to r1.lan.buetow.org:.
    ++ '[' '!' -d /etc/wireguard ']'
    ++ chmod 700 /etc/wireguard
    ++ mv -v wg0.conf /etc/wireguard
    +renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf'
    ++ chmod 644 /etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on r1
    +Uploading cmd.sh to r1.lan.buetow.org:.
    ++ systemctl reload wg-quick@wg0.service
    ++ rm cmd.sh
    +Uploading dist/r2/etc/wireguard/wg0.conf to r2.lan.buetow.org:.
    +Installing Wireguard config on r2
    +Uploading cmd.sh to r2.lan.buetow.org:.
    ++ '[' '!' -d /etc/wireguard ']'
    ++ chmod 700 /etc/wireguard
    ++ mv -v wg0.conf /etc/wireguard
    +renamed 'wg0.conf' -> '/etc/wireguard/wg0.conf'
    ++ chmod 644 /etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on r2
    +Uploading cmd.sh to r2.lan.buetow.org:.
    ++ systemctl reload wg-quick@wg0.service
    ++ rm cmd.sh
    +Uploading dist/blowfish/etc/wireguard/wg0.conf to blowfish.buetow.org:.
    +Installing Wireguard config on blowfish
    +Uploading cmd.sh to blowfish.buetow.org:.
    ++ [ ! -d /etc/wireguard ]
    ++ doas chmod 700 /etc/wireguard
    ++ doas mv -v wg0.conf /etc/wireguard
    +wg0.conf -> /etc/wireguard/wg0.conf
    ++ doas chmod 644 /etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on blowfish
    +Uploading cmd.sh to blowfish.buetow.org:.
    ++ doas sh /etc/netstart wg0
    ++ rm cmd.sh
    +Uploading dist/fishfinger/etc/wireguard/wg0.conf to fishfinger.buetow.org:.
    +Installing Wireguard config on fishfinger
    +Uploading cmd.sh to fishfinger.buetow.org:.
    ++ [ ! -d /etc/wireguard ]
    ++ doas chmod 700 /etc/wireguard
    ++ doas mv -v wg0.conf /etc/wireguard
    +wg0.conf -> /etc/wireguard/wg0.conf
    ++ doas chmod 644 /etc/wireguard/wg0.conf
    ++ rm cmd.sh
    +Reloading Wireguard on fishfinger
    +Uploading cmd.sh to fishfinger.buetow.org:.
    ++ doas sh /etc/netstart wg0
    ++ rm cmd.sh
     

    Re-generating mesh and installing the wg0.conf files again


    @@ -1015,9 +1010,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > rake clean
    -> rake generate
    -> rake install
    +
    > rake clean
    +> rake generate
    +> rake install
     

    That would also delete and re-generate all the keys involved.
    @@ -1034,8 +1029,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > sudo dnf install qrencode
    -> qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0.conf
    +
    > sudo dnf install qrencode
    +> qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0.conf
     

    Scan the QR code with the WireGuard app to import the configuration. The phone will then route all traffic through the VPN when the tunnel is activated. Note that WireGuard does not support automatic failover between the two gateways (blowfish and fishfinger)—if one fails, manual disconnection and reconnection is required to switch to the other.
    @@ -1048,10 +1043,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    > sudo cp dist/earth/etc/wireguard/wg0.conf /etc/wireguard/
    -> sudo chmod 600 /etc/wireguard/wg0.conf
    -> sudo systemctl start wg-quick@wg0.service  # Start manually
    -> sudo systemctl disable wg-quick@wg0.service  # Prevent auto-start
    +
    > sudo cp dist/earth/etc/wireguard/wg0.conf /etc/wireguard/
    +> sudo chmod 600 /etc/wireguard/wg0.conf
    +> sudo systemctl start wg-quick@wg0.service  # Start manually
    +> sudo systemctl disable wg-quick@wg0.service  # Prevent auto-start
     

    The service is disabled from auto-start so the VPN is only active when manually started. This allows selective VPN usage based on need.
    @@ -1095,19 +1090,19 @@ fd42:beef:cafe:2::201/64 - pixel7pro.wg0 (roaming phone) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    def address
    -  return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD'
    -
    -  ipv4 = hosts[myself]['wg0']['ip']
    -  ipv6 = hosts[myself]['wg0']['ipv6']
    -
    -  # WireGuard supports multiple Address directives for dual-stack
    -  if ipv6
    -    "Address = #{ipv4}\nAddress = #{ipv6}/64"
    -  else
    -    "Address = #{ipv4}"
    -  end
    -end
    +
    def address
    +  return '# No Address = ... for OpenBSD here' if hosts[myself]['os'] == 'OpenBSD'
    +
    +  ipv4 = hosts[myself]['wg0']['ip']
    +  ipv6 = hosts[myself]['wg0']['ipv6']
    +
    +  # WireGuard supports multiple Address directives for dual-stack
    +  if ipv6
    +    "Address = #{ipv4}\nAddress = #{ipv6}/64"
    +  else
    +    "Address = #{ipv4}"
    +  end
    +end
     

    **2. AllowedIPs generation (peers method)**
    @@ -1118,14 +1113,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    if is_roaming
    -  allowed_ips = '0.0.0.0/0, ::/0'
    -else
    -  # For mesh peers, allow both IPv4 and IPv6 if present
    -  ipv4 = data['wg0']['ip']
    -  ipv6 = data['wg0']['ipv6']
    -  allowed_ips = ipv6 ? "#{ipv4}/32, #{ipv6}/128" : "#{ipv4}/32"
    -end
    +
    if is_roaming
    +  allowed_ips = '0.0.0.0/0, ::/0'
    +else
    +  # For mesh peers, allow both IPv4 and IPv6 if present
    +  ipv4 = data['wg0']['ip']
    +  ipv6 = data['wg0']['ipv6']
    +  allowed_ips = ipv6 ? "#{ipv4}/32, #{ipv6}/128" : "#{ipv4}/32"
    +end
     

    Roaming clients keep AllowedIPs = 0.0.0.0/0, ::/0 to route all traffic (IPv4 and IPv6) through the VPN.
    @@ -1157,7 +1152,7 @@ pass in inet6 proto udp from any to any port 56709 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    rex@blowfish:~ $ doas vi /etc/hostname.wg0
    +
    rex@blowfish:~ $ doas vi /etc/hostname.wg0
     

    Add the IPv6 address (note the order - IPv6 must be configured before up):
    @@ -1177,9 +1172,9 @@ up by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    rex@blowfish:~ $ doas sh /etc/netstart wg0
    -rex@blowfish:~ $ ifconfig wg0 | grep inet6
    -inet6 fd42:beef:cafe:2::110 prefixlen 64
    +
    rex@blowfish:~ $ doas sh /etc/netstart wg0
    +rex@blowfish:~ $ ifconfig wg0 | grep inet6
    +inet6 fd42:beef:cafe:2::110 prefixlen 64
     

    Repeat for fishfinger with address fd42:beef:cafe:2::111.
    @@ -1194,14 +1189,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # From r0 (Rocky Linux VM)
    -root@r0:~ # ping -c 2 192.168.2.130  # IPv4 to f0
    -64 bytes from 192.168.2.130: icmp_seq=1 ttl=64 time=2.12 ms
    -64 bytes from 192.168.2.130: icmp_seq=2 ttl=64 time=0.681 ms
    -
    -root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130  # IPv6 to f0
    -64 bytes from fd42:beef:cafe:2::130: icmp_seq=1 ttl=64 time=2.16 ms
    -64 bytes from fd42:beef:cafe:2::130: icmp_seq=2 ttl=64 time=0.909 ms
    +
    # From r0 (Rocky Linux VM)
    +root@r0:~ # ping -c 2 192.168.2.130  # IPv4 to f0
    +64 bytes from 192.168.2.130: icmp_seq=1 ttl=64 time=2.12 ms
    +64 bytes from 192.168.2.130: icmp_seq=2 ttl=64 time=0.681 ms
    +
    +root@r0:~ # ping6 -c 2 fd42:beef:cafe:2::130  # IPv6 to f0
    +64 bytes from fd42:beef:cafe:2::130: icmp_seq=1 ttl=64 time=2.16 ms
    +64 bytes from fd42:beef:cafe:2::130: icmp_seq=2 ttl=64 time=0.909 ms
     

    The dual-stack configuration is backward compatible—hosts without the ipv6 field in the YAML configuration will continue to generate IPv4-only configs.
    @@ -1224,52 +1219,52 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas wg show
    -interface: wg0
    -  public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y=
    -  private key: (hidden)
    -  listening port: 56709
    -
    -peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns=
    -  preshared key: (hidden)
    -  endpoint: 46.23.94.99:56709
    -  allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128
    -  latest handshake: 1 minute, 46 seconds ago
    -  transfer: 124 B received, 1.75 KiB sent
    -  persistent keepalive: every 25 seconds
    -
    -peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns=
    -  preshared key: (hidden)
    -  endpoint: 23.88.35.144:56709
    -  allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128
    -  latest handshake: 1 minute, 52 seconds ago
    -  transfer: 124 B received, 1.60 KiB sent
    -  persistent keepalive: every 25 seconds
    -
    -peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.120:56709
    -  allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128
    -
    -peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.131:56709
    -  allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128
    -
    -peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.122:56709
    -  allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128
    -
    -peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.121:56709
    -  allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128
    -
    -peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.132:56709
    -  allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128
    +
    paul@f0:~ % doas wg show
    +interface: wg0
    +  public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y=
    +  private key: (hidden)
    +  listening port: 56709
    +
    +peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns=
    +  preshared key: (hidden)
    +  endpoint: 46.23.94.99:56709
    +  allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128
    +  latest handshake: 1 minute, 46 seconds ago
    +  transfer: 124 B received, 1.75 KiB sent
    +  persistent keepalive: every 25 seconds
    +
    +peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns=
    +  preshared key: (hidden)
    +  endpoint: 23.88.35.144:56709
    +  allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128
    +  latest handshake: 1 minute, 52 seconds ago
    +  transfer: 124 B received, 1.60 KiB sent
    +  persistent keepalive: every 25 seconds
    +
    +peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.120:56709
    +  allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128
    +
    +peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.131:56709
    +  allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128
    +
    +peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.122:56709
    +  allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128
    +
    +peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.121:56709
    +  allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128
    +
    +peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.132:56709
    +  allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128
     

    All the hosts are pingable as well, e.g.:
    @@ -1278,65 +1273,65 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % foreach peer ( f1 f2 r0 r1 r2 blowfish fishfinger )
    -foreach? ping -c2 $peer.wg0
    -foreach? echo
    -foreach? end
    -PING f1.wg0 (192.168.2.131): 56 data bytes
    -64 bytes from 192.168.2.131: icmp_seq=0 ttl=64 time=0.334 ms
    -64 bytes from 192.168.2.131: icmp_seq=1 ttl=64 time=0.260 ms
    -
    ---- f1.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 0.260/0.297/0.334/0.037 ms
    -
    -PING f2.wg0 (192.168.2.132): 56 data bytes
    -64 bytes from 192.168.2.132: icmp_seq=0 ttl=64 time=0.323 ms
    -64 bytes from 192.168.2.132: icmp_seq=1 ttl=64 time=0.303 ms
    -
    ---- f2.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 0.303/0.313/0.323/0.010 ms
    -
    -PING r0.wg0 (192.168.2.120): 56 data bytes
    -64 bytes from 192.168.2.120: icmp_seq=0 ttl=64 time=0.716 ms
    -64 bytes from 192.168.2.120: icmp_seq=1 ttl=64 time=0.406 ms
    -
    ---- r0.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 0.406/0.561/0.716/0.155 ms
    -
    -PING r1.wg0 (192.168.2.121): 56 data bytes
    -64 bytes from 192.168.2.121: icmp_seq=0 ttl=64 time=0.639 ms
    -64 bytes from 192.168.2.121: icmp_seq=1 ttl=64 time=0.629 ms
    -
    ---- r1.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 0.629/0.634/0.639/0.005 ms
    -
    -PING r2.wg0 (192.168.2.122): 56 data bytes
    -64 bytes from 192.168.2.122: icmp_seq=0 ttl=64 time=0.569 ms
    -64 bytes from 192.168.2.122: icmp_seq=1 ttl=64 time=0.479 ms
    -
    ---- r2.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 0.479/0.524/0.569/0.045 ms
    -
    -PING blowfish.wg0 (192.168.2.110): 56 data bytes
    -64 bytes from 192.168.2.110: icmp_seq=0 ttl=255 time=35.745 ms
    -64 bytes from 192.168.2.110: icmp_seq=1 ttl=255 time=35.481 ms
    -
    ---- blowfish.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 35.481/35.613/35.745/0.132 ms
    -
    -PING fishfinger.wg0 (192.168.2.111): 56 data bytes
    -64 bytes from 192.168.2.111: icmp_seq=0 ttl=255 time=33.992 ms
    -64 bytes from 192.168.2.111: icmp_seq=1 ttl=255 time=33.751 ms
    -
    ---- fishfinger.wg0 ping statistics ---
    -2 packets transmitted, 2 packets received, 0.0% packet loss
    -round-trip min/avg/max/stddev = 33.751/33.872/33.992/0.120 ms
    +
    paul@f0:~ % foreach peer ( f1 f2 r0 r1 r2 blowfish fishfinger )
    +foreach? ping -c2 $peer.wg0
    +foreach? echo
    +foreach? end
    +PING f1.wg0 (192.168.2.131): 56 data bytes
    +64 bytes from 192.168.2.131: icmp_seq=0 ttl=64 time=0.334 ms
    +64 bytes from 192.168.2.131: icmp_seq=1 ttl=64 time=0.260 ms
    +
    +--- f1.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 0.260/0.297/0.334/0.037 ms
    +
    +PING f2.wg0 (192.168.2.132): 56 data bytes
    +64 bytes from 192.168.2.132: icmp_seq=0 ttl=64 time=0.323 ms
    +64 bytes from 192.168.2.132: icmp_seq=1 ttl=64 time=0.303 ms
    +
    +--- f2.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 0.303/0.313/0.323/0.010 ms
    +
    +PING r0.wg0 (192.168.2.120): 56 data bytes
    +64 bytes from 192.168.2.120: icmp_seq=0 ttl=64 time=0.716 ms
    +64 bytes from 192.168.2.120: icmp_seq=1 ttl=64 time=0.406 ms
    +
    +--- r0.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 0.406/0.561/0.716/0.155 ms
    +
    +PING r1.wg0 (192.168.2.121): 56 data bytes
    +64 bytes from 192.168.2.121: icmp_seq=0 ttl=64 time=0.639 ms
    +64 bytes from 192.168.2.121: icmp_seq=1 ttl=64 time=0.629 ms
    +
    +--- r1.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 0.629/0.634/0.639/0.005 ms
    +
    +PING r2.wg0 (192.168.2.122): 56 data bytes
    +64 bytes from 192.168.2.122: icmp_seq=0 ttl=64 time=0.569 ms
    +64 bytes from 192.168.2.122: icmp_seq=1 ttl=64 time=0.479 ms
    +
    +--- r2.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 0.479/0.524/0.569/0.045 ms
    +
    +PING blowfish.wg0 (192.168.2.110): 56 data bytes
    +64 bytes from 192.168.2.110: icmp_seq=0 ttl=255 time=35.745 ms
    +64 bytes from 192.168.2.110: icmp_seq=1 ttl=255 time=35.481 ms
    +
    +--- blowfish.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 35.481/35.613/35.745/0.132 ms
    +
    +PING fishfinger.wg0 (192.168.2.111): 56 data bytes
    +64 bytes from 192.168.2.111: icmp_seq=0 ttl=255 time=33.992 ms
    +64 bytes from 192.168.2.111: icmp_seq=1 ttl=255 time=33.751 ms
    +
    +--- fishfinger.wg0 ping statistics ---
    +2 packets transmitted, 2 packets received, 0.0% packet loss
    +round-trip min/avg/max/stddev = 33.751/33.872/33.992/0.120 ms
     

    Note that the loop above is a tcsh loop, the default shell used in FreeBSD. Of course, all other peers can ping their peers as well!
    @@ -1347,60 +1342,60 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas wg show
    -interface: wg0
    -  public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y=
    -  private key: (hidden)
    -  listening port: 56709
    -
    -peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.122:56709
    -  allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128
    -  latest handshake: 10 seconds ago
    -  transfer: 440 B received, 532 B sent
    -
    -peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.121:56709
    -  allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128
    -  latest handshake: 12 seconds ago
    -  transfer: 440 B received, 564 B sent
    -
    -peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.120:56709
    -  allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128
    -  latest handshake: 14 seconds ago
    -  transfer: 440 B received, 564 B sent
    -
    -peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.132:56709
    -  allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128
    -  latest handshake: 17 seconds ago
    -  transfer: 472 B received, 564 B sent
    -
    -peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns=
    -  preshared key: (hidden)
    -  endpoint: 23.88.35.144:56709
    -  allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128
    -  latest handshake: 55 seconds ago
    -  transfer: 472 B received, 596 B sent
    -  persistent keepalive: every 25 seconds
    -
    -peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns=
    -  preshared key: (hidden)
    -  endpoint: 46.23.94.99:56709
    -  allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128
    -  latest handshake: 55 seconds ago
    -  transfer: 472 B received, 596 B sent
    -  persistent keepalive: every 25 seconds
    -
    -peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8=
    -  preshared key: (hidden)
    -  endpoint: 192.168.1.131:56709
    -  allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128
    +
    paul@f0:~ % doas wg show
    +interface: wg0
    +  public key: Jm6YItMt94++dIeOyVi1I9AhNt2qQcryxCZezoX7X2Y=
    +  private key: (hidden)
    +  listening port: 56709
    +
    +peer: 0Y/H20W8YIbF7DA1sMwMacLI8WS9yG+1/QO7m2oyllg=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.122:56709
    +  allowed ips: 192.168.2.122/32, fd42:beef:cafe:2::122/128
    +  latest handshake: 10 seconds ago
    +  transfer: 440 B received, 532 B sent
    +
    +peer: Hhy9kMPOOjChXV2RA5WeCGs+J0FE3rcNPDw/TLSn7i8=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.121:56709
    +  allowed ips: 192.168.2.121/32, fd42:beef:cafe:2::121/128
    +  latest handshake: 12 seconds ago
    +  transfer: 440 B received, 564 B sent
    +
    +peer: s3e93XoY7dPUQgLiVO4d8x/SRCFgEew+/wP7+zwgehI=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.120:56709
    +  allowed ips: 192.168.2.120/32, fd42:beef:cafe:2::120/128
    +  latest handshake: 14 seconds ago
    +  transfer: 440 B received, 564 B sent
    +
    +peer: SlGVsACE1wiaRoGvCR3f7AuHfRS+1jjhS+YwEJ2HvF0=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.132:56709
    +  allowed ips: 192.168.2.132/32, fd42:beef:cafe:2::132/128
    +  latest handshake: 17 seconds ago
    +  transfer: 472 B received, 564 B sent
    +
    +peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns=
    +  preshared key: (hidden)
    +  endpoint: 23.88.35.144:56709
    +  allowed ips: 192.168.2.110/32, fd42:beef:cafe:2::110/128
    +  latest handshake: 55 seconds ago
    +  transfer: 472 B received, 596 B sent
    +  persistent keepalive: every 25 seconds
    +
    +peer: 8PvGZH1NohHpZPVJyjhctBX9xblsNvYBhpg68FsFcns=
    +  preshared key: (hidden)
    +  endpoint: 46.23.94.99:56709
    +  allowed ips: 192.168.2.111/32, fd42:beef:cafe:2::111/128
    +  latest handshake: 55 seconds ago
    +  transfer: 472 B received, 596 B sent
    +  persistent keepalive: every 25 seconds
    +
    +peer: 2htXdNcxzpI2FdPDJy4T4VGtm1wpMEQu1AkQHjNY6F8=
    +  preshared key: (hidden)
    +  endpoint: 192.168.1.131:56709
    +  allowed ips: 192.168.2.131/32, fd42:beef:cafe:2::131/128
     

    Managing Roaming Client Tunnels


    @@ -1427,8 +1422,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-blowfish.conf
    -qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-fishfinger.conf
    +
    qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-blowfish.conf
    +qrencode -t ansiutf8 < dist/pixel7pro/etc/wireguard/wg0-fishfinger.conf
     

    Import both QR codes using the WireGuard app to create two separate tunnel profiles. You can then manually enable/disable each tunnel to select which gateway to use. Only enable one tunnel at a time.
    @@ -1447,8 +1442,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    sudo cp dist/earth/etc/wireguard/wg0-blowfish.conf /etc/wireguard/
    -sudo cp dist/earth/etc/wireguard/wg0-fishfinger.conf /etc/wireguard/
    +
    sudo cp dist/earth/etc/wireguard/wg0-blowfish.conf /etc/wireguard/
    +sudo cp dist/earth/etc/wireguard/wg0-fishfinger.conf /etc/wireguard/
     

    This approach provides explicit control over which gateway handles roaming client traffic, useful when one gateway needs maintenance or experiences connectivity issues.
    @@ -1461,27 +1456,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Start with blowfish gateway
    -earth$ sudo systemctl start wg-quick@wg0-blowfish.service
    -
    -# Or start with fishfinger gateway
    -earth$ sudo systemctl start wg-quick@wg0-fishfinger.service
    -
    -# Check tunnel status (example with blowfish gateway)
    -earth$ sudo wg show
    -interface: wg0
    -  public key: Mc1CpSS3rbLN9A2w9c75XugQyXUkGPHKI2iCGbh8DRo=
    -  private key: (hidden)
    -  listening port: 56709
    -  fwmark: 0xca6c
    -
    -peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns=
    -  preshared key: (hidden)
    -  endpoint: 23.88.35.144:56709
    -  allowed ips: 0.0.0.0/0, ::/0
    -  latest handshake: 5 seconds ago
    -  transfer: 15.89 KiB received, 32.15 KiB sent
    -  persistent keepalive: every 25 seconds
    +
    # Start with blowfish gateway
    +earth$ sudo systemctl start wg-quick@wg0-blowfish.service
    +
    +# Or start with fishfinger gateway
    +earth$ sudo systemctl start wg-quick@wg0-fishfinger.service
    +
    +# Check tunnel status (example with blowfish gateway)
    +earth$ sudo wg show
    +interface: wg0
    +  public key: Mc1CpSS3rbLN9A2w9c75XugQyXUkGPHKI2iCGbh8DRo=
    +  private key: (hidden)
    +  listening port: 56709
    +  fwmark: 0xca6c
    +
    +peer: Xow+d3qVXgUMk4pcRSQ6Fe+vhYBa3VDyHX/4jrGoKns=
    +  preshared key: (hidden)
    +  endpoint: 23.88.35.144:56709
    +  allowed ips: 0.0.0.0/0, ::/0
    +  latest handshake: 5 seconds ago
    +  transfer: 15.89 KiB received, 32.15 KiB sent
    +  persistent keepalive: every 25 seconds
     

    Stopping the tunnel:
    @@ -1490,12 +1485,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    earth$ sudo systemctl stop wg-quick@wg0-blowfish.service
    -# Or if using fishfinger:
    -earth$ sudo systemctl stop wg-quick@wg0-fishfinger.service
    +
    earth$ sudo systemctl stop wg-quick@wg0-blowfish.service
    +# Or if using fishfinger:
    +earth$ sudo systemctl stop wg-quick@wg0-fishfinger.service
     
    -earth$ sudo wg show
    -# No output - WireGuard interface is down
    +earth$ sudo wg show
    +# No output - WireGuard interface is down
     

    Switching between gateways:
    @@ -1504,9 +1499,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Switch from blowfish to fishfinger
    -earth$ sudo systemctl stop wg-quick@wg0-blowfish.service
    -earth$ sudo systemctl start wg-quick@wg0-fishfinger.service
    +
    # Switch from blowfish to fishfinger
    +earth$ sudo systemctl stop wg-quick@wg0-blowfish.service
    +earth$ sudo systemctl start wg-quick@wg0-fishfinger.service
     

    The services remain disabled to prevent auto-start on boot, allowing manual control of when the VPN is active and which gateway to use.
    @@ -1563,10 +1558,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # From earth laptop:
    -earth$ ping -c2 blowfish.wg0
    -earth$ ping -c2 fishfinger.wg0
    -earth$ curl https://ifconfig.me  # Should show gateway's public IP
    +
    # From earth laptop:
    +earth$ ping -c2 blowfish.wg0
    +earth$ ping -c2 fishfinger.wg0
    +earth$ curl https://ifconfig.me  # Should show gateway's public IP
     

    Check which gateway is active: Check the transfer statistics with sudo wg show on earth to see which peer shows recent handshakes and increasing transfer bytes. On Android, the WireGuard app shows the active tunnel with data transfer statistics.
    @@ -1599,11 +1594,11 @@ http://www.gnu.org/software/src-highlite -->
    Back to the main site
    diff --git a/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html b/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html index 6371f9de..48774fd1 100644 --- a/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html +++ b/gemfeed/2025-06-07-a-monks-guide-to-happiness-book-notes.html @@ -2,17 +2,12 @@ - 'A Monk's Guide to Happiness' book notes -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -119,11 +114,11 @@
    Back to the main site
    diff --git a/gemfeed/2025-06-22-task-samurai.html b/gemfeed/2025-06-22-task-samurai.html index c37d0e6e..9e10795d 100644 --- a/gemfeed/2025-06-22-task-samurai.html +++ b/gemfeed/2025-06-22-task-samurai.html @@ -2,17 +2,12 @@ - Task Samurai: An agentic coding learning experiment -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -155,11 +150,11 @@
    Back to the main site
    diff --git a/gemfeed/2025-07-01-posts-from-january-to-june-2025.html b/gemfeed/2025-07-01-posts-from-january-to-june-2025.html index 61dce763..09e3446f 100644 --- a/gemfeed/2025-07-01-posts-from-january-to-june-2025.html +++ b/gemfeed/2025-07-01-posts-from-january-to-june-2025.html @@ -2,17 +2,12 @@ - Posts from January to June 2025 -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -754,11 +749,11 @@
    Back to the main site
    diff --git a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html index 9a818942..a66bad32 100644 --- a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html +++ b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 6: Storage -
    -
    -
    -

    Home | Markdown | Gemini

    @@ -124,16 +119,16 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas zpool create -m /data zdata /dev/ada1
    -paul@f0:~ % zpool list
    -NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
    -zdata   928G  12.1M   928G        -         -     0%     0%  1.00x    ONLINE  -
    -zroot   472G  29.0G   443G        -         -     0%     6%  1.00x    ONLINE  -
    +
    paul@f0:~ % doas zpool create -m /data zdata /dev/ada1
    +paul@f0:~ % zpool list
    +NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
    +zdata   928G  12.1M   928G        -         -     0%     0%  1.00x    ONLINE  -
    +zroot   472G  29.0G   443G        -         -     0%     6%  1.00x    ONLINE  -
     
    -paul@f0:/ % doas camcontrol devlist
    -<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
    -<Samsung SSD 870 EVO 1TB SVT03B6Q>  at scbus1 target 0 lun 0 (pass1,ada1)
    -paul@f0:/ %
    +paul@f0:/ % doas camcontrol devlist
    +<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
    +<Samsung SSD 870 EVO 1TB SVT03B6Q>  at scbus1 target 0 lun 0 (pass1,ada1)
    +paul@f0:/ %
     

    To verify that we have a different SSD on the second node (the third node has the same drive as the first):
    @@ -142,9 +137,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f1:/ % doas camcontrol devlist
    -<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
    -<CT1000BX500SSD1 M6CR072>          at scbus1 target 0 lun 0 (pass1,ada1)
    +
    paul@f1:/ % doas camcontrol devlist
    +<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
    +<CT1000BX500SSD1 M6CR072>          at scbus1 target 0 lun 0 (pass1,ada1)
     

    ZFS encryption keys


    @@ -182,21 +177,21 @@ paul@f0:/ % by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/ % doas newfs /dev/da0
    -/dev/da0: 15000.0MB (30720000 sectors) block size 32768, fragment size 4096
    -        using 24 cylinder groups of 625.22MB, 20007 blks, 80128 inodes.
    -        with soft updates
    -super-block backups (for fsck_ffs -b #) at:
    - 192, 1280640, 2561088, 3841536, 5121984, 6402432, 7682880, 8963328, 10243776,
    -11524224, 12804672, 14085120, 15365568, 16646016, 17926464, 19206912,k 20487360,
    -...
    +
    paul@f0:/ % doas newfs /dev/da0
    +/dev/da0: 15000.0MB (30720000 sectors) block size 32768, fragment size 4096
    +        using 24 cylinder groups of 625.22MB, 20007 blks, 80128 inodes.
    +        with soft updates
    +super-block backups (for fsck_ffs -b #) at:
    + 192, 1280640, 2561088, 3841536, 5121984, 6402432, 7682880, 8963328, 10243776,
    +11524224, 12804672, 14085120, 15365568, 16646016, 17926464, 19206912,k 20487360,
    +...
     
    -paul@f0:/ % echo '/dev/da0 /keys ufs rw 0 2' | doas tee -a /etc/fstab
    -/dev/da0 /keys ufs rw 0 2
    -paul@f0:/ % doas mkdir /keys
    -paul@f0:/ % doas mount /keys
    -paul@f0:/ % df | grep keys
    -/dev/da0             14877596       8  13687384     0%    /keys
    +paul@f0:/ % echo '/dev/da0 /keys ufs rw 0 2' | doas tee -a /etc/fstab
    +/dev/da0 /keys ufs rw 0 2
    +paul@f0:/ % doas mkdir /keys
    +paul@f0:/ % doas mount /keys
    +paul@f0:/ % df | grep keys
    +/dev/da0             14877596       8  13687384     0%    /keys
     

    USB keys stuck in
    @@ -235,18 +230,18 @@ total 20 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \
    -  keylocation=file:///keys/`hostname`:zdata.key zdata/enc
    -paul@f0:/ % zfs list | grep zdata
    -zdata                                          836K   899G    96K  /data
    -zdata/enc                                      200K   899G   200K  /data/enc
    +
    paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \
    +  keylocation=file:///keys/`hostname`:zdata.key zdata/enc
    +paul@f0:/ % zfs list | grep zdata
    +zdata                                          836K   899G    96K  /data
    +zdata/enc                                      200K   899G   200K  /data/enc
     
    -paul@f0:/keys % zfs get all zdata/enc | grep -E -i '(encryption|key)'
    -zdata/enc  encryption            aes-256-gcm                               -
    -zdata/enc  keylocation           file:///keys/f0.lan.buetow.org:zdata.key  local
    -zdata/enc  keyformat             raw                                       -
    -zdata/enc  encryptionroot        zdata/enc                                 -
    -zdata/enc  keystatus             available                                 -
    +paul@f0:/keys % zfs get all zdata/enc | grep -E -i '(encryption|key)'
    +zdata/enc  encryption            aes-256-gcm                               -
    +zdata/enc  keylocation           file:///keys/f0.lan.buetow.org:zdata.key  local
    +zdata/enc  keyformat             raw                                       -
    +zdata/enc  encryptionroot        zdata/enc                                 -
    +zdata/enc  keystatus             available                                 -
     

    All future data sets within zdata/enc will inherit the same encryption key.
    @@ -259,12 +254,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/keys % doas vm stop rocky
    -Sending ACPI shutdown to rocky
    +
    paul@f0:/keys % doas vm stop rocky
    +Sending ACPI shutdown to rocky
     
    -paul@f0:/keys % doas vm list
    -NAME     DATASTORE  LOADER     CPU  MEMORY  VNC  AUTO     STATE
    -rocky    default    uefi       4    14G     -    Yes [1]  Stopped
    +paul@f0:/keys % doas vm list
    +NAME     DATASTORE  LOADER     CPU  MEMORY  VNC  AUTO     STATE
    +rocky    default    uefi       4    14G     -    Yes [1]  Stopped
     

    After this, we rename the unencrypted data set to _old, create a new encrypted data set, and also snapshot it as @hamburger.
    @@ -273,14 +268,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/keys % doas zfs rename zroot/bhyve zroot/bhyve_old
    -paul@f0:/keys % doas zfs set mountpoint=/mnt zroot/bhyve_old
    -paul@f0:/keys % doas zfs snapshot zroot/bhyve_old/rocky@hamburger
    +
    paul@f0:/keys % doas zfs rename zroot/bhyve zroot/bhyve_old
    +paul@f0:/keys % doas zfs set mountpoint=/mnt zroot/bhyve_old
    +paul@f0:/keys % doas zfs snapshot zroot/bhyve_old/rocky@hamburger
     
    -paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \
    -  keylocation=file:///keys/`hostname`:bhyve.key zroot/bhyve
    -paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve zroot/bhyve
    -paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve/rocky zroot/bhyve/rocky
    +paul@f0:/keys % doas zfs create -o encryption=on -o keyformat=raw -o \
    +  keylocation=file:///keys/`hostname`:bhyve.key zroot/bhyve
    +paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve zroot/bhyve
    +paul@f0:/keys % doas zfs set mountpoint=/zroot/bhyve/rocky zroot/bhyve/rocky
     

    Once done, we import the snapshot into the encrypted dataset and also copy some other metadata files from vm-bhyve back over.
    @@ -300,17 +295,17 @@ paul@f0:/keys % doas cp -Rp /mnt/.iso /zroot/bhyve/ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:/keys % doas sysrc zfskeys_enable=YES
    -zfskeys_enable:  -> YES
    -paul@f0:/keys % doas vm init
    -paul@f0:/keys % doas reboot
    -.
    -.
    -.
    -paul@f0:~ % doas vm list
    -paul@f0:~ % doas vm list
    -NAME     DATASTORE  LOADER     CPU  MEMORY  VNC           AUTO     STATE
    -rocky    default    uefi       4    14G     0.0.0.0:5900  Yes [1]  Running (2265)
    +
    paul@f0:/keys % doas sysrc zfskeys_enable=YES
    +zfskeys_enable:  -> YES
    +paul@f0:/keys % doas vm init
    +paul@f0:/keys % doas reboot
    +.
    +.
    +.
    +paul@f0:~ % doas vm list
    +paul@f0:~ % doas vm list
    +NAME     DATASTORE  LOADER     CPU  MEMORY  VNC           AUTO     STATE
    +rocky    default    uefi       4    14G     0.0.0.0:5900  Yes [1]  Running (2265)
     

    As you can see, the VM is running. This means the encrypted zroot/bhyve was mounted successfully after the reboot! Now we can destroy the old, unencrypted, and now unused bhyve dataset:
    @@ -319,7 +314,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas zfs destroy -R zroot/bhyve_old
    +
    paul@f0:~ % doas zfs destroy -R zroot/bhyve_old
     

    To verify once again that zroot/bhyve and zroot/bhyve/rocky are now both encrypted, we run:
    @@ -328,19 +323,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % zfs get all zroot/bhyve | grep -E '(encryption|key)'
    -zroot/bhyve  encryption            aes-256-gcm                               -
    -zroot/bhyve  keylocation           file:///keys/f0.lan.buetow.org:bhyve.key  local
    -zroot/bhyve  keyformat             raw                                       -
    -zroot/bhyve  encryptionroot        zroot/bhyve                               -
    -zroot/bhyve  keystatus             available                                 -
    +
    paul@f0:~ % zfs get all zroot/bhyve | grep -E '(encryption|key)'
    +zroot/bhyve  encryption            aes-256-gcm                               -
    +zroot/bhyve  keylocation           file:///keys/f0.lan.buetow.org:bhyve.key  local
    +zroot/bhyve  keyformat             raw                                       -
    +zroot/bhyve  encryptionroot        zroot/bhyve                               -
    +zroot/bhyve  keystatus             available                                 -
     
    -paul@f0:~ % zfs get all zroot/bhyve/rocky | grep -E '(encryption|key)'
    -zroot/bhyve/rocky  encryption            aes-256-gcm            -
    -zroot/bhyve/rocky  keylocation           none                   default
    -zroot/bhyve/rocky  keyformat             raw                    -
    -zroot/bhyve/rocky  encryptionroot        zroot/bhyve            -
    -zroot/bhyve/rocky  keystatus             available              -
    +paul@f0:~ % zfs get all zroot/bhyve/rocky | grep -E '(encryption|key)'
    +zroot/bhyve/rocky  encryption            aes-256-gcm            -
    +zroot/bhyve/rocky  keylocation           none                   default
    +zroot/bhyve/rocky  keyformat             raw                    -
    +zroot/bhyve/rocky  encryptionroot        zroot/bhyve            -
    +zroot/bhyve/rocky  keystatus             available              -
     

    ZFS Replication with zrepl


    @@ -375,7 +370,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas pkg install -y zrepl
    +
    paul@f0:~ % doas pkg install -y zrepl
     

    Then, we verify the pools and datasets on both hosts:
    @@ -384,25 +379,25 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0
    -paul@f0:~ % doas zpool list
    -NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
    -zdata   928G  1.03M   928G        -         -     0%     0%  1.00x    ONLINE  -
    -zroot   472G  26.7G   445G        -         -     0%     5%  1.00x    ONLINE  -
    +
    # On f0
    +paul@f0:~ % doas zpool list
    +NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
    +zdata   928G  1.03M   928G        -         -     0%     0%  1.00x    ONLINE  -
    +zroot   472G  26.7G   445G        -         -     0%     5%  1.00x    ONLINE  -
     
    -paul@f0:~ % doas zfs list -r zdata/enc
    -NAME        USED  AVAIL  REFER  MOUNTPOINT
    -zdata/enc   200K   899G   200K  /data/enc
    +paul@f0:~ % doas zfs list -r zdata/enc
    +NAME        USED  AVAIL  REFER  MOUNTPOINT
    +zdata/enc   200K   899G   200K  /data/enc
     
    -# On f1
    -paul@f1:~ % doas zpool list
    -NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
    -zdata   928G   956K   928G        -         -     0%     0%  1.00x    ONLINE  -
    -zroot   472G  11.7G   460G        -         -     0%     2%  1.00x    ONLINE  -
    +# On f1
    +paul@f1:~ % doas zpool list
    +NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
    +zdata   928G   956K   928G        -         -     0%     0%  1.00x    ONLINE  -
    +zroot   472G  11.7G   460G        -         -     0%     2%  1.00x    ONLINE  -
     
    -paul@f1:~ % doas zfs list -r zdata/enc
    -NAME        USED  AVAIL  REFER  MOUNTPOINT
    -zdata/enc   200K   899G   200K  /data/enc
    +paul@f1:~ % doas zfs list -r zdata/enc
    +NAME        USED  AVAIL  REFER  MOUNTPOINT
    +zdata/enc   200K   899G   200K  /data/enc
     

    Since we have a WireGuard tunnel between f0 and f1, we'll use TCP transport over the secure tunnel instead of SSH. First, check the WireGuard IP addresses:
    @@ -411,12 +406,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Check WireGuard interface IPs
    -paul@f0:~ % ifconfig wg0 | grep inet
    -	inet 192.168.2.130 netmask 0xffffff00
    +
    # Check WireGuard interface IPs
    +paul@f0:~ % ifconfig wg0 | grep inet
    +	inet 192.168.2.130 netmask 0xffffff00
     
    -paul@f1:~ % ifconfig wg0 | grep inet
    -	inet 192.168.2.131 netmask 0xffffff00
    +paul@f1:~ % ifconfig wg0 | grep inet
    +	inet 192.168.2.131 netmask 0xffffff00
     

    Let's create a dedicated dataset for NFS data that will be replicated:
    @@ -425,8 +420,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Create the nfsdata dataset that will hold all data exposed via NFS
    -paul@f0:~ % doas zfs create zdata/enc/nfsdata
    +
    # Create the nfsdata dataset that will hold all data exposed via NFS
    +paul@f0:~ % doas zfs create zdata/enc/nfsdata
     

    Afterwards, we create the zrepl configuration on f0:
    @@ -435,68 +430,68 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF'
    -global:
    -  logging:
    -    - type: stdout
    -      level: info
    -      format: human
    -
    -jobs:
    -  - name: f0_to_f1_nfsdata
    -    type: push
    -    connect:
    -      type: tcp
    -      address: "192.168.2.131:8888"
    -    filesystems:
    -      "zdata/enc/nfsdata": true
    -    send:
    -      encrypted: true
    -    snapshotting:
    -      type: periodic
    -      prefix: zrepl_
    -      interval: 1m
    -    pruning:
    -      keep_sender:
    -        - type: last_n
    -          count: 10
    -        - type: grid
    -          grid: 4x7d | 6x30d
    -          regex: "^zrepl_.*"
    -      keep_receiver:
    -        - type: last_n
    -          count: 10
    -        - type: grid
    -          grid: 4x7d | 6x30d
    -          regex: "^zrepl_.*"
    -
    -  - name: f0_to_f1_freebsd
    -    type: push
    -    connect:
    -      type: tcp
    -      address: "192.168.2.131:8888"
    -    filesystems:
    -      "zroot/bhyve/freebsd": true
    -    send:
    -      encrypted: true
    -    snapshotting:
    -      type: periodic
    -      prefix: zrepl_
    -      interval: 10m
    -    pruning:
    -      keep_sender:
    -        - type: last_n
    -          count: 10
    -        - type: grid
    -          grid: 4x7d
    -          regex: "^zrepl_.*"
    -      keep_receiver:
    -        - type: last_n
    -          count: 10
    -        - type: grid
    -          grid: 4x7d
    -          regex: "^zrepl_.*"
    -EOF
    +
    paul@f0:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF'
    +global:
    +  logging:
    +    - type: stdout
    +      level: info
    +      format: human
    +
    +jobs:
    +  - name: f0_to_f1_nfsdata
    +    type: push
    +    connect:
    +      type: tcp
    +      address: "192.168.2.131:8888"
    +    filesystems:
    +      "zdata/enc/nfsdata": true
    +    send:
    +      encrypted: true
    +    snapshotting:
    +      type: periodic
    +      prefix: zrepl_
    +      interval: 1m
    +    pruning:
    +      keep_sender:
    +        - type: last_n
    +          count: 10
    +        - type: grid
    +          grid: 4x7d | 6x30d
    +          regex: "^zrepl_.*"
    +      keep_receiver:
    +        - type: last_n
    +          count: 10
    +        - type: grid
    +          grid: 4x7d | 6x30d
    +          regex: "^zrepl_.*"
    +
    +  - name: f0_to_f1_freebsd
    +    type: push
    +    connect:
    +      type: tcp
    +      address: "192.168.2.131:8888"
    +    filesystems:
    +      "zroot/bhyve/freebsd": true
    +    send:
    +      encrypted: true
    +    snapshotting:
    +      type: periodic
    +      prefix: zrepl_
    +      interval: 10m
    +    pruning:
    +      keep_sender:
    +        - type: last_n
    +          count: 10
    +        - type: grid
    +          grid: 4x7d
    +          regex: "^zrepl_.*"
    +      keep_receiver:
    +        - type: last_n
    +          count: 10
    +        - type: grid
    +          grid: 4x7d
    +          regex: "^zrepl_.*"
    +EOF
     

    We're using two separate replication jobs with different intervals:
    @@ -521,29 +516,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # First, create a dedicated sink dataset
    -paul@f1:~ % doas zfs create zdata/sink
    +
    # First, create a dedicated sink dataset
    +paul@f1:~ % doas zfs create zdata/sink
     
    -paul@f1:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF'
    -global:
    -  logging:
    -    - type: stdout
    -      level: info
    -      format: human
    +paul@f1:~ % doas tee /usr/local/etc/zrepl/zrepl.yml <<'EOF'
    +global:
    +  logging:
    +    - type: stdout
    +      level: info
    +      format: human
     
    -jobs:
    -  - name: sink
    -    type: sink
    -    serve:
    -      type: tcp
    -      listen: "192.168.2.131:8888"
    -      clients:
    -        "192.168.2.130": "f0"
    -    recv:
    -      placeholder:
    -        encryption: inherit
    -    root_fs: "zdata/sink"
    -EOF
    +jobs:
    +  - name: sink
    +    type: sink
    +    serve:
    +      type: tcp
    +      listen: "192.168.2.131:8888"
    +      clients:
    +        "192.168.2.130": "f0"
    +    recv:
    +      placeholder:
    +        encryption: inherit
    +    root_fs: "zdata/sink"
    +EOF
     

    Enabling and starting zrepl services


    @@ -554,17 +549,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0
    -paul@f0:~ % doas sysrc zrepl_enable=YES
    -zrepl_enable:  -> YES
    -paul@f0:~ % doas service `zrepl` start
    -Starting zrepl.
    +
    # On f0
    +paul@f0:~ % doas sysrc zrepl_enable=YES
    +zrepl_enable:  -> YES
    +paul@f0:~ % doas service `zrepl` start
    +Starting zrepl.
     
    -# On f1
    -paul@f1:~ % doas sysrc zrepl_enable=YES
    -zrepl_enable:  -> YES
    -paul@f1:~ % doas service `zrepl` start
    -Starting zrepl.
    +# On f1
    +paul@f1:~ % doas sysrc zrepl_enable=YES
    +zrepl_enable:  -> YES
    +paul@f1:~ % doas service `zrepl` start
    +Starting zrepl.
     

    To check the replication status, we run:
    @@ -573,35 +568,35 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0, check `zrepl` status (use raw mode for non-tty)
    -paul@f0:~ % doas pkg install jq
    -paul@f0:~ % doas zrepl status --mode raw | grep -A2 "Replication" | jq .
    -"Replication":{"StartAt":"2025-07-01T22:31:48.712143123+03:00"...
    +
    # On f0, check `zrepl` status (use raw mode for non-tty)
    +paul@f0:~ % doas pkg install jq
    +paul@f0:~ % doas zrepl status --mode raw | grep -A2 "Replication" | jq .
    +"Replication":{"StartAt":"2025-07-01T22:31:48.712143123+03:00"...
     
    -# Check if services are running
    -paul@f0:~ % doas service zrepl status
    -zrepl is running as pid 2649.
    +# Check if services are running
    +paul@f0:~ % doas service zrepl status
    +zrepl is running as pid 2649.
     
    -paul@f1:~ % doas service zrepl status
    -zrepl is running as pid 2574.
    +paul@f1:~ % doas service zrepl status
    +zrepl is running as pid 2574.
     
    -# Check for `zrepl` snapshots on source
    -paul@f0:~ % doas zfs list -t snapshot -r zdata/enc | grep zrepl
    -zdata/enc@zrepl_20250701_193148_000    0B      -   176K  -
    +# Check for `zrepl` snapshots on source
    +paul@f0:~ % doas zfs list -t snapshot -r zdata/enc | grep zrepl
    +zdata/enc@zrepl_20250701_193148_000    0B      -   176K  -
     
    -# On f1, verify the replicated datasets  
    -paul@f1:~ % doas zfs list -r zdata | grep f0
    -zdata/f0             576K   899G   200K  none
    -zdata/f0/zdata       376K   899G   200K  none
    -zdata/f0/zdata/enc   176K   899G   176K  none
    +# On f1, verify the replicated datasets  
    +paul@f1:~ % doas zfs list -r zdata | grep f0
    +zdata/f0             576K   899G   200K  none
    +zdata/f0/zdata       376K   899G   200K  none
    +zdata/f0/zdata/enc   176K   899G   176K  none
     
    -# Check replicated snapshots on f1
    -paul@f1:~ % doas zfs list -t snapshot -r zdata | grep zrepl
    -zdata/f0/zdata/enc@zrepl_20250701_193148_000     0B      -   176K  -
    -zdata/f0/zdata/enc@zrepl_20250701_194148_000     0B      -   176K  -
    -.
    -.
    -.
    +# Check replicated snapshots on f1
    +paul@f1:~ % doas zfs list -t snapshot -r zdata | grep zrepl
    +zdata/f0/zdata/enc@zrepl_20250701_193148_000     0B      -   176K  -
    +zdata/f0/zdata/enc@zrepl_20250701_194148_000     0B      -   176K  -
    +.
    +.
    +.
     

    Monitoring replication


    @@ -612,7 +607,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas zrepl status
    +
    paul@f0:~ % doas zrepl status
     

    zrepl status
    @@ -635,29 +630,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % uptime
    -11:17PM  up 1 min, 0 users, load averages: 0.16, 0.06, 0.02
    +
    paul@f0:~ % uptime
    +11:17PM  up 1 min, 0 users, load averages: 0.16, 0.06, 0.02
     
    -paul@f0:~ % doas service `zrepl` status
    -zrepl is running as pid 2366.
    +paul@f0:~ % doas service `zrepl` status
    +zrepl is running as pid 2366.
     
    -paul@f1:~ % doas service `zrepl` status
    -zrepl is running as pid 2309.
    +paul@f1:~ % doas service `zrepl` status
    +zrepl is running as pid 2309.
     
    -# Check that new snapshots are being created and replicated
    -paul@f0:~ % doas zfs list -t snapshot | grep `zrepl` | tail -2
    -zdata/enc/nfsdata@zrepl_20250701_202530_000                0B      -   200K  -
    -zroot/bhyve/freebsd@zrepl_20250701_202530_000               0B      -  2.97G  -
    -.
    -.
    -.
    +# Check that new snapshots are being created and replicated
    +paul@f0:~ % doas zfs list -t snapshot | grep `zrepl` | tail -2
    +zdata/enc/nfsdata@zrepl_20250701_202530_000                0B      -   200K  -
    +zroot/bhyve/freebsd@zrepl_20250701_202530_000               0B      -  2.97G  -
    +.
    +.
    +.
     
    -paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep 202530
    -zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_202530_000      0B      -   176K  -
    -zdata/sink/f0/zroot/bhyve/freebsd@zrepl_20250701_202530_000     0B      -  2.97G  -
    -.
    -.
    -.
    +paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep 202530
    +zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_202530_000      0B      -   176K  -
    +zdata/sink/f0/zroot/bhyve/freebsd@zrepl_20250701_202530_000     0B      -  2.97G  -
    +.
    +.
    +.
     

    The timestamps confirm that replication resumed automatically after the reboot, ensuring continuous data protection. We can also write a test file to the NFS data directory on f0 and verify whether it appears on f1 after a minute.
    @@ -682,14 +677,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0 - set mountpoint for the primary nfsdata
    -paul@f0:~ % doas zfs set mountpoint=/data/nfs zdata/enc/nfsdata
    -paul@f0:~ % doas mkdir -p /data/nfs
    +
    # On f0 - set mountpoint for the primary nfsdata
    +paul@f0:~ % doas zfs set mountpoint=/data/nfs zdata/enc/nfsdata
    +paul@f0:~ % doas mkdir -p /data/nfs
     
    -# Verify it's mounted
    -paul@f0:~ % df -h /data/nfs
    -Filesystem           Size    Used   Avail Capacity  Mounted on
    -zdata/enc/nfsdata    899G    204K    899G     0%    /data/nfs
    +# Verify it's mounted
    +paul@f0:~ % df -h /data/nfs
    +Filesystem           Size    Used   Avail Capacity  Mounted on
    +zdata/enc/nfsdata    899G    204K    899G     0%    /data/nfs
     

    On f1, we need to handle the encryption key and mount the standby copy:
    @@ -698,27 +693,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f1 - first check encryption status
    -paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata
    -NAME                             PROPERTY   VALUE        SOURCE
    -zdata/sink/f0/zdata/enc/nfsdata  keystatus  unavailable  -
    +
    # On f1 - first check encryption status
    +paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata
    +NAME                             PROPERTY   VALUE        SOURCE
    +zdata/sink/f0/zdata/enc/nfsdata  keystatus  unavailable  -
     
    -# Load the encryption key (using f0's key stored on the USB)
    -paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \
    -    zdata/sink/f0/zdata/enc/nfsdata
    +# Load the encryption key (using f0's key stored on the USB)
    +paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \
    +    zdata/sink/f0/zdata/enc/nfsdata
     
    -# Set mountpoint and mount (same path as f0 for easier failover)
    -paul@f1:~ % doas mkdir -p /data/nfs
    -paul@f1:~ % doas zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata
    -paul@f1:~ % doas zfs mount zdata/sink/f0/zdata/enc/nfsdata
    +# Set mountpoint and mount (same path as f0 for easier failover)
    +paul@f1:~ % doas mkdir -p /data/nfs
    +paul@f1:~ % doas zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata
    +paul@f1:~ % doas zfs mount zdata/sink/f0/zdata/enc/nfsdata
     
    -# Make it read-only to prevent accidental writes that would break replication
    -paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
    +# Make it read-only to prevent accidental writes that would break replication
    +paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
     
    -# Verify
    -paul@f1:~ % df -h /data/nfs
    -Filesystem                         Size    Used   Avail Capacity  Mounted on
    -zdata/sink/f0/zdata/enc/nfsdata    896G    204K    896G     0%    /data/nfs
    +# Verify
    +paul@f1:~ % df -h /data/nfs
    +Filesystem                         Size    Used   Avail Capacity  Mounted on
    +zdata/sink/f0/zdata/enc/nfsdata    896G    204K    896G     0%    /data/nfs
     

    Note: The dataset is mounted at the same path (/data/nfs) on both hosts to simplify failover procedures. The dataset on f1 is set to readonly=on to prevent accidental modifications, which, as mentioned earlier, would break replication. If we did, replication from f0 to f1 would fail like this:
    @@ -731,11 +726,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Option 1: Rollback to the last common snapshot (loses local changes)
    -paul@f1:~ % doas zfs rollback zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_204054_000
    +
    # Option 1: Rollback to the last common snapshot (loses local changes)
    +paul@f1:~ % doas zfs rollback zdata/sink/f0/zdata/enc/nfsdata@zrepl_20250701_204054_000
     
    -# Option 2: Make it read-only to prevent accidents again
    -paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
    +# Option 2: Make it read-only to prevent accidents again
    +paul@f1:~ % doas zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
     

    And replication should work again!
    @@ -748,8 +743,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas zfs list -o name,mountpoint,mounted | grep nfsdata
    -zdata/enc/nfsdata                             /data/nfs             yes
    +
    paul@f0:~ % doas zfs list -o name,mountpoint,mounted | grep nfsdata
    +zdata/enc/nfsdata                             /data/nfs             yes
     

    If it shows no, the dataset isn't mounted! This means files are being written to the root filesystem, not ZFS. Next, we should check whether the encryption key is loaded:
    @@ -758,12 +753,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata
    -NAME               PROPERTY   VALUE        SOURCE
    -zdata/enc/nfsdata  keystatus  available    -
    -# If "unavailable", load the key:
    -paul@f0:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata
    -paul@f0:~ % doas zfs mount zdata/enc/nfsdata
    +
    paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata
    +NAME               PROPERTY   VALUE        SOURCE
    +zdata/enc/nfsdata  keystatus  available    -
    +# If "unavailable", load the key:
    +paul@f0:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata
    +paul@f0:~ % doas zfs mount zdata/enc/nfsdata
     

    You can also verify that files are in the snapshot (not just the directory):
    @@ -772,7 +767,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % ls -la /data/nfs/.zfs/snapshot/zrepl_*/
    +
    paul@f0:~ % ls -la /data/nfs/.zfs/snapshot/zrepl_*/
     

    This issue commonly occurs after a reboot if the encryption keys aren't configured to load automatically.
    @@ -785,26 +780,26 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0 - configure all encrypted datasets
    -paul@f0:~ % doas sysrc zfskeys_enable=YES
    -zfskeys_enable: YES -> YES
    -paul@f0:~ % doas sysrc zfskeys_datasets="zdata/enc zdata/enc/nfsdata zroot/bhyve"
    -zfskeys_datasets:  -> zdata/enc zdata/enc/nfsdata zroot/bhyve
    +
    # On f0 - configure all encrypted datasets
    +paul@f0:~ % doas sysrc zfskeys_enable=YES
    +zfskeys_enable: YES -> YES
    +paul@f0:~ % doas sysrc zfskeys_datasets="zdata/enc zdata/enc/nfsdata zroot/bhyve"
    +zfskeys_datasets:  -> zdata/enc zdata/enc/nfsdata zroot/bhyve
     
    -# Set correct key locations for all datasets
    -paul@f0:~ % doas zfs set \
    -  keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata
    +# Set correct key locations for all datasets
    +paul@f0:~ % doas zfs set \
    +  keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata
     
    -# On f1 - include the replicated dataset
    -paul@f1:~ % doas sysrc zfskeys_enable=YES
    -zfskeys_enable: YES -> YES
    -paul@f1:~ % doas sysrc \
    -  zfskeys_datasets="zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata"
    -zfskeys_datasets:  -> zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata
    +# On f1 - include the replicated dataset
    +paul@f1:~ % doas sysrc zfskeys_enable=YES
    +zfskeys_enable: YES -> YES
    +paul@f1:~ % doas sysrc \
    +  zfskeys_datasets="zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata"
    +zfskeys_datasets:  -> zdata/enc zroot/bhyve zdata/sink/f0/zdata/enc/nfsdata
     
    -# Set key location for replicated dataset
    -paul@f1:~ % doas zfs set \
    -  keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata
    +# Set key location for replicated dataset
    +paul@f1:~ % doas zfs set \
    +  keylocation=file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata
     

    Important notes:
    @@ -827,13 +822,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Check service status on both f0 and f1
    -paul@f0:~ % doas service zrepl status
    -paul@f1:~ % doas service zrepl status
    +
    # Check service status on both f0 and f1
    +paul@f0:~ % doas service zrepl status
    +paul@f1:~ % doas service zrepl status
     
    -# If not running, start the service
    -paul@f0:~ % doas service zrepl start
    -paul@f1:~ % doas service zrepl start
    +# If not running, start the service
    +paul@f0:~ % doas service zrepl start
    +paul@f1:~ % doas service zrepl start
     

    Check zrepl Status for Errors


    @@ -844,11 +839,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Check detailed status (use --mode raw for non-tty environments)
    -paul@f0:~ % doas zrepl status --mode raw
    +
    # Check detailed status (use --mode raw for non-tty environments)
    +paul@f0:~ % doas zrepl status --mode raw
     
    -# Look for error messages in the replication section
    -# Common errors include "no common snapshot" or connection failures
    +# Look for error messages in the replication section
    +# Common errors include "no common snapshot" or connection failures
     

    Fixing "No Common Snapshot" Errors


    @@ -871,27 +866,27 @@ no common snapshot or suitable bookmark between sender and receiver by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # First, identify the destination dataset on f1
    -paul@f1:~ % doas zfs list | grep sink
    +
    # First, identify the destination dataset on f1
    +paul@f1:~ % doas zfs list | grep sink
     
    -# Check existing snapshots on the problematic dataset
    -paul@f1:~ % doas zfs list -t snapshot | grep nfsdata
    +# Check existing snapshots on the problematic dataset
    +paul@f1:~ % doas zfs list -t snapshot | grep nfsdata
     
    -# If you see snapshots with different naming (e.g., @daily-*, @weekly-*)
    -# these conflict with zrepl's @zrepl_* snapshots
    +# If you see snapshots with different naming (e.g., @daily-*, @weekly-*)
    +# these conflict with zrepl's @zrepl_* snapshots
     
    -# Destroy the entire destination dataset to allow clean replication
    -paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zdata/enc/nfsdata
    +# Destroy the entire destination dataset to allow clean replication
    +paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zdata/enc/nfsdata
     
    -# For VM replication, do the same for the freebsd dataset
    -paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zroot/bhyve/freebsd
    +# For VM replication, do the same for the freebsd dataset
    +paul@f1:~ % doas zfs destroy -r zdata/sink/f0/zroot/bhyve/freebsd
     
    -# Wake up zrepl to start fresh replication
    -paul@f0:~ % doas zrepl signal wakeup f0_to_f1_nfsdata
    -paul@f0:~ % doas zrepl signal wakeup f0_to_f1_freebsd
    +# Wake up zrepl to start fresh replication
    +paul@f0:~ % doas zrepl signal wakeup f0_to_f1_nfsdata
    +paul@f0:~ % doas zrepl signal wakeup f0_to_f1_freebsd
     
    -# Check replication status
    -paul@f0:~ % doas zrepl status --mode raw
    +# Check replication status
    +paul@f0:~ % doas zrepl status --mode raw
     

    **Verification that replication is working:**
    @@ -900,14 +895,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Look for "stepping" state and active zfs send processes
    -paul@f0:~ % doas zrepl status --mode raw | grep -A5 "State.*stepping"
    +
    # Look for "stepping" state and active zfs send processes
    +paul@f0:~ % doas zrepl status --mode raw | grep -A5 "State.*stepping"
     
    -# Check for active ZFS commands
    -paul@f0:~ % doas zrepl status --mode raw | grep -A10 "ZFSCmds.*Active"
    +# Check for active ZFS commands
    +paul@f0:~ % doas zrepl status --mode raw | grep -A10 "ZFSCmds.*Active"
     
    -# Monitor progress - bytes replicated should be increasing
    -paul@f0:~ % doas zrepl status --mode raw | grep BytesReplicated
    +# Monitor progress - bytes replicated should be increasing
    +paul@f0:~ % doas zrepl status --mode raw | grep BytesReplicated
     

    Network Connectivity Issues


    @@ -918,14 +913,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Test connectivity between nodes
    -paul@f0:~ % nc -zv 192.168.2.131 8888
    +
    # Test connectivity between nodes
    +paul@f0:~ % nc -zv 192.168.2.131 8888
     
    -# Check if zrepl is listening on f1
    -paul@f1:~ % doas netstat -an | grep 8888
    +# Check if zrepl is listening on f1
    +paul@f1:~ % doas netstat -an | grep 8888
     
    -# Verify WireGuard tunnel is working
    -paul@f0:~ % ping 192.168.2.131
    +# Verify WireGuard tunnel is working
    +paul@f0:~ % ping 192.168.2.131
     

    Encryption Key Issues


    @@ -936,13 +931,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Verify encryption keys are available on both nodes
    -paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata
    -paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata
    +
    # Verify encryption keys are available on both nodes
    +paul@f0:~ % doas zfs get keystatus zdata/enc/nfsdata
    +paul@f1:~ % doas zfs get keystatus zdata/sink/f0/zdata/enc/nfsdata
     
    -# Load keys if unavailable
    -paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \
    -    zdata/sink/f0/zdata/enc/nfsdata
    +# Load keys if unavailable
    +paul@f1:~ % doas zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key \
    +    zdata/sink/f0/zdata/enc/nfsdata
     

    Monitoring Ongoing Replication


    @@ -953,18 +948,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Monitor replication progress (run repeatedly to check status)
    -paul@f0:~ % doas zrepl status --mode raw | grep -A10 BytesReplicated
    +
    # Monitor replication progress (run repeatedly to check status)
    +paul@f0:~ % doas zrepl status --mode raw | grep -A10 BytesReplicated
     
    -# Or install watch from ports and use it
    -paul@f0:~ % doas pkg install watch
    -paul@f0:~ % watch -n 5 'doas zrepl status --mode raw | grep -A10 BytesReplicated'
    +# Or install watch from ports and use it
    +paul@f0:~ % doas pkg install watch
    +paul@f0:~ % watch -n 5 'doas zrepl status --mode raw | grep -A10 BytesReplicated'
     
    -# Check for new snapshots being created
    -paul@f0:~ % doas zfs list -t snapshot | grep zrepl | tail -5
    +# Check for new snapshots being created
    +paul@f0:~ % doas zfs list -t snapshot | grep zrepl | tail -5
     
    -# Verify snapshots appear on receiver
    -paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep zrepl | tail -5
    +# Verify snapshots appear on receiver
    +paul@f1:~ % doas zfs list -t snapshot -r zdata/sink | grep zrepl | tail -5
     

    This troubleshooting process resolves the most common zrepl issues and ensures continuous data replication between your storage nodes.
    @@ -997,11 +992,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0 - The virtual IP 192.168.1.138 will float between f0 and f1
    -ifconfig_re0_alias0="inet vhid 1 pass testpass alias 192.168.1.138/32"
    +
    # On f0 - The virtual IP 192.168.1.138 will float between f0 and f1
    +ifconfig_re0_alias0="inet vhid 1 pass testpass alias 192.168.1.138/32"
     
    -# On f1 - Higher advskew means lower priority, so f0 wins elections
    -ifconfig_re0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.138/32"
    +# On f1 - Higher advskew means lower priority, so f0 wins elections
    +ifconfig_re0_alias0="inet vhid 1 advskew 100 pass testpass alias 192.168.1.138/32"
     

    Whereas:
    @@ -1031,16 +1026,16 @@ fd42:beef:cafe:2::138 f3s-storage-ha f3s-storage-ha.wg0 f3s-storage-ha.wg0.wan.b by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % cat <<END | doas tee -a /etc/devd.conf
    -notify 0 {
    -        match "system"          "CARP";
    -        match "subsystem"       "[0-9]+@[0-9a-z.]+";
    -        match "type"            "(MASTER|BACKUP)";
    -        action "/usr/local/bin/carpcontrol.sh $subsystem $type";
    -};
    -END
    +
    paul@f0:~ % cat <<END | doas tee -a /etc/devd.conf
    +notify 0 {
    +        match "system"          "CARP";
    +        match "subsystem"       "[0-9]+@[0-9a-z.]+";
    +        match "type"            "(MASTER|BACKUP)";
    +        action "/usr/local/bin/carpcontrol.sh $subsystem $type";
    +};
    +END
     
    -paul@f0:~ % doas service devd restart
    +paul@f0:~ % doas service devd restart
     

    Next, we create the CARP control script that will restart stunnel when the CARP state changes:
    @@ -1051,29 +1046,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF'
    -#!/bin/sh
    -# CARP state change control script
    +
    paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF'
    +#!/bin/sh
    +# CARP state change control script
     
    -case "$2" in
    -    MASTER)
    -        logger "CARP state changed to MASTER, starting services"
    -        ;;
    -    BACKUP)
    -        logger "CARP state changed to BACKUP, stopping services"
    -        ;;
    -    *)
    -        logger "CARP state changed to $2 (unhandled)"
    -        ;;
    -esac
    -EOF
    +case "$2" in
    +    MASTER)
    +        logger "CARP state changed to MASTER, starting services"
    +        ;;
    +    BACKUP)
    +        logger "CARP state changed to BACKUP, stopping services"
    +        ;;
    +    *)
    +        logger "CARP state changed to $2 (unhandled)"
    +        ;;
    +esac
    +EOF
     
    -paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
    +paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
     
    -# Copy the same script to f1
    -paul@f0:~ % scp /usr/local/bin/carpcontrol.sh f1:/tmp/
    -paul@f1:~ % doas mv /tmp/carpcontrol.sh /usr/local/bin/
    -paul@f1:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
    +# Copy the same script to f1
    +paul@f0:~ % scp /usr/local/bin/carpcontrol.sh f1:/tmp/
    +paul@f1:~ % doas mv /tmp/carpcontrol.sh /usr/local/bin/
    +paul@f1:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
     

    Note that carpcontrol.sh doesn't do anything useful yet. We will provide more details (including starting and stopping services upon failover) later in this blog post.
    @@ -1084,10 +1079,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf
    -carp_load="YES"
    -paul@f1:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf  
    -carp_load="YES"
    +
    paul@f0:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf
    +carp_load="YES"
    +paul@f1:~ % echo 'carp_load="YES"' | doas tee -a /boot/loader.conf  
    +carp_load="YES"
     

    Then reboot both hosts or run doas kldload carp to load the module immediately.
    @@ -1104,18 +1099,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas sysrc nfs_server_enable=YES
    -nfs_server_enable: YES -> YES
    -paul@f0:~ % doas sysrc nfsv4_server_enable=YES
    -nfsv4_server_enable: YES -> YES
    -paul@f0:~ % doas sysrc nfsuserd_enable=YES
    -nfsuserd_enable: YES -> YES
    -paul@f0:~ % doas sysrc nfsuserd_flags="-domain lan.buetow.org"
    -nfsuserd_flags: "" -> "-domain lan.buetow.org"
    -paul@f0:~ % doas sysrc mountd_enable=YES
    -mountd_enable: NO -> YES
    -paul@f0:~ % doas sysrc rpcbind_enable=YES
    -rpcbind_enable: NO -> YES
    +
    paul@f0:~ % doas sysrc nfs_server_enable=YES
    +nfs_server_enable: YES -> YES
    +paul@f0:~ % doas sysrc nfsv4_server_enable=YES
    +nfsv4_server_enable: YES -> YES
    +paul@f0:~ % doas sysrc nfsuserd_enable=YES
    +nfsuserd_enable: YES -> YES
    +paul@f0:~ % doas sysrc nfsuserd_flags="-domain lan.buetow.org"
    +nfsuserd_flags: "" -> "-domain lan.buetow.org"
    +paul@f0:~ % doas sysrc mountd_enable=YES
    +mountd_enable: NO -> YES
    +paul@f0:~ % doas sysrc rpcbind_enable=YES
    +rpcbind_enable: NO -> YES
     

    Update: 08.08.2025: I've added the domain to nfsuserd_flags
    @@ -1126,14 +1121,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # First, ensure the dataset is mounted
    -paul@f0:~ % doas zfs get mounted zdata/enc/nfsdata
    -NAME               PROPERTY  VALUE    SOURCE
    -zdata/enc/nfsdata  mounted   yes      -
    +
    # First, ensure the dataset is mounted
    +paul@f0:~ % doas zfs get mounted zdata/enc/nfsdata
    +NAME               PROPERTY  VALUE    SOURCE
    +zdata/enc/nfsdata  mounted   yes      -
     
    -# Create the k3svolumes directory
    -paul@f0:~ % doas mkdir -p /data/nfs/k3svolumes
    -paul@f0:~ % doas chmod 755 /data/nfs/k3svolumes
    +# Create the k3svolumes directory
    +paul@f0:~ % doas mkdir -p /data/nfs/k3svolumes
    +paul@f0:~ % doas chmod 755 /data/nfs/k3svolumes
     

    We also create the /etc/exports file. Since we're using stunnel for encryption, ALL clients must connect through stunnel, which appears as localhost (127.0.0.1) to the NFS server:
    @@ -1142,10 +1137,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas tee /etc/exports <<'EOF'
    -V4: /data/nfs -sec=sys
    -/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255
    -EOF
    +
    paul@f0:~ % doas tee /etc/exports <<'EOF'
    +V4: /data/nfs -sec=sys
    +/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255
    +EOF
     

    The exports configuration:
    @@ -1161,14 +1156,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas service rpcbind start
    -Starting rpcbind.
    -paul@f0:~ % doas service mountd start
    -Starting mountd.
    -paul@f0:~ % doas service nfsd start
    -Starting nfsd.
    -paul@f0:~ % doas service nfsuserd start
    -Starting nfsuserd.
    +
    paul@f0:~ % doas service rpcbind start
    +Starting rpcbind.
    +paul@f0:~ % doas service mountd start
    +Starting mountd.
    +paul@f0:~ % doas service nfsd start
    +Starting nfsd.
    +paul@f0:~ % doas service nfsuserd start
    +Starting nfsuserd.
     

    Configuring Stunnel for NFS Encryption with CARP Failover


    @@ -1207,32 +1202,32 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0 - Create CA
    -paul@f0:~ % doas mkdir -p /usr/local/etc/stunnel/ca
    -paul@f0:~ % cd /usr/local/etc/stunnel/ca
    -paul@f0:~ % doas openssl genrsa -out ca-key.pem 4096
    -paul@f0:~ % doas openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem \
    -  -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=F3S Stunnel CA'
    +
    # On f0 - Create CA
    +paul@f0:~ % doas mkdir -p /usr/local/etc/stunnel/ca
    +paul@f0:~ % cd /usr/local/etc/stunnel/ca
    +paul@f0:~ % doas openssl genrsa -out ca-key.pem 4096
    +paul@f0:~ % doas openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem \
    +  -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=F3S Stunnel CA'
     
    -# Create server certificate
    -paul@f0:~ % cd /usr/local/etc/stunnel
    -paul@f0:~ % doas openssl genrsa -out server-key.pem 4096
    -paul@f0:~ % doas openssl req -new -key server-key.pem -out server.csr \
    -  -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=f3s-storage-ha.lan'
    -paul@f0:~ % doas openssl x509 -req -days 3650 -in server.csr -CA ca/ca-cert.pem \
    -  -CAkey ca/ca-key.pem -CAcreateserial -out server-cert.pem
    +# Create server certificate
    +paul@f0:~ % cd /usr/local/etc/stunnel
    +paul@f0:~ % doas openssl genrsa -out server-key.pem 4096
    +paul@f0:~ % doas openssl req -new -key server-key.pem -out server.csr \
    +  -subj '/C=US/ST=State/L=City/O=F3S Storage/CN=f3s-storage-ha.lan'
    +paul@f0:~ % doas openssl x509 -req -days 3650 -in server.csr -CA ca/ca-cert.pem \
    +  -CAkey ca/ca-key.pem -CAcreateserial -out server-cert.pem
     
    -# Create client certificates for authorised clients
    -paul@f0:~ % cd /usr/local/etc/stunnel/ca
    -paul@f0:~ % doas sh -c 'for client in r0 r1 r2 earth; do 
    -  openssl genrsa -out ${client}-key.pem 4096
    -  openssl req -new -key ${client}-key.pem -out ${client}.csr \
    -    -subj "/C=US/ST=State/L=City/O=F3S Storage/CN=${client}.lan.buetow.org"
    -  openssl x509 -req -days 3650 -in ${client}.csr -CA ca-cert.pem \
    -    -CAkey ca-key.pem -CAcreateserial -out ${client}-cert.pem
    -  # Combine cert and key into a single file for stunnel client
    -  cat ${client}-cert.pem ${client}-key.pem > ${client}-stunnel.pem
    -done'
    +# Create client certificates for authorised clients
    +paul@f0:~ % cd /usr/local/etc/stunnel/ca
    +paul@f0:~ % doas sh -c 'for client in r0 r1 r2 earth; do 
    +  openssl genrsa -out ${client}-key.pem 4096
    +  openssl req -new -key ${client}-key.pem -out ${client}.csr \
    +    -subj "/C=US/ST=State/L=City/O=F3S Storage/CN=${client}.lan.buetow.org"
    +  openssl x509 -req -days 3650 -in ${client}.csr -CA ca-cert.pem \
    +    -CAkey ca-key.pem -CAcreateserial -out ${client}-cert.pem
    +  # Combine cert and key into a single file for stunnel client
    +  cat ${client}-cert.pem ${client}-key.pem > ${client}-stunnel.pem
    +done'
     

    Install and Configure Stunnel on f0


    @@ -1241,35 +1236,35 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Install stunnel
    -paul@f0:~ % doas pkg install -y stunnel
    +
    # Install stunnel
    +paul@f0:~ % doas pkg install -y stunnel
     
    -# Configure stunnel server with client certificate authentication
    -paul@f0:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF'
    -cert = /usr/local/etc/stunnel/server-cert.pem
    -key = /usr/local/etc/stunnel/server-key.pem
    +# Configure stunnel server with client certificate authentication
    +paul@f0:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF'
    +cert = /usr/local/etc/stunnel/server-cert.pem
    +key = /usr/local/etc/stunnel/server-key.pem
     
    -setuid = stunnel
    -setgid = stunnel
    +setuid = stunnel
    +setgid = stunnel
     
    -[nfs-tls]
    -accept = 192.168.1.138:2323
    -connect = 127.0.0.1:2049
    -CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem
    -verify = 2
    -requireCert = yes
    -EOF
    +[nfs-tls]
    +accept = 192.168.1.138:2323
    +connect = 127.0.0.1:2049
    +CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem
    +verify = 2
    +requireCert = yes
    +EOF
     
    -# Enable and start stunnel
    -paul@f0:~ % doas sysrc stunnel_enable=YES
    -stunnel_enable:  -> YES
    -paul@f0:~ % doas service stunnel start
    -Starting stunnel.
    +# Enable and start stunnel
    +paul@f0:~ % doas sysrc stunnel_enable=YES
    +stunnel_enable:  -> YES
    +paul@f0:~ % doas service stunnel start
    +Starting stunnel.
     
    -# Restart stunnel to apply the CARP VIP binding
    -paul@f0:~ % doas service stunnel restart
    -Stopping stunnel.
    -Starting stunnel.
    +# Restart stunnel to apply the CARP VIP binding
    +paul@f0:~ % doas service stunnel restart
    +Stopping stunnel.
    +Starting stunnel.
     

    The configuration includes:
    @@ -1287,30 +1282,30 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f1:~ % doas sysrc nfs_server_enable=YES
    -nfs_server_enable: NO -> YES
    -paul@f1:~ % doas sysrc nfsv4_server_enable=YES
    -nfsv4_server_enable: NO -> YES
    -paul@f1:~ % doas sysrc nfsuserd_enable=YES
    -nfsuserd_enable: NO -> YES
    -paul@f1:~ % doas sysrc mountd_enable=YES
    -mountd_enable: NO -> YES
    -paul@f1:~ % doas sysrc rpcbind_enable=YES
    -rpcbind_enable: NO -> YES
    +
    paul@f1:~ % doas sysrc nfs_server_enable=YES
    +nfs_server_enable: NO -> YES
    +paul@f1:~ % doas sysrc nfsv4_server_enable=YES
    +nfsv4_server_enable: NO -> YES
    +paul@f1:~ % doas sysrc nfsuserd_enable=YES
    +nfsuserd_enable: NO -> YES
    +paul@f1:~ % doas sysrc mountd_enable=YES
    +mountd_enable: NO -> YES
    +paul@f1:~ % doas sysrc rpcbind_enable=YES
    +rpcbind_enable: NO -> YES
     
    -paul@f1:~ % doas tee /etc/exports <<'EOF'
    -V4: /data/nfs -sec=sys
    -/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255
    -EOF
    +paul@f1:~ % doas tee /etc/exports <<'EOF'
    +V4: /data/nfs -sec=sys
    +/data/nfs -alldirs -maproot=root -network 127.0.0.1 -mask 255.255.255.255
    +EOF
     
    -paul@f1:~ % doas service rpcbind start
    -Starting rpcbind.
    -paul@f1:~ % doas service mountd start
    -Starting mountd.
    -paul@f1:~ % doas service nfsd start
    -Starting nfsd.
    -paul@f1:~ % doas service nfsuserd start
    -Starting nfsuserd.
    +paul@f1:~ % doas service rpcbind start
    +Starting rpcbind.
    +paul@f1:~ % doas service mountd start
    +Starting mountd.
    +paul@f1:~ % doas service nfsd start
    +Starting nfsd.
    +paul@f1:~ % doas service nfsuserd start
    +Starting nfsuserd.
     

    And to configure stunnel on f1, we run:
    @@ -1319,42 +1314,42 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Install stunnel
    -paul@f1:~ % doas pkg install -y stunnel
    +
    # Install stunnel
    +paul@f1:~ % doas pkg install -y stunnel
     
    -# Copy certificates from f0
    -paul@f0:~ % doas tar -cf /tmp/stunnel-certs.tar \
    -  -C /usr/local/etc/stunnel server-cert.pem server-key.pem ca
    -paul@f0:~ % scp /tmp/stunnel-certs.tar f1:/tmp/
    +# Copy certificates from f0
    +paul@f0:~ % doas tar -cf /tmp/stunnel-certs.tar \
    +  -C /usr/local/etc/stunnel server-cert.pem server-key.pem ca
    +paul@f0:~ % scp /tmp/stunnel-certs.tar f1:/tmp/
     
    -paul@f1:~ % cd /usr/local/etc/stunnel && doas tar -xf /tmp/stunnel-certs.tar
    +paul@f1:~ % cd /usr/local/etc/stunnel && doas tar -xf /tmp/stunnel-certs.tar
     
    -# Configure stunnel server on f1 with client certificate authentication
    -paul@f1:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF'
    -cert = /usr/local/etc/stunnel/server-cert.pem
    -key = /usr/local/etc/stunnel/server-key.pem
    +# Configure stunnel server on f1 with client certificate authentication
    +paul@f1:~ % doas tee /usr/local/etc/stunnel/stunnel.conf <<'EOF'
    +cert = /usr/local/etc/stunnel/server-cert.pem
    +key = /usr/local/etc/stunnel/server-key.pem
     
    -setuid = stunnel
    -setgid = stunnel
    +setuid = stunnel
    +setgid = stunnel
     
    -[nfs-tls]
    -accept = 192.168.1.138:2323
    -connect = 127.0.0.1:2049
    -CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem
    -verify = 2
    -requireCert = yes
    -EOF
    +[nfs-tls]
    +accept = 192.168.1.138:2323
    +connect = 127.0.0.1:2049
    +CAfile = /usr/local/etc/stunnel/ca/ca-cert.pem
    +verify = 2
    +requireCert = yes
    +EOF
     
    -# Enable and start stunnel
    -paul@f1:~ % doas sysrc stunnel_enable=YES
    -stunnel_enable:  -> YES
    -paul@f1:~ % doas service stunnel start
    -Starting stunnel.
    +# Enable and start stunnel
    +paul@f1:~ % doas sysrc stunnel_enable=YES
    +stunnel_enable:  -> YES
    +paul@f1:~ % doas service stunnel start
    +Starting stunnel.
     
    -# Restart stunnel to apply the CARP VIP binding
    -paul@f1:~ % doas service stunnel restart
    -Stopping stunnel.
    -Starting stunnel.
    +# Restart stunnel to apply the CARP VIP binding
    +paul@f1:~ % doas service stunnel restart
    +Stopping stunnel.
    +Starting stunnel.
     

    CARP Control Script for Clean Failover


    @@ -1381,54 +1376,54 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Create CARP control script on both f0 and f1
    -paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF'
    -#!/bin/sh
    -# CARP state change control script
    -
    -HOSTNAME=`hostname`
    -
    -if [ ! -f /data/nfs/nfs.DO_NOT_REMOVE ]; then
    -    logger '/data/nfs not mounted, mounting it now!'
    -    if [ "$HOSTNAME" = 'f0.lan.buetow.org' ]; then
    -        zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata
    -        zfs set mountpoint=/data/nfs zdata/enc/nfsdata
    -    else
    -        zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata
    -        zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata
    -        zfs mount zdata/sink/f0/zdata/enc/nfsdata
    -        zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
    -    fi
    -    service nfsd stop 2>&1
    -    service mountd stop 2>&1
    -fi
    -
    -
    -case "$2" in
    -    MASTER)
    -        logger "CARP state changed to MASTER, starting services"
    -        service rpcbind start >/dev/null 2>&1
    -        service mountd start >/dev/null 2>&1
    -        service nfsd start >/dev/null 2>&1
    -        service nfsuserd start >/dev/null 2>&1
    -        service stunnel restart >/dev/null 2>&1
    -        logger "CARP MASTER: NFS and stunnel services started"
    -        ;;
    -    BACKUP)
    -        logger "CARP state changed to BACKUP, stopping services"
    -        service stunnel stop >/dev/null 2>&1
    -        service nfsd stop >/dev/null 2>&1
    -        service mountd stop >/dev/null 2>&1
    -        service nfsuserd stop >/dev/null 2>&1
    -        logger "CARP BACKUP: NFS and stunnel services stopped"
    -        ;;
    -    *)
    -        logger "CARP state changed to $2 (unhandled)"
    -        ;;
    -esac
    -EOF
    -
    -paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
    +
    # Create CARP control script on both f0 and f1
    +paul@f0:~ % doas tee /usr/local/bin/carpcontrol.sh <<'EOF'
    +#!/bin/sh
    +# CARP state change control script
    +
    +HOSTNAME=`hostname`
    +
    +if [ ! -f /data/nfs/nfs.DO_NOT_REMOVE ]; then
    +    logger '/data/nfs not mounted, mounting it now!'
    +    if [ "$HOSTNAME" = 'f0.lan.buetow.org' ]; then
    +        zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/enc/nfsdata
    +        zfs set mountpoint=/data/nfs zdata/enc/nfsdata
    +    else
    +        zfs load-key -L file:///keys/f0.lan.buetow.org:zdata.key zdata/sink/f0/zdata/enc/nfsdata
    +        zfs set mountpoint=/data/nfs zdata/sink/f0/zdata/enc/nfsdata
    +        zfs mount zdata/sink/f0/zdata/enc/nfsdata
    +        zfs set readonly=on zdata/sink/f0/zdata/enc/nfsdata
    +    fi
    +    service nfsd stop 2>&1
    +    service mountd stop 2>&1
    +fi
    +
    +
    +case "$2" in
    +    MASTER)
    +        logger "CARP state changed to MASTER, starting services"
    +        service rpcbind start >/dev/null 2>&1
    +        service mountd start >/dev/null 2>&1
    +        service nfsd start >/dev/null 2>&1
    +        service nfsuserd start >/dev/null 2>&1
    +        service stunnel restart >/dev/null 2>&1
    +        logger "CARP MASTER: NFS and stunnel services started"
    +        ;;
    +    BACKUP)
    +        logger "CARP state changed to BACKUP, stopping services"
    +        service stunnel stop >/dev/null 2>&1
    +        service nfsd stop >/dev/null 2>&1
    +        service mountd stop >/dev/null 2>&1
    +        service nfsuserd stop >/dev/null 2>&1
    +        logger "CARP BACKUP: NFS and stunnel services stopped"
    +        ;;
    +    *)
    +        logger "CARP state changed to $2 (unhandled)"
    +        ;;
    +esac
    +EOF
    +
    +paul@f0:~ % doas chmod +x /usr/local/bin/carpcontrol.sh
     

    CARP Management Script


    @@ -1439,113 +1434,113 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Create the CARP management script
    -paul@f0:~ % doas tee /usr/local/bin/carp <<'EOF'
    -#!/bin/sh
    -# CARP state management script
    -# Usage: carp [master|backup|auto-failback enable|auto-failback disable]
    -# Without arguments: shows current state
    -
    -# Find the interface with CARP configured
    -CARP_IF=$(ifconfig -l | xargs -n1 | while read if; do
    -    ifconfig "$if" 2>/dev/null | grep -q "carp:" && echo "$if" && break
    -done)
    -
    -if [ -z "$CARP_IF" ]; then
    -    echo "Error: No CARP interface found"
    -    exit 1
    -fi
    -
    -# Get CARP VHID
    -VHID=$(ifconfig "$CARP_IF" | grep "carp:" | sed -n 's/.*vhid \([0-9]*\).*/\1/p')
    -
    -if [ -z "$VHID" ]; then
    -    echo "Error: Could not determine CARP VHID"
    -    exit 1
    -fi
    -
    -# Function to get the current state
    -get_state() {
    -    ifconfig "$CARP_IF" | grep "carp:" | awk '{print $2}'
    -}
    -
    -# Check for auto-failback block file
    -BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK"
    -check_auto_failback() {
    -    if [ -f "$BLOCK_FILE" ]; then
    -        echo "WARNING: Auto-failback is DISABLED (file exists: $BLOCK_FILE)"
    -    fi
    -}
    -
    -# Main logic
    -case "$1" in
    -    "")
    -        # No argument - show current state
    -        STATE=$(get_state)
    -        echo "CARP state on $CARP_IF (vhid $VHID): $STATE"
    -        check_auto_failback
    -        ;;
    -    master)
    -        # Force to MASTER state
    -        echo "Setting CARP to MASTER state..."
    -        ifconfig "$CARP_IF" vhid "$VHID" state master
    -        sleep 1
    -        STATE=$(get_state)
    -        echo "CARP state on $CARP_IF (vhid $VHID): $STATE"
    -        check_auto_failback
    -        ;;
    -    backup)
    -        # Force to BACKUP state
    -        echo "Setting CARP to BACKUP state..."
    -        ifconfig "$CARP_IF" vhid "$VHID" state backup
    -        sleep 1
    -        STATE=$(get_state)
    -        echo "CARP state on $CARP_IF (vhid $VHID): $STATE"
    -        check_auto_failback
    -        ;;
    -    auto-failback)
    -        case "$2" in
    -            enable)
    -                if [ -f "$BLOCK_FILE" ]; then
    -                    rm "$BLOCK_FILE"
    -                    echo "Auto-failback ENABLED (removed $BLOCK_FILE)"
    -                else
    -                    echo "Auto-failback was already enabled"
    -                fi
    -                ;;
    -            disable)
    -                if [ ! -f "$BLOCK_FILE" ]; then
    -                    touch "$BLOCK_FILE"
    -                    echo "Auto-failback DISABLED (created $BLOCK_FILE)"
    -                else
    -                    echo "Auto-failback was already disabled"
    -                fi
    -                ;;
    -            *)
    -                echo "Usage: $0 auto-failback [enable|disable]"
    -                echo "  enable:  Remove block file to allow automatic failback"
    -                echo "  disable: Create block file to prevent automatic failback"
    -                exit 1
    -                ;;
    -        esac
    -        ;;
    -    *)
    -        echo "Usage: $0 [master|backup|auto-failback enable|auto-failback disable]"
    -        echo "  Without arguments: show current CARP state"
    -        echo "  master: force this node to become CARP MASTER"
    -        echo "  backup: force this node to become CARP BACKUP"
    -        echo "  auto-failback enable:  allow automatic failback to f0"
    -        echo "  auto-failback disable: prevent automatic failback to f0"
    -        exit 1
    -        ;;
    -esac
    -EOF
    -
    -paul@f0:~ % doas chmod +x /usr/local/bin/carp
    -
    -# Copy to f1 as well
    -paul@f0:~ % scp /usr/local/bin/carp f1:/tmp/
    -paul@f1:~ % doas cp /tmp/carp /usr/local/bin/carp && doas chmod +x /usr/local/bin/carp
    +
    # Create the CARP management script
    +paul@f0:~ % doas tee /usr/local/bin/carp <<'EOF'
    +#!/bin/sh
    +# CARP state management script
    +# Usage: carp [master|backup|auto-failback enable|auto-failback disable]
    +# Without arguments: shows current state
    +
    +# Find the interface with CARP configured
    +CARP_IF=$(ifconfig -l | xargs -n1 | while read if; do
    +    ifconfig "$if" 2>/dev/null | grep -q "carp:" && echo "$if" && break
    +done)
    +
    +if [ -z "$CARP_IF" ]; then
    +    echo "Error: No CARP interface found"
    +    exit 1
    +fi
    +
    +# Get CARP VHID
    +VHID=$(ifconfig "$CARP_IF" | grep "carp:" | sed -n 's/.*vhid \([0-9]*\).*/\1/p')
    +
    +if [ -z "$VHID" ]; then
    +    echo "Error: Could not determine CARP VHID"
    +    exit 1
    +fi
    +
    +# Function to get the current state
    +get_state() {
    +    ifconfig "$CARP_IF" | grep "carp:" | awk '{print $2}'
    +}
    +
    +# Check for auto-failback block file
    +BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK"
    +check_auto_failback() {
    +    if [ -f "$BLOCK_FILE" ]; then
    +        echo "WARNING: Auto-failback is DISABLED (file exists: $BLOCK_FILE)"
    +    fi
    +}
    +
    +# Main logic
    +case "$1" in
    +    "")
    +        # No argument - show current state
    +        STATE=$(get_state)
    +        echo "CARP state on $CARP_IF (vhid $VHID): $STATE"
    +        check_auto_failback
    +        ;;
    +    master)
    +        # Force to MASTER state
    +        echo "Setting CARP to MASTER state..."
    +        ifconfig "$CARP_IF" vhid "$VHID" state master
    +        sleep 1
    +        STATE=$(get_state)
    +        echo "CARP state on $CARP_IF (vhid $VHID): $STATE"
    +        check_auto_failback
    +        ;;
    +    backup)
    +        # Force to BACKUP state
    +        echo "Setting CARP to BACKUP state..."
    +        ifconfig "$CARP_IF" vhid "$VHID" state backup
    +        sleep 1
    +        STATE=$(get_state)
    +        echo "CARP state on $CARP_IF (vhid $VHID): $STATE"
    +        check_auto_failback
    +        ;;
    +    auto-failback)
    +        case "$2" in
    +            enable)
    +                if [ -f "$BLOCK_FILE" ]; then
    +                    rm "$BLOCK_FILE"
    +                    echo "Auto-failback ENABLED (removed $BLOCK_FILE)"
    +                else
    +                    echo "Auto-failback was already enabled"
    +                fi
    +                ;;
    +            disable)
    +                if [ ! -f "$BLOCK_FILE" ]; then
    +                    touch "$BLOCK_FILE"
    +                    echo "Auto-failback DISABLED (created $BLOCK_FILE)"
    +                else
    +                    echo "Auto-failback was already disabled"
    +                fi
    +                ;;
    +            *)
    +                echo "Usage: $0 auto-failback [enable|disable]"
    +                echo "  enable:  Remove block file to allow automatic failback"
    +                echo "  disable: Create block file to prevent automatic failback"
    +                exit 1
    +                ;;
    +        esac
    +        ;;
    +    *)
    +        echo "Usage: $0 [master|backup|auto-failback enable|auto-failback disable]"
    +        echo "  Without arguments: show current CARP state"
    +        echo "  master: force this node to become CARP MASTER"
    +        echo "  backup: force this node to become CARP BACKUP"
    +        echo "  auto-failback enable:  allow automatic failback to f0"
    +        echo "  auto-failback disable: prevent automatic failback to f0"
    +        exit 1
    +        ;;
    +esac
    +EOF
    +
    +paul@f0:~ % doas chmod +x /usr/local/bin/carp
    +
    +# Copy to f1 as well
    +paul@f0:~ % scp /usr/local/bin/carp f1:/tmp/
    +paul@f1:~ % doas cp /tmp/carp /usr/local/bin/carp && doas chmod +x /usr/local/bin/carp
     

    Now you can easily manage CARP states and auto-failback:
    @@ -1554,27 +1549,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Check current CARP state
    -paul@f0:~ % doas carp
    -CARP state on re0 (vhid 1): MASTER
    +
    # Check current CARP state
    +paul@f0:~ % doas carp
    +CARP state on re0 (vhid 1): MASTER
     
    -# If auto-failback is disabled, you'll see a warning
    -paul@f0:~ % doas carp
    -CARP state on re0 (vhid 1): MASTER
    -WARNING: Auto-failback is DISABLED (file exists: /data/nfs/nfs.NO_AUTO_FAILBACK)
    +# If auto-failback is disabled, you'll see a warning
    +paul@f0:~ % doas carp
    +CARP state on re0 (vhid 1): MASTER
    +WARNING: Auto-failback is DISABLED (file exists: /data/nfs/nfs.NO_AUTO_FAILBACK)
     
    -# Force f0 to become BACKUP (triggers failover to f1)
    -paul@f0:~ % doas carp backup
    -Setting CARP to BACKUP state...
    -CARP state on re0 (vhid 1): BACKUP
    +# Force f0 to become BACKUP (triggers failover to f1)
    +paul@f0:~ % doas carp backup
    +Setting CARP to BACKUP state...
    +CARP state on re0 (vhid 1): BACKUP
     
    -# Disable auto-failback (useful for maintenance)
    -paul@f0:~ % doas carp auto-failback disable
    -Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK)
    +# Disable auto-failback (useful for maintenance)
    +paul@f0:~ % doas carp auto-failback disable
    +Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK)
     
    -# Enable auto-failback
    -paul@f0:~ % doas carp auto-failback enable
    -Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK)
    +# Enable auto-failback
    +paul@f0:~ % doas carp auto-failback enable
    +Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK)
     

    Automatic Failback After Reboot


    @@ -1587,60 +1582,60 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas tee /usr/local/bin/carp-auto-failback.sh <<'EOF'
    -#!/bin/sh
    -# CARP automatic failback script for f0
    -# Ensures f0 reclaims MASTER role after reboot when storage is ready
    +
    paul@f0:~ % doas tee /usr/local/bin/carp-auto-failback.sh <<'EOF'
    +#!/bin/sh
    +# CARP automatic failback script for f0
    +# Ensures f0 reclaims MASTER role after reboot when storage is ready
     
    -LOGFILE="/var/log/carp-auto-failback.log"
    -MARKER_FILE="/data/nfs/nfs.DO_NOT_REMOVE"
    -BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK"
    +LOGFILE="/var/log/carp-auto-failback.log"
    +MARKER_FILE="/data/nfs/nfs.DO_NOT_REMOVE"
    +BLOCK_FILE="/data/nfs/nfs.NO_AUTO_FAILBACK"
     
    -log_message() {
    -    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOGFILE"
    -}
    +log_message() {
    +    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOGFILE"
    +}
     
    -# Check if we're already MASTER
    -CURRENT_STATE=$(/usr/local/bin/carp | awk '{print $NF}')
    -if [ "$CURRENT_STATE" = "MASTER" ]; then
    -    exit 0
    -fi
    +# Check if we're already MASTER
    +CURRENT_STATE=$(/usr/local/bin/carp | awk '{print $NF}')
    +if [ "$CURRENT_STATE" = "MASTER" ]; then
    +    exit 0
    +fi
     
    -# Check if /data/nfs is mounted
    -if ! mount | grep -q "on /data/nfs "; then
    -    log_message "SKIP: /data/nfs not mounted"
    -    exit 0
    -fi
    +# Check if /data/nfs is mounted
    +if ! mount | grep -q "on /data/nfs "; then
    +    log_message "SKIP: /data/nfs not mounted"
    +    exit 0
    +fi
     
    -# Check if the marker file exists
    -# (identifies that the ZFS data set is properly mounted)
    -if [ ! -f "$MARKER_FILE" ]; then
    -    log_message "SKIP: Marker file $MARKER_FILE not found"
    -    exit 0
    -fi
    +# Check if the marker file exists
    +# (identifies that the ZFS data set is properly mounted)
    +if [ ! -f "$MARKER_FILE" ]; then
    +    log_message "SKIP: Marker file $MARKER_FILE not found"
    +    exit 0
    +fi
     
    -# Check if failback is blocked (for maintenance)
    -if [ -f "$BLOCK_FILE" ]; then
    -    log_message "SKIP: Failback blocked by $BLOCK_FILE"
    -    exit 0
    -fi
    +# Check if failback is blocked (for maintenance)
    +if [ -f "$BLOCK_FILE" ]; then
    +    log_message "SKIP: Failback blocked by $BLOCK_FILE"
    +    exit 0
    +fi
     
    -# All conditions met - promote to MASTER
    -log_message "CONDITIONS MET: Promoting to MASTER (was $CURRENT_STATE)"
    -/usr/local/bin/carp master
    +# All conditions met - promote to MASTER
    +log_message "CONDITIONS MET: Promoting to MASTER (was $CURRENT_STATE)"
    +/usr/local/bin/carp master
     
    -# Log result
    -sleep 2
    -NEW_STATE=$(/usr/local/bin/carp | awk '{print $NF}')
    -log_message "Failback complete: State is now $NEW_STATE"
    +# Log result
    +sleep 2
    +NEW_STATE=$(/usr/local/bin/carp | awk '{print $NF}')
    +log_message "Failback complete: State is now $NEW_STATE"
     
    -# If successful, log to the system log too
    -if [ "$NEW_STATE" = "MASTER" ]; then
    -    logger "CARP: f0 automatically reclaimed MASTER role"
    -fi
    -EOF
    +# If successful, log to the system log too
    +if [ "$NEW_STATE" = "MASTER" ]; then
    +    logger "CARP: f0 automatically reclaimed MASTER role"
    +fi
    +EOF
     
    -paul@f0:~ % doas chmod +x /usr/local/bin/carp-auto-failback.sh
    +paul@f0:~ % doas chmod +x /usr/local/bin/carp-auto-failback.sh
     

    The marker file identifies that the ZFS data set is mounted correctly. We create it with:
    @@ -1649,7 +1644,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas touch /data/nfs/nfs.DO_NOT_REMOVE
    +
    paul@f0:~ % doas touch /data/nfs/nfs.DO_NOT_REMOVE
     

    We add a cron job to check every minute:
    @@ -1658,7 +1653,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % echo "* * * * * /usr/local/bin/carp-auto-failback.sh" | doas crontab -
    +
    paul@f0:~ % echo "* * * * * /usr/local/bin/carp-auto-failback.sh" | doas crontab -
     

    The enhanced CARP script provides integrated control over auto-failback. To temporarily turn off automatic failback (e.g., for f0 maintenance), we run:
    @@ -1667,8 +1662,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas carp auto-failback disable
    -Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK)
    +
    paul@f0:~ % doas carp auto-failback disable
    +Auto-failback DISABLED (created /data/nfs/nfs.NO_AUTO_FAILBACK)
     

    And to re-enable it:
    @@ -1677,8 +1672,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas carp auto-failback enable
    -Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK)
    +
    paul@f0:~ % doas carp auto-failback enable
    +Auto-failback ENABLED (removed /data/nfs/nfs.NO_AUTO_FAILBACK)
     

    To check whether auto-failback is enabled, we run:
    @@ -1687,9 +1682,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas carp
    -CARP state on re0 (vhid 1): MASTER
    -# If disabled, you'll see: WARNING: Auto-failback is DISABLED
    +
    paul@f0:~ % doas carp
    +CARP state on re0 (vhid 1): MASTER
    +# If disabled, you'll see: WARNING: Auto-failback is DISABLED
     

    The failback attempts are logged to /var/log/carp-auto-failback.log!
    @@ -1716,29 +1711,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Install stunnel on client (example for `r0`)
    -[root@r0 ~]# dnf install -y stunnel nfs-utils
    +
    # Install stunnel on client (example for `r0`)
    +[root@r0 ~]# dnf install -y stunnel nfs-utils
     
    -# Copy client certificate and CA certificate from f0
    -[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/r0-stunnel.pem /etc/stunnel/
    -[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/ca-cert.pem /etc/stunnel/
    +# Copy client certificate and CA certificate from f0
    +[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/r0-stunnel.pem /etc/stunnel/
    +[root@r0 ~]# scp f0:/usr/local/etc/stunnel/ca/ca-cert.pem /etc/stunnel/
     
    -# Configure stunnel client with certificate authentication
    -[root@r0 ~]# tee /etc/stunnel/stunnel.conf <<'EOF'
    -cert = /etc/stunnel/r0-stunnel.pem
    -CAfile = /etc/stunnel/ca-cert.pem
    -client = yes
    -verify = 2
    +# Configure stunnel client with certificate authentication
    +[root@r0 ~]# tee /etc/stunnel/stunnel.conf <<'EOF'
    +cert = /etc/stunnel/r0-stunnel.pem
    +CAfile = /etc/stunnel/ca-cert.pem
    +client = yes
    +verify = 2
     
    -[nfs-ha]
    -accept = 127.0.0.1:2323
    -connect = 192.168.1.138:2323
    -EOF
    +[nfs-ha]
    +accept = 127.0.0.1:2323
    +connect = 192.168.1.138:2323
    +EOF
     
    -# Enable and start stunnel
    -[root@r0 ~]# systemctl enable --now stunnel
    +# Enable and start stunnel
    +[root@r0 ~]# systemctl enable --now stunnel
     
    -# Repeat for r1 and r2 with their respective certificates
    +# Repeat for r1 and r2 with their respective certificates
     

    Note: Each client must use its certificate file (r0-stunnel.pem, r1-stunnel.pem, r2-stunnel.pem, or earth-stunnel.pem - the latter is for my Laptop, which can also mount the NFS shares).
    @@ -1764,8 +1759,8 @@ Domain = lan.buetow.org by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# echo 'fs.inotify.max_user_instances = 512' > /etc/sysctl.d/99-inotify.conf
    -[root@r0 ~]# sysctl -w fs.inotify.max_user_instances=512
    +
    [root@r0 ~]# echo 'fs.inotify.max_user_instances = 512' > /etc/sysctl.d/99-inotify.conf
    +[root@r0 ~]# sysctl -w fs.inotify.max_user_instances=512
     

    And afterwards, we need to run the following on all 3 Rocky hosts:
    @@ -1774,8 +1769,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# systemctl start nfs-idmapd
    -[root@r0 ~]# systemctl enable --now nfs-client.target
    +
    [root@r0 ~]# systemctl start nfs-idmapd
    +[root@r0 ~]# systemctl enable --now nfs-client.target
     

    and then, safest, reboot those.
    @@ -1788,21 +1783,21 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Create a mount point
    -[root@r0 ~]# mkdir -p /data/nfs/k3svolumes
    +
    # Create a mount point
    +[root@r0 ~]# mkdir -p /data/nfs/k3svolumes
     
    -# Mount through stunnel (using localhost and NFSv4)
    -[root@r0 ~]# mount -t nfs4 -o port=2323 127.0.0.1:/k3svolumes /data/nfs/k3svolumes
    +# Mount through stunnel (using localhost and NFSv4)
    +[root@r0 ~]# mount -t nfs4 -o port=2323 127.0.0.1:/k3svolumes /data/nfs/k3svolumes
     
    -# Verify mount
    -[root@r0 ~]# mount | grep k3svolumes
    -127.0.0.1:/k3svolumes on /data/nfs/k3svolumes 
    -  type nfs4 (rw,relatime,vers=4.2,rsize=131072,wsize=131072,
    -  namlen=255,hard,proto=tcp,port=2323,timeo=600,retrans=2,sec=sys,
    -  clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1)
    +# Verify mount
    +[root@r0 ~]# mount | grep k3svolumes
    +127.0.0.1:/k3svolumes on /data/nfs/k3svolumes 
    +  type nfs4 (rw,relatime,vers=4.2,rsize=131072,wsize=131072,
    +  namlen=255,hard,proto=tcp,port=2323,timeo=600,retrans=2,sec=sys,
    +  clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1)
     
    -# For persistent mount, add to /etc/fstab:
    -127.0.0.1:/k3svolumes /data/nfs/k3svolumes nfs4 port=2323,_netdev,soft,timeo=10,retrans=2,intr 0 0
    +# For persistent mount, add to /etc/fstab:
    +127.0.0.1:/k3svolumes /data/nfs/k3svolumes nfs4 port=2323,_netdev,soft,timeo=10,retrans=2,intr 0 0
     

    Note: The mount uses localhost (127.0.0.1) because stunnel is listening locally and forwarding the encrypted traffic to the remote server.
    @@ -1815,20 +1810,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # On f0 (current MASTER) - trigger failover
    -paul@f0:~ % doas ifconfig re0 vhid 1 state backup
    +
    # On f0 (current MASTER) - trigger failover
    +paul@f0:~ % doas ifconfig re0 vhid 1 state backup
     
    -# On f1 - verify it becomes MASTER
    -paul@f1:~ % ifconfig re0 | grep carp
    -    inet 192.168.1.138 netmask 0xffffffff broadcast 192.168.1.138 vhid 1
    +# On f1 - verify it becomes MASTER
    +paul@f1:~ % ifconfig re0 | grep carp
    +    inet 192.168.1.138 netmask 0xffffffff broadcast 192.168.1.138 vhid 1
     
    -# Check stunnel is now listening on f1
    -paul@f1:~ % doas sockstat -l | grep 2323
    -stunnel  stunnel    4567  3  tcp4   192.168.1.138:2323    *:*
    +# Check stunnel is now listening on f1
    +paul@f1:~ % doas sockstat -l | grep 2323
    +stunnel  stunnel    4567  3  tcp4   192.168.1.138:2323    *:*
     
    -# On client - verify NFS mount still works
    -[root@r0 ~]# ls /data/nfs/k3svolumes/
    -[root@r0 ~]# echo "Test after failover" > /data/nfs/k3svolumes/failover-test.txt
    +# On client - verify NFS mount still works
    +[root@r0 ~]# ls /data/nfs/k3svolumes/
    +[root@r0 ~]# echo "Test after failover" > /data/nfs/k3svolumes/failover-test.txt
     

    After a CARP failover, NFS clients may experience "Stale file handle" errors because they cached file handles from the previous server. To resolve this manually, we can run:
    @@ -1837,9 +1832,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # Force unmount and remount
    -[root@r0 ~]# umount -f /data/nfs/k3svolumes
    -[root@r0 ~]# mount /data/nfs/k3svolumes
    +
    # Force unmount and remount
    +[root@r0 ~]# umount -f /data/nfs/k3svolumes
    +[root@r0 ~]# mount /data/nfs/k3svolumes
     

    For the automatic recovery, we create a script:
    @@ -1848,72 +1843,72 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# cat > /usr/local/bin/check-nfs-mount.sh << 'EOF'
    -#!/bin/bash
    -# Fast NFS mount health monitor - runs every 10 seconds via systemd timer
    -
    -MOUNT_POINT="/data/nfs/k3svolumes"
    -LOCK_FILE="/var/run/nfs-mount-check.lock"
    -
    -# Use a lock file to prevent concurrent runs
    -if [ -f "$LOCK_FILE" ]; then
    -    exit 0
    -fi
    -touch "$LOCK_FILE"
    -trap "rm -f $LOCK_FILE" EXIT
    -
    -fix_mount () {
    -    echo "Attempting to remount NFS mount $MOUNT_POINT"
    -    if mount -o remount -f "$MOUNT_POINT" 2>/dev/null; then
    -        echo "Remount command issued for $MOUNT_POINT"
    -    else
    -        echo "Failed to remount NFS mount $MOUNT_POINT"
    -    fi
    -
    -    echo "Checking if $MOUNT_POINT is a mountpoint"
    -    if mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then
    -        echo "$MOUNT_POINT is a valid mountpoint"
    -    else
    -        echo "$MOUNT_POINT is not a valid mountpoint, attempting mount"
    -        if mount "$MOUNT_POINT"; then
    -            echo "Successfully mounted $MOUNT_POINT"
    -            return
    -        else
    -            echo "Failed to mount $MOUNT_POINT"
    -        fi
    -    fi
    -
    -    echo "Attempting to unmount $MOUNT_POINT"
    -    if umount -f "$MOUNT_POINT" 2>/dev/null; then
    -        echo "Successfully unmounted $MOUNT_POINT"
    -    else
    -        echo "Failed to unmount $MOUNT_POINT (it might not be mounted)"
    -    fi
    -
    -    echo "Attempting to mount $MOUNT_POINT"
    -    if mount "$MOUNT_POINT"; then
    -        echo "NFS mount $MOUNT_POINT mounted successfully"
    -        return
    -    else
    -        echo "Failed to mount NFS mount $MOUNT_POINT"
    -    fi
    -
    -    echo "Failed to fix NFS mount $MOUNT_POINT"
    -    exit 1
    -}
    -
    -if ! mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then
    -    echo "NFS mount $MOUNT_POINT not found"
    -    fix_mount
    -fi
    -
    -if ! timeout 2s stat "$MOUNT_POINT" >/dev/null 2>&1; then
    -    echo "NFS mount $MOUNT_POINT appears to be unresponsive"
    -    fix_mount
    -fi
    -EOF
    -
    -[root@r0 ~]# chmod +x /usr/local/bin/check-nfs-mount.sh
    +
    [root@r0 ~]# cat > /usr/local/bin/check-nfs-mount.sh << 'EOF'
    +#!/bin/bash
    +# Fast NFS mount health monitor - runs every 10 seconds via systemd timer
    +
    +MOUNT_POINT="/data/nfs/k3svolumes"
    +LOCK_FILE="/var/run/nfs-mount-check.lock"
    +
    +# Use a lock file to prevent concurrent runs
    +if [ -f "$LOCK_FILE" ]; then
    +    exit 0
    +fi
    +touch "$LOCK_FILE"
    +trap "rm -f $LOCK_FILE" EXIT
    +
    +fix_mount () {
    +    echo "Attempting to remount NFS mount $MOUNT_POINT"
    +    if mount -o remount -f "$MOUNT_POINT" 2>/dev/null; then
    +        echo "Remount command issued for $MOUNT_POINT"
    +    else
    +        echo "Failed to remount NFS mount $MOUNT_POINT"
    +    fi
    +
    +    echo "Checking if $MOUNT_POINT is a mountpoint"
    +    if mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then
    +        echo "$MOUNT_POINT is a valid mountpoint"
    +    else
    +        echo "$MOUNT_POINT is not a valid mountpoint, attempting mount"
    +        if mount "$MOUNT_POINT"; then
    +            echo "Successfully mounted $MOUNT_POINT"
    +            return
    +        else
    +            echo "Failed to mount $MOUNT_POINT"
    +        fi
    +    fi
    +
    +    echo "Attempting to unmount $MOUNT_POINT"
    +    if umount -f "$MOUNT_POINT" 2>/dev/null; then
    +        echo "Successfully unmounted $MOUNT_POINT"
    +    else
    +        echo "Failed to unmount $MOUNT_POINT (it might not be mounted)"
    +    fi
    +
    +    echo "Attempting to mount $MOUNT_POINT"
    +    if mount "$MOUNT_POINT"; then
    +        echo "NFS mount $MOUNT_POINT mounted successfully"
    +        return
    +    else
    +        echo "Failed to mount NFS mount $MOUNT_POINT"
    +    fi
    +
    +    echo "Failed to fix NFS mount $MOUNT_POINT"
    +    exit 1
    +}
    +
    +if ! mountpoint "$MOUNT_POINT" >/dev/null 2>&1; then
    +    echo "NFS mount $MOUNT_POINT not found"
    +    fix_mount
    +fi
    +
    +if ! timeout 2s stat "$MOUNT_POINT" >/dev/null 2>&1; then
    +    echo "NFS mount $MOUNT_POINT appears to be unresponsive"
    +    fix_mount
    +fi
    +EOF
    +
    +[root@r0 ~]# chmod +x /usr/local/bin/check-nfs-mount.sh
     

    And we create the systemd service as follows:
    @@ -1922,17 +1917,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.service << 'EOF'
    -[Unit]
    -Description=NFS Mount Health Monitor
    -After=network-online.target
    +
    [root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.service << 'EOF'
    +[Unit]
    +Description=NFS Mount Health Monitor
    +After=network-online.target
     
    -[Service]
    -Type=oneshot
    -ExecStart=/usr/local/bin/check-nfs-mount.sh
    -StandardOutput=journal
    -StandardError=journal
    -EOF
    +[Service]
    +Type=oneshot
    +ExecStart=/usr/local/bin/check-nfs-mount.sh
    +StandardOutput=journal
    +StandardError=journal
    +EOF
     

    And we also create the systemd timer (runs every 10 seconds):
    @@ -1941,19 +1936,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.timer << 'EOF'
    -[Unit]
    -Description=Run NFS Mount Health Monitor every 10 seconds
    -Requires=nfs-mount-monitor.service
    +
    [root@r0 ~]# cat > /etc/systemd/system/nfs-mount-monitor.timer << 'EOF'
    +[Unit]
    +Description=Run NFS Mount Health Monitor every 10 seconds
    +Requires=nfs-mount-monitor.service
     
    -[Timer]
    -OnBootSec=30s
    -OnUnitActiveSec=10s
    -AccuracySec=1s
    +[Timer]
    +OnBootSec=30s
    +OnUnitActiveSec=10s
    +AccuracySec=1s
     
    -[Install]
    -WantedBy=timers.target
    -EOF
    +[Install]
    +WantedBy=timers.target
    +EOF
     

    To enable and start the timer, we run:
    @@ -1962,19 +1957,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    [root@r0 ~]# systemctl daemon-reload
    -[root@r0 ~]# systemctl enable nfs-mount-monitor.timer
    -[root@r0 ~]# systemctl start nfs-mount-monitor.timer
    +
    [root@r0 ~]# systemctl daemon-reload
    +[root@r0 ~]# systemctl enable nfs-mount-monitor.timer
    +[root@r0 ~]# systemctl start nfs-mount-monitor.timer
     
    -# Check status
    -[root@r0 ~]# systemctl status nfs-mount-monitor.timer
    -● nfs-mount-monitor.timer - Run NFS Mount Health Monitor every 10 seconds
    -     Loaded: loaded (/etc/systemd/system/nfs-mount-monitor.timer; enabled)
    -     Active: active (waiting) since Sat 2025-07-06 10:00:00 EEST
    -    Trigger: Sat 2025-07-06 10:00:10 EEST; 8s left
    +# Check status
    +[root@r0 ~]# systemctl status nfs-mount-monitor.timer
    +● nfs-mount-monitor.timer - Run NFS Mount Health Monitor every 10 seconds
    +     Loaded: loaded (/etc/systemd/system/nfs-mount-monitor.timer; enabled)
    +     Active: active (waiting) since Sat 2025-07-06 10:00:00 EEST
    +    Trigger: Sat 2025-07-06 10:00:10 EEST; 8s left
     
    -# Monitor logs
    -[root@r0 ~]# journalctl -u nfs-mount-monitor -f
    +# Monitor logs
    +[root@r0 ~]# journalctl -u nfs-mount-monitor -f
     

    Note: Stale file handles are inherent to NFS failover because file handles are server-specific. The best approach depends on your application's tolerance for brief disruptions. Of course, all the changes made to r0 above must also be applied to r1 and r2.
    @@ -1987,30 +1982,30 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    # 1. Check the initial state
    -paul@f0:~ % ifconfig re0 | grep carp
    -    carp: MASTER vhid 1 advbase 1 advskew 0
    -paul@f1:~ % ifconfig re0 | grep carp
    -    carp: BACKUP vhid 1 advbase 1 advskew 100
    +
    # 1. Check the initial state
    +paul@f0:~ % ifconfig re0 | grep carp
    +    carp: MASTER vhid 1 advbase 1 advskew 0
    +paul@f1:~ % ifconfig re0 | grep carp
    +    carp: BACKUP vhid 1 advbase 1 advskew 100
     
    -# 2. Create a test file from a client
    -[root@r0 ~]# echo "test before failover" > /data/nfs/k3svolumes/test-before.txt
    +# 2. Create a test file from a client
    +[root@r0 ~]# echo "test before failover" > /data/nfs/k3svolumes/test-before.txt
     
    -# 3. Trigger failover (f0 → f1)
    -paul@f0:~ % doas ifconfig re0 vhid 1 state backup
    +# 3. Trigger failover (f0 → f1)
    +paul@f0:~ % doas ifconfig re0 vhid 1 state backup
     
    -# 4. Monitor client behaviour
    -[root@r0 ~]# ls /data/nfs/k3svolumes/
    -ls: cannot access '/data/nfs/k3svolumes/': Stale file handle
    +# 4. Monitor client behaviour
    +[root@r0 ~]# ls /data/nfs/k3svolumes/
    +ls: cannot access '/data/nfs/k3svolumes/': Stale file handle
     
    -# 5. Check automatic recovery (within 10 seconds)
    -[root@r0 ~]# journalctl -u nfs-mount-monitor -f
    -Jul 06 10:15:32 r0 nfs-monitor[1234]: NFS mount unhealthy detected at \
    -  Sun Jul 6 10:15:32 EEST 2025
    -Jul 06 10:15:32 r0 nfs-monitor[1234]: Attempting to fix stale NFS mount at \
    -  Sun Jul 6 10:15:32 EEST 2025
    -Jul 06 10:15:33 r0 nfs-monitor[1234]: NFS mount fixed at \
    -  Sun Jul 6 10:15:33 EEST 2025
    +# 5. Check automatic recovery (within 10 seconds)
    +[root@r0 ~]# journalctl -u nfs-mount-monitor -f
    +Jul 06 10:15:32 r0 nfs-monitor[1234]: NFS mount unhealthy detected at \
    +  Sun Jul 6 10:15:32 EEST 2025
    +Jul 06 10:15:32 r0 nfs-monitor[1234]: Attempting to fix stale NFS mount at \
    +  Sun Jul 6 10:15:32 EEST 2025
    +Jul 06 10:15:33 r0 nfs-monitor[1234]: NFS mount fixed at \
    +  Sun Jul 6 10:15:33 EEST 2025
     

    Failover Timeline:
    @@ -2068,7 +2063,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
    paul@f0:~ % doas zpool online -e /dev/ada1
    +
    paul@f0:~ % doas zpool online -e /dev/ada1
     

      @@ -2081,15 +2076,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas zpool list
      -NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
      -zdata  3.63T   677G  2.97T        -         -     3%    18%  1.00x    ONLINE  -
      -zroot   472G  68.4G   404G        -         -    13%    14%  1.00x    ONLINE  -
      +
      paul@f0:~ % doas zpool list
      +NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
      +zdata  3.63T   677G  2.97T        -         -     3%    18%  1.00x    ONLINE  -
      +zroot   472G  68.4G   404G        -         -    13%    14%  1.00x    ONLINE  -
       
      -paul@f0:~ % doas camcontrol devlist
      -<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
      -<SD Ultra 3D 4TB 530500WD>         at scbus1 target 0 lun 0 (pass1,ada1)
      -<Generic Flash Disk 8.07>          at scbus2 target 0 lun 0 (da0,pass2)
      +paul@f0:~ % doas camcontrol devlist
      +<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
      +<SD Ultra 3D 4TB 530500WD>         at scbus1 target 0 lun 0 (pass1,ada1)
      +<Generic Flash Disk 8.07>          at scbus2 target 0 lun 0 (da0,pass2)
       

      We're still using different SSD models on f1 (WD Blue SA510 4TB) to avoid simultaneous failures:
      @@ -2098,10 +2093,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f1:~ % doas camcontrol devlist
      -<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
      -<WD Blue SA510 2.5 4TB 530500WD>   at scbus1 target 0 lun 0 (pass1,ada1)
      -<Generic Flash Disk 8.07>          at scbus2 target 0 lun 0 (da0,pass2)
      +
      paul@f1:~ % doas camcontrol devlist
      +<512GB SSD D910R170>               at scbus0 target 0 lun 0 (pass0,ada0)
      +<WD Blue SA510 2.5 4TB 530500WD>   at scbus1 target 0 lun 0 (pass1,ada1)
      +<Generic Flash Disk 8.07>          at scbus2 target 0 lun 0 (da0,pass2)
       

      Conclusion


      @@ -2170,11 +2165,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/2025-08-05-local-coding-llm-with-ollama.html b/gemfeed/2025-08-05-local-coding-llm-with-ollama.html index 6108297f..f2a9d99d 100644 --- a/gemfeed/2025-08-05-local-coding-llm-with-ollama.html +++ b/gemfeed/2025-08-05-local-coding-llm-with-ollama.html @@ -2,17 +2,12 @@ - Local LLM for Coding with Ollama on macOS -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -110,9 +105,9 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      brew install ollama
      -rehash
      -ollama serve
      +
      brew install ollama
      +rehash
      +ollama serve
       

      Which started up the Ollama server with something like this (the screenshots shows already some requests made):
      @@ -125,7 +120,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      ollama pull qwen2.5-coder:14b-instruct
      +
      ollama pull qwen2.5-coder:14b-instruct
       

      Now, I was ready to go! It wasn't so difficult. Now, let's see how I used this model for coding tasks.
      @@ -138,27 +133,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      time echo "Write a function in golang to print out the Nth fibonacci number, \
      -  only the function without the boilerplate" | ollama run qwen2.5-coder:14b-instruct
      -
      -Output:
      -
      -func fibonacci(n int) int {
      -    if n <= 1 {
      -        return n
      -    }
      -    a, b := 0, 1
      -    for i := 2; i <= n; i++ {
      -        a, b = b, a+b
      -    }
      -    return b
      -}
      -
      -Execution Metrics:
      -
      -Executed in    4.90 secs      fish           external
      -   usr time   15.54 millis    0.31 millis   15.24 millis
      -   sys time   19.68 millis    1.02 millis   18.66 millis
      +
      time echo "Write a function in golang to print out the Nth fibonacci number, \
      +  only the function without the boilerplate" | ollama run qwen2.5-coder:14b-instruct
      +
      +Output:
      +
      +func fibonacci(n int) int {
      +    if n <= 1 {
      +        return n
      +    }
      +    a, b := 0, 1
      +    for i := 2; i <= n; i++ {
      +        a, b = b, a+b
      +    }
      +    return b
      +}
      +
      +Execution Metrics:
      +
      +Executed in    4.90 secs      fish           external
      +   usr time   15.54 millis    0.31 millis   15.24 millis
      +   sys time   19.68 millis    1.02 millis   18.66 millis
       

      Note, after having written this blog post, I tried the same with the newer model qwen3-coder:30b-a3b-q4_K_M (which "just" came out, and it's a quantised 30B model), and it was much faster:
      @@ -183,9 +178,9 @@ Executed in 1.83 secs fish external by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      brew install aider
      -mkdir -p ~/git/aitest && cd ~/git/aitest && git init
      -aider --model ollama_chat/qwen2.5-coder:14b-instruct
      +
      brew install aider
      +mkdir -p ~/git/aitest && cd ~/git/aitest && git init
      +aider --model ollama_chat/qwen2.5-coder:14b-instruct
       

      https://aider.chat
      @@ -228,11 +223,11 @@ Create a Go project with these files: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      go build cmd/aitest/main.go
      -./main -v
      -0.0.0
      -./main -dir .
      -Number of files in directory .: 4
      +
      go build cmd/aitest/main.go
      +./main -v
      +0.0.0
      +./main -dir .
      +Number of files in directory .: 4
       

      The code


      @@ -243,38 +238,38 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      package main
      -
      -import (
      -	"flag"
      -	"fmt"
      -	"os"
      -
      -	"aitest/internal"
      -)
      -
      -func main() {
      -	var versionFlag bool
      -	flag.BoolVar(&versionFlag, "v", false, "print version")
      -	dir := flag.String("dir", "", "directory to count files in")
      -	flag.Parse()
      -
      -	if versionFlag {
      -		fmt.Println(internal.GetVersion())
      -		return
      -	}
      -
      -	if *dir != "" {
      -		fileCount, err := internal.CountFiles(*dir)
      -		if err != nil {
      -			fmt.Fprintf(os.Stderr, "Error counting files: %v\n", err)
      -			os.Exit(1)
      -		}
      -		fmt.Printf("Number of files in directory %s: %d\n", *dir, fileCount)
      -	} else {
      -		fmt.Println("No directory specified. No count given.")
      -	}
      -}
      +
      package main
      +
      +import (
      +	"flag"
      +	"fmt"
      +	"os"
      +
      +	"aitest/internal"
      +)
      +
      +func main() {
      +	var versionFlag bool
      +	flag.BoolVar(&versionFlag, "v", false, "print version")
      +	dir := flag.String("dir", "", "directory to count files in")
      +	flag.Parse()
      +
      +	if versionFlag {
      +		fmt.Println(internal.GetVersion())
      +		return
      +	}
      +
      +	if *dir != "" {
      +		fileCount, err := internal.CountFiles(*dir)
      +		if err != nil {
      +			fmt.Fprintf(os.Stderr, "Error counting files: %v\n", err)
      +			os.Exit(1)
      +		}
      +		fmt.Printf("Number of files in directory %s: %d\n", *dir, fileCount)
      +	} else {
      +		fmt.Println("No directory specified. No count given.")
      +	}
      +}
       

      The ./internal/version.go file:
      @@ -283,13 +278,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      package internal
      +
      package internal
       
      -var Version = "0.0.0"
      +var Version = "0.0.0"
       
      -func GetVersion() string {
      -	return Version
      -}
      +func GetVersion() string {
      +	return Version
      +}
       

      The ./internal/count.go file:
      @@ -298,27 +293,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      package internal
      -
      -import (
      -	"os"
      -)
      -
      -func CountFiles(dir string) (int, error) {
      -	files, err := os.ReadDir(dir)
      -	if err != nil {
      -		return 0, err
      -	}
      -
      -	count := 0
      -	for _, file := range files {
      -		if !file.IsDir() {
      -			count++
      -		}
      -	}
      -
      -	return count, nil
      -}
      +
      package internal
      +
      +import (
      +	"os"
      +)
      +
      +func CountFiles(dir string) (int, error) {
      +	files, err := os.ReadDir(dir)
      +	if err != nil {
      +		return 0, err
      +	}
      +
      +	count := 0
      +	for _, file := range files {
      +		if !file.IsDir() {
      +			count++
      +		}
      +	}
      +
      +	return count, nil
      +}
       

      The code is quite straightforward, especially for generating boilerplate code this will be useful for many use cases!
      @@ -338,7 +333,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      cargo install lsp-ai
      +
      cargo install lsp-ai
       

      Helix Configuration


      @@ -476,11 +471,11 @@ content = "{CODE}"
      Back to the main site
      diff --git a/gemfeed/2025-08-15-random-weird-things-iii.html b/gemfeed/2025-08-15-random-weird-things-iii.html index 3b4003cb..748c828c 100644 --- a/gemfeed/2025-08-15-random-weird-things-iii.html +++ b/gemfeed/2025-08-15-random-weird-things-iii.html @@ -2,17 +2,12 @@ - Random Weird Things - Part Ⅲ -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -134,11 +129,11 @@
      Back to the main site
      diff --git a/gemfeed/2025-09-14-bash-golf-part-4.html b/gemfeed/2025-09-14-bash-golf-part-4.html index b0b5459c..457b15d8 100644 --- a/gemfeed/2025-09-14-bash-golf-part-4.html +++ b/gemfeed/2025-09-14-bash-golf-part-4.html @@ -2,17 +2,12 @@ - Bash Golf Part 4 -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -75,9 +70,9 @@ somecommand \ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      printf 'a\nb\n' \
      -    | tee >(sed 's/.*/X:&/; s/$/ :c1/') >(tr a-z A-Z | sed 's/$/ :c2/') \
      -    | sed 's/$/ :c3/'
      +
      printf 'a\nb\n' \
      +    | tee >(sed 's/.*/X:&/; s/$/ :c1/') >(tr a-z A-Z | sed 's/$/ :c2/') \
      +    | sed 's/$/ :c3/'
       

      Output:
      @@ -99,8 +94,8 @@ X:b :c1 :c3 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      /bin/sh -c 'echo hi | tee >(cat)'
      -# /bin/sh: 1: Syntax error: "(" unexpected
      +
      /bin/sh -c 'echo hi | tee >(cat)'
      +# /bin/sh: 1: Syntax error: "(" unexpected
       

      Combine with set -o pipefail if failures in side branches should fail the whole pipeline.
      @@ -111,9 +106,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      set -o pipefail
      -printf 'ok\n' | tee >(false) | cat >/dev/null
      -echo $?   # 1 because a side branch failed
      +
      set -o pipefail
      +printf 'ok\n' | tee >(false) | cat >/dev/null
      +echo $?   # 1 because a side branch failed
       

      Further reading:
      @@ -128,19 +123,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      ssh "$SSH_USER@$SSH_HOST" <<EOF
      -    # Go to the work directory
      -    cd "$WORK_DIR"
      -  
      -    # Make a git pull
      -    git pull
      -  
      -    # Export environment variables required for the service to run
      -    export AUTH_TOKEN="$APP_AUTH_TOKEN"
      -  
      -    # Start the service
      -    docker compose up -d --build
      -EOF
      +
      ssh "$SSH_USER@$SSH_HOST" <<EOF
      +    # Go to the work directory
      +    cd "$WORK_DIR"
      +  
      +    # Make a git pull
      +    git pull
      +  
      +    # Export environment variables required for the service to run
      +    export AUTH_TOKEN="$APP_AUTH_TOKEN"
      +  
      +    # Start the service
      +    docker compose up -d --build
      +EOF
       

      Tips:
      @@ -153,10 +148,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      FOO=bar
      -cat <<'EOF'
      -$FOO is not expanded here
      -EOF
      +
      FOO=bar
      +cat <<'EOF'
      +$FOO is not expanded here
      +EOF
       

      Prefer explicit quoting for variables (as above) to avoid surprises. Example (spaces preserved only when quoted):
      @@ -165,11 +160,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      WORK_DIR="/tmp/my work"
      -ssh host <<EOF
      -    cd $WORK_DIR      # may break if unquoted
      -    cd "$WORK_DIR"   # safe
      -EOF
      +
      WORK_DIR="/tmp/my work"
      +ssh host <<EOF
      +    cd $WORK_DIR      # may break if unquoted
      +    cd "$WORK_DIR"   # safe
      +EOF
       

      Consider set -euo pipefail at the top of the remote block for stricter error handling. Example:
      @@ -178,11 +173,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      ssh host <<'EOF'
      -    set -euo pipefail
      -    false   # causes immediate failure
      -    echo never
      -EOF
      +
      ssh host <<'EOF'
      +    set -euo pipefail
      +    false   # causes immediate failure
      +    echo never
      +EOF
       

      Indent-friendly variant: use a dash to strip leading tabs in the body:
      @@ -191,10 +186,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      cat <<-EOF > script.sh
      -	#!/usr/bin/env bash
      -	echo "tab-indented content is dedented"
      -EOF
      +
      cat <<-EOF > script.sh
      +	#!/usr/bin/env bash
      +	echo "tab-indented content is dedented"
      +EOF
       

      Further reading:
      @@ -209,23 +204,23 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      #!/usr/bin/env bash
      -set -euo pipefail
      +
      #!/usr/bin/env bash
      +set -euo pipefail
       
      -super() {
      -    local -r fn=${FUNCNAME[1]}
      -    # Split name on :: and dispatch to base implementation
      -    local -a parts=( ${fn//::/ } )
      -    "${parts[0]}::base::${parts[2]}" "$@"
      -}
      +super() {
      +    local -r fn=${FUNCNAME[1]}
      +    # Split name on :: and dispatch to base implementation
      +    local -a parts=( ${fn//::/ } )
      +    "${parts[0]}::base::${parts[2]}" "$@"
      +}
       
      -foo::base::greet() { echo "base: $@"; }
      -foo::german::greet()  { super "Guten Tag, $@!"; }
      -foo::english::greet() { super "Good day,  $@!"; }
      +foo::base::greet() { echo "base: $@"; }
      +foo::german::greet()  { super "Guten Tag, $@!"; }
      +foo::english::greet() { super "Good day,  $@!"; }
       
      -for lang in german english; do
      -    foo::$lang::greet Paul
      -done
      +for lang in german english; do
      +    foo::$lang::greet Paul
      +done
       

      Output:
      @@ -243,11 +238,11 @@ base: Good day, Paul! by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      user_name=paul
      -declare -n ref=user_name
      -echo "$ref"       # paul
      -ref=julia
      -echo "$user_name" # julia
      +
      user_name=paul
      +declare -n ref=user_name
      +echo "$ref"       # paul
      +ref=julia
      +echo "$user_name" # julia
       

      Output:
      @@ -265,20 +260,20 @@ julia by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      make_var() {
      -    local idx=$1; shift
      -    local name="slot_$idx"
      -    printf -v "$name" '%s' "$*"   # create variable slot_$idx
      -}
      +
      make_var() {
      +    local idx=$1; shift
      +    local name="slot_$idx"
      +    printf -v "$name" '%s' "$*"   # create variable slot_$idx
      +}
       
      -get_var() {
      -    local idx=$1
      -    local -n ref="slot_$idx"      # bind ref to slot_$idx
      -    printf '%s\n' "$ref"
      -}
      +get_var() {
      +    local idx=$1
      +    local -n ref="slot_$idx"      # bind ref to slot_$idx
      +    printf '%s\n' "$ref"
      +}
       
      -make_var 7 "seven"
      -get_var 7
      +make_var 7 "seven"
      +get_var 7
       

      Output:
      @@ -295,9 +290,9 @@ seven by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      foo() { echo foo; }
      -function foo { echo foo; }
      -function foo() { echo foo; }
      +
      foo() { echo foo; }
      +function foo { echo foo; }
      +function foo() { echo foo; }
       

      Recommendation: prefer name() { ... } for portability and consistency.
      @@ -310,14 +305,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      deploy_check() { test -f deploy.yaml; }
      -smoke_test()   { curl -fsS http://localhost/healthz >/dev/null; }
      +
      deploy_check() { test -f deploy.yaml; }
      +smoke_test()   { curl -fsS http://localhost/healthz >/dev/null; }
       
      -if deploy_check || smoke_test; then
      -    echo "All good."
      -else
      -    echo "Something failed." >&2
      -fi
      +if deploy_check || smoke_test; then
      +    echo "All good."
      +else
      +    echo "Something failed." >&2
      +fi
       

      You can also compress it golf-style:
      @@ -326,7 +321,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      deploy_check || smoke_test && echo ok || echo fail >&2
      +
      deploy_check || smoke_test && echo ok || echo fail >&2
       

      Grep, sed, awk quickies


      @@ -337,14 +332,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      cat > /tmp/ctx.txt <<EOF
      -one
      -foo
      -two
      -three
      -bar
      -EOF
      -grep -C1 foo /tmp/ctx.txt
      +
      cat > /tmp/ctx.txt <<EOF
      +one
      +foo
      +two
      +three
      +bar
      +EOF
      +grep -C1 foo /tmp/ctx.txt
       

      Output:
      @@ -361,10 +356,10 @@ two by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      mkdir -p /tmp/golf/foo /tmp/golf/src
      -printf 'bar\n' > /tmp/golf/src/a.txt
      -printf 'bar\n' > /tmp/golf/foo/skip.txt
      -grep -R --exclude-dir=foo 'bar' /tmp/golf
      +
      mkdir -p /tmp/golf/foo /tmp/golf/src
      +printf 'bar\n' > /tmp/golf/src/a.txt
      +printf 'bar\n' > /tmp/golf/foo/skip.txt
      +grep -R --exclude-dir=foo 'bar' /tmp/golf
       

      Output:
      @@ -379,8 +374,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      printf 'A\nB\nC\n' > /tmp/s.txt
      -sed -e '1iHEAD' -e '3iMID' /tmp/s.txt
      +
      printf 'A\nB\nC\n' > /tmp/s.txt
      +sed -e '1iHEAD' -e '3iMID' /tmp/s.txt
       

      Output:
      @@ -399,10 +394,10 @@ C by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      printf 'a b c\nx y z\n' > /tmp/t.txt
      -cat /tmp/t.txt
      -echo
      -awk 'NF{NF-=1};1' /tmp/t.txt
      +
      printf 'a b c\nx y z\n' > /tmp/t.txt
      +cat /tmp/t.txt
      +echo
      +awk 'NF{NF-=1};1' /tmp/t.txt
       

      Output:
      @@ -423,7 +418,7 @@ x y by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      find . -type f -name '*.log' -print0 | xargs -0 rm -f
      +
      find . -type f -name '*.log' -print0 | xargs -0 rm -f
       

      Example with spaces and NULs only:
      @@ -432,7 +427,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      printf 'a\0b c\0' | xargs -0 -I{} printf '<%s>\n' {}
      +
      printf 'a\0b c\0' | xargs -0 -I{} printf '<%s>\n' {}
       

      Output:
      @@ -450,7 +445,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      cfg=$(<config.ini)
      +
      cfg=$(<config.ini)
       

      Read lines into an array safely with mapfile (aka readarray):
      @@ -459,8 +454,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      mapfile -t lines < <(grep -v '^#' config.ini)
      -printf '%s\n' "${lines[@]}"
      +
      mapfile -t lines < <(grep -v '^#' config.ini)
      +printf '%s\n' "${lines[@]}"
       

      Assign formatted strings without a subshell using printf -v:
      @@ -469,8 +464,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      printf -v msg 'Hello %s, id=%04d' "$USER" 42
      -echo "$msg"
      +
      printf -v msg 'Hello %s, id=%04d' "$USER" 42
      +echo "$msg"
       

      Output:
      @@ -485,8 +480,8 @@ Hello paul, id=0042 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      mapfile -d '' -t files < <(find . -type f -print0)
      -printf '%s\n' "${files[@]}"
      +
      mapfile -d '' -t files < <(find . -type f -print0)
      +printf '%s\n' "${files[@]}"
       

      Quick password generator


      @@ -497,7 +492,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      LC_ALL=C tr -dc 'A-Za-z0-9_' </dev/urandom | head -c 16; echo
      +
      LC_ALL=C tr -dc 'A-Za-z0-9_' </dev/urandom | head -c 16; echo
       

      Alternative using openssl:
      @@ -506,7 +501,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      openssl rand -base64 16 | tr -d '\n' | cut -c1-22
      +
      openssl rand -base64 16 | tr -d '\n' | cut -c1-22
       

      yes for automation


      @@ -517,9 +512,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      yes | rm -r large_directory        # auto-confirm
      -yes n | dangerous-command          # auto-decline
      -yes anything | head -n1            # prints one line: anything
      +
      yes | rm -r large_directory        # auto-confirm
      +yes n | dangerous-command          # auto-decline
      +yes anything | head -n1            # prints one line: anything
       

      Forcing true to fail (and vice versa)


      @@ -530,15 +525,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      true()  { return 1; }
      -false() { return 0; }
      +
      true()  { return 1; }
      +false() { return 0; }
       
      -true  || echo 'true failed'
      -false && echo 'false succeeded'
      +true  || echo 'true failed'
      +false && echo 'false succeeded'
       
      -# Bypass function with builtin/command
      -builtin true # returns 0
      -command true # returns 0
      +# Bypass function with builtin/command
      +builtin true # returns 0
      +command true # returns 0
       

      To disable a builtin entirely: enable -n true (re-enable with enable true).
      @@ -566,11 +561,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      rbash -c 'cd /'            # cd: restricted
      -rbash -c 'PATH=/tmp'       # PATH: restricted
      -rbash -c 'echo hi > out'   # redirection: restricted
      -rbash -c '/bin/echo hi'    # commands with /: restricted
      -rbash -c 'exec ls'         # exec: restricted
      +
      rbash -c 'cd /'            # cd: restricted
      +rbash -c 'PATH=/tmp'       # PATH: restricted
      +rbash -c 'echo hi > out'   # redirection: restricted
      +rbash -c '/bin/echo hi'    # commands with /: restricted
      +rbash -c 'exec ls'         # exec: restricted
       

      Useless use of cat (and when it’s ok)


      @@ -581,12 +576,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      # Prefer
      -grep -i foo file
      -<file grep -i foo        # or feed via redirection
      +
      # Prefer
      +grep -i foo file
      +<file grep -i foo        # or feed via redirection
       
      -# Over
      -cat file | grep -i foo
      +# Over
      +cat file | grep -i foo
       

      But for interactive composition, or when you truly need to concatenate multiple sources into a single stream, cat is fine, as you may think, "First I need the content, then I do X." Changing the "useless use of cat" in retrospect is really a waste of time for one-time interactive use:
      @@ -595,7 +590,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      cat file1 file2 | grep -i foo
      +
      cat file1 file2 | grep -i foo
       

      From notes: “Good for interactivity; Useless use of cat” — use judgment.
      @@ -608,15 +603,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      lockdir=/tmp/myjob.lock
      -if mkdir "$lockdir" 2>/dev/null; then
      -    trap 'rmdir "$lockdir"' EXIT INT TERM
      -    # critical section
      -    do_work
      -else
      -    echo "Another instance is running" >&2
      -    exit 1
      -fi
      +
      lockdir=/tmp/myjob.lock
      +if mkdir "$lockdir" 2>/dev/null; then
      +    trap 'rmdir "$lockdir"' EXIT INT TERM
      +    # critical section
      +    do_work
      +else
      +    echo "Another instance is running" >&2
      +    exit 1
      +fi
       

      This works well on Linux. Remove the lock in trap so crashes don’t leave stale locks.
      @@ -631,7 +626,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      find . -name '*.log' -exec gzip -9 {} +
      +
      find . -name '*.log' -exec gzip -9 {} +
       

      Example for extglob (exclude two dirs from listing):
      @@ -640,8 +635,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      shopt -s extglob
      -ls -d -- !(.git|node_modules) 2>/dev/null
      +
      shopt -s extglob
      +ls -d -- !(.git|node_modules) 2>/dev/null
       

      E-Mail your comments to paul@nospam.buetow.org :-)
      @@ -657,11 +652,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.html b/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.html index 12484015..f638e1fc 100644 --- a/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.html +++ b/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -87,10 +82,10 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ git clone https://codeberg.org/snonux/conf.git
      -$ cd conf
      -$ git checkout 15a86f3  # Last commit before ArgoCD migration
      -$ cd f3s/
      +
      $ git clone https://codeberg.org/snonux/conf.git
      +$ cd conf
      +$ git checkout 15a86f3  # Last commit before ArgoCD migration
      +$ cd f3s/
       

      **Current master branch** contains the ArgoCD-managed versions with:
      @@ -109,8 +104,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      dnf update -y
      -reboot
      +
      dnf update -y
      +reboot
       

      On the FreeBSD hosts, I upgraded from FreeBSD 14.2 to 14.3-RELEASE, running this on all three hosts f0, f1 and f2:
      @@ -119,29 +114,29 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas freebsd-update fetch
      -paul@f0:~ % doas freebsd-update install
      -paul@f0:~ % doas reboot
      -.
      -.
      -.
      -paul@f0:~ % doas freebsd-update -r 14.3-RELEASE upgrade
      -paul@f0:~ % doas freebsd-update install
      -paul@f0:~ % doas freebsd-update install
      -paul@f0:~ % doas reboot
      -.
      -.
      -.
      -paul@f0:~ % doas freebsd-update install
      -paul@f0:~ % doas pkg update
      -paul@f0:~ % doas pkg upgrade
      -paul@f0:~ % doas reboot
      -.
      -.
      -.
      -paul@f0:~ % uname -a
      -FreeBSD f0.lan.buetow.org 14.3-RELEASE FreeBSD 14.3-RELEASE
      -        releng/14.3-n271432-8c9ce319fef7 GENERIC amd64
      +
      paul@f0:~ % doas freebsd-update fetch
      +paul@f0:~ % doas freebsd-update install
      +paul@f0:~ % doas reboot
      +.
      +.
      +.
      +paul@f0:~ % doas freebsd-update -r 14.3-RELEASE upgrade
      +paul@f0:~ % doas freebsd-update install
      +paul@f0:~ % doas freebsd-update install
      +paul@f0:~ % doas reboot
      +.
      +.
      +.
      +paul@f0:~ % doas freebsd-update install
      +paul@f0:~ % doas pkg update
      +paul@f0:~ % doas pkg upgrade
      +paul@f0:~ % doas reboot
      +.
      +.
      +.
      +paul@f0:~ % uname -a
      +FreeBSD f0.lan.buetow.org 14.3-RELEASE FreeBSD 14.3-RELEASE
      +        releng/14.3-n271432-8c9ce319fef7 GENERIC amd64
       

      Installing k3s


      @@ -154,7 +149,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# echo -n SECRET_TOKEN > ~/.k3s_token
      +
      [root@r0 ~]# echo -n SECRET_TOKEN > ~/.k3s_token
       

      The following steps are also documented on the k3s website:
      @@ -167,17 +162,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s_token) \
      -        sh -s - server --cluster-init \
      -        --node-ip=192.168.2.120 \
      -        --advertise-address=192.168.2.120 \
      -        --tls-san=r0.wg0.wan.buetow.org
      -[INFO]  Finding release for channel stable
      -[INFO]  Using v1.32.6+k3s1 as release
      -.
      -.
      -.
      -[INFO]  systemd: Starting k3s
      +
      [root@r0 ~]# curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s_token) \
      +        sh -s - server --cluster-init \
      +        --node-ip=192.168.2.120 \
      +        --advertise-address=192.168.2.120 \
      +        --tls-san=r0.wg0.wan.buetow.org
      +[INFO]  Finding release for channel stable
      +[INFO]  Using v1.32.6+k3s1 as release
      +.
      +.
      +.
      +[INFO]  systemd: Starting k3s
       

      Note: The --node-ip and --advertise-address flags are important to ensure that the embedded etcd cluster communicates over the WireGuard interface (192.168.2.x) rather than the LAN interface (192.168.1.x). This ensures that all control plane traffic is encrypted via WireGuard.
      @@ -190,20 +185,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r1 ~]# curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s_token) \
      -        sh -s - server --server https://r0.wg0.wan.buetow.org:6443 \
      -        --node-ip=192.168.2.121 \
      -        --advertise-address=192.168.2.121 \
      -        --tls-san=r1.wg0.wan.buetow.org
      +
      [root@r1 ~]# curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s_token) \
      +        sh -s - server --server https://r0.wg0.wan.buetow.org:6443 \
      +        --node-ip=192.168.2.121 \
      +        --advertise-address=192.168.2.121 \
      +        --tls-san=r1.wg0.wan.buetow.org
       
      -[root@r2 ~]# curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s_token) \
      -        sh -s - server --server https://r0.wg0.wan.buetow.org:6443 \
      -        --node-ip=192.168.2.122 \
      -        --advertise-address=192.168.2.122 \
      -        --tls-san=r2.wg0.wan.buetow.org
      -.
      -.
      -.
      +[root@r2 ~]# curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s_token) \
      +        sh -s - server --server https://r0.wg0.wan.buetow.org:6443 \
      +        --node-ip=192.168.2.122 \
      +        --advertise-address=192.168.2.122 \
      +        --tls-san=r2.wg0.wan.buetow.org
      +.
      +.
      +.
       
       

      @@ -213,23 +208,23 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# kubectl get nodes
      -NAME                STATUS   ROLES                       AGE     VERSION
      -r0.lan.buetow.org   Ready    control-plane,etcd,master   4m44s   v1.32.6+k3s1
      -r1.lan.buetow.org   Ready    control-plane,etcd,master   3m13s   v1.32.6+k3s1
      -r2.lan.buetow.org   Ready    control-plane,etcd,master   30s     v1.32.6+k3s1
      +
      [root@r0 ~]# kubectl get nodes
      +NAME                STATUS   ROLES                       AGE     VERSION
      +r0.lan.buetow.org   Ready    control-plane,etcd,master   4m44s   v1.32.6+k3s1
      +r1.lan.buetow.org   Ready    control-plane,etcd,master   3m13s   v1.32.6+k3s1
      +r2.lan.buetow.org   Ready    control-plane,etcd,master   30s     v1.32.6+k3s1
       
      -[root@r0 ~]# kubectl get pods --all-namespaces
      -NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
      -kube-system   coredns-5688667fd4-fs2jj                  1/1     Running     0          5m27s
      -kube-system   helm-install-traefik-crd-f9hgd            0/1     Completed   0          5m27s
      -kube-system   helm-install-traefik-zqqqk                0/1     Completed   2          5m27s
      -kube-system   local-path-provisioner-774c6665dc-jqlnc   1/1     Running     0          5m27s
      -kube-system   metrics-server-6f4c6675d5-5xpmp           1/1     Running     0          5m27s
      -kube-system   svclb-traefik-411cec5b-cdp2l              2/2     Running     0          78s
      -kube-system   svclb-traefik-411cec5b-f625r              2/2     Running     0          4m58s
      -kube-system   svclb-traefik-411cec5b-twrd7              2/2     Running     0          4m2s
      -kube-system   traefik-c98fdf6fb-lt6fx                   1/1     Running     0          4m58s
      +[root@r0 ~]# kubectl get pods --all-namespaces
      +NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
      +kube-system   coredns-5688667fd4-fs2jj                  1/1     Running     0          5m27s
      +kube-system   helm-install-traefik-crd-f9hgd            0/1     Completed   0          5m27s
      +kube-system   helm-install-traefik-zqqqk                0/1     Completed   2          5m27s
      +kube-system   local-path-provisioner-774c6665dc-jqlnc   1/1     Running     0          5m27s
      +kube-system   metrics-server-6f4c6675d5-5xpmp           1/1     Running     0          5m27s
      +kube-system   svclb-traefik-411cec5b-cdp2l              2/2     Running     0          78s
      +kube-system   svclb-traefik-411cec5b-f625r              2/2     Running     0          4m58s
      +kube-system   svclb-traefik-411cec5b-twrd7              2/2     Running     0          4m2s
      +kube-system   traefik-c98fdf6fb-lt6fx                   1/1     Running     0          4m58s
       

      In order to connect with kubectl from my Fedora laptop, I had to copy /etc/rancher/k3s/k3s.yaml from r0 to ~/.kube/config and then replace the value of the server field with r0.lan.buetow.org. kubectl can now manage the cluster. Note that this step has to be repeated when I want to connect to another node of the cluster (e.g. when r0 is down).
      @@ -244,19 +239,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl create namespace test
      -namespace/test created
      +
      > ~ kubectl create namespace test
      +namespace/test created
       
      -> ~ kubectl get namespaces
      -NAME              STATUS   AGE
      -default           Active   6h11m
      -kube-node-lease   Active   6h11m
      -kube-public       Active   6h11m
      -kube-system       Active   6h11m
      -test              Active   5s
      +> ~ kubectl get namespaces
      +NAME              STATUS   AGE
      +default           Active   6h11m
      +kube-node-lease   Active   6h11m
      +kube-public       Active   6h11m
      +kube-system       Active   6h11m
      +test              Active   5s
       
      -> ~ kubectl config set-context --current --namespace=test
      -Context "default" modified.
      +> ~ kubectl config set-context --current --namespace=test
      +Context "default" modified.
       

      And let's also create an Apache test pod:
      @@ -265,42 +260,42 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ cat <<END > apache-deployment.yaml
      -# Apache HTTP Server Deployment
      -apiVersion: apps/v1
      -kind: Deployment
      -metadata:
      -  name: apache-deployment
      -spec:
      -  replicas: 1
      -  selector:
      -    matchLabels:
      -      app: apache
      -  template:
      -    metadata:
      -      labels:
      -        app: apache
      -    spec:
      -      containers:
      -      - name: apache
      -        image: httpd:latest
      -        ports:
      -        # Container port where Apache listens
      -        - containerPort: 80
      -END
      +
      > ~ cat <<END > apache-deployment.yaml
      +# Apache HTTP Server Deployment
      +apiVersion: apps/v1
      +kind: Deployment
      +metadata:
      +  name: apache-deployment
      +spec:
      +  replicas: 1
      +  selector:
      +    matchLabels:
      +      app: apache
      +  template:
      +    metadata:
      +      labels:
      +        app: apache
      +    spec:
      +      containers:
      +      - name: apache
      +        image: httpd:latest
      +        ports:
      +        # Container port where Apache listens
      +        - containerPort: 80
      +END
       
      -> ~ kubectl apply -f apache-deployment.yaml
      -deployment.apps/apache-deployment created
      +> ~ kubectl apply -f apache-deployment.yaml
      +deployment.apps/apache-deployment created
       
      -> ~ kubectl get all
      -NAME                                     READY   STATUS    RESTARTS   AGE
      -pod/apache-deployment-5fd955856f-4pjmf   1/1     Running   0          7s
      +> ~ kubectl get all
      +NAME                                     READY   STATUS    RESTARTS   AGE
      +pod/apache-deployment-5fd955856f-4pjmf   1/1     Running   0          7s
       
      -NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
      -deployment.apps/apache-deployment   1/1     1            1           7s
      +NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
      +deployment.apps/apache-deployment   1/1     1            1           7s
       
      -NAME                                           DESIRED   CURRENT   READY   AGE
      -replicaset.apps/apache-deployment-5fd955856f   1         1         1       7s
      +NAME                                           DESIRED   CURRENT   READY   AGE
      +replicaset.apps/apache-deployment-5fd955856f   1         1         1       7s
       

      Let's also create a service:
      @@ -309,31 +304,31 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ cat <<END > apache-service.yaml
      -apiVersion: v1
      -kind: Service
      -metadata:
      -  labels:
      -    app: apache
      -  name: apache-service
      -spec:
      -  ports:
      -    - name: web
      -      port: 80
      -      protocol: TCP
      -      # Expose port 80 on the service
      -      targetPort: 80
      -  selector:
      -  # Link this service to pods with the label app=apache
      -    app: apache
      -END
      +
      > ~ cat <<END > apache-service.yaml
      +apiVersion: v1
      +kind: Service
      +metadata:
      +  labels:
      +    app: apache
      +  name: apache-service
      +spec:
      +  ports:
      +    - name: web
      +      port: 80
      +      protocol: TCP
      +      # Expose port 80 on the service
      +      targetPort: 80
      +  selector:
      +  # Link this service to pods with the label app=apache
      +    app: apache
      +END
       
      -> ~ kubectl apply -f apache-service.yaml
      -service/apache-service created
      +> ~ kubectl apply -f apache-service.yaml
      +service/apache-service created
       
      -> ~ kubectl get service
      -NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
      -apache-service   ClusterIP   10.43.249.165   <none>        80/TCP    4s
      +> ~ kubectl get service
      +NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
      +apache-service   ClusterIP   10.43.249.165   <none>        80/TCP    4s
       

      Now let's create an ingress:
      @@ -344,72 +339,72 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ cat <<END > apache-ingress.yaml
      +
      > ~ cat <<END > apache-ingress.yaml
       
      -apiVersion: networking.k8s.io/v1
      -kind: Ingress
      -metadata:
      -  name: apache-ingress
      -  namespace: test
      -  annotations:
      -    spec.ingressClassName: traefik
      -    traefik.ingress.kubernetes.io/router.entrypoints: web
      -spec:
      -  rules:
      -    - host: f3s.foo.zone
      -      http:
      -        paths:
      -          - path: /
      -            pathType: Prefix
      -            backend:
      -              service:
      -                name: apache-service
      -                port:
      -                  number: 80
      -    - host: standby.f3s.foo.zone
      -      http:
      -        paths:
      -          - path: /
      -            pathType: Prefix
      -            backend:
      -              service:
      -                name: apache-service
      -                port:
      -                  number: 80
      -    - host: www.f3s.foo.zone
      -      http:
      -        paths:
      -          - path: /
      -            pathType: Prefix
      -            backend:
      -              service:
      -                name: apache-service
      -                port:
      -                  number: 80
      -END
      +apiVersion: networking.k8s.io/v1
      +kind: Ingress
      +metadata:
      +  name: apache-ingress
      +  namespace: test
      +  annotations:
      +    spec.ingressClassName: traefik
      +    traefik.ingress.kubernetes.io/router.entrypoints: web
      +spec:
      +  rules:
      +    - host: f3s.foo.zone
      +      http:
      +        paths:
      +          - path: /
      +            pathType: Prefix
      +            backend:
      +              service:
      +                name: apache-service
      +                port:
      +                  number: 80
      +    - host: standby.f3s.foo.zone
      +      http:
      +        paths:
      +          - path: /
      +            pathType: Prefix
      +            backend:
      +              service:
      +                name: apache-service
      +                port:
      +                  number: 80
      +    - host: www.f3s.foo.zone
      +      http:
      +        paths:
      +          - path: /
      +            pathType: Prefix
      +            backend:
      +              service:
      +                name: apache-service
      +                port:
      +                  number: 80
      +END
       
      -> ~ kubectl apply -f apache-ingress.yaml
      -ingress.networking.k8s.io/apache-ingress created
      +> ~ kubectl apply -f apache-ingress.yaml
      +ingress.networking.k8s.io/apache-ingress created
       
      -> ~ kubectl describe ingress
      -Name:             apache-ingress
      -Labels:           <none>
      -Namespace:        test
      -Address:          192.168.2.120,192.168.2.121,192.168.2.122
      -Ingress Class:    traefik
      -Default backend:  <default>
      -Rules:
      -  Host                    Path  Backends
      -  ----                    ----  --------
      -  f3s.foo.zone
      -                          /   apache-service:80 (10.42.1.11:80)
      -  standby.f3s.foo.zone
      -                          /   apache-service:80 (10.42.1.11:80)
      -  www.f3s.foo.zone
      -                          /   apache-service:80 (10.42.1.11:80)
      -Annotations:              spec.ingressClassName: traefik
      -                          traefik.ingress.kubernetes.io/router.entrypoints: web
      -Events:                   <none>
      +> ~ kubectl describe ingress
      +Name:             apache-ingress
      +Labels:           <none>
      +Namespace:        test
      +Address:          192.168.2.120,192.168.2.121,192.168.2.122
      +Ingress Class:    traefik
      +Default backend:  <default>
      +Rules:
      +  Host                    Path  Backends
      +  ----                    ----  --------
      +  f3s.foo.zone
      +                          /   apache-service:80 (10.42.1.11:80)
      +  standby.f3s.foo.zone
      +                          /   apache-service:80 (10.42.1.11:80)
      +  www.f3s.foo.zone
      +                          /   apache-service:80 (10.42.1.11:80)
      +Annotations:              spec.ingressClassName: traefik
      +                          traefik.ingress.kubernetes.io/router.entrypoints: web
      +Events:                   <none>
       

      Notes:
      @@ -423,8 +418,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ curl -H "Host: www.f3s.foo.zone" http://r0.lan.buetow.org:80
      -<html><body><h1>It works!</h1></body></html>
      +
      > ~ curl -H "Host: www.f3s.foo.zone" http://r0.lan.buetow.org:80
      +<html><body><h1>It works!</h1></body></html>
       

      Test deployment with persistent volume claim


      @@ -435,142 +430,142 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ cat <<END > apache-deployment.yaml
      -# Apache HTTP Server Deployment
      -apiVersion: apps/v1
      -kind: Deployment
      -metadata:
      -  name: apache-deployment
      -  namespace: test
      -spec:
      -  replicas: 2
      -  selector:
      -    matchLabels:
      -      app: apache
      -  template:
      -    metadata:
      -      labels:
      -        app: apache
      -    spec:
      -      containers:
      -      - name: apache
      -        image: httpd:latest
      -        ports:
      -        # Container port where Apache listens
      -        - containerPort: 80
      -        readinessProbe:
      -          httpGet:
      -            path: /
      -            port: 80
      -          initialDelaySeconds: 5
      -          periodSeconds: 10
      -        livenessProbe:
      -          httpGet:
      -            path: /
      -            port: 80
      -          initialDelaySeconds: 15
      -          periodSeconds: 10
      -        volumeMounts:
      -        - name: apache-htdocs
      -          mountPath: /usr/local/apache2/htdocs/
      -      volumes:
      -      - name: apache-htdocs
      -        persistentVolumeClaim:
      -          claimName: example-apache-pvc
      -END
      +
      > ~ cat <<END > apache-deployment.yaml
      +# Apache HTTP Server Deployment
      +apiVersion: apps/v1
      +kind: Deployment
      +metadata:
      +  name: apache-deployment
      +  namespace: test
      +spec:
      +  replicas: 2
      +  selector:
      +    matchLabels:
      +      app: apache
      +  template:
      +    metadata:
      +      labels:
      +        app: apache
      +    spec:
      +      containers:
      +      - name: apache
      +        image: httpd:latest
      +        ports:
      +        # Container port where Apache listens
      +        - containerPort: 80
      +        readinessProbe:
      +          httpGet:
      +            path: /
      +            port: 80
      +          initialDelaySeconds: 5
      +          periodSeconds: 10
      +        livenessProbe:
      +          httpGet:
      +            path: /
      +            port: 80
      +          initialDelaySeconds: 15
      +          periodSeconds: 10
      +        volumeMounts:
      +        - name: apache-htdocs
      +          mountPath: /usr/local/apache2/htdocs/
      +      volumes:
      +      - name: apache-htdocs
      +        persistentVolumeClaim:
      +          claimName: example-apache-pvc
      +END
       
      -> ~ cat <<END > apache-ingress.yaml
      -apiVersion: networking.k8s.io/v1
      -kind: Ingress
      -metadata:
      -  name: apache-ingress
      -  namespace: test
      -  annotations:
      -    spec.ingressClassName: traefik
      -    traefik.ingress.kubernetes.io/router.entrypoints: web
      -spec:
      -  rules:
      -    - host: f3s.foo.zone
      -      http:
      -        paths:
      -          - path: /
      -            pathType: Prefix
      -            backend:
      -              service:
      -                name: apache-service
      -                port:
      -                  number: 80
      -    - host: standby.f3s.foo.zone
      -      http:
      -        paths:
      -          - path: /
      -            pathType: Prefix
      -            backend:
      -              service:
      -                name: apache-service
      -                port:
      -                  number: 80
      -    - host: www.f3s.foo.zone
      -      http:
      -        paths:
      -          - path: /
      -            pathType: Prefix
      -            backend:
      -              service:
      -                name: apache-service
      -                port:
      -                  number: 80
      -END
      +> ~ cat <<END > apache-ingress.yaml
      +apiVersion: networking.k8s.io/v1
      +kind: Ingress
      +metadata:
      +  name: apache-ingress
      +  namespace: test
      +  annotations:
      +    spec.ingressClassName: traefik
      +    traefik.ingress.kubernetes.io/router.entrypoints: web
      +spec:
      +  rules:
      +    - host: f3s.foo.zone
      +      http:
      +        paths:
      +          - path: /
      +            pathType: Prefix
      +            backend:
      +              service:
      +                name: apache-service
      +                port:
      +                  number: 80
      +    - host: standby.f3s.foo.zone
      +      http:
      +        paths:
      +          - path: /
      +            pathType: Prefix
      +            backend:
      +              service:
      +                name: apache-service
      +                port:
      +                  number: 80
      +    - host: www.f3s.foo.zone
      +      http:
      +        paths:
      +          - path: /
      +            pathType: Prefix
      +            backend:
      +              service:
      +                name: apache-service
      +                port:
      +                  number: 80
      +END
       
      -> ~ cat <<END > apache-persistent-volume.yaml
      -apiVersion: v1
      -kind: PersistentVolume
      -metadata:
      -  name: example-apache-pv
      -spec:
      -  capacity:
      -    storage: 1Gi
      -  volumeMode: Filesystem
      -  accessModes:
      -    - ReadWriteOnce
      -  persistentVolumeReclaimPolicy: Retain
      -  hostPath:
      -    path: /data/nfs/k3svolumes/example-apache-volume-claim
      -    type: Directory
      ----
      -apiVersion: v1
      -kind: PersistentVolumeClaim
      -metadata:
      -  name: example-apache-pvc
      -  namespace: test
      -spec:
      -  storageClassName: ""
      -  accessModes:
      -    - ReadWriteOnce
      -  resources:
      -    requests:
      -      storage: 1Gi
      -END
      +> ~ cat <<END > apache-persistent-volume.yaml
      +apiVersion: v1
      +kind: PersistentVolume
      +metadata:
      +  name: example-apache-pv
      +spec:
      +  capacity:
      +    storage: 1Gi
      +  volumeMode: Filesystem
      +  accessModes:
      +    - ReadWriteOnce
      +  persistentVolumeReclaimPolicy: Retain
      +  hostPath:
      +    path: /data/nfs/k3svolumes/example-apache-volume-claim
      +    type: Directory
      +---
      +apiVersion: v1
      +kind: PersistentVolumeClaim
      +metadata:
      +  name: example-apache-pvc
      +  namespace: test
      +spec:
      +  storageClassName: ""
      +  accessModes:
      +    - ReadWriteOnce
      +  resources:
      +    requests:
      +      storage: 1Gi
      +END
       
      -> ~ cat <<END > apache-service.yaml
      -apiVersion: v1
      -kind: Service
      -metadata:
      -  labels:
      -    app: apache
      -  name: apache-service
      -  namespace: test
      -spec:
      -  ports:
      -    - name: web
      -      port: 80
      -      protocol: TCP
      -      # Expose port 80 on the service
      -      targetPort: 80
      -  selector:
      -  # Link this service to pods with the label app=apache
      -    app: apache
      -END
      +> ~ cat <<END > apache-service.yaml
      +apiVersion: v1
      +kind: Service
      +metadata:
      +  labels:
      +    app: apache
      +  name: apache-service
      +  namespace: test
      +spec:
      +  ports:
      +    - name: web
      +      port: 80
      +      protocol: TCP
      +      # Expose port 80 on the service
      +      targetPort: 80
      +  selector:
      +  # Link this service to pods with the label app=apache
      +    app: apache
      +END
       

      I applied the manifests:
      @@ -579,10 +574,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl apply -f apache-persistent-volume.yaml
      -> ~ kubectl apply -f apache-service.yaml
      -> ~ kubectl apply -f apache-deployment.yaml
      -> ~ kubectl apply -f apache-ingress.yaml
      +
      > ~ kubectl apply -f apache-persistent-volume.yaml
      +> ~ kubectl apply -f apache-service.yaml
      +> ~ kubectl apply -f apache-deployment.yaml
      +> ~ kubectl apply -f apache-ingress.yaml
       

      Looking at the deployment, I could see it failed because the directory didn't exist yet on the NFS share (note that I also increased the replica count to 2 so if one node goes down there's already a replica running on another node for faster failover):
      @@ -591,20 +586,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl get pods
      -NAME                                 READY   STATUS              RESTARTS   AGE
      -apache-deployment-5b96bd6b6b-fv2jx   0/1     ContainerCreating   0          9m15s
      -apache-deployment-5b96bd6b6b-ax2ji   0/1     ContainerCreating   0          9m15s
      +
      > ~ kubectl get pods
      +NAME                                 READY   STATUS              RESTARTS   AGE
      +apache-deployment-5b96bd6b6b-fv2jx   0/1     ContainerCreating   0          9m15s
      +apache-deployment-5b96bd6b6b-ax2ji   0/1     ContainerCreating   0          9m15s
       
      -> ~ kubectl describe pod apache-deployment-5b96bd6b6b-fv2jx | tail -n 5
      -Events:
      -  Type     Reason       Age                   From               Message
      -  ----     ------       ----                  ----               -------
      -  Normal   Scheduled    9m34s                 default-scheduler  Successfully
      -    assigned test/apache-deployment-5b96bd6b6b-fv2jx to r2.lan.buetow.org
      -  Warning  FailedMount  80s (x12 over 9m34s)  kubelet            MountVolume.SetUp
      -    failed for volume "example-apache-pv" : hostPath type check failed:
      -    /data/nfs/k3svolumes/example-apache is not a directory
      +> ~ kubectl describe pod apache-deployment-5b96bd6b6b-fv2jx | tail -n 5
      +Events:
      +  Type     Reason       Age                   From               Message
      +  ----     ------       ----                  ----               -------
      +  Normal   Scheduled    9m34s                 default-scheduler  Successfully
      +    assigned test/apache-deployment-5b96bd6b6b-fv2jx to r2.lan.buetow.org
      +  Warning  FailedMount  80s (x12 over 9m34s)  kubelet            MountVolume.SetUp
      +    failed for volume "example-apache-pv" : hostPath type check failed:
      +    /data/nfs/k3svolumes/example-apache is not a directory
       

      That's intentional—I needed to create the directory on the NFS share first, so I did that (e.g. on r0):
      @@ -613,20 +608,20 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# mkdir /data/nfs/k3svolumes/example-apache-volume-claim/
      +
      [root@r0 ~]# mkdir /data/nfs/k3svolumes/example-apache-volume-claim/
       
      -[root@r0 ~]# cat <<END > /data/nfs/k3svolumes/example-apache-volume-claim/index.html
      -<!DOCTYPE html>
      -<html>
      -<head>
      -  <title>Hello, it works</title>
      -</head>
      -<body>
      -  <h1>Hello, it works!</h1>
      -  <p>This site is served via a PVC!</p>
      -</body>
      -</html>
      -END
      +[root@r0 ~]# cat <<END > /data/nfs/k3svolumes/example-apache-volume-claim/index.html
      +<!DOCTYPE html>
      +<html>
      +<head>
      +  <title>Hello, it works</title>
      +</head>
      +<body>
      +  <h1>Hello, it works!</h1>
      +  <p>This site is served via a PVC!</p>
      +</body>
      +</html>
      +END
       

      The index.html file gives us some actual content to serve. After deleting the pod, it recreates itself and the volume mounts correctly:
      @@ -635,19 +630,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl delete pod apache-deployment-5b96bd6b6b-fv2jx
      +
      > ~ kubectl delete pod apache-deployment-5b96bd6b6b-fv2jx
       
      -> ~ curl -H "Host: www.f3s.foo.zone" http://r0.lan.buetow.org:80
      -<!DOCTYPE html>
      -<html>
      -<head>
      -  <title>Hello, it works</title>
      -</head>
      -<body>
      -  <h1>Hello, it works!</h1>
      -  <p>This site is served via a PVC!</p>
      -</body>
      -</html>
      +> ~ curl -H "Host: www.f3s.foo.zone" http://r0.lan.buetow.org:80
      +<!DOCTYPE html>
      +<html>
      +<head>
      +  <title>Hello, it works</title>
      +</head>
      +<body>
      +  <h1>Hello, it works!</h1>
      +  <p>This site is served via a PVC!</p>
      +</body>
      +</html>
       

      Scaling Traefik for faster failover


      @@ -658,7 +653,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl -n kube-system scale deployment traefik --replicas=2
      +
      > ~ kubectl -n kube-system scale deployment traefik --replicas=2
       

      And the result:
      @@ -667,9 +662,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl -n kube-system get pods -l app.kubernetes.io/name=traefik
      -kube-system   traefik-c98fdf6fb-97kqk   1/1   Running   19 (53d ago)   64d
      -kube-system   traefik-c98fdf6fb-9npg2   1/1   Running   11 (53d ago)   61d
      +
      > ~ kubectl -n kube-system get pods -l app.kubernetes.io/name=traefik
      +kube-system   traefik-c98fdf6fb-97kqk   1/1   Running   19 (53d ago)   64d
      +kube-system   traefik-c98fdf6fb-9npg2   1/1   Running   11 (53d ago)   61d
       

      Make it accessible from the public internet


      @@ -688,14 +683,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ curl https://f3s.foo.zone
      -<html><body><h1>It works!</h1></body></html>
      +
      > ~ curl https://f3s.foo.zone
      +<html><body><h1>It works!</h1></body></html>
       
      -> ~ curl https://www.f3s.foo.zone
      -<html><body><h1>It works!</h1></body></html>
      +> ~ curl https://www.f3s.foo.zone
      +<html><body><h1>It works!</h1></body></html>
       
      -> ~ curl https://standby.f3s.foo.zone
      -<html><body><h1>It works!</h1></body></html>
      +> ~ curl https://standby.f3s.foo.zone
      +<html><body><h1>It works!</h1></body></html>
       

      This is how it works in relayd.conf on OpenBSD:
      @@ -869,32 +864,32 @@ server "anki.f3s.foo.zone" { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      <!DOCTYPE html>
      -<html>
      -<head>
      -    <title>Server turned off</title>
      -    <style>
      -        body {
      -            font-family: sans-serif;
      -            text-align: center;
      -            padding-top: 50px;
      -        }
      -        .container {
      -            max-width: 600px;
      -            margin: 0 auto;
      -        }
      -    </style>
      -</head>
      -<body>
      -    <div class="container">
      -        <h1>Server turned off</h1>
      -        <p>The servers are all currently turned off.</p>
      -        <p>Please try again later.</p>
      -        <p>Or email <a href="mailto:paul@nospam.buetow.org">paul@nospam.buetow.org</a>
      -           - so I can turn them back on for you!</p>
      -    </div>
      -</body>
      -</html>
      +
      <!DOCTYPE html>
      +<html>
      +<head>
      +    <title>Server turned off</title>
      +    <style>
      +        body {
      +            font-family: sans-serif;
      +            text-align: center;
      +            padding-top: 50px;
      +        }
      +        .container {
      +            max-width: 600px;
      +            margin: 0 auto;
      +        }
      +    </style>
      +</head>
      +<body>
      +    <div class="container">
      +        <h1>Server turned off</h1>
      +        <p>The servers are all currently turned off.</p>
      +        <p>Please try again later.</p>
      +        <p>Or email <a href="mailto:paul@nospam.buetow.org">paul@nospam.buetow.org</a>
      +           - so I can turn them back on for you!</p>
      +    </div>
      +</body>
      +</html>
       

      This approach provides several benefits:
      @@ -953,17 +948,17 @@ LAN → FreeBSD CARP VIP (192.168.1.138) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd conf/f3s/cert-manager
      -$ just install
      -kubectl apply -f cert-manager.yaml
      -# ... cert-manager CRDs and resources created ...
      -kubectl apply -f self-signed-issuer.yaml
      -clusterissuer.cert-manager.io/selfsigned-issuer created
      -clusterissuer.cert-manager.io/selfsigned-ca-issuer created
      -kubectl apply -f ca-certificate.yaml
      -certificate.cert-manager.io/selfsigned-ca created
      -kubectl apply -f wildcard-certificate.yaml
      -certificate.cert-manager.io/f3s-lan-wildcard created
      +
      $ cd conf/f3s/cert-manager
      +$ just install
      +kubectl apply -f cert-manager.yaml
      +# ... cert-manager CRDs and resources created ...
      +kubectl apply -f self-signed-issuer.yaml
      +clusterissuer.cert-manager.io/selfsigned-issuer created
      +clusterissuer.cert-manager.io/selfsigned-ca-issuer created
      +kubectl apply -f ca-certificate.yaml
      +certificate.cert-manager.io/selfsigned-ca created
      +kubectl apply -f wildcard-certificate.yaml
      +certificate.cert-manager.io/f3s-lan-wildcard created
       

      This creates:
      @@ -980,10 +975,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get certificate -n cert-manager
      -NAME               READY   SECRET                 AGE
      -f3s-lan-wildcard   True    f3s-lan-tls            5m
      -selfsigned-ca      True    selfsigned-ca-secret   5m
      +
      $ kubectl get certificate -n cert-manager
      +NAME               READY   SECRET                 AGE
      +f3s-lan-wildcard   True    f3s-lan-tls            5m
      +selfsigned-ca      True    selfsigned-ca-secret   5m
       

      The wildcard certificate (f3s-lan-tls) needs to be copied to any namespace that uses it:
      @@ -992,9 +987,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get secret f3s-lan-tls -n cert-manager -o yaml | \
      -    sed 's/namespace: cert-manager/namespace: services/' | \
      -    kubectl apply -f -
      +
      $ kubectl get secret f3s-lan-tls -n cert-manager -o yaml | \
      +    sed 's/namespace: cert-manager/namespace: services/' | \
      +    kubectl apply -f -
       

      Configuring FreeBSD relayd for LAN access


      @@ -1005,7 +1000,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas pkg install -y relayd
      +
      paul@f0:~ % doas pkg install -y relayd
       

      Create /usr/local/etc/relayd.conf:
      @@ -1041,10 +1036,10 @@ pass out quick by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas sysrc pf_enable=YES pflog_enable=YES relayd_enable=YES
      -paul@f0:~ % doas service pf start
      -paul@f0:~ % doas service pflog start
      -paul@f0:~ % doas service relayd start
      +
      paul@f0:~ % doas sysrc pf_enable=YES pflog_enable=YES relayd_enable=YES
      +paul@f0:~ % doas service pf start
      +paul@f0:~ % doas service pflog start
      +paul@f0:~ % doas service relayd start
       

      Verify relayd is listening on the CARP VIP:
      @@ -1053,9 +1048,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas sockstat -4 -l | grep 192.168.1.138
      -_relayd  relayd   2903  11  tcp4   192.168.1.138:80      *:*
      -_relayd  relayd   2903  12  tcp4   192.168.1.138:443     *:*
      +
      paul@f0:~ % doas sockstat -4 -l | grep 192.168.1.138
      +_relayd  relayd   2903  11  tcp4   192.168.1.138:80      *:*
      +_relayd  relayd   2903  12  tcp4   192.168.1.138:443     *:*
       

      Repeat the same configuration on f1. Both hosts will run relayd listening on the CARP VIP, but only the CARP MASTER will respond to traffic. When failover occurs, the new MASTER takes over seamlessly.
      @@ -1107,12 +1102,12 @@ spec: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl apply -f ingress-lan.yaml
      -ingress.networking.k8s.io/ingress-lan created
      +
      $ kubectl apply -f ingress-lan.yaml
      +ingress.networking.k8s.io/ingress-lan created
       
      -$ curl -k https://f3s.lan.foo.zone
      -HTTP/2 302 
      -location: /app/
      +$ curl -k https://f3s.lan.foo.zone
      +HTTP/2 302 
      +location: /app/
       

      Client-side DNS and CA setup


      @@ -1125,10 +1120,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ sudo tee -a /etc/hosts << 'EOF'
      -# f3s LAN services
      -192.168.1.138  f3s.lan.foo.zone
      -EOF
      +
      $ sudo tee -a /etc/hosts << 'EOF'
      +# f3s LAN services
      +192.168.1.138  f3s.lan.foo.zone
      +EOF
       

      The CARP VIP 192.168.1.138 provides high availability—traffic automatically fails over to the backup host if the master goes down.
      @@ -1139,8 +1134,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get secret selfsigned-ca-secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | \
      -    base64 -d > f3s-lan-ca.crt
      +
      $ kubectl get secret selfsigned-ca-secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | \
      +    base64 -d > f3s-lan-ca.crt
       

      Install the CA certificate on Linux (Fedora/Rocky):
      @@ -1149,8 +1144,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ sudo cp f3s-lan-ca.crt /etc/pki/ca-trust/source/anchors/
      -$ sudo update-ca-trust
      +
      $ sudo cp f3s-lan-ca.crt /etc/pki/ca-trust/source/anchors/
      +$ sudo update-ca-trust
       

      After trusting the CA, browsers will accept the LAN certificates without warnings.
      @@ -1199,7 +1194,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# mkdir -p /data/nfs/k3svolumes/registry
      +
      [root@r0 ~]# mkdir -p /data/nfs/k3svolumes/registry
       

      Install (or upgrade) the chart


      @@ -1210,9 +1205,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ git clone https://codeberg.org/snonux/conf/f3s.git
      -$ cd conf/f3s/examples/conf/f3s/registry
      -$ helm upgrade --install registry ./helm-chart --namespace infra --create-namespace
      +
      $ git clone https://codeberg.org/snonux/conf/f3s.git
      +$ cd conf/f3s/examples/conf/f3s/registry
      +$ helm upgrade --install registry ./helm-chart --namespace infra --create-namespace
       

      Helm creates the infra namespace if it does not exist, provisions a PersistentVolume/PersistentVolumeClaim pair that points at /data/nfs/k3svolumes/registry, and spins up a single registry pod exposed via the docker-registry-service NodePort (30001). Verify everything is up before continuing:
      @@ -1221,13 +1216,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get pods --namespace infra
      -NAME                               READY   STATUS    RESTARTS      AGE
      -docker-registry-6bc9bb46bb-6grkr   1/1     Running   6 (53d ago)   54d
      +
      $ kubectl get pods --namespace infra
      +NAME                               READY   STATUS    RESTARTS      AGE
      +docker-registry-6bc9bb46bb-6grkr   1/1     Running   6 (53d ago)   54d
       
      -$ kubectl get svc docker-registry-service -n infra
      -NAME                      TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
      -docker-registry-service   NodePort   10.43.141.56   <none>        5000:30001/TCP   54d
      +$ kubectl get svc docker-registry-service -n infra
      +NAME                      TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
      +docker-registry-service   NodePort   10.43.141.56   <none>        5000:30001/TCP   54d
       

      Allow nodes and workstations to trust the registry


      @@ -1245,16 +1240,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cat <<"EOF" | sudo tee /etc/docker/daemon.json >/dev/null
      -{
      -  "insecure-registries": [
      -    "r0.lan.buetow.org:30001",
      -    "r1.lan.buetow.org:30001",
      -    "r2.lan.buetow.org:30001"
      -  ]
      -}
      -EOF
      -$ sudo systemctl restart docker
      +
      $ cat <<"EOF" | sudo tee /etc/docker/daemon.json >/dev/null
      +{
      +  "insecure-registries": [
      +    "r0.lan.buetow.org:30001",
      +    "r1.lan.buetow.org:30001",
      +    "r2.lan.buetow.org:30001"
      +  ]
      +}
      +EOF
      +$ sudo systemctl restart docker
       

      On each k3s node, make registry.lan.buetow.org resolve locally and point k3s at the NodePort:
      @@ -1263,19 +1258,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ for node in r0 r1 r2; do
      ->   ssh root@$node "echo '127.0.0.1 registry.lan.buetow.org' >> /etc/hosts"
      -> done
      +
      $ for node in r0 r1 r2; do
      +>   ssh root@$node "echo '127.0.0.1 registry.lan.buetow.org' >> /etc/hosts"
      +> done
       
      -$ for node in r0 r1 r2; do
      -> ssh root@$node "cat <<'EOF' > /etc/rancher/k3s/registries.yaml
      -mirrors:
      -  "registry.lan.buetow.org:30001":
      -    endpoint:
      -      - "http://localhost:30001"
      -EOF
      -systemctl restart k3s"
      -> done
      +$ for node in r0 r1 r2; do
      +> ssh root@$node "cat <<'EOF' > /etc/rancher/k3s/registries.yaml
      +mirrors:
      +  "registry.lan.buetow.org:30001":
      +    endpoint:
      +      - "http://localhost:30001"
      +EOF
      +systemctl restart k3s"
      +> done
       

      Thanks to the relayd configuration earlier in the post, the external hostnames (f3s.foo.zone, etc.) can already reach NodePort 30001, so publishing the registry later to the outside world is just a matter of wiring the DNS the same way as the ingress hosts. But by default, that's not enabled for now due to security reasons.
      @@ -1288,8 +1283,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ docker tag my-app:latest r0.lan.buetow.org:30001/my-app:latest
      -$ docker push r0.lan.buetow.org:30001/my-app:latest
      +
      $ docker tag my-app:latest r0.lan.buetow.org:30001/my-app:latest
      +$ docker push r0.lan.buetow.org:30001/my-app:latest
       

      Inside the cluster (or from other nodes), reference the image via the service name that Helm created:
      @@ -1304,9 +1299,9 @@ image: docker-registry-service:5000/my-app:latest by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl run registry-test \
      ->   --image=docker-registry-service:5000/my-app:latest \
      ->   --restart=Never -n test --command -- sleep 300
      +
      $ kubectl run registry-test \
      +>   --image=docker-registry-service:5000/my-app:latest \
      +>   --restart=Never -n test --command -- sleep 300
       

      If the pod pulls successfully, the private registry is ready for use by the rest of the workloads. Note, that the commands above actually don't work, they are only for illustration purpose mentioned here.
      @@ -1323,11 +1318,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd conf/f3s/examples/conf/f3s/anki-sync-server/docker-image
      -$ docker build -t anki-sync-server:25.07.5b --build-arg ANKI_VERSION=25.07.5 .
      -$ docker tag anki-sync-server:25.07.5b \
      -    r0.lan.buetow.org:30001/anki-sync-server:25.07.5b
      -$ docker push r0.lan.buetow.org:30001/anki-sync-server:25.07.5b
      +
      $ cd conf/f3s/examples/conf/f3s/anki-sync-server/docker-image
      +$ docker build -t anki-sync-server:25.07.5b --build-arg ANKI_VERSION=25.07.5 .
      +$ docker tag anki-sync-server:25.07.5b \
      +    r0.lan.buetow.org:30001/anki-sync-server:25.07.5b
      +$ docker push r0.lan.buetow.org:30001/anki-sync-server:25.07.5b
       

      Because every k3s node treats registry.lan.buetow.org:30001 as an insecure mirror (see above), the push succeeds regardless of which node answers. If you prefer the shortcut, just f3s in that directory performs the same build/tag/push sequence.
      @@ -1340,11 +1335,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ ssh root@r0 "mkdir -p /data/nfs/k3svolumes/anki-sync-server/anki_data"
      -$ kubectl create namespace services
      -$ kubectl create secret generic anki-sync-server-secret \
      -    --from-literal=SYNC_USER1='paul:SECRETPASSWORD' \
      -    -n services
      +
      $ ssh root@r0 "mkdir -p /data/nfs/k3svolumes/anki-sync-server/anki_data"
      +$ kubectl create namespace services
      +$ kubectl create secret generic anki-sync-server-secret \
      +    --from-literal=SYNC_USER1='paul:SECRETPASSWORD' \
      +    -n services
       

      If the services namespace already exists, you can skip that line or let Kubernetes tell you the namespace is unchanged.
      @@ -1357,8 +1352,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd ../helm-chart
      -$ helm upgrade --install anki-sync-server . -n services
      +
      $ cd ../helm-chart
      +$ helm upgrade --install anki-sync-server . -n services
       

      Helm provisions everything referenced in the templates:
      @@ -1377,9 +1372,9 @@ containers: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get pods -n services
      -$ kubectl get ingress anki-sync-server-ingress -n services
      -$ curl https://anki.f3s.foo.zone/health
      +
      $ kubectl get pods -n services
      +$ kubectl get ingress anki-sync-server-ingress -n services
      +$ curl https://anki.f3s.foo.zone/health
       

      All of this runs solely on first-party images that now live in the private registry, proving the full flow from local bild to WireGuard-exposed service.
      @@ -1394,14 +1389,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      > ~ kubectl exec -n services deploy/miniflux-postgres -- id postgres
      -uid=999(postgres) gid=999(postgres) groups=999(postgres)
      +
      > ~ kubectl exec -n services deploy/miniflux-postgres -- id postgres
      +uid=999(postgres) gid=999(postgres) groups=999(postgres)
       
      -[root@r0 ~]# id postgres
      -uid=999(postgres) gid=999(postgres) groups=999(postgres)
      +[root@r0 ~]# id postgres
      +uid=999(postgres) gid=999(postgres) groups=999(postgres)
       
      -paul@f0:~ % doas id postgres
      -uid=999(postgres) gid=99(postgres) groups=999(postgres)
      +paul@f0:~ % doas id postgres
      +uid=999(postgres) gid=99(postgres) groups=999(postgres)
       

      The Rocky Linux workers get their matching user with plain useradd/groupadd (repeat on r0, r1, and r2):
      @@ -1410,10 +1405,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# groupadd --gid 999 postgres
      -[root@r0 ~]# useradd --uid 999 --gid 999 \
      -                --home-dir /var/lib/pgsql \
      -                --shell /sbin/nologin postgres
      +
      [root@r0 ~]# groupadd --gid 999 postgres
      +[root@r0 ~]# useradd --uid 999 --gid 999 \
      +                --home-dir /var/lib/pgsql \
      +                --shell /sbin/nologin postgres
       

      FreeBSD uses pw, so on each NFS server (f0, f1, f2) I created the same account and disabled shell access:
      @@ -1422,9 +1417,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas pw groupadd postgres -g 999
      -paul@f0:~ % doas pw useradd postgres -u 999 -g postgres \
      -                -d /var/db/postgres -s /usr/sbin/nologin
      +
      paul@f0:~ % doas pw groupadd postgres -g 999
      +paul@f0:~ % doas pw useradd postgres -u 999 -g postgres \
      +                -d /var/db/postgres -s /usr/sbin/nologin
       

      Once the UID/GID exist everywhere, the Miniflux chart in examples/conf/f3s/miniflux deploys cleanly. The chart provisions both the application and its bundled Postgres database, mounts the exported directory, and builds the DSN at runtime. The important bits live in helm-chart/templates/persistent-volumes.yaml and deployment.yaml:
      @@ -1449,13 +1444,13 @@ containers: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd examples/conf/f3s/miniflux/helm-chart
      -$ mkdir -p /data/nfs/k3svolumes/miniflux/data
      -$ kubectl create secret generic miniflux-db-password \
      -    --from-literal=fluxdb_password='YOUR_PASSWORD' -n services
      -$ kubectl create secret generic miniflux-admin-password \
      -    --from-literal=admin_password='YOUR_ADMIN_PASSWORD' -n services
      -$ helm upgrade --install miniflux . -n services --create-namespace
      +
      $ cd examples/conf/f3s/miniflux/helm-chart
      +$ mkdir -p /data/nfs/k3svolumes/miniflux/data
      +$ kubectl create secret generic miniflux-db-password \
      +    --from-literal=fluxdb_password='YOUR_PASSWORD' -n services
      +$ kubectl create secret generic miniflux-admin-password \
      +    --from-literal=admin_password='YOUR_ADMIN_PASSWORD' -n services
      +$ helm upgrade --install miniflux . -n services --create-namespace
       

      And to verify it's all up:
      @@ -1515,11 +1510,11 @@ replicaset.apps/miniflux-server-85d7c64664 1 1 1 54d
      Back to the main site
      diff --git a/gemfeed/2025-10-11-key-takeaways-from-the-well-grounded-rubyist.html b/gemfeed/2025-10-11-key-takeaways-from-the-well-grounded-rubyist.html index 58c71922..1d521fd6 100644 --- a/gemfeed/2025-10-11-key-takeaways-from-the-well-grounded-rubyist.html +++ b/gemfeed/2025-10-11-key-takeaways-from-the-well-grounded-rubyist.html @@ -2,17 +2,12 @@ - Key Takeaways from The Well-Grounded Rubyist -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -59,19 +54,19 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      # At the top level, self is the main object
      -p self
      -# => main
      -p self.class
      -# => Object
      +
      # At the top level, self is the main object
      +p self
      +# => main
      +p self.class
      +# => Object
       
      -def foo
      -  # Inside a method, self is the object that received the call
      -  p self
      -end
      +def foo
      +  # Inside a method, self is the object that received the call
      +  p self
      +end
       
      -foo
      -# => main
      +foo
      +# => main
       

      This code demonstrates how self changes depending on the context. At the top level, it's main, an instance of Object. When foo is called without a receiver, it's called on main.
      @@ -84,17 +79,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      obj = "a string"
      +
      obj = "a string"
       
      -def obj.shout
      -  self.upcase + "!"
      -end
      +def obj.shout
      +  self.upcase + "!"
      +end
       
      -p obj.shout
      -# => "A STRING!"
      +p obj.shout
      +# => "A STRING!"
       
      -obj2 = "another string"
      -# obj2.shout would raise a NoMethodError
      +obj2 = "another string"
      +# obj2.shout would raise a NoMethodError
       

      Here, the shout method is only available on the obj object. This is a powerful feature for adding behavior to specific instances.
      @@ -107,15 +102,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      MyClass = Class.new do
      -  def say_hello
      -    puts "Hello from a dynamically created class!"
      -  end
      -end
      +
      MyClass = Class.new do
      +  def say_hello
      +    puts "Hello from a dynamically created class!"
      +  end
      +end
       
      -instance = MyClass.new
      -instance.say_hello
      -# => Hello from a dynamically created class!
      +instance = MyClass.new
      +instance.say_hello
      +# => Hello from a dynamically created class!
       

      This shows how to create a new class and assign it to a constant. This is what happens behind the scenes when you use the class keyword.
      @@ -132,27 +127,27 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      # For ranges, it checks for inclusion
      -p (1..5) === 3 # => true
      +
      # For ranges, it checks for inclusion
      +p (1..5) === 3 # => true
       
      -# For classes, it checks if the object is an instance of the class
      -p String === "hello" # => true
      +# For classes, it checks if the object is an instance of the class
      +p String === "hello" # => true
       
      -# For regexes, it checks for a match
      -p /llo/ === "hello" # => true
      +# For regexes, it checks for a match
      +p /llo/ === "hello" # => true
       
      -def check(value)
      -  case value
      -  when String
      -    "It's a string"
      -  when (1..10)
      -    "It's a number between 1 and 10"
      -  else
      -    "Something else"
      -  end
      -end
      +def check(value)
      +  case value
      +  when String
      +    "It's a string"
      +  when (1..10)
      +    "It's a number between 1 and 10"
      +  else
      +    "Something else"
      +  end
      +end
       
      -p check(5) # => "It's a number between 1 and 10"
      +p check(5) # => "It's a number between 1 and 10"
       

      Blocks and yield


      @@ -163,18 +158,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      def my_iterator
      -  puts "Entering the method"
      -  yield
      -  puts "Back in the method"
      -  yield
      -end
      +
      def my_iterator
      +  puts "Entering the method"
      +  yield
      +  puts "Back in the method"
      +  yield
      +end
       
      -my_iterator { puts "Inside the block" }
      -# Entering the method
      -# Inside the block
      -# Back in the method
      -# Inside the block
      +my_iterator { puts "Inside the block" }
      +# Entering the method
      +# Inside the block
      +# Back in the method
      +# Inside the block
       

      This simple iterator shows how yield transfers control to the block. You can also pass arguments to yield and get a return value from the block.
      @@ -183,13 +178,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      def with_return
      -  result = yield(5)
      -  puts "The block returned #{result}"
      -end
      +
      def with_return
      +  result = yield(5)
      +  puts "The block returned #{result}"
      +end
       
      -with_return { |n| n * 2 }
      -# => The block returned 10
      +with_return { |n| n * 2 }
      +# => The block returned 10
       

      This demonstrates passing an argument to the block and using its return value.
      @@ -206,17 +201,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      # Two strings with the same content are different objects
      -p "foo".object_id
      -p "foo".object_id
      +
      # Two strings with the same content are different objects
      +p "foo".object_id
      +p "foo".object_id
       
      -# Two symbols with the same content are the same object
      -p :foo.object_id
      -p :foo.object_id
      +# Two symbols with the same content are the same object
      +p :foo.object_id
      +p :foo.object_id
       
      -# Modern hash syntax uses symbols as keys
      -my_hash = { name: "Paul", language: "Ruby" }
      -p my_hash[:name] # => "Paul"
      +# Modern hash syntax uses symbols as keys
      +my_hash = { name: "Paul", language: "Ruby" }
      +p my_hash[:name] # => "Paul"
       

      This code highlights the difference between strings and symbols and shows the convenient hash syntax.
      @@ -229,13 +224,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      # Array of strings
      -p %w[one two three]
      -# => ["one", "two", "three"]
      +
      # Array of strings
      +p %w[one two three]
      +# => ["one", "two", "three"]
       
      -# Array of symbols
      -p %i[one two three]
      -# => [:one, :two, :three]
      +# Array of symbols
      +p %i[one two three]
      +# => [:one, :two, :three]
       

      A quick way to create arrays. You can also retrieve multiple values at once.
      @@ -244,13 +239,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      arr = [10, 20, 30, 40, 50]
      -p arr.values_at(0, 2, 4)
      -# => [10, 30, 50]
      +
      arr = [10, 20, 30, 40, 50]
      +p arr.values_at(0, 2, 4)
      +# => [10, 30, 50]
       
      -hash = { a: 1, b: 2, c: 3 }
      -p hash.values_at(:a, :c)
      -# => [1, 3]
      +hash = { a: 1, b: 2, c: 3 }
      +p hash.values_at(:a, :c)
      +# => [1, 3]
       

      The values_at method is a concise way to get multiple elements.
      @@ -269,11 +264,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/2025-11-02-perl-new-features-and-foostats.html b/gemfeed/2025-11-02-perl-new-features-and-foostats.html index 6cffc902..7e3ab9c9 100644 --- a/gemfeed/2025-11-02-perl-new-features-and-foostats.html +++ b/gemfeed/2025-11-02-perl-new-features-and-foostats.html @@ -2,17 +2,12 @@ - Perl New Features and Foostats -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -146,8 +141,8 @@ animation of sorts. by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      fishfinger$ grep foostats /etc/daily.local
      -perl /usr/local/bin/foostats.pl --parse-logs --replicate --report
      +
      fishfinger$ grep foostats /etc/daily.local
      +perl /usr/local/bin/foostats.pl --parse-logs --replicate --report
       

      Internally, Foostats::Logreader parses each line of the log files /var/log/daemon* and /var/www/logs/access_log*, turns timestamps into YYYYMMDD/HHMMSS values, hashes IP addresses with SHA3 (for anonymization), and hands a normalized event to Foostats::Filter. The filter compares the URI against entries in fooodds.txt, tracks how many times an IP address requests within the exact second, and drops anything suspicious (e.g., from web crawlers or malicious attackers). Valid events reach Foostats::Aggregator, which counts requests per protocol, records unique visitors for the Gemtext and Atom feeds, and remembers page-level IP sets. Foostats::FileOutputter writes the result as gzipped JSON files—one per day and per protocol—with IPv4/IPv6 splits, filtered counters, feed readership, and hashes for long URLs.
      @@ -205,17 +200,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      package foo;
      +
      package foo;
       
      -sub hello {
      -    print "Hello from package foo\n";
      -}
      +sub hello {
      +    print "Hello from package foo\n";
      +}
       
      -package bar;
      +package bar;
       
      -sub hello {
      -    print "Hello from package bar\n";
      -}
      +sub hello {
      +    print "Hello from package bar\n";
      +}
       

      But now it is also possible to do this:
      @@ -224,17 +219,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      package foo {
      -    sub hello {
      -        print "Hello from package foo\n";
      -    }
      -}
      +
      package foo {
      +    sub hello {
      +        print "Hello from package foo\n";
      +    }
      +}
       
      -package bar {
      -    sub hello {
      -        print "Hello from package bar\n";
      -    }
      -}
      +package bar {
      +    sub hello {
      +        print "Hello from package bar\n";
      +    }
      +}
       

      Postfix dereferencing keeps data structures tidy


      @@ -249,9 +244,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      for my $elem (@{$array_ref}) {
      -    print "$elem\n";
      -}
      +
      for my $elem (@{$array_ref}) {
      +    print "$elem\n";
      +}
       

      one can now do:
      @@ -260,9 +255,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      for my $elem ($array_ref->@*) {
      -    print "$elem\n";
      -}
      +
      for my $elem ($array_ref->@*) {
      +    print "$elem\n";
      +}
       

      You see that this feature becomes increasingly useful with nested data structures, e.g. to print all keys of the nested hash:
      @@ -271,7 +266,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      print for keys $hash->{stats}->%*;
      +
      print for keys $hash->{stats}->%*;
       

      Loops over like $stats->{page_ips}->{urls}->%* or $merge{$key}->{$_}->%* show which level of the structure is in play. The merger in Foostats updates host and URL statistics without building temporary arrays, and the reporter code mirrors the layout of the final tables. Before postfix dereferencing, the same code relied on braces within braces and was harder to read.
      @@ -284,10 +279,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      use v5.38;
      +
      use v5.38;
       
      -print "Hello, world!\n";    # old way
      -say "Hello, world!";        # new way
      +print "Hello, world!\n";    # old way
      +say "Hello, world!";        # new way
       

      Lexical subs promote local reasoning


      @@ -300,18 +295,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      use v5.38;
      +
      use v5.38;
       
      -sub process_lines (@lines) {
      -    my sub trim ($str) {
      -        $str =~ s/^\s+|\s+$//gr;
      -    }
      -    return [ map { trim($_) } @lines ];
      -}
      +sub process_lines (@lines) {
      +    my sub trim ($str) {
      +        $str =~ s/^\s+|\s+$//gr;
      +    }
      +    return [ map { trim($_) } @lines ];
      +}
       
      -my @raw = ("  foo  ", " bar", "baz ");
      -my $cleaned = process_lines(@raw);
      -say for @$cleaned; # prints "foo", "bar", "baz"
      +my @raw = ("  foo  ", " bar", "baz ");
      +my $cleaned = process_lines(@raw);
      +say for @$cleaned; # prints "foo", "bar", "baz"
       

      Reference aliasing makes intent explicit


      @@ -322,13 +317,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      use feature qw(refaliasing);
      +
      use feature qw(refaliasing);
       
      -my $hash = { foo => 42 };
      -\my $foo = \$hash->{foo};
      +my $hash = { foo => 42 };
      +\my $foo = \$hash->{foo};
       
      -$foo = 99;
      -print $hash->{foo}; # prints 99
      +$foo = 99;
      +print $hash->{foo}; # prints 99
       

      The aggregator in Foostats aliases $self->{stats}{$date_key} before updating counters, so the structure remains intact. Combined with subroutine signatures, this makes it obvious when a piece of data is shared instead of copied, preventing silent bugs. This enables having shorter names for long nested data structures.
      @@ -343,15 +338,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      sub counter {
      -    state $count = 0;
      -    $count++;
      -    return $count;
      -}
      +
      sub counter {
      +    state $count = 0;
      +    $count++;
      +    return $count;
      +}
       
      -say counter(); # 1
      -say counter(); # 2
      -say counter(); # 3
      +say counter(); # 1
      +say counter(); # 2
      +say counter(); # 3
       

      Hash and array state variables have been supported since state arrived in Perl 5.10. Scalar state variables were already supported previously.
      @@ -372,16 +367,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      # Old way
      -sub greet_old { my $name = shift; print "Hello, $name!\n" }
      +
      # Old way
      +sub greet_old { my $name = shift; print "Hello, $name!\n" }
       
      -# Another old way
      -sub greet_old2 ($) { my $name = shift; print "Hello, $name!\n" }
      +# Another old way
      +sub greet_old2 ($) { my $name = shift; print "Hello, $name!\n" }
       
      -# New way
      -sub greet ($name) { say "Hello, $name!"; }
      +# New way
      +sub greet ($name) { say "Hello, $name!"; }
       
      -greet("Alice"); # prints "Hello, Alice!"
      +greet("Alice"); # prints "Hello, Alice!"
       

      In Foostats, constructors declare sub new ($class, $odds_file, $log_path), anonymous callbacks expose sub ($event), and helper subs list the values they expect, e.g.:
      @@ -390,11 +385,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      my $anon = sub ($name) {
      -    say "Hello, $name!";
      -};
      +
      my $anon = sub ($name) {
      +    say "Hello, $name!";
      +};
       
      -$anon->("World"); # prints "Hello, World!"
      +$anon->("World"); # prints "Hello, World!"
       

      Defined-or assignment for defaults without boilerplate


      @@ -405,12 +400,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      my $foo;
      -$foo //= 42;
      -say $foo; # prints 42
      +
      my $foo;
      +$foo //= 42;
      +say $foo; # prints 42
       
      -$foo //= 99;
      -say $foo; # still prints 42, because $foo was already defined
      +$foo //= 99;
      +say $foo; # still prints 42, because $foo was already defined
       

      Cleanup with defer


      @@ -423,17 +418,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      use feature qw(defer);
      +
      use feature qw(defer);
       
      -sub parse_log_file ($path) {
      -    open my $fh, '<', $path or die "Cannot open $path: $!";
      -    defer { close $fh };
      +sub parse_log_file ($path) {
      +    open my $fh, '<', $path or die "Cannot open $path: $!";
      +    defer { close $fh };
       
      -    while (my $line = <$fh>) {
      -        # ... parsing logic that might throw an exception ...
      -    }
      -    # $fh is automatically closed here
      -}
      +    while (my $line = <$fh>) {
      +        # ... parsing logic that might throw an exception ...
      +    }
      +    # $fh is automatically closed here
      +}
       

      This pattern replaces manual close calls in every exit path of the subroutine and is more robust than relying solely on object destructors.
      @@ -462,11 +457,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/2025-11-02-the-courage-to-be-disliked-book-notes.html b/gemfeed/2025-11-02-the-courage-to-be-disliked-book-notes.html index 59294cde..c09a659b 100644 --- a/gemfeed/2025-11-02-the-courage-to-be-disliked-book-notes.html +++ b/gemfeed/2025-11-02-the-courage-to-be-disliked-book-notes.html @@ -2,17 +2,12 @@ - 'The Courage To Be Disliked' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -153,11 +148,11 @@
      Back to the main site
      diff --git a/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.html b/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.html index 0cda1b53..f5280913 100644 --- a/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.html +++ b/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part 8: Observability -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -125,10 +120,10 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ git clone https://codeberg.org/snonux/conf.git
      -$ cd conf
      -$ git checkout 15a86f3  # Last commit before ArgoCD migration
      -$ cd f3s/prometheus/
      +
      $ git clone https://codeberg.org/snonux/conf.git
      +$ cd conf
      +$ git checkout 15a86f3  # Last commit before ArgoCD migration
      +$ cd f3s/prometheus/
       

      **Current master branch** contains the ArgoCD-managed versions with:
      @@ -165,8 +160,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl create namespace monitoring
      -namespace/monitoring created
      +
      $ kubectl create namespace monitoring
      +namespace/monitoring created
       

      Installing Prometheus and Grafana


      @@ -181,8 +176,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
      -$ helm repo update
      +
      $ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
      +$ helm repo update
       

      Create the directories on the NFS server for persistent storage:
      @@ -191,8 +186,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# mkdir -p /data/nfs/k3svolumes/prometheus/data
      -[root@r0 ~]# mkdir -p /data/nfs/k3svolumes/grafana/data
      +
      [root@r0 ~]# mkdir -p /data/nfs/k3svolumes/prometheus/data
      +[root@r0 ~]# mkdir -p /data/nfs/k3svolumes/grafana/data
       

      Deploying with the Justfile


      @@ -208,18 +203,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd conf/f3s/prometheus
      -$ just install
      -kubectl apply -f persistent-volumes.yaml
      -persistentvolume/prometheus-data-pv created
      -persistentvolume/grafana-data-pv created
      -persistentvolumeclaim/grafana-data-pvc created
      -helm install prometheus prometheus-community/kube-prometheus-stack \
      -    --namespace monitoring -f persistence-values.yaml
      -NAME: prometheus
      -LAST DEPLOYED: ...
      -NAMESPACE: monitoring
      -STATUS: deployed
      +
      $ cd conf/f3s/prometheus
      +$ just install
      +kubectl apply -f persistent-volumes.yaml
      +persistentvolume/prometheus-data-pv created
      +persistentvolume/grafana-data-pv created
      +persistentvolumeclaim/grafana-data-pvc created
      +helm install prometheus prometheus-community/kube-prometheus-stack \
      +    --namespace monitoring -f persistence-values.yaml
      +NAME: prometheus
      +LAST DEPLOYED: ...
      +NAMESPACE: monitoring
      +STATUS: deployed
       

      The persistence-values.yaml configures Prometheus and Grafana to use the NFS-backed persistent volumes I mentioned earlier, ensuring data survives pod restarts. It also enables scraping of etcd and kube-controller-manager metrics:
      @@ -258,12 +253,12 @@ kubeControllerManager: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# cat >> /etc/rancher/k3s/config.yaml << 'EOF'
      -kube-controller-manager-arg:
      -  - bind-address=0.0.0.0
      -etcd-expose-metrics: true
      -EOF
      -[root@r0 ~]# systemctl restart k3s
      +
      [root@r0 ~]# cat >> /etc/rancher/k3s/config.yaml << 'EOF'
      +kube-controller-manager-arg:
      +  - bind-address=0.0.0.0
      +etcd-expose-metrics: true
      +EOF
      +[root@r0 ~]# systemctl restart k3s
       

      Repeat for r1 and r2. After restarting all nodes, the controller-manager metrics endpoint will be accessible and etcd metrics are available on port 2381. Prometheus can now scrape both.
      @@ -274,8 +269,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# curl -s http://127.0.0.1:2381/metrics | grep etcd_server_has_leader
      -etcd_server_has_leader 1
      +
      [root@r0 ~]# curl -s http://127.0.0.1:2381/metrics | grep etcd_server_has_leader
      +etcd_server_has_leader 1
       

      The full persistence-values.yaml and all other Prometheus configuration files are available on Codeberg:
      @@ -296,9 +291,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get svc -n monitoring prometheus-kube-prometheus-prometheus
      -NAME                                    TYPE        CLUSTER-IP      PORT(S)
      -prometheus-kube-prometheus-prometheus   ClusterIP   10.43.152.163   9090/TCP,8080/TCP
      +
      $ kubectl get svc -n monitoring prometheus-kube-prometheus-prometheus
      +NAME                                    TYPE        CLUSTER-IP      PORT(S)
      +prometheus-kube-prometheus-prometheus   ClusterIP   10.43.152.163   9090/TCP,8080/TCP
       

      Grafana connects to Prometheus using the internal service URL http://prometheus-kube-prometheus-prometheus.monitoring.svc.cluster.local:9090. The default Grafana credentials are admin/prom-operator, which should be changed immediately after first login.
      @@ -323,7 +318,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      [root@r0 ~]# mkdir -p /data/nfs/k3svolumes/loki/data
      +
      [root@r0 ~]# mkdir -p /data/nfs/k3svolumes/loki/data
       

      Deploying Loki and Alloy


      @@ -338,24 +333,24 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd conf/f3s/loki
      -$ just install
      -helm repo add grafana https://grafana.github.io/helm-charts || true
      -helm repo update
      -kubectl apply -f persistent-volumes.yaml
      -persistentvolume/loki-data-pv created
      -persistentvolumeclaim/loki-data-pvc created
      -helm install loki grafana/loki --namespace monitoring -f values.yaml
      -NAME: loki
      -LAST DEPLOYED: ...
      -NAMESPACE: monitoring
      -STATUS: deployed
      -...
      -helm install alloy grafana/alloy --namespace monitoring -f alloy-values.yaml
      -NAME: alloy
      -LAST DEPLOYED: ...
      -NAMESPACE: monitoring
      -STATUS: deployed
      +
      $ cd conf/f3s/loki
      +$ just install
      +helm repo add grafana https://grafana.github.io/helm-charts || true
      +helm repo update
      +kubectl apply -f persistent-volumes.yaml
      +persistentvolume/loki-data-pv created
      +persistentvolumeclaim/loki-data-pvc created
      +helm install loki grafana/loki --namespace monitoring -f values.yaml
      +NAME: loki
      +LAST DEPLOYED: ...
      +NAMESPACE: monitoring
      +STATUS: deployed
      +...
      +helm install alloy grafana/alloy --namespace monitoring -f alloy-values.yaml
      +NAME: alloy
      +LAST DEPLOYED: ...
      +NAMESPACE: monitoring
      +STATUS: deployed
       

      Loki runs in single-binary mode with a single replica (loki-0), which is appropriate for a home lab cluster. This means there's only one Loki pod running at any time. If the node hosting Loki fails, Kubernetes will automatically reschedule the pod to another worker node—but there will be a brief downtime (typically under a minute) while this happens. For my home lab use case, this is perfectly acceptable.
      @@ -370,44 +365,44 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      discovery.kubernetes "pods" {
      -  role = "pod"
      -}
      +
      discovery.kubernetes "pods" {
      +  role = "pod"
      +}
       
      -discovery.relabel "pods" {
      -  targets = discovery.kubernetes.pods.targets
      +discovery.relabel "pods" {
      +  targets = discovery.kubernetes.pods.targets
       
      -  rule {
      -    source_labels = ["__meta_kubernetes_namespace"]
      -    target_label  = "namespace"
      -  }
      +  rule {
      +    source_labels = ["__meta_kubernetes_namespace"]
      +    target_label  = "namespace"
      +  }
       
      -  rule {
      -    source_labels = ["__meta_kubernetes_pod_name"]
      -    target_label  = "pod"
      -  }
      +  rule {
      +    source_labels = ["__meta_kubernetes_pod_name"]
      +    target_label  = "pod"
      +  }
       
      -  rule {
      -    source_labels = ["__meta_kubernetes_pod_container_name"]
      -    target_label  = "container"
      -  }
      +  rule {
      +    source_labels = ["__meta_kubernetes_pod_container_name"]
      +    target_label  = "container"
      +  }
       
      -  rule {
      -    source_labels = ["__meta_kubernetes_pod_label_app"]
      -    target_label  = "app"
      -  }
      -}
      +  rule {
      +    source_labels = ["__meta_kubernetes_pod_label_app"]
      +    target_label  = "app"
      +  }
      +}
       
      -loki.source.kubernetes "pods" {
      -  targets    = discovery.relabel.pods.output
      -  forward_to = [loki.write.default.receiver]
      -}
      +loki.source.kubernetes "pods" {
      +  targets    = discovery.relabel.pods.output
      +  forward_to = [loki.write.default.receiver]
      +}
       
      -loki.write "default" {
      -  endpoint {
      -    url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
      -  }
      -}
      +loki.write "default" {
      +  endpoint {
      +    url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
      +  }
      +}
       

      This configuration automatically labels each log line with the namespace, pod name, container name, and app label, making it easy to filter logs in Grafana.
      @@ -420,9 +415,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get svc -n monitoring loki
      -NAME   TYPE        CLUSTER-IP    PORT(S)
      -loki   ClusterIP   10.43.64.60   3100/TCP,9095/TCP
      +
      $ kubectl get svc -n monitoring loki
      +NAME   TYPE        CLUSTER-IP    PORT(S)
      +loki   ClusterIP   10.43.64.60   3100/TCP,9095/TCP
       

      To add Loki as a data source in Grafana:
      @@ -446,21 +441,21 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get pods -n monitoring
      -NAME                                                     READY   STATUS    RESTARTS   AGE
      -alertmanager-prometheus-kube-prometheus-alertmanager-0   2/2     Running   0          42d
      -alloy-g5fgj                                              2/2     Running   0          29m
      -alloy-nfw8w                                              2/2     Running   0          29m
      -alloy-tg9vj                                              2/2     Running   0          29m
      -loki-0                                                   2/2     Running   0          25m
      -prometheus-grafana-868f9dc7cf-lg2vl                      3/3     Running   0          42d
      -prometheus-kube-prometheus-operator-8d7bbc48c-p4sf4      1/1     Running   0          42d
      -prometheus-kube-state-metrics-7c5fb9d798-hh2fx           1/1     Running   0          42d
      -prometheus-prometheus-kube-prometheus-prometheus-0       2/2     Running   0          42d
      -prometheus-prometheus-node-exporter-2nsg9                1/1     Running   0          42d
      -prometheus-prometheus-node-exporter-mqr25                1/1     Running   0          42d
      -prometheus-prometheus-node-exporter-wp4ds                1/1     Running   0          42d
      -tempo-0                                                  1/1     Running   0          1d
      +
      $ kubectl get pods -n monitoring
      +NAME                                                     READY   STATUS    RESTARTS   AGE
      +alertmanager-prometheus-kube-prometheus-alertmanager-0   2/2     Running   0          42d
      +alloy-g5fgj                                              2/2     Running   0          29m
      +alloy-nfw8w                                              2/2     Running   0          29m
      +alloy-tg9vj                                              2/2     Running   0          29m
      +loki-0                                                   2/2     Running   0          25m
      +prometheus-grafana-868f9dc7cf-lg2vl                      3/3     Running   0          42d
      +prometheus-kube-prometheus-operator-8d7bbc48c-p4sf4      1/1     Running   0          42d
      +prometheus-kube-state-metrics-7c5fb9d798-hh2fx           1/1     Running   0          42d
      +prometheus-prometheus-kube-prometheus-prometheus-0       2/2     Running   0          42d
      +prometheus-prometheus-node-exporter-2nsg9                1/1     Running   0          42d
      +prometheus-prometheus-node-exporter-mqr25                1/1     Running   0          42d
      +prometheus-prometheus-node-exporter-wp4ds                1/1     Running   0          42d
      +tempo-0                                                  1/1     Running   0          1d
       

      Note: Tempo (tempo-0) is deployed later in this post in the "Distributed Tracing with Grafana Tempo" section. It is included in the pod listing here for completeness.
      @@ -471,19 +466,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get svc -n monitoring
      -NAME                                      TYPE        CLUSTER-IP      PORT(S)
      -alertmanager-operated                     ClusterIP   None            9093/TCP,9094/TCP
      -alloy                                     ClusterIP   10.43.74.14     12345/TCP
      -loki                                      ClusterIP   10.43.64.60     3100/TCP,9095/TCP
      -loki-headless                             ClusterIP   None            3100/TCP
      -prometheus-grafana                        ClusterIP   10.43.46.82     80/TCP
      -prometheus-kube-prometheus-alertmanager   ClusterIP   10.43.208.43    9093/TCP,8080/TCP
      -prometheus-kube-prometheus-operator       ClusterIP   10.43.246.121   443/TCP
      -prometheus-kube-prometheus-prometheus     ClusterIP   10.43.152.163   9090/TCP,8080/TCP
      -prometheus-kube-state-metrics             ClusterIP   10.43.64.26     8080/TCP
      -prometheus-prometheus-node-exporter       ClusterIP   10.43.127.242   9100/TCP
      -tempo                                     ClusterIP   10.43.91.44     3200/TCP,4317/TCP,4318/TCP
      +
      $ kubectl get svc -n monitoring
      +NAME                                      TYPE        CLUSTER-IP      PORT(S)
      +alertmanager-operated                     ClusterIP   None            9093/TCP,9094/TCP
      +alloy                                     ClusterIP   10.43.74.14     12345/TCP
      +loki                                      ClusterIP   10.43.64.60     3100/TCP,9095/TCP
      +loki-headless                             ClusterIP   None            3100/TCP
      +prometheus-grafana                        ClusterIP   10.43.46.82     80/TCP
      +prometheus-kube-prometheus-alertmanager   ClusterIP   10.43.208.43    9093/TCP,8080/TCP
      +prometheus-kube-prometheus-operator       ClusterIP   10.43.246.121   443/TCP
      +prometheus-kube-prometheus-prometheus     ClusterIP   10.43.152.163   9090/TCP,8080/TCP
      +prometheus-kube-state-metrics             ClusterIP   10.43.64.26     8080/TCP
      +prometheus-prometheus-node-exporter       ClusterIP   10.43.127.242   9100/TCP
      +tempo                                     ClusterIP   10.43.91.44     3200/TCP,4317/TCP,4318/TCP
       

      Let me break down what each pod does:
      @@ -560,7 +555,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas pkg install -y node_exporter
      +
      paul@f0:~ % doas pkg install -y node_exporter
       

      Enable the service to start at boot:
      @@ -569,8 +564,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas sysrc node_exporter_enable=YES
      -node_exporter_enable:  -> YES
      +
      paul@f0:~ % doas sysrc node_exporter_enable=YES
      +node_exporter_enable:  -> YES
       

      Configure node_exporter to listen on the WireGuard interface. This ensures metrics are only accessible through the secure tunnel, not the public network. Replace the IP with the host's WireGuard address:
      @@ -579,8 +574,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas sysrc node_exporter_args='--web.listen-address=192.168.2.130:9100'
      -node_exporter_args:  -> --web.listen-address=192.168.2.130:9100
      +
      paul@f0:~ % doas sysrc node_exporter_args='--web.listen-address=192.168.2.130:9100'
      +node_exporter_args:  -> --web.listen-address=192.168.2.130:9100
       

      Start the service:
      @@ -589,8 +584,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % doas service node_exporter start
      -Starting node_exporter.
      +
      paul@f0:~ % doas service node_exporter start
      +Starting node_exporter.
       

      Verify it's running:
      @@ -599,10 +594,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      paul@f0:~ % curl -s http://192.168.2.130:9100/metrics | head -3
      -# HELP go_gc_duration_seconds A summary of the wall-time pause...
      -# TYPE go_gc_duration_seconds summary
      -go_gc_duration_seconds{quantile="0"} 0
      +
      paul@f0:~ % curl -s http://192.168.2.130:9100/metrics | head -3
      +# HELP go_gc_duration_seconds A summary of the wall-time pause...
      +# TYPE go_gc_duration_seconds summary
      +go_gc_duration_seconds{quantile="0"} 0
       

      Repeat for the other FreeBSD hosts (f1, f2) with their respective WireGuard IPs.
      @@ -630,9 +625,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl create secret generic additional-scrape-configs \
      -    --from-file=additional-scrape-configs.yaml \
      -    -n monitoring
      +
      $ kubectl create secret generic additional-scrape-configs \
      +    --from-file=additional-scrape-configs.yaml \
      +    -n monitoring
       

      Update persistence-values.yaml to reference the secret:
      @@ -652,7 +647,7 @@ prometheus: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ just upgrade
      +
      $ just upgrade
       

      After a minute or so, the FreeBSD hosts appear in the Prometheus targets and in the Node Exporter dashboards in Grafana.
      @@ -1020,10 +1015,10 @@ zfs_pool_free_bytes{pool="zdata"} 3.48809678848e+11 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      blowfish:~ $ doas pkg_add node_exporter
      -quirks-7.103 signed on 2025-10-13T22:55:16Z
      -The following new rcscripts were installed: /etc/rc.d/node_exporter
      -See rcctl(8) for details.
      +
      blowfish:~ $ doas pkg_add node_exporter
      +quirks-7.103 signed on 2025-10-13T22:55:16Z
      +The following new rcscripts were installed: /etc/rc.d/node_exporter
      +See rcctl(8) for details.
       

      Enable the service to start at boot:
      @@ -1032,7 +1027,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      blowfish:~ $ doas rcctl enable node_exporter
      +
      blowfish:~ $ doas rcctl enable node_exporter
       

      Configure node_exporter to listen on the WireGuard interface. This ensures metrics are only accessible through the secure tunnel, not the public network. Replace the IP with the host's WireGuard address:
      @@ -1041,7 +1036,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      blowfish:~ $ doas rcctl set node_exporter flags '--web.listen-address=192.168.2.110:9100'
      +
      blowfish:~ $ doas rcctl set node_exporter flags '--web.listen-address=192.168.2.110:9100'
       

      Start the service:
      @@ -1050,8 +1045,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      blowfish:~ $ doas rcctl start node_exporter
      -node_exporter(ok)
      +
      blowfish:~ $ doas rcctl start node_exporter
      +node_exporter(ok)
       

      Verify it's running:
      @@ -1060,10 +1055,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      blowfish:~ $ curl -s http://192.168.2.110:9100/metrics | head -3
      -# HELP go_gc_duration_seconds A summary of the wall-time pause...
      -# TYPE go_gc_duration_seconds summary
      -go_gc_duration_seconds{quantile="0"} 0
      +
      blowfish:~ $ curl -s http://192.168.2.110:9100/metrics | head -3
      +# HELP go_gc_duration_seconds A summary of the wall-time pause...
      +# TYPE go_gc_duration_seconds summary
      +go_gc_duration_seconds{quantile="0"} 0
       

      Repeat for the other OpenBSD host (fishfinger) with its respective WireGuard IP (192.168.2.111).
      @@ -1425,35 +1420,35 @@ opentelemetry-instrumentation-requests==0.49b0 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      from opentelemetry import trace
      -from opentelemetry.sdk.trace import TracerProvider
      -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
      -from opentelemetry.instrumentation.flask import FlaskInstrumentor
      -from opentelemetry.instrumentation.requests import RequestsInstrumentor
      -from opentelemetry.sdk.resources import Resource
      +
      from opentelemetry import trace
      +from opentelemetry.sdk.trace import TracerProvider
      +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
      +from opentelemetry.instrumentation.flask import FlaskInstrumentor
      +from opentelemetry.instrumentation.requests import RequestsInstrumentor
      +from opentelemetry.sdk.resources import Resource
       
      -# Define service identity
      -resource = Resource(attributes={
      -    "service.name": "frontend",
      -    "service.namespace": "tracing-demo",
      -    "service.version": "1.0.0"
      -})
      +# Define service identity
      +resource = Resource(attributes={
      +    "service.name": "frontend",
      +    "service.namespace": "tracing-demo",
      +    "service.version": "1.0.0"
      +})
       
      -provider = TracerProvider(resource=resource)
      +provider = TracerProvider(resource=resource)
       
      -# Export to Alloy
      -otlp_exporter = OTLPSpanExporter(
      -    endpoint="http://alloy.monitoring.svc.cluster.local:4317",
      -    insecure=True
      -)
      +# Export to Alloy
      +otlp_exporter = OTLPSpanExporter(
      +    endpoint="http://alloy.monitoring.svc.cluster.local:4317",
      +    insecure=True
      +)
       
      -processor = BatchSpanProcessor(otlp_exporter)
      -provider.add_span_processor(processor)
      -trace.set_tracer_provider(provider)
      +processor = BatchSpanProcessor(otlp_exporter)
      +provider.add_span_processor(processor)
      +trace.set_tracer_provider(provider)
       
      -# Auto-instrument Flask and requests
      -FlaskInstrumentor().instrument_app(app)
      -RequestsInstrumentor().instrument()
      +# Auto-instrument Flask and requests
      +FlaskInstrumentor().instrument_app(app)
      +RequestsInstrumentor().instrument()
       

      The auto-instrumentation automatically:
      @@ -1630,29 +1625,29 @@ curl -H "Host: tracing-demo.f3s.buetow.org" http://r0/api/process by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      {
      -  "middleware_response": {
      -    "backend_data": {
      -      "data": {
      -        "id": 12345,
      -        "query_time_ms": 100.0,
      -        "timestamp": "2025-12-28T18:35:01.064538",
      -        "value": "Sample data from backend service"
      -      },
      -      "service": "backend"
      -    },
      -    "middleware_processed": true,
      -    "original_data": {
      -      "source": "GET request"
      -    },
      -    "transformation_time_ms": 50
      -  },
      -  "request_data": {
      -    "source": "GET request"
      -  },
      -  "service": "frontend",
      -  "status": "success"
      -}
      +
      {
      +  "middleware_response": {
      +    "backend_data": {
      +      "data": {
      +        "id": 12345,
      +        "query_time_ms": 100.0,
      +        "timestamp": "2025-12-28T18:35:01.064538",
      +        "value": "Sample data from backend service"
      +      },
      +      "service": "backend"
      +    },
      +    "middleware_processed": true,
      +    "original_data": {
      +      "source": "GET request"
      +    },
      +    "transformation_time_ms": 50
      +  },
      +  "request_data": {
      +    "source": "GET request"
      +  },
      +  "service": "frontend",
      +  "status": "success"
      +}
       

      **2. Find the trace in Tempo via API:**
      @@ -1671,12 +1666,12 @@ kubectl exec -n monitoring tempo-0 -- wget -qO- \ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      {
      -  "traceID": "4be1151c0bdcd5625ac7e02b98d95bd5",
      -  "rootServiceName": "frontend",
      -  "rootTraceName": "GET /api/process",
      -  "durationMs": 221
      -}
      +
      {
      +  "traceID": "4be1151c0bdcd5625ac7e02b98d95bd5",
      +  "rootServiceName": "frontend",
      +  "rootTraceName": "GET /api/process",
      +  "durationMs": 221
      +}
       

      **3. Fetch complete trace details:**
      @@ -1818,11 +1813,11 @@ kubectl exec -n monitoring <tempo-pod> -- df -h /var/tempo
      Back to the main site
      diff --git a/gemfeed/2025-12-24-x-rag-observability-hackathon.html b/gemfeed/2025-12-24-x-rag-observability-hackathon.html index a5446a34..d3c42ced 100644 --- a/gemfeed/2025-12-24-x-rag-observability-hackathon.html +++ b/gemfeed/2025-12-24-x-rag-observability-hackathon.html @@ -2,17 +2,12 @@ - X-RAG Observability Hackathon -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -264,20 +259,20 @@ loki.write "default" { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      from prometheus_client import Histogram, Counter, Gauge
      +
      from prometheus_client import Histogram, Counter, Gauge
       
      -search_duration = Histogram(
      -    "search_service_request_duration_seconds",
      -    "Total duration of Search Service requests",
      -    ["method"],
      -    buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 20.0, 30.0, 60.0],
      -)
      +search_duration = Histogram(
      +    "search_service_request_duration_seconds",
      +    "Total duration of Search Service requests",
      +    ["method"],
      +    buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 20.0, 30.0, 60.0],
      +)
       
      -errors_total = Counter(
      -    "search_service_errors_total",
      -    "Error count by type",
      -    ["method", "error_type"],
      -)
      +errors_total = Counter(
      +    "search_service_errors_total",
      +    "Error count by type",
      +    ["method", "error_type"],
      +)
       

      Initially, I used Prometheus scraping—each service exposed a /metrics endpoint, and Prometheus pulled metrics every 15 seconds. This worked, but I wanted a unified pipeline.
      @@ -520,17 +515,17 @@ traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
      -from opentelemetry.instrumentation.grpc import GrpcAioInstrumentorClient
      +
      from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
      +from opentelemetry.instrumentation.grpc import GrpcAioInstrumentorClient
       
      -# Auto-instrument frameworks
      -FastAPIInstrumentor.instrument_app(app)
      -GrpcAioInstrumentorClient().instrument()
      +# Auto-instrument frameworks
      +FastAPIInstrumentor.instrument_app(app)
      +GrpcAioInstrumentorClient().instrument()
       
      -# Manual spans for custom operations
      -with tracer.start_as_current_span("llm.rag_completion") as span:
      -    span.set_attribute("llm.model", model_name)
      -    result = await generate_answer(query, context)
      +# Manual spans for custom operations
      +with tracer.start_as_current_span("llm.rag_completion") as span:
      +    span.set_attribute("llm.model", model_name)
      +    result = await generate_answer(query, context)
       

      Auto-instrumentation is the quick win: one line of code and you get spans for every HTTP request, gRPC call, or database query. The instrumentor patches the framework at runtime, so existing code works without modification. The downside? You only get what the library authors decided to capture—generic HTTP attributes like http.method and http.status_code, but nothing domain-specific. Auto-instrumented spans also can't know your business logic, so a slow request shows up as "POST /api/search took 5 seconds" without revealing which internal operation caused the delay.
      @@ -920,11 +915,11 @@ $ curl -s -G "http://localhost:3200/api/search" \
      Back to the main site
      diff --git a/gemfeed/2026-01-01-cloudless-kobo-forma-with-koreader.html b/gemfeed/2026-01-01-cloudless-kobo-forma-with-koreader.html index 6a9878ca..d22c72d7 100644 --- a/gemfeed/2026-01-01-cloudless-kobo-forma-with-koreader.html +++ b/gemfeed/2026-01-01-cloudless-kobo-forma-with-koreader.html @@ -2,17 +2,12 @@ - Cloudless Kobo Forma with KOReader -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -157,11 +152,11 @@ SideloadedMode=true
      Back to the main site
      diff --git a/gemfeed/2026-01-01-posts-from-july-to-december-2025.html b/gemfeed/2026-01-01-posts-from-july-to-december-2025.html index 17de68f7..3478321f 100644 --- a/gemfeed/2026-01-01-posts-from-july-to-december-2025.html +++ b/gemfeed/2026-01-01-posts-from-july-to-december-2025.html @@ -2,17 +2,12 @@ - Posts from July to December 2025 -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -1049,11 +1044,11 @@
      Back to the main site
      diff --git a/gemfeed/2026-01-01-using-supernote-nomad-offline.html b/gemfeed/2026-01-01-using-supernote-nomad-offline.html index 96647714..0c1db515 100644 --- a/gemfeed/2026-01-01-using-supernote-nomad-offline.html +++ b/gemfeed/2026-01-01-using-supernote-nomad-offline.html @@ -2,17 +2,12 @@ - Using Supernote Nomad offline -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -84,32 +79,32 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      #!/usr/bin/env bash
      +
      #!/usr/bin/env bash
       
      -convert () {
      -  find . -name \*.note \
      -    | while read -r note; do
      -        echo supernote-tool convert -a -t pdf "$note" "${note/.note/.pdf}"
      -        supernote-tool convert -a -t pdf "$note" "${note/.note/.pdf}.tmp"
      -        mv "${note/.note/.pdf}.tmp" "${note/.note/.pdf}"
      -        du -hs "$note" "${note/.note/.pdf}"
      -        echo
      -      done
      -}
      +convert () {
      +  find . -name \*.note \
      +    | while read -r note; do
      +        echo supernote-tool convert -a -t pdf "$note" "${note/.note/.pdf}"
      +        supernote-tool convert -a -t pdf "$note" "${note/.note/.pdf}.tmp"
      +        mv "${note/.note/.pdf}.tmp" "${note/.note/.pdf}"
      +        du -hs "$note" "${note/.note/.pdf}"
      +        echo
      +      done
      +}
       
      -# Make the PDFs available on my Phone as well
      -copy () {
      -  if [ ! -d ~/Documents/Supernote ]; then
      -    echo "Directory ~/Documents/Supernote does not exist, skipping"
      -    exit 1
      -  fi
      +# Make the PDFs available on my Phone as well
      +copy () {
      +  if [ ! -d ~/Documents/Supernote ]; then
      +    echo "Directory ~/Documents/Supernote does not exist, skipping"
      +    exit 1
      +  fi
       
      -  rsync -delete -av --include='*/' --include='*.pdf' --exclude='*' . ~/Documents/Supernote/
      -  echo This was copied from $(pwd) so dont edit manually >~/Documents/Supernote/README.txt
      -}
      +  rsync -delete -av --include='*/' --include='*.pdf' --exclude='*' . ~/Documents/Supernote/
      +  echo This was copied from $(pwd) so dont edit manually >~/Documents/Supernote/README.txt
      +}
       
      -convert
      -copy
      +convert
      +copy
       

      This script does two things:
      @@ -154,11 +149,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html b/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html index 3a934362..42199650 100644 --- a/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html +++ b/gemfeed/2026-02-02-tmux-popup-editor-for-cursor-agent-prompts.html @@ -2,17 +2,12 @@ - A tmux popup editor for Cursor Agent CLI prompts -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -107,252 +102,252 @@ bind-key e run-shell -b "tmux display-message -p '#{pane_id}' by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      #!/usr/bin/env bash
      -set -u -o pipefail
      +
      #!/usr/bin/env bash
      +set -u -o pipefail
       
      -LOG_ENABLED=0
      -log_file="${TMPDIR:-/tmp}/tmux-edit-send.log"
      -log() {
      -  if [ "$LOG_ENABLED" -eq 1 ]; then
      -    printf '%s\n' "$*" >> "$log_file"
      -  fi
      -}
      +LOG_ENABLED=0
      +log_file="${TMPDIR:-/tmp}/tmux-edit-send.log"
      +log() {
      +  if [ "$LOG_ENABLED" -eq 1 ]; then
      +    printf '%s\n' "$*" >> "$log_file"
      +  fi
      +}
       
      -# Read the target pane id from a temp file created by tmux binding.
      -read_target_from_file() {
      -  local file_path="$1"
      -  local pane_id
      -  if [ -n "$file_path" ] && [ -f "$file_path" ]; then
      -    pane_id="$(sed -n '1p' "$file_path" | tr -d '[:space:]')"
      -    # Ensure pane ID has % prefix
      -    if [ -n "$pane_id" ] && [[ "$pane_id" != %* ]]; then
      -      pane_id="%${pane_id}"
      -    fi
      -    printf '%s' "$pane_id"
      -  fi
      -}
      +# Read the target pane id from a temp file created by tmux binding.
      +read_target_from_file() {
      +  local file_path="$1"
      +  local pane_id
      +  if [ -n "$file_path" ] && [ -f "$file_path" ]; then
      +    pane_id="$(sed -n '1p' "$file_path" | tr -d '[:space:]')"
      +    # Ensure pane ID has % prefix
      +    if [ -n "$pane_id" ] && [[ "$pane_id" != %* ]]; then
      +      pane_id="%${pane_id}"
      +    fi
      +    printf '%s' "$pane_id"
      +  fi
      +}
       
      -# Read the target pane id from tmux environment if present.
      -read_target_from_env() {
      -  local env_line pane_id
      -  env_line="$(tmux show-environment -g TMUX_EDIT_TARGET 2>/dev/null || true)"
      -  case "$env_line" in
      -    TMUX_EDIT_TARGET=*)
      -      pane_id="${env_line#TMUX_EDIT_TARGET=}"
      -      # Ensure pane ID has % prefix
      -      if [ -n "$pane_id" ] && [[ "$pane_id" != %* ]] && [[ "$pane_id" =~ ^[0-9]+$ ]]; then
      -        pane_id="%${pane_id}"
      -      fi
      -      printf '%s' "$pane_id"
      -      ;;
      -  esac
      -}
      +# Read the target pane id from tmux environment if present.
      +read_target_from_env() {
      +  local env_line pane_id
      +  env_line="$(tmux show-environment -g TMUX_EDIT_TARGET 2>/dev/null || true)"
      +  case "$env_line" in
      +    TMUX_EDIT_TARGET=*)
      +      pane_id="${env_line#TMUX_EDIT_TARGET=}"
      +      # Ensure pane ID has % prefix
      +      if [ -n "$pane_id" ] && [[ "$pane_id" != %* ]] && [[ "$pane_id" =~ ^[0-9]+$ ]]; then
      +        pane_id="%${pane_id}"
      +      fi
      +      printf '%s' "$pane_id"
      +      ;;
      +  esac
      +}
       
      -# Resolve the target pane id, falling back to the last pane.
      -resolve_target_pane() {
      -  local candidate="$1"
      -  local current_pane last_pane
      +# Resolve the target pane id, falling back to the last pane.
      +resolve_target_pane() {
      +  local candidate="$1"
      +  local current_pane last_pane
       
      -  current_pane="$(tmux display-message -p "#{pane_id}" 2>/dev/null || true)"
      -  log "current pane=${current_pane:-<empty>}"
      -  
      -  # Ensure candidate has % prefix if it's a pane ID
      -  if [ -n "$candidate" ] && [[ "$candidate" =~ ^[0-9]+$ ]]; then
      -    candidate="%${candidate}"
      -    log "normalized candidate to $candidate"
      -  fi
      -  
      -  if [ -n "$candidate" ] && [[ "$candidate" == *"#{"* ]]; then
      -    log "format target detected, clearing"
      -    candidate=""
      -  fi
      -  if [ -z "$candidate" ]; then
      -    candidate="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
      -    log "using last pane as fallback: $candidate"
      -  elif [ "$candidate" = "$current_pane" ]; then
      -    last_pane="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
      -    if [ -n "$last_pane" ]; then
      -      candidate="$last_pane"
      -      log "candidate was current, using last pane: $candidate"
      -    fi
      -  fi
      -  printf '%s' "$candidate"
      -}
      +  current_pane="$(tmux display-message -p "#{pane_id}" 2>/dev/null || true)"
      +  log "current pane=${current_pane:-<empty>}"
      +  
      +  # Ensure candidate has % prefix if it's a pane ID
      +  if [ -n "$candidate" ] && [[ "$candidate" =~ ^[0-9]+$ ]]; then
      +    candidate="%${candidate}"
      +    log "normalized candidate to $candidate"
      +  fi
      +  
      +  if [ -n "$candidate" ] && [[ "$candidate" == *"#{"* ]]; then
      +    log "format target detected, clearing"
      +    candidate=""
      +  fi
      +  if [ -z "$candidate" ]; then
      +    candidate="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
      +    log "using last pane as fallback: $candidate"
      +  elif [ "$candidate" = "$current_pane" ]; then
      +    last_pane="$(tmux display-message -p "#{last_pane}" 2>/dev/null || true)"
      +    if [ -n "$last_pane" ]; then
      +      candidate="$last_pane"
      +      log "candidate was current, using last pane: $candidate"
      +    fi
      +  fi
      +  printf '%s' "$candidate"
      +}
       
      -# Capture the latest multi-line prompt content from the pane.
      -capture_prompt_text() {
      -  local target="$1"
      -  tmux capture-pane -p -t "$target" -S -2000 2>/dev/null | awk '
      -    function trim_box(line) {
      -      sub(/^ *│ ?/, "", line)
      -      sub(/ *│ *$/, "", line)
      -      sub(/[[:space:]]+$/, "", line)
      -      return line
      -    }
      -    /^ *│ *→/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0 {
      -      if (text != "") last = text
      -      text = ""
      -      capture = 1
      -      line = $0
      -      sub(/^.*→ ?/, "", line)
      -      line = trim_box(line)
      -      if (line != "") text = line
      -      next
      -    }
      -    capture {
      -      if ($0 ~ /^ *└/) {
      -        capture = 0
      -        if (text != "") last = text
      -        next
      -      }
      -      if ($0 ~ /^ *│/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0) {
      -        line = trim_box($0)
      -        if (line != "") {
      -          if (text != "") text = text " " line
      -          else text = line
      -        }
      -      }
      -    }
      -    END {
      -      if (text != "") last = text
      -      if (last != "") print last
      -    }
      -  '
      -}
      +# Capture the latest multi-line prompt content from the pane.
      +capture_prompt_text() {
      +  local target="$1"
      +  tmux capture-pane -p -t "$target" -S -2000 2>/dev/null | awk '
      +    function trim_box(line) {
      +      sub(/^ *│ ?/, "", line)
      +      sub(/ *│ *$/, "", line)
      +      sub(/[[:space:]]+$/, "", line)
      +      return line
      +    }
      +    /^ *│ *→/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0 {
      +      if (text != "") last = text
      +      text = ""
      +      capture = 1
      +      line = $0
      +      sub(/^.*→ ?/, "", line)
      +      line = trim_box(line)
      +      if (line != "") text = line
      +      next
      +    }
      +    capture {
      +      if ($0 ~ /^ *└/) {
      +        capture = 0
      +        if (text != "") last = text
      +        next
      +      }
      +      if ($0 ~ /^ *│/ && index($0,"INSERT")==0 && index($0,"Add a follow-up")==0) {
      +        line = trim_box($0)
      +        if (line != "") {
      +          if (text != "") text = text " " line
      +          else text = line
      +        }
      +      }
      +    }
      +    END {
      +      if (text != "") last = text
      +      if (last != "") print last
      +    }
      +  '
      +}
       
      -# Write captured prompt text into the temp file if available.
      -prefill_tmpfile() {
      -  local tmpfile="$1"
      -  local prompt_text="$2"
      -  if [ -n "$prompt_text" ]; then
      -    printf '%s\n' "$prompt_text" > "$tmpfile"
      -  fi
      -}
      +# Write captured prompt text into the temp file if available.
      +prefill_tmpfile() {
      +  local tmpfile="$1"
      +  local prompt_text="$2"
      +  if [ -n "$prompt_text" ]; then
      +    printf '%s\n' "$prompt_text" > "$tmpfile"
      +  fi
      +}
       
      -# Ensure the target pane exists before sending keys.
      -validate_target_pane() {
      -  local target="$1"
      -  local pane target_found
      -  if [ -z "$target" ]; then
      -    log "error: no target pane determined"
      -    echo "Could not determine target pane." >&2
      -    return 1
      -  fi
      -  target_found=0
      -  log "validate: looking for target='$target' in all panes:"
      -  for pane in $(tmux list-panes -a -F "#{pane_id}" 2>/dev/null || true); do
      -    log "validate: checking pane='$pane'"
      -    if [ "$pane" = "$target" ]; then
      -      target_found=1
      -      log "validate: MATCH FOUND!"
      -      break
      -    fi
      -  done
      -  if [ "$target_found" -ne 1 ]; then
      -    log "error: target pane not found: $target"
      -    echo "Target pane not found: $target" >&2
      -    return 1
      -  fi
      -  log "validate: target pane validated successfully"
      -}
      +# Ensure the target pane exists before sending keys.
      +validate_target_pane() {
      +  local target="$1"
      +  local pane target_found
      +  if [ -z "$target" ]; then
      +    log "error: no target pane determined"
      +    echo "Could not determine target pane." >&2
      +    return 1
      +  fi
      +  target_found=0
      +  log "validate: looking for target='$target' in all panes:"
      +  for pane in $(tmux list-panes -a -F "#{pane_id}" 2>/dev/null || true); do
      +    log "validate: checking pane='$pane'"
      +    if [ "$pane" = "$target" ]; then
      +      target_found=1
      +      log "validate: MATCH FOUND!"
      +      break
      +    fi
      +  done
      +  if [ "$target_found" -ne 1 ]; then
      +    log "error: target pane not found: $target"
      +    echo "Target pane not found: $target" >&2
      +    return 1
      +  fi
      +  log "validate: target pane validated successfully"
      +}
       
      -# Send temp file contents to the target pane line by line.
      -send_content() {
      -  local target="$1"
      -  local tmpfile="$2"
      -  local prompt_text="$3"
      -  local first_line=1
      -  local line
      -  log "send_content: target=$target, prompt_text='$prompt_text'"
      -  while IFS= read -r line || [ -n "$line" ]; do
      -    log "send_content: read line='$line'"
      -    if [ "$first_line" -eq 1 ] && [ -n "$prompt_text" ]; then
      -      if [[ "$line" == "$prompt_text"* ]]; then
      -        local old_line="$line"
      -        line="${line#"$prompt_text"}"
      -        log "send_content: stripped prompt, was='$old_line' now='$line'"
      -      fi
      -    fi
      -    first_line=0
      -    log "send_content: sending line='$line'"
      -    tmux send-keys -t "$target" -l "$line"
      -    tmux send-keys -t "$target" Enter
      -  done < "$tmpfile"
      -  log "sent content to $target"
      -}
      +# Send temp file contents to the target pane line by line.
      +send_content() {
      +  local target="$1"
      +  local tmpfile="$2"
      +  local prompt_text="$3"
      +  local first_line=1
      +  local line
      +  log "send_content: target=$target, prompt_text='$prompt_text'"
      +  while IFS= read -r line || [ -n "$line" ]; do
      +    log "send_content: read line='$line'"
      +    if [ "$first_line" -eq 1 ] && [ -n "$prompt_text" ]; then
      +      if [[ "$line" == "$prompt_text"* ]]; then
      +        local old_line="$line"
      +        line="${line#"$prompt_text"}"
      +        log "send_content: stripped prompt, was='$old_line' now='$line'"
      +      fi
      +    fi
      +    first_line=0
      +    log "send_content: sending line='$line'"
      +    tmux send-keys -t "$target" -l "$line"
      +    tmux send-keys -t "$target" Enter
      +  done < "$tmpfile"
      +  log "sent content to $target"
      +}
       
      -# Main entry point.
      -main() {
      -  local target_file="${1:-}"
      -  local target
      -  local editor="${EDITOR:-vi}"
      -  local tmpfile
      -  local prompt_text
      +# Main entry point.
      +main() {
      +  local target_file="${1:-}"
      +  local target
      +  local editor="${EDITOR:-vi}"
      +  local tmpfile
      +  local prompt_text
       
      -  log "=== tmux-edit-send starting ==="
      -  log "target_file=$target_file"
      -  log "EDITOR=$editor"
      -  
      -  target="$(read_target_from_file "$target_file" || true)"
      -  if [ -n "$target" ]; then
      -    log "file target=${target:-<empty>}"
      -    rm -f "$target_file"
      -  fi
      -  if [ -z "$target" ]; then
      -    target="${TMUX_EDIT_TARGET:-}"
      -  fi
      -  log "env target=${target:-<empty>}"
      -  if [ -z "$target" ]; then
      -    target="$(read_target_from_env || true)"
      -  fi
      -  log "tmux env target=${target:-<empty>}"
      -  target="$(resolve_target_pane "$target")"
      -  log "fallback target=${target:-<empty>}"
      +  log "=== tmux-edit-send starting ==="
      +  log "target_file=$target_file"
      +  log "EDITOR=$editor"
      +  
      +  target="$(read_target_from_file "$target_file" || true)"
      +  if [ -n "$target" ]; then
      +    log "file target=${target:-<empty>}"
      +    rm -f "$target_file"
      +  fi
      +  if [ -z "$target" ]; then
      +    target="${TMUX_EDIT_TARGET:-}"
      +  fi
      +  log "env target=${target:-<empty>}"
      +  if [ -z "$target" ]; then
      +    target="$(read_target_from_env || true)"
      +  fi
      +  log "tmux env target=${target:-<empty>}"
      +  target="$(resolve_target_pane "$target")"
      +  log "fallback target=${target:-<empty>}"
       
      -  tmpfile="$(mktemp)"
      -  log "created tmpfile=$tmpfile"
      -  if [ ! -f "$tmpfile" ]; then
      -    log "ERROR: mktemp failed to create file"
      -    echo "ERROR: mktemp failed" >&2
      -    exit 1
      -  fi
      -  mv "$tmpfile" "${tmpfile}.md" 2>&1 | while read -r line; do log "mv output: $line"; done
      -  tmpfile="${tmpfile}.md"
      -  log "renamed to tmpfile=$tmpfile"
      -  if [ ! -f "$tmpfile" ]; then
      -    log "ERROR: tmpfile does not exist after rename"
      -    echo "ERROR: tmpfile rename failed" >&2
      -    exit 1
      -  fi
      -  trap 'rm -f "$tmpfile"' EXIT
      +  tmpfile="$(mktemp)"
      +  log "created tmpfile=$tmpfile"
      +  if [ ! -f "$tmpfile" ]; then
      +    log "ERROR: mktemp failed to create file"
      +    echo "ERROR: mktemp failed" >&2
      +    exit 1
      +  fi
      +  mv "$tmpfile" "${tmpfile}.md" 2>&1 | while read -r line; do log "mv output: $line"; done
      +  tmpfile="${tmpfile}.md"
      +  log "renamed to tmpfile=$tmpfile"
      +  if [ ! -f "$tmpfile" ]; then
      +    log "ERROR: tmpfile does not exist after rename"
      +    echo "ERROR: tmpfile rename failed" >&2
      +    exit 1
      +  fi
      +  trap 'rm -f "$tmpfile"' EXIT
       
      -  log "capturing prompt text from target=$target"
      -  prompt_text="$(capture_prompt_text "$target")"
      -  log "captured prompt_text='$prompt_text'"
      -  prefill_tmpfile "$tmpfile" "$prompt_text"
      -  log "prefilled tmpfile"
      +  log "capturing prompt text from target=$target"
      +  prompt_text="$(capture_prompt_text "$target")"
      +  log "captured prompt_text='$prompt_text'"
      +  prefill_tmpfile "$tmpfile" "$prompt_text"
      +  log "prefilled tmpfile"
       
      -  log "launching editor: $editor $tmpfile"
      -  "$editor" "$tmpfile"
      -  local editor_exit=$?
      -  log "editor exited with status $editor_exit"
      +  log "launching editor: $editor $tmpfile"
      +  "$editor" "$tmpfile"
      +  local editor_exit=$?
      +  log "editor exited with status $editor_exit"
       
      -  if [ ! -s "$tmpfile" ]; then
      -    log "empty file, nothing sent"
      -    exit 0
      -  fi
      -  
      -  log "tmpfile contents:"
      -  log "$(cat "$tmpfile")"
      +  if [ ! -s "$tmpfile" ]; then
      +    log "empty file, nothing sent"
      +    exit 0
      +  fi
      +  
      +  log "tmpfile contents:"
      +  log "$(cat "$tmpfile")"
       
      -  log "validating target pane"
      -  validate_target_pane "$target"
      -  log "sending content to target=$target"
      -  send_content "$target" "$tmpfile" "$prompt_text"
      -  log "=== tmux-edit-send completed ==="
      -}
      +  log "validating target pane"
      +  validate_target_pane "$target"
      +  log "sending content to target=$target"
      +  send_content "$target" "$tmpfile" "$prompt_text"
      +  log "=== tmux-edit-send completed ==="
      +}
       
      -main "$@"
      +main "$@"
       

      Challenges and small discoveries


      @@ -398,11 +393,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/2026-02-22-my-desk-rack.html b/gemfeed/2026-02-22-my-desk-rack.html index b8cb761d..aaf2b45d 100644 --- a/gemfeed/2026-02-22-my-desk-rack.html +++ b/gemfeed/2026-02-22-my-desk-rack.html @@ -2,17 +2,12 @@ - My desk rack: DeskPi RackMate T0 -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -125,11 +120,11 @@
      E-Mail your comments to paul@nospam.buetow.org :-)
      diff --git a/gemfeed/2026-03-01-loadbars-0.13.0-released.html b/gemfeed/2026-03-01-loadbars-0.13.0-released.html index 6cd03adc..201d4966 100644 --- a/gemfeed/2026-03-01-loadbars-0.13.0-released.html +++ b/gemfeed/2026-03-01-loadbars-0.13.0-released.html @@ -2,17 +2,12 @@ - Loadbars 0.13.0 released -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -224,11 +219,11 @@ mage test
      Back to the main site
      diff --git a/gemfeed/2026-03-01-site-reliability-engineering-part-5.html b/gemfeed/2026-03-01-site-reliability-engineering-part-5.html index dbfdf0d4..deaccb8f 100644 --- a/gemfeed/2026-03-01-site-reliability-engineering-part-5.html +++ b/gemfeed/2026-03-01-site-reliability-engineering-part-5.html @@ -2,17 +2,12 @@ - Site Reliability Engineering - Part 5: System Design, Incidents, and Learning -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -83,11 +78,11 @@
      Back to the main site
      diff --git a/gemfeed/2026-03-02-rcm-ruby-configuration-management-dsl.html b/gemfeed/2026-03-02-rcm-ruby-configuration-management-dsl.html index 6edbd73e..ac88d6bb 100644 --- a/gemfeed/2026-03-02-rcm-ruby-configuration-management-dsl.html +++ b/gemfeed/2026-03-02-rcm-ruby-configuration-management-dsl.html @@ -2,17 +2,12 @@ - RCM: The Ruby Configuration Management DSL -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -69,20 +64,20 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  given { hostname is :earth }
      +
      configure do
      +  given { hostname is :earth }
       
      -  file '/tmp/test/wg0.conf' do
      -    requires file '/etc/hosts.test'
      -    manage directory
      -    from template
      -    'content with <%= 1 + 2 %>'
      -  end
      +  file '/tmp/test/wg0.conf' do
      +    requires file '/etc/hosts.test'
      +    manage directory
      +    from template
      +    'content with <%= 1 + 2 %>'
      +  end
       
      -  file '/etc/hosts.test' do
      -    line '192.168.1.101 earth'
      -  end
      -end
      +  file '/etc/hosts.test' do
      +    line '192.168.1.101 earth'
      +  end
      +end
       

      Which would look like this when run:
      @@ -91,14 +86,14 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      % sudo ruby example.rb
      -INFO 20260301-213817 dsl(0) => Configuring...
      -INFO 20260301-213817 file('/tmp/test/wg0.conf') => Registered dependency on file('/etc/hosts.test')
      -INFO 20260301-213817 file('/tmp/test/wg0.conf') => Evaluating...
      -INFO 20260301-213817 file('/etc/hosts.test') => Evaluating...
      -INFO 20260301-213817 file('/etc/hosts.test') => Writing file /etc/hosts.test
      -INFO 20260301-213817 file('/tmp/test/wg0.conf') => Creating parent directory /tmp/test
      -INFO 20260301-213817 file('/tmp/test/wg0.conf') => Writing file /tmp/test/wg0.conf
      +
      % sudo ruby example.rb
      +INFO 20260301-213817 dsl(0) => Configuring...
      +INFO 20260301-213817 file('/tmp/test/wg0.conf') => Registered dependency on file('/etc/hosts.test')
      +INFO 20260301-213817 file('/tmp/test/wg0.conf') => Evaluating...
      +INFO 20260301-213817 file('/etc/hosts.test') => Evaluating...
      +INFO 20260301-213817 file('/etc/hosts.test') => Writing file /etc/hosts.test
      +INFO 20260301-213817 file('/tmp/test/wg0.conf') => Creating parent directory /tmp/test
      +INFO 20260301-213817 file('/tmp/test/wg0.conf') => Writing file /tmp/test/wg0.conf
       

      The idea is that you describe the desired state and RCM worries about the steps. The given block can short‑circuit the whole run (for example, only run on a specific hostname). Each file resource can either manage a complete file (from a template) or just make sure individual lines are present.
      @@ -134,9 +129,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      file '/etc/hosts.test' do
      -  line '192.168.1.101 earth'
      -end
      +
      file '/etc/hosts.test' do
      +  line '192.168.1.101 earth'
      +end
       

      Ruby turns file into a method call and '/etc/hosts.test' into a normal argument. Inside RCM, that method builds a File resource object and stores it for later. The block you pass is just a Ruby block; RCM calls it with the file resource as self, so method calls like line configure that resource. There is no special parser here, just plain Ruby method and block dispatch.
      @@ -147,7 +142,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      given { hostname is :earth }
      +
      given { hostname is :earth }
       

      RCM uses Ruby's dynamic method lookup to interpret hostname and is in that block and to decide whether the rest of the configuration should run at all. Features like method_missing, blocks, and the ability to change what self means in a block make this kind of DSL possible with very little code. You still get all the power of Ruby (conditionals, loops, helper methods), but the surface reads like a small language of its own.
      @@ -162,7 +157,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      given { hostname is :earth }
      +
      given { hostname is :earth }
       

      Inside that block, calls such as hostname and is don't map to normal Ruby methods. Instead, RCM's DSL objects see those calls in method_missing, and interpret them as "check the current hostname" and "compare it to this symbol". This lets the DSL stay small and flexible: adding a new keyword can be as simple as handling another case in method_missing, without changing the Ruby syntax at all.
      @@ -175,25 +170,25 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      class HostCondition
      -  def initialize
      -    @current_hostname = Socket.gethostname.to_sym
      -  end
      +
      class HostCondition
      +  def initialize
      +    @current_hostname = Socket.gethostname.to_sym
      +  end
       
      -  def method_missing(name, *args, &)
      -    case name
      -    when :hostname
      -      @left = @current_hostname
      -      self               # allow chaining: hostname is :earth
      -    when :is
      -      @left == args.first
      -    else
      -      super
      -    end
      -  end
      -end
      +  def method_missing(name, *args, &)
      +    case name
      +    when :hostname
      +      @left = @current_hostname
      +      self               # allow chaining: hostname is :earth
      +    when :is
      +      @left == args.first
      +    else
      +      super
      +    end
      +  end
      +end
       
      -HostCondition.new.hostname.is(:earth)
      +HostCondition.new.hostname.is(:earth)
       

      RCM's real code is more sophisticated, but the idea is the same: Ruby happily calls method_missing for unknown methods like hostname and is, and the DSL turns those calls into a value (true/false) that decides whether the rest of the configuration should run.
      @@ -264,12 +259,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  file './.file_example.rcmtmp' do
      -    from template
      -    'One plus two is <%= 1 + 2 %>!'
      -  end
      -end
      +
      configure do
      +  file './.file_example.rcmtmp' do
      +    from template
      +    'One plus two is <%= 1 + 2 %>!'
      +  end
      +end
       

      Ensuring a line is absent from a file


      @@ -278,12 +273,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  file './.file_example.rcmtmp' do
      -    line 'Whats up?'
      -    is absent
      -  end
      -end
      +
      configure do
      +  file './.file_example.rcmtmp' do
      +    line 'Whats up?'
      +    is absent
      +  end
      +end
       

      Guarding a configuration run on the current hostname


      @@ -292,10 +287,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  given { hostname Socket.gethostname }
      -  ...
      -end
      +
      configure do
      +  given { hostname Socket.gethostname }
      +  ...
      +end
       

      Creating and deleting directories, and purging a directory tree


      @@ -304,16 +299,16 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  directory './.directory_example.rcmtmp' do
      -    is present
      -  end
      +
      configure do
      +  directory './.directory_example.rcmtmp' do
      +    is present
      +  end
       
      -  directory delete do
      -    path './.directory_example.rcmtmp'
      -    is absent
      -  end
      -end
      +  directory delete do
      +    path './.directory_example.rcmtmp'
      +    is absent
      +  end
      +end
       

      Managing file and directory modes and ownership


      @@ -322,15 +317,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  touch './.mode_example.rcmtmp' do
      -    mode 0o600
      -  end
      +
      configure do
      +  touch './.mode_example.rcmtmp' do
      +    mode 0o600
      +  end
       
      -  directory './.mode_example_dir.rcmtmp' do
      -    mode 0o705
      -  end
      -end
      +  directory './.mode_example_dir.rcmtmp' do
      +    mode 0o705
      +  end
      +end
       

      Using a chained, more natural language style for notifications


      @@ -341,11 +336,11 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  notify hello dear world do
      -    thank you to be part of you
      -  end
      -end
      +
      configure do
      +  notify hello dear world do
      +    thank you to be part of you
      +  end
      +end
       

      Touching files and updating their timestamps


      @@ -354,9 +349,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  touch './.touch_example.rcmtmp'
      -end
      +
      configure do
      +  touch './.touch_example.rcmtmp'
      +end
       

      Expressing dependencies between notifications


      @@ -365,19 +360,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  notify foo do
      -    requires notify bar and requires notify baz
      -    'foo_message'
      -  end
      +
      configure do
      +  notify foo do
      +    requires notify bar and requires notify baz
      +    'foo_message'
      +  end
       
      -  notify bar
      +  notify bar
       
      -  notify baz do
      -    requires notify bar
      -    'baz_message'
      -  end
      -end
      +  notify baz do
      +    requires notify bar
      +    'baz_message'
      +  end
      +end
       


      @@ -386,12 +381,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  symlink './.symlink_example.rcmtmp' do
      -    manage directory
      -    './.symlink_target_example.rcmtmp'
      -  end
      -end
      +
      configure do
      +  symlink './.symlink_example.rcmtmp' do
      +    manage directory
      +    './.symlink_target_example.rcmtmp'
      +  end
      +end
       

      Detecting duplicate resource definitions at configure time


      @@ -400,10 +395,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      configure do
      -  notify :foo
      -  notify :foo # raises RCM::DSL::DuplicateResource
      -end
      +
      configure do
      +  notify :foo
      +  notify :foo # raises RCM::DSL::DuplicateResource
      +end
       

      If you find RCM interesting, feel free to browse the code, adapt it to your own setup, or just steal ideas for your own Ruby DSLs. I will probably extend it with more features over time as my own needs evolve.
      @@ -419,11 +414,11 @@ http://www.gnu.org/software/src-highlite -->
      Back to the main site
      diff --git a/gemfeed/DRAFT-distributed-systems-simulator.html b/gemfeed/DRAFT-distributed-systems-simulator.html index b91420cf..9a9f51ef 100644 --- a/gemfeed/DRAFT-distributed-systems-simulator.html +++ b/gemfeed/DRAFT-distributed-systems-simulator.html @@ -2,17 +2,12 @@ - Distributed Systems Simulator -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -317,11 +312,11 @@ Table 2.1: Color differentiation of processes and messages
      Back to the main site
      diff --git a/gemfeed/DRAFT-f3s-kubernetes-with-freebsd-part-X.html b/gemfeed/DRAFT-f3s-kubernetes-with-freebsd-part-X.html index 038ee894..5d58d4c5 100644 --- a/gemfeed/DRAFT-f3s-kubernetes-with-freebsd-part-X.html +++ b/gemfeed/DRAFT-f3s-kubernetes-with-freebsd-part-X.html @@ -2,17 +2,12 @@ - f3s: Kubernetes with FreeBSD - Part X: GitOps with ArgoCD -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -160,8 +155,8 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl create namespace cicd
      -namespace/cicd created
      +
      $ kubectl create namespace cicd
      +namespace/cicd created
       

      Installing ArgoCD


      @@ -176,18 +171,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd conf/f3s/argocd
      -$ just install
      -helm repo add argo https://argoproj.github.io/argo-helm
      -helm repo update
      -helm install argocd argo/argo-cd \
      -    --namespace cicd \
      -    --version 7.7.12 \
      -    -f values.yaml
      -NAME: argocd
      -LAST DEPLOYED: ...
      -NAMESPACE: cicd
      -STATUS: deployed
      +
      $ cd conf/f3s/argocd
      +$ just install
      +helm repo add argo https://argoproj.github.io/argo-helm
      +helm repo update
      +helm install argocd argo/argo-cd \
      +    --namespace cicd \
      +    --version 7.7.12 \
      +    -f values.yaml
      +NAME: argocd
      +LAST DEPLOYED: ...
      +NAMESPACE: cicd
      +STATUS: deployed
       

      The values.yaml file configures several important aspects:
      @@ -219,12 +214,12 @@ configs: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ ARGOCD_ADMIN_PASSWORD=$(pwgen -s 32 1)
      -$ BCRYPT_HASH=$(htpasswd -nbBC 10 "" "$ARGOCD_ADMIN_PASSWORD" | tr -d ':\n' | sed 's/$2y/$2a/')
      -$ kubectl create secret generic argocd-secret \
      -    --from-literal=admin.password="$BCRYPT_HASH" \
      -    -n cicd
      -$ echo "ArgoCD admin password: $ARGOCD_ADMIN_PASSWORD"
      +
      $ ARGOCD_ADMIN_PASSWORD=$(pwgen -s 32 1)
      +$ BCRYPT_HASH=$(htpasswd -nbBC 10 "" "$ARGOCD_ADMIN_PASSWORD" | tr -d ':\n' | sed 's/$2y/$2a/')
      +$ kubectl create secret generic argocd-secret \
      +    --from-literal=admin.password="$BCRYPT_HASH" \
      +    -n cicd
      +$ echo "ArgoCD admin password: $ARGOCD_ADMIN_PASSWORD"
       

      **Server configuration**: Enabled insecure mode since TLS is handled by the OpenBSD edge relays:
      @@ -242,15 +237,15 @@ server: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get pods -n cicd
      -NAME                                                READY   STATUS    RESTARTS   AGE
      -argocd-application-controller-0                     1/1     Running   0          45d
      -argocd-applicationset-controller-66d6b9b8f4-vhm9k   1/1     Running   0          45d
      -argocd-dex-server-7fb556b7dd-xjr2l                  1/1     Running   0          45d
      -argocd-notifications-controller-6d8dd4c5f5-b8vwl    1/1     Running   0          45d
      -argocd-redis-77b8d6c6d4-mz9hg                       1/1     Running   0          45d
      -argocd-repo-server-5f98f77b97-8xtcq                 1/1     Running   0          45d
      -argocd-server-6b9c4b4f8d-kxw7p                      1/1     Running   0          45d
      +
      $ kubectl get pods -n cicd
      +NAME                                                READY   STATUS    RESTARTS   AGE
      +argocd-application-controller-0                     1/1     Running   0          45d
      +argocd-applicationset-controller-66d6b9b8f4-vhm9k   1/1     Running   0          45d
      +argocd-dex-server-7fb556b7dd-xjr2l                  1/1     Running   0          45d
      +argocd-notifications-controller-6d8dd4c5f5-b8vwl    1/1     Running   0          45d
      +argocd-redis-77b8d6c6d4-mz9hg                       1/1     Running   0          45d
      +argocd-repo-server-5f98f77b97-8xtcq                 1/1     Running   0          45d
      +argocd-server-6b9c4b4f8d-kxw7p                      1/1     Running   0          45d
       

      I created an ingress to expose the ArgoCD web UI:
      @@ -288,8 +283,8 @@ spec: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ argocd login argocd.f3s.buetow.org
      -$ argocd app list
      +
      $ argocd login argocd.f3s.buetow.org
      +$ argocd app list
       

      ArgoCD Application Structure


      @@ -460,22 +455,22 @@ spec: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      NAMESPACE := "services"
      -APP_NAME := "miniflux"
      +
      NAMESPACE := "services"
      +APP_NAME := "miniflux"
       
      -install:
      -    kubectl apply -f helm-chart/persistent-volumes.yaml
      -    helm install {{APP_NAME}} ./helm-chart --namespace {{NAMESPACE}}
      +install:
      +    kubectl apply -f helm-chart/persistent-volumes.yaml
      +    helm install {{APP_NAME}} ./helm-chart --namespace {{NAMESPACE}}
       
      -upgrade:
      -    helm upgrade {{APP_NAME}} ./helm-chart --namespace {{NAMESPACE}}
      +upgrade:
      +    helm upgrade {{APP_NAME}} ./helm-chart --namespace {{NAMESPACE}}
       
      -uninstall:
      -    helm uninstall {{APP_NAME}} --namespace {{NAMESPACE}}
      -    kubectl delete -f helm-chart/persistent-volumes.yaml
      +uninstall:
      +    helm uninstall {{APP_NAME}} --namespace {{NAMESPACE}}
      +    kubectl delete -f helm-chart/persistent-volumes.yaml
       
      -status:
      -    @kubectl get all -n {{NAMESPACE}} -l app={{APP_NAME}}
      +status:
      +    @kubectl get all -n {{NAMESPACE}} -l app={{APP_NAME}}
       

      Workflow:
      @@ -524,30 +519,30 @@ spec: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      NAMESPACE := "services"
      -APP_NAME := "miniflux"
      +
      NAMESPACE := "services"
      +APP_NAME := "miniflux"
       
      -status:
      -    @echo "=== Pods ==="
      -    @kubectl get pods -n {{NAMESPACE}} -l app={{APP_NAME}}
      -    @echo ""
      -    @echo "=== Services ==="
      -    @kubectl get svc -n {{NAMESPACE}} -l app={{APP_NAME}}
      -    @echo ""
      -    @echo "=== ArgoCD Status ==="
      -    @kubectl get application {{APP_NAME}} -n cicd -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}' 2>/dev/null && echo ""
      +status:
      +    @echo "=== Pods ==="
      +    @kubectl get pods -n {{NAMESPACE}} -l app={{APP_NAME}}
      +    @echo ""
      +    @echo "=== Services ==="
      +    @kubectl get svc -n {{NAMESPACE}} -l app={{APP_NAME}}
      +    @echo ""
      +    @echo "=== ArgoCD Status ==="
      +    @kubectl get application {{APP_NAME}} -n cicd -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}' 2>/dev/null && echo ""
       
      -sync:
      -    @echo "Triggering ArgoCD sync..."
      -    @kubectl annotate application {{APP_NAME}} -n cicd argocd.argoproj.io/refresh=normal --overwrite
      -    @sleep 2
      -    @kubectl get application {{APP_NAME}} -n cicd -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}' && echo ""
      +sync:
      +    @echo "Triggering ArgoCD sync..."
      +    @kubectl annotate application {{APP_NAME}} -n cicd argocd.argoproj.io/refresh=normal --overwrite
      +    @sleep 2
      +    @kubectl get application {{APP_NAME}} -n cicd -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}' && echo ""
       
      -argocd-status:
      -    argocd app get {{APP_NAME}} --core
      +argocd-status:
      +    argocd app get {{APP_NAME}} --core
       
      -logs:
      -    kubectl logs -n {{NAMESPACE}} -l app={{APP_NAME}} --tail=100 -f
      +logs:
      +    kubectl logs -n {{NAMESPACE}} -l app={{APP_NAME}} --tail=100 -f
       

      New workflow:
      @@ -563,8 +558,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ helm get values miniflux -n services > /tmp/miniflux-backup-values.yaml
      -$ kubectl get all,ingress -n services -o yaml > /tmp/miniflux-backup.yaml
      +
      $ helm get values miniflux -n services > /tmp/miniflux-backup-values.yaml
      +$ kubectl get all,ingress -n services -o yaml > /tmp/miniflux-backup.yaml
       

      2. **Create Application manifest**:
      @@ -572,8 +567,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl apply -f argocd-apps/services/miniflux.yaml
      -application.argoproj.io/miniflux created
      +
      $ kubectl apply -f argocd-apps/services/miniflux.yaml
      +application.argoproj.io/miniflux created
       

      3. **Verify ArgoCD adopted the resources**:
      @@ -581,19 +576,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ argocd app get miniflux
      -Name:               miniflux
      -Project:            default
      -Server:             https://kubernetes.default.svc
      -Namespace:          services
      -URL:                https://argocd.f3s.buetow.org/applications/miniflux
      -Repo:               https://codeberg.org/snonux/conf.git
      -Target:             master
      -Path:               f3s/miniflux/helm-chart
      -SyncWindow:         Sync Allowed
      -Sync Policy:        Automated (Prune)
      -Sync Status:        Synced to master (4e3c216)
      -Health Status:      Healthy
      +
      $ argocd app get miniflux
      +Name:               miniflux
      +Project:            default
      +Server:             https://kubernetes.default.svc
      +Namespace:          services
      +URL:                https://argocd.f3s.buetow.org/applications/miniflux
      +Repo:               https://codeberg.org/snonux/conf.git
      +Target:             master
      +Path:               f3s/miniflux/helm-chart
      +SyncWindow:         Sync Allowed
      +Sync Policy:        Automated (Prune)
      +Sync Status:        Synced to master (4e3c216)
      +Health Status:      Healthy
       

      4. **Monitor for issues**:
      @@ -601,10 +596,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl get pods -n services -l app=miniflux -w
      -NAME                                READY   STATUS    RESTARTS   AGE
      -miniflux-postgres-556444cb8d-xvv2p  1/1     Running   0          54d
      -miniflux-server-85d7c64664-stmt9    1/1     Running   0          54d
      +
      $ kubectl get pods -n services -l app=miniflux -w
      +NAME                                READY   STATUS    RESTARTS   AGE
      +miniflux-postgres-556444cb8d-xvv2p  1/1     Running   0          54d
      +miniflux-server-85d7c64664-stmt9    1/1     Running   0          54d
       

      5. **Test the application**:
      @@ -612,8 +607,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ curl -I https://flux.f3s.buetow.org
      -HTTP/2 200
      +
      $ curl -I https://flux.f3s.buetow.org
      +HTTP/2 200
       

      6. **Update Justfile** and commit changes
      @@ -747,30 +742,30 @@ spec: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ argocd app list
      -NAME                      CLUSTER                         NAMESPACE    PROJECT  STATUS  HEALTH   SYNCPOLICY
      -alloy                     https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      -anki-sync-server          https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -audiobookshelf            https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -example-apache            https://kubernetes.default.svc  test         default  Synced  Healthy  Auto-Prune
      -example-apache-volume-... https://kubernetes.default.svc  test         default  Synced  Healthy  Auto-Prune
      -filebrowser               https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -freshrss                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -grafana-ingress           https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      -immich                    https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -keybr                     https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -kobo-sync-server          https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -loki                      https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      -miniflux                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -opodsync                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -prometheus                https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto
      -pushgateway               https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      -radicale                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -registry                  https://kubernetes.default.svc  infra        default  Synced  Healthy  Auto-Prune
      -syncthing                 https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -tempo                     https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      -wallabag                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      -webdav                    https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +
      $ argocd app list
      +NAME                      CLUSTER                         NAMESPACE    PROJECT  STATUS  HEALTH   SYNCPOLICY
      +alloy                     https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      +anki-sync-server          https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +audiobookshelf            https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +example-apache            https://kubernetes.default.svc  test         default  Synced  Healthy  Auto-Prune
      +example-apache-volume-... https://kubernetes.default.svc  test         default  Synced  Healthy  Auto-Prune
      +filebrowser               https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +freshrss                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +grafana-ingress           https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      +immich                    https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +keybr                     https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +kobo-sync-server          https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +loki                      https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      +miniflux                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +opodsync                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +prometheus                https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto
      +pushgateway               https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      +radicale                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +registry                  https://kubernetes.default.svc  infra        default  Synced  Healthy  Auto-Prune
      +syncthing                 https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +tempo                     https://kubernetes.default.svc  monitoring   default  Synced  Healthy  Auto-Prune
      +wallabag                  https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
      +webdav                    https://kubernetes.default.svc  services     default  Synced  Healthy  Auto-Prune
       

      All 21 applications: **Synced** and **Healthy**.
      @@ -791,10 +786,10 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ git clone https://codeberg.org/snonux/conf.git
      -$ cd conf/f3s
      -$ ls argocd-apps/
      -alloy.yaml  anki-sync-server.yaml  audiobookshelf.yaml  ...
      +
      $ git clone https://codeberg.org/snonux/conf.git
      +$ cd conf/f3s
      +$ ls argocd-apps/
      +alloy.yaml  anki-sync-server.yaml  audiobookshelf.yaml  ...
       

      2. Automatic Synchronization


      @@ -805,12 +800,12 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ cd conf/f3s/miniflux/helm-chart
      -$ vim values.yaml  # Change replica count from 1 to 2
      -$ git add values.yaml
      -$ git commit -m "Scale miniflux to 2 replicas"
      -$ git push
      -# ArgoCD detects change within 3 minutes and syncs automatically
      +
      $ cd conf/f3s/miniflux/helm-chart
      +$ vim values.yaml  # Change replica count from 1 to 2
      +$ git add values.yaml
      +$ git commit -m "Scale miniflux to 2 replicas"
      +$ git push
      +# ArgoCD detects change within 3 minutes and syncs automatically
       

      No need to SSH to a workstation, pull the repo, and run just upgrade.
      @@ -823,13 +818,13 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl scale deployment miniflux-server -n services --replicas=3
      -deployment.apps/miniflux-server scaled
      +
      $ kubectl scale deployment miniflux-server -n services --replicas=3
      +deployment.apps/miniflux-server scaled
       
      -# ArgoCD detects drift within 3 minutes
      -$ argocd app get miniflux
      -...
      -Sync Status:        OutOfSync from master (4e3c216)
      +# ArgoCD detects drift within 3 minutes
      +$ argocd app get miniflux
      +...
      +Sync Status:        OutOfSync from master (4e3c216)
       

      With selfHeal: true, ArgoCD automatically reverts the change back to 2 replicas (the value in Git).
      @@ -842,9 +837,9 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ git revert HEAD
      -$ git push
      -# ArgoCD automatically rolls back to the previous state
      +
      $ git revert HEAD
      +$ git push
      +# ArgoCD automatically rolls back to the previous state
       

      Or rollback to a specific commit:
      @@ -853,7 +848,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ argocd app rollback miniflux <revision-id>
      +
      $ argocd app rollback miniflux <revision-id>
       

      5. Disaster Recovery


      @@ -868,7 +863,7 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl apply -f argocd-apps/
      +
      $ kubectl apply -f argocd-apps/
       
      5. ArgoCD deploys all 21 applications to their desired state

      @@ -894,19 +889,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ git checkout -b test-prometheus-upgrade
      -$ vim argocd-apps/prometheus.yaml  # Bump chart version
      -$ git commit -am "Test Prometheus 56.0.0"
      -$ git push origin test-prometheus-upgrade
      +
      $ git checkout -b test-prometheus-upgrade
      +$ vim argocd-apps/prometheus.yaml  # Bump chart version
      +$ git commit -am "Test Prometheus 56.0.0"
      +$ git push origin test-prometheus-upgrade
       
      -# Temporarily point ArgoCD at the feature branch
      -$ kubectl patch application prometheus -n cicd \
      -    --type merge \
      -    -p '{"spec":{"source":{"targetRevision":"test-prometheus-upgrade"}}}'
      +# Temporarily point ArgoCD at the feature branch
      +$ kubectl patch application prometheus -n cicd \
      +    --type merge \
      +    -p '{"spec":{"source":{"targetRevision":"test-prometheus-upgrade"}}}'
       
      -# Verify changes in ArgoCD Web UI
      -# If good: merge to master
      -# If bad: revert the patch
      +# Verify changes in ArgoCD Web UI
      +# If good: merge to master
      +# If bad: revert the patch
       

      Challenges and Solutions


      @@ -980,14 +975,14 @@ The Helm operation failed with an error: release miniflux failed, and has been u by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      install:
      -    helm install miniflux ./helm-chart -n services
      +
      install:
      +    helm install miniflux ./helm-chart -n services
       
      -upgrade:
      -    helm upgrade miniflux ./helm-chart -n services
      +upgrade:
      +    helm upgrade miniflux ./helm-chart -n services
       
      -uninstall:
      -    helm uninstall miniflux -n services
      +uninstall:
      +    helm uninstall miniflux -n services
       

      **After (ArgoCD utilities)**:
      @@ -995,18 +990,18 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      status:
      -    @kubectl get pods -n services -l app=miniflux
      -    @kubectl get application miniflux -n cicd -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}'
      +
      status:
      +    @kubectl get pods -n services -l app=miniflux
      +    @kubectl get application miniflux -n cicd -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}'
       
      -sync:
      -    @kubectl annotate application miniflux -n cicd argocd.argoproj.io/refresh=normal --overwrite
      +sync:
      +    @kubectl annotate application miniflux -n cicd argocd.argoproj.io/refresh=normal --overwrite
       
      -argocd-status:
      -    argocd app get miniflux --core
      +argocd-status:
      +    argocd app get miniflux --core
       
      -logs:
      -    kubectl logs -n services -l app=miniflux --tail=100 -f
      +logs:
      +    kubectl logs -n services -l app=miniflux --tail=100 -f
       

      The Justfiles now provide:
      @@ -1142,13 +1137,13 @@ spec: by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ kubectl apply -f root-app.yaml
      -# Root app deploys all 21 applications automatically
      +
      $ kubectl apply -f root-app.yaml
      +# Root app deploys all 21 applications automatically
       
      -# Or apply by namespace
      -$ kubectl apply -f root-monitoring.yaml
      -$ kubectl apply -f root-services.yaml
      -$ kubectl apply -f root-infra.yaml
      +# Or apply by namespace
      +$ kubectl apply -f root-monitoring.yaml
      +$ kubectl apply -f root-services.yaml
      +$ kubectl apply -f root-infra.yaml
       

      4. ArgoCD Image Updater


      @@ -1222,11 +1217,11 @@ metadata:
      Back to the main site
      diff --git a/gemfeed/DRAFT-ipv6test-deployment.html b/gemfeed/DRAFT-ipv6test-deployment.html index a858bf63..9bc33ffb 100644 --- a/gemfeed/DRAFT-ipv6test-deployment.html +++ b/gemfeed/DRAFT-ipv6test-deployment.html @@ -2,17 +2,12 @@ - Deploying an IPv6 Test Service on Kubernetes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -48,15 +43,15 @@ Client → relayd (OpenBSD) → Traefik (k3s) → Apache + CGI (Pod) by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      #!/usr/bin/perl
      -use strict;
      -use warnings;
      +
      #!/usr/bin/perl
      +use strict;
      +use warnings;
       
      -print "Content-type: text/html\n\n";
      +print "Content-type: text/html\n\n";
       
      -my $is_ipv4 = ($ENV{REMOTE_ADDR} =~ /(?:\d+\.){3}\d/);
      -print "You are using: " . ($is_ipv4 ? "IPv4" : "IPv6") . "\n";
      -print "Client address: $ENV{REMOTE_ADDR}\n";
      +my $is_ipv4 = ($ENV{REMOTE_ADDR} =~ /(?:\d+\.){3}\d/);
      +print "You are using: " . ($is_ipv4 ? "IPv4" : "IPv6") . "\n";
      +print "Client address: $ENV{REMOTE_ADDR}\n";
       

      Docker Image


      @@ -153,17 +148,17 @@ http protocol "https" { by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      <% for my $host (@$f3s_hosts) {
      -     my $is_ipv6_only = $host =~ /^ipv6\./;
      -     my $is_ipv4_only = $host =~ /^ipv4\./;
      --%>
      -<% unless ($is_ipv6_only) { -%>
      -<%= $host %>.         300 IN A <%= $ips->{current_master}{ipv4} %>
      -<% } -%>
      -<% unless ($is_ipv4_only) { -%>
      -<%= $host %>.         300 IN AAAA <%= $ips->{current_master}{ipv6} %>
      -<% } -%>
      -<% } -%>
      +
      <% for my $host (@$f3s_hosts) {
      +     my $is_ipv6_only = $host =~ /^ipv6\./;
      +     my $is_ipv4_only = $host =~ /^ipv4\./;
      +-%>
      +<% unless ($is_ipv6_only) { -%>
      +<%= $host %>.         300 IN A <%= $ips->{current_master}{ipv4} %>
      +<% } -%>
      +<% unless ($is_ipv4_only) { -%>
      +<%= $host %>.         300 IN AAAA <%= $ips->{current_master}{ipv6} %>
      +<% } -%>
      +<% } -%>
       

      This ensures:
      @@ -184,22 +179,22 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      <% for my $host (@$acme_hosts) {
      -     # Skip ipv4/ipv6 subdomains - they're included as SANs in parent cert
      -     next if $host =~ /^(ipv4|ipv6)\./;
      --%>
      -<%   my @alt_names = ("www.$host");
      -     for my $sub_host (@$acme_hosts) {
      -         if ($sub_host =~ /^(ipv4|ipv6)\.\Q$host\E$/) {
      -             push @alt_names, $sub_host;
      -         }
      -     }
      --%>
      -domain <%= $host %> {
      -    alternative names { <%= join(' ', @alt_names) %> }
      -    ...
      -}
      -<% } -%>
      +
      <% for my $host (@$acme_hosts) {
      +     # Skip ipv4/ipv6 subdomains - they're included as SANs in parent cert
      +     next if $host =~ /^(ipv4|ipv6)\./;
      +-%>
      +<%   my @alt_names = ("www.$host");
      +     for my $sub_host (@$acme_hosts) {
      +         if ($sub_host =~ /^(ipv4|ipv6)\.\Q$host\E$/) {
      +             push @alt_names, $sub_host;
      +         }
      +     }
      +-%>
      +domain <%= $host %> {
      +    alternative names { <%= join(' ', @alt_names) %> }
      +    ...
      +}
      +<% } -%>
       

      This generates a single certificate for ipv6test.f3s.buetow.org that includes:
      @@ -216,19 +211,19 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      our @f3s_hosts = qw/
      -    ...
      -    ipv6test.f3s.buetow.org
      -    ipv4.ipv6test.f3s.buetow.org
      -    ipv6.ipv6test.f3s.buetow.org
      -/;
      +
      our @f3s_hosts = qw/
      +    ...
      +    ipv6test.f3s.buetow.org
      +    ipv4.ipv6test.f3s.buetow.org
      +    ipv6.ipv6test.f3s.buetow.org
      +/;
       
      -our @acme_hosts = qw/
      -    ...
      -    ipv6test.f3s.buetow.org
      -    ipv4.ipv6test.f3s.buetow.org
      -    ipv6.ipv6test.f3s.buetow.org
      -/;
      +our @acme_hosts = qw/
      +    ...
      +    ipv6test.f3s.buetow.org
      +    ipv4.ipv6test.f3s.buetow.org
      +    ipv6.ipv6test.f3s.buetow.org
      +/;
       

      Running rex nsd httpd acme acme_invoke relayd deploys the DNS zone, configures httpd for ACME challenges, obtains the certificates, and reloads relayd.
      @@ -241,17 +236,17 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ dig ipv4.ipv6test.f3s.buetow.org A +short
      -46.23.94.99
      +
      $ dig ipv4.ipv6test.f3s.buetow.org A +short
      +46.23.94.99
       
      -$ dig ipv4.ipv6test.f3s.buetow.org AAAA +short
      -(no output - IPv4 only)
      +$ dig ipv4.ipv6test.f3s.buetow.org AAAA +short
      +(no output - IPv4 only)
       
      -$ dig ipv6.ipv6test.f3s.buetow.org AAAA +short
      -2a03:6000:6f67:624::99
      +$ dig ipv6.ipv6test.f3s.buetow.org AAAA +short
      +2a03:6000:6f67:624::99
       
      -$ dig ipv6.ipv6test.f3s.buetow.org A +short
      -(no output - IPv6 only)
      +$ dig ipv6.ipv6test.f3s.buetow.org A +short
      +(no output - IPv6 only)
       

      Verify the application shows the correct test type:
      @@ -260,8 +255,8 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      $ curl -s https://ipv4.ipv6test.f3s.buetow.org/cgi-bin/index.pl | grep "Test Results"
      -<h3>IPv4 Only Test Results:</h3>
      +
      $ curl -s https://ipv4.ipv6test.f3s.buetow.org/cgi-bin/index.pl | grep "Test Results"
      +<h3>IPv4 Only Test Results:</h3>
       

      The displayed IP should be the real client IP, not an internal cluster address.
      @@ -278,15 +273,15 @@ http://www.gnu.org/software/src-highlite --> by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      sub html_escape {
      -    my $str = shift;
      -    $str =~ s/&/&amp;/g;
      -    $str =~ s/</&lt;/g;
      -    $str =~ s/>/&gt;/g;
      -    return $str;
      -}
      +
      sub html_escape {
      +    my $str = shift;
      +    $str =~ s/&/&amp;/g;
      +    $str =~ s/</&lt;/g;
      +    $str =~ s/>/&gt;/g;
      +    return $str;
      +}
       
      -my $digremote = html_escape(`dig -x $ENV{REMOTE_ADDR}`);
      +my $digremote = html_escape(`dig -x $ENV{REMOTE_ADDR}`);
       

      You can verify the output passes validation:
      @@ -317,11 +312,11 @@ http://www.gnu.org/software/src-highlite -->
      ← Back to the index
      diff --git a/gemfeed/index.html b/gemfeed/index.html index 4c19a253..77f396d0 100644 --- a/gemfeed/index.html +++ b/gemfeed/index.html @@ -2,17 +2,12 @@ - Gemfeed of foo.zone -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -123,11 +118,11 @@ 2008-12-29 - Using my Nokia N95 for fixing my MTA
      2008-06-26 - Perl Poetry
      diff --git a/heading.ttf b/heading.ttf index 608f2ad6..3e10e02f 100644 Binary files a/heading.ttf and b/heading.ttf differ diff --git a/index.html b/index.html index 55f8950f..f7a97b0c 100644 --- a/index.html +++ b/index.html @@ -2,17 +2,12 @@ - Hello! -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -151,11 +146,11 @@ 2008-12-29 - Using my Nokia N95 for fixing my MTA
      2008-06-26 - Perl Poetry
      diff --git a/notes/97-things-every-sre-should-know.html b/notes/97-things-every-sre-should-know.html index 0a31f583..1f886ce4 100644 --- a/notes/97-things-every-sre-should-know.html +++ b/notes/97-things-every-sre-should-know.html @@ -2,17 +2,12 @@ - '97 Things Every SRE Should Know' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -359,11 +354,11 @@
      Back to the main site
      diff --git a/notes/a-monks-guide-to-happiness.html b/notes/a-monks-guide-to-happiness.html index 99b6462f..ffcf38d4 100644 --- a/notes/a-monks-guide-to-happiness.html +++ b/notes/a-monks-guide-to-happiness.html @@ -2,17 +2,12 @@ - 'A Monk's Guide to Happiness' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -119,11 +114,11 @@
      Back to the main site
      diff --git a/notes/career-guide-and-soft-skills.html b/notes/career-guide-and-soft-skills.html index 8c097680..c45a54de 100644 --- a/notes/career-guide-and-soft-skills.html +++ b/notes/career-guide-and-soft-skills.html @@ -2,17 +2,12 @@ - 'Software Developers Career Guide and Soft Skills' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -365,11 +360,11 @@
      Back to the main site
      diff --git a/notes/eat-that-frog.html b/notes/eat-that-frog.html index 0db400c9..dbbfda51 100644 --- a/notes/eat-that-frog.html +++ b/notes/eat-that-frog.html @@ -2,17 +2,12 @@ - 'Eat That Frog' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -124,11 +119,11 @@
      Back to the main site
      diff --git a/notes/fluent-forever.html b/notes/fluent-forever.html index bee404b4..9e0d4b72 100644 --- a/notes/fluent-forever.html +++ b/notes/fluent-forever.html @@ -2,17 +2,12 @@ - 'Fluent Forever' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -109,11 +104,11 @@
      Back to the main site
      diff --git a/notes/implementing-service-level-objectives.html b/notes/implementing-service-level-objectives.html index 610c14e9..48d72572 100644 --- a/notes/implementing-service-level-objectives.html +++ b/notes/implementing-service-level-objectives.html @@ -2,17 +2,12 @@ - 'Implementing Service Level Objectives' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -106,11 +101,11 @@
      Back to the main site
      diff --git a/notes/index.html b/notes/index.html index ee8eed9a..75c4879a 100644 --- a/notes/index.html +++ b/notes/index.html @@ -2,17 +2,12 @@ - Notes on foo.zone -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -50,11 +45,11 @@
      Go back to main site
      diff --git a/notes/influence-wihout-authority.html b/notes/influence-wihout-authority.html index 208ca3db..4f5b5cfe 100644 --- a/notes/influence-wihout-authority.html +++ b/notes/influence-wihout-authority.html @@ -2,17 +2,12 @@ - 'Influence without Authority' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -89,11 +84,11 @@
      Back to the main site
      diff --git a/notes/joy-on-demand.html b/notes/joy-on-demand.html index 66af71c8..562892bd 100644 --- a/notes/joy-on-demand.html +++ b/notes/joy-on-demand.html @@ -2,17 +2,12 @@ - 'Joy On Domand' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -221,11 +216,11 @@
      Back to the main site
      diff --git a/notes/love-people-use-things.html b/notes/love-people-use-things.html index e0b6ae14..68098169 100644 --- a/notes/love-people-use-things.html +++ b/notes/love-people-use-things.html @@ -2,17 +2,12 @@ - 'Love People, Use Things' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -142,11 +137,11 @@
      Back to the main site
      diff --git a/notes/meditation-for-mortals.html b/notes/meditation-for-mortals.html index 0149c89a..93b2118a 100644 --- a/notes/meditation-for-mortals.html +++ b/notes/meditation-for-mortals.html @@ -2,17 +2,12 @@ - 'Meditation for Mortals' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -167,11 +162,11 @@
      Back to the main site
      diff --git a/notes/mental-combat.html b/notes/mental-combat.html index 999424cf..8c6b3917 100644 --- a/notes/mental-combat.html +++ b/notes/mental-combat.html @@ -2,17 +2,12 @@ - 'Mental Combat' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -53,11 +48,11 @@
      Back to the main site
      diff --git a/notes/mind-management.html b/notes/mind-management.html index 1da949ec..eba289de 100644 --- a/notes/mind-management.html +++ b/notes/mind-management.html @@ -2,17 +2,12 @@ - 'Mind Management' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -151,11 +146,11 @@ Back to the main site

      diff --git a/notes/never-split-the-difference.html b/notes/never-split-the-difference.html index 29b363a5..26d7aeb6 100644 --- a/notes/never-split-the-difference.html +++ b/notes/never-split-the-difference.html @@ -2,17 +2,12 @@ - 'Never split the difference' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -180,11 +175,11 @@
      Back to the main site
      diff --git a/notes/search-inside-yourself.html b/notes/search-inside-yourself.html index 79999671..57c265b5 100644 --- a/notes/search-inside-yourself.html +++ b/notes/search-inside-yourself.html @@ -2,17 +2,12 @@ - 'Search Inside Yourself' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -849,11 +844,11 @@
      Back to the main site
      diff --git a/notes/site-reliability-engineering.html b/notes/site-reliability-engineering.html index 3ba830e4..310e01da 100644 --- a/notes/site-reliability-engineering.html +++ b/notes/site-reliability-engineering.html @@ -2,17 +2,12 @@ - 'Site Reliability Engineering' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -109,11 +104,11 @@
      Back to the main site
      diff --git a/notes/slow-productivity.html b/notes/slow-productivity.html index 73cf057b..ec338ea3 100644 --- a/notes/slow-productivity.html +++ b/notes/slow-productivity.html @@ -2,17 +2,12 @@ - 'Slow Productivity' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -177,11 +172,11 @@ Back to the main site

      diff --git a/notes/staff-engineer.html b/notes/staff-engineer.html index 33ee4075..d2a0a379 100644 --- a/notes/staff-engineer.html +++ b/notes/staff-engineer.html @@ -2,17 +2,12 @@ - 'Staff Engineer' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -158,11 +153,11 @@ Back to the main site

      diff --git a/notes/the-courage-to-be-disliked.html b/notes/the-courage-to-be-disliked.html index 48530ec1..7d815a5b 100644 --- a/notes/the-courage-to-be-disliked.html +++ b/notes/the-courage-to-be-disliked.html @@ -2,17 +2,12 @@ - 'The Courage To Be Disliked' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -153,11 +148,11 @@
      Back to the main site
      diff --git a/notes/the-obstacle-is-the-way.html b/notes/the-obstacle-is-the-way.html index 46797805..64992a4f 100644 --- a/notes/the-obstacle-is-the-way.html +++ b/notes/the-obstacle-is-the-way.html @@ -2,17 +2,12 @@ - 'The Obstacle is the Way' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -133,11 +128,11 @@
      Back to the main site
      diff --git a/notes/the-power-of-neuroplasticity.html b/notes/the-power-of-neuroplasticity.html index 44495133..4f27929a 100644 --- a/notes/the-power-of-neuroplasticity.html +++ b/notes/the-power-of-neuroplasticity.html @@ -2,17 +2,12 @@ - 'The Power of Neuroplasticity' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -132,11 +127,11 @@
      Back to the main site
      diff --git a/notes/the-pragmatic-programmer.html b/notes/the-pragmatic-programmer.html index dd91aa03..024084b9 100644 --- a/notes/the-pragmatic-programmer.html +++ b/notes/the-pragmatic-programmer.html @@ -2,17 +2,12 @@ - 'The Pragmatic Programmer' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -117,11 +112,11 @@
      Back to the main site
      diff --git a/notes/the-science-of-living.html b/notes/the-science-of-living.html index 89044024..70c47b1e 100644 --- a/notes/the-science-of-living.html +++ b/notes/the-science-of-living.html @@ -2,17 +2,12 @@ - 'Science of Living' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -70,11 +65,11 @@
      Back to the main site
      diff --git a/notes/the-stoic-challenge.html b/notes/the-stoic-challenge.html index 85ac253d..70dd736d 100644 --- a/notes/the-stoic-challenge.html +++ b/notes/the-stoic-challenge.html @@ -2,17 +2,12 @@ - 'The Stoic Challenge' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -92,11 +87,11 @@
      Back to the main site
      diff --git a/notes/when.html b/notes/when.html index 3b0db11c..2fd5c684 100644 --- a/notes/when.html +++ b/notes/when.html @@ -2,17 +2,12 @@ - 'When: The Scientific Secrets of Perfect Timing' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -138,11 +133,11 @@ __ejm\___/________dwb`---`______________________
      Back to the main site
      diff --git a/notes/yoga-nidra-made-easy.html b/notes/yoga-nidra-made-easy.html index a4ded711..eaca3aae 100644 --- a/notes/yoga-nidra-made-easy.html +++ b/notes/yoga-nidra-made-easy.html @@ -2,17 +2,12 @@ - 'Yoga Nidra Made Easy' book notes -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -198,11 +193,11 @@
      Back to the main site
      diff --git a/site-mirrors.html b/site-mirrors.html index e7c73830..562c2014 100644 --- a/site-mirrors.html +++ b/site-mirrors.html @@ -2,17 +2,12 @@ - Site mirrors -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -47,11 +42,11 @@
      Go back to the main site
      diff --git a/stats.html b/stats.html index 16bdc123..96e3e35e 100644 --- a/stats.html +++ b/stats.html @@ -2,17 +2,12 @@ - Stats -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -24,11 +19,11 @@ Site statistics (HTTP)
      Site statistics (Gemini)
      diff --git a/style.css b/style.css index 74970c3a..4d428726 100644 --- a/style.css +++ b/style.css @@ -1,532 +1,101 @@ @font-face { font-family: 'text'; - src: url('./text.ttf') format('truetype'); + src: url("./text.ttf") format("truetype"); } @font-face { font-family: 'heading'; - src: url('./heading.ttf') format('truetype'); + src: url("./heading.ttf") format("truetype"); } @font-face { font-family: 'code'; - src: url('./code.ttf') format('truetype'); + src: url("./code.ttf") format("truetype"); } @font-face { font-family: 'handnotes'; - src: url('./handnotes.ttf') format('truetype'); -} - -@font-face { - font-family: 'typewriter'; - src: url('./typewriter.ttf') format('truetype'); -} - -:root { - --bg-void: #05030a; - --bg-deep: #0f0620; - --bg-electric: #1f0e3f; - --neon-cyan: #2ef7ff; - --neon-magenta: #ff2fa9; - --neon-lime: #8eff58; - --neon-gold: #ffd866; - --text-main: #d8fcff; - --text-muted: #9be9f4; - --panel-bg: rgba(6, 12, 31, 0.82); - --panel-border: rgba(46, 247, 255, 0.5); - --panel-shadow: 0 0 22px rgba(46, 247, 255, 0.24), 0 0 70px rgba(255, 47, 169, 0.14); - --rfx-scroll: 0px; + src: url("./handnotes.ttf") format("truetype"); } html { - min-height: 100%; - background-color: var(--bg-void); - background-image: - radial-gradient(circle at 10% 18%, rgba(255, 47, 169, 0.3), rgba(255, 47, 169, 0) 32%), - radial-gradient(circle at 88% 12%, rgba(46, 247, 255, 0.26), rgba(46, 247, 255, 0) 30%), - radial-gradient(circle at 50% 120%, rgba(142, 255, 88, 0.12), rgba(142, 255, 88, 0) 45%), - linear-gradient(160deg, var(--bg-void), var(--bg-deep) 40%, var(--bg-electric)); - background-size: cover; - color: var(--text-main); + background-color: #f0f0f0; + background-image: linear-gradient(45deg, #e6e6e6 25%, transparent 25%, transparent 75%, #e6e6e6 75%, #e6e6e6), + linear-gradient(45deg, #e6e6e6 25%, transparent 25%, transparent 75%, #e6e6e6 75%, #e6e6e6); + background-size: 20px 20px; + background-position: 0 0, 10px 10px; } body { font-family: text, sans-serif; - color: var(--text-main); - max-width: 980px; - margin: 1.3rem auto; - padding: 1.3rem 1.6rem; - border: 1px solid var(--panel-border); - border-radius: 12px; - background: var(--panel-bg); - box-shadow: var(--panel-shadow); + background: #ffffff; + max-width: 1200px; + padding: 20px; + margin: 20px auto; + border: 5px solid #dddddd; + border-radius: 15px; word-wrap: break-word; - overflow-x: hidden; - transform-origin: 50% 0%; - animation: body-boot 0.8s ease-out forwards; -} - -body > :not(#rfx-stars):not(.rfx-overlay-grid):not(.rfx-overlay-scanlines):not(.rfx-vignette) { - position: relative; - z-index: 2; -} - -#rfx-stars, -.rfx-overlay-grid, -.rfx-overlay-scanlines, -.rfx-vignette { - position: fixed; - left: 0; - top: 0; - width: 100%; - height: 100%; - pointer-events: none; -} - -#rfx-stars { - z-index: 0; - overflow: hidden; } -#rfx-stars .star { - position: absolute; - border-radius: 50%; - background: rgba(255, 255, 255, 0.9); - box-shadow: 0 0 6px rgba(46, 247, 255, 0.75), 0 0 12px rgba(255, 47, 169, 0.28); - animation: twinkle var(--twinkle-speed, 3.8s) ease-in-out infinite; -} - -.rfx-overlay-grid { - z-index: 0; - opacity: 0.24; - background-image: - linear-gradient(rgba(46, 247, 255, 0.1) 1px, transparent 1px), - linear-gradient(90deg, rgba(46, 247, 255, 0.1) 1px, transparent 1px), - linear-gradient(0deg, rgba(255, 47, 169, 0.15), rgba(255, 47, 169, 0)); - background-size: 48px 48px, 48px 48px, 100% 100%; - background-position: - 0 calc(var(--rfx-scroll) * -0.06), - calc(var(--rfx-scroll) * 0.03) 0, - 0 0; -} - -.rfx-overlay-scanlines { - z-index: 1; - opacity: 0.2; - background: repeating-linear-gradient( - 0deg, - rgba(255, 255, 255, 0.06) 0px, - rgba(255, 255, 255, 0.02) 1px, - rgba(0, 0, 0, 0.2) 2px, - rgba(0, 0, 0, 0) 4px - ); - mix-blend-mode: screen; - animation: scanline-shift 8s linear infinite; -} - -.rfx-vignette { - z-index: 1; - background: radial-gradient(circle at 50% 40%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0.52) 100%); -} - -h1, -h2, -h3 { - display: inline-block !important; +h1, h2, h3 { font-family: heading, serif; - font-weight: normal; - margin-top: 1.1rem; - margin-bottom: 0.2rem; - color: var(--neon-cyan); - letter-spacing: 0.1em; - text-transform: uppercase; - text-shadow: - 0 0 4px rgba(46, 247, 255, 0.9), - 0 0 11px rgba(46, 247, 255, 0.7), - 0 0 22px rgba(255, 47, 169, 0.35); -} - -h1 { - color: var(--neon-lime); -} - -h1::after, -h2::after, -h3::after { - content: ''; - display: block; - width: 100%; - height: 2px; - margin-top: 2px; - background: linear-gradient(90deg, rgba(255, 47, 169, 0.05), rgba(46, 247, 255, 0.9), rgba(255, 47, 169, 0.05)); - box-shadow: 0 0 7px rgba(46, 247, 255, 0.6); -} - -h1.glitch, -h2.glitch, -h3.glitch { - position: relative; - animation: glitch-shake 0.22s steps(2, end) 1; -} - -h1.glitch::before, -h1.glitch::after, -h2.glitch::before, -h2.glitch::after, -h3.glitch::before, -h3.glitch::after { - content: attr(data-rfx-text); - position: absolute; - left: 0; - top: 0; - width: 100%; - overflow: hidden; - pointer-events: none; -} - -h1.glitch::before, -h2.glitch::before, -h3.glitch::before { - color: var(--neon-magenta); - transform: translate(-2px, 0); - opacity: 0.7; - clip-path: inset(0 0 48% 0); -} - -h1.glitch::after, -h2.glitch::after, -h3.glitch::after { - color: var(--neon-cyan); - transform: translate(2px, 0); - opacity: 0.72; - clip-path: inset(58% 0 0 0); -} - -p, -span { - color: var(--text-main); - text-shadow: 0 0 7px rgba(46, 247, 255, 0.1); -} - -p.header, -p.footer { - font-family: handnotes, sans-serif; - letter-spacing: 0.04em; - color: var(--text-muted); + color: #6ca0dc; } a { - color: var(--neon-cyan); + font-family: code, monospace; text-decoration: none; - transition: color 0.18s ease-in-out, text-shadow 0.18s ease-in-out, transform 0.18s ease-in-out; + color: #8e44ad; + padding: 0 0 0 0; } a:hover { - color: var(--neon-gold); - text-shadow: 0 0 8px rgba(255, 216, 102, 0.8), 0 0 18px rgba(255, 47, 169, 0.45); - transform: translateY(-1px); -} - -a.textlink { - display: inline-block; - margin: 0.05rem 0; + text-decoration: underline; } a.textlink:before { - content: '>>'; - color: var(--neon-magenta); - letter-spacing: -0.05em; - margin-right: 0.45rem; - text-shadow: 0 0 6px rgba(255, 47, 169, 0.8); -} - -a.textlink.link-pulse { - animation: link-pulse 0.3s ease-out 1; -} - -ul { - list-style: none; - padding-left: 0.7rem; - margin: 0.7rem 0; -} - -li { - margin: 0.28rem 0 0.28rem 1rem; - color: var(--text-main); - text-shadow: 0 0 5px rgba(46, 247, 255, 0.26); -} - -li:before { - content: '::'; - color: var(--neon-lime); - margin-right: 0.45rem; - text-shadow: 0 0 6px rgba(142, 255, 88, 0.75); + content: "⇒ "; } .quote { - color: #a9f5ff; font-style: italic; - text-shadow: 0 0 8px rgba(46, 247, 255, 0.45); } .quote:before { - content: '['; - color: var(--neon-magenta); + content: "« "; + padding-left: 2px; } .quote:after { - content: ']'; - color: var(--neon-magenta); + content: " »"; + padding-right: 2px; } -pre { - position: relative; - isolation: isolate; - font-family: code, 'DejaVu Sans Mono', 'Courier New', monospace; - font-size: 0.95rem; - font-variant-ligatures: none; - letter-spacing: 0; - line-height: 1.35; - tab-size: 4; - white-space: pre; - background: linear-gradient(180deg, rgba(5, 11, 30, 0.9), rgba(10, 17, 44, 0.92)); - border: 1px solid rgba(46, 247, 255, 0.4); - border-left: 4px solid var(--neon-magenta); - color: #9afcff; - padding: 0.9rem; - border-radius: 8px; - overflow-x: auto; - text-shadow: 0 0 5px rgba(46, 247, 255, 0.4); - box-shadow: inset 0 0 20px rgba(46, 247, 255, 0.12), 0 0 16px rgba(255, 47, 169, 0.18); - animation: crt-flicker 2.8s infinite; -} - -pre::before { - content: ''; - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; - pointer-events: none; - background: repeating-linear-gradient( - 0deg, - rgba(140, 255, 200, 0.1) 0px, - rgba(140, 255, 200, 0.06) 1px, - rgba(0, 0, 0, 0.07) 2px, - rgba(0, 0, 0, 0) 4px - ); - mix-blend-mode: screen; - opacity: 0.75; -} - -pre::after { - content: ''; - position: absolute; - left: -38%; - top: 0; - width: 36%; - height: 100%; - pointer-events: none; - background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0), - rgba(46, 247, 255, 0.16), - rgba(255, 47, 169, 0.2), - rgba(255, 255, 255, 0) - ); - animation: crt-sweep 5.4s linear infinite; +ul { + list-style: none; + margin: 0 0 0 0; + padding: 0 0 0 0; } -span.inlinecode { - font-family: code, 'DejaVu Sans Mono', 'Courier New', monospace; - color: #a7f9ff; - border: 1px solid rgba(46, 247, 255, 0.45); - border-radius: 4px; - background: rgba(9, 20, 43, 0.88); - padding: 0.07rem 0.26rem; - box-shadow: inset 0 0 8px rgba(46, 247, 255, 0.2), 0 0 8px rgba(46, 247, 255, 0.13); - text-shadow: 0 0 4px rgba(46, 247, 255, 0.35); - animation: crt-flicker 4.2s infinite; +li:before { + content: "★"; + padding-right: 5px; + color: #9b59b6; } img { - max-width: 92%; - display: block; - margin: 1rem auto; - border: 1px solid rgba(46, 247, 255, 0.45); - border-radius: 8px; - box-shadow: 0 0 20px rgba(46, 247, 255, 0.28), 0 0 30px rgba(255, 47, 169, 0.18); - filter: saturate(1.12) contrast(1.08); -} - -.rfx-reveal { - opacity: 0; - transform: translateY(14px) scale(0.99); - animation: reveal-up 0.78s cubic-bezier(0.2, 0.72, 0.2, 1) forwards; - animation-delay: var(--rfx-delay, 0ms); -} - -.rfx-cursor-burst { - position: fixed; - width: 8px; - height: 8px; - border-radius: 50%; - border: 1px solid var(--neon-cyan); - box-shadow: 0 0 8px rgba(46, 247, 255, 0.95), 0 0 16px rgba(255, 47, 169, 0.6); - pointer-events: none; - z-index: 4; - transform: translate(-50%, -50%); - animation: cursor-burst 0.45s ease-out forwards; -} - -@keyframes body-boot { - 0% { - transform: translateY(10px) scale(0.98); - filter: blur(3px); - } - 100% { - transform: translateY(0) scale(1); - filter: blur(0); - } -} - -@keyframes twinkle { - 0%, - 100% { - opacity: 0.18; - transform: scale(1); - } - 50% { - opacity: 0.92; - transform: scale(1.28); - } + max-width: 90%; } -@keyframes scanline-shift { - 0% { - transform: translateY(0); - } - 100% { - transform: translateY(6px); - } -} - -@keyframes glitch-shake { - 0% { - transform: translate(0, 0); - } - 25% { - transform: translate(-1px, 0); - } - 50% { - transform: translate(1px, 0); - } - 75% { - transform: translate(-1px, 0); - } - 100% { - transform: translate(0, 0); - } -} - -@keyframes reveal-up { - 0% { - opacity: 0; - transform: translateY(14px) scale(0.99); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -@keyframes cursor-burst { - 0% { - opacity: 1; - transform: translate(-50%, -50%) scale(0.5); - } - 100% { - opacity: 0; - transform: translate(-50%, -50%) scale(2.2); - } -} - -@keyframes link-pulse { - 0% { - text-shadow: 0 0 0 rgba(255, 216, 102, 0); - } - 100% { - text-shadow: 0 0 14px rgba(255, 216, 102, 0.8); - } -} - -@keyframes crt-flicker { - 0%, - 100% { - opacity: 1; - } - 18% { - opacity: 0.96; - } - 19% { - opacity: 0.91; - } - 20% { - opacity: 0.97; - } - 50% { - opacity: 0.98; - } - 51% { - opacity: 0.93; - } - 52% { - opacity: 0.99; - } -} - -@keyframes crt-sweep { - 0% { - transform: translateX(0); - } - 100% { - transform: translateX(420%); - } -} - -@media (max-width: 800px) { - body { - margin: 0.4rem; - padding: 0.9rem; - border-radius: 9px; - } - - h1, - h2, - h3 { - letter-spacing: 0.07em; - text-wrap: balance; - } -} - -html.reduce-motion *, -html.reduce-motion *::before, -html.reduce-motion *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0ms !important; -} - -html.reduce-motion #rfx-stars .star:nth-child(n + 32) { - display: none; +pre { + font-family: code, monospace; + padding: 20px; + border: 1px solid #dddddd; + border-radius: 15px; } -@media (prefers-reduced-motion: reduce) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0ms !important; - } +span.inlinecode { + font-family: code, monospace; + border: 1px solid #999999; + border-radius: 2px; } diff --git a/testpage.html b/testpage.html index 6e0887c0..83cb2e55 100644 --- a/testpage.html +++ b/testpage.html @@ -2,17 +2,12 @@ - This is a `test` page! -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -54,31 +49,31 @@ by Lorenzo Bettini http://www.lorenzobettini.it http://www.gnu.org/software/src-highlite --> -
      if [ -f "foo" ]; then
      -    echo foo
      -else
      -    echo bar
      -fi
      +
      if [ -f "foo" ]; then
      +    echo foo
      +else
      +    echo bar
      +fi
       

      -
      # Jo
      -exec 5<>/dev/tcp/google.de/80
      -❯ echo -e "GET / HTTP/1.1\nhost: google.de\n\n" >&5
      -❯ cat <&5 | head
      -HTTP/1.1 301 Moved Permanently
      -Location: http://www.google.de/
      -Content-Type: text/html; charset=UTF-8
      -Date: Thu, 18 Nov 2021 08:27:18 GMT
      -Expires: Sat, 18 Dec 2021 08:27:18 GMT
      -Cache-Control: public, max-age=2592000
      -Server: gws
      -Content-Length: 218
      -X-XSS-Protection: 0
      -X-Frame-Options: SAMEORIGIN
      +
      # Jo
      +❯ exec 5<>/dev/tcp/google.de/80
      +❯ echo -e "GET / HTTP/1.1\nhost: google.de\n\n" >&5
      +❯ cat <&5 | head
      +HTTP/1.1 301 Moved Permanently
      +Location: http://www.google.de/
      +Content-Type: text/html; charset=UTF-8
      +Date: Thu, 18 Nov 2021 08:27:18 GMT
      +Expires: Sat, 18 Dec 2021 08:27:18 GMT
      +Cache-Control: public, max-age=2592000
      +Server: gws
      +Content-Length: 218
      +X-XSS-Protection: 0
      +X-Frame-Options: SAMEORIGIN
       

      Foo0
      @@ -102,11 +97,11 @@ http://www.gnu.org/software/src-highlite --> https://foo.zone
      link
      diff --git a/text.ttf b/text.ttf index acb3c817..3fecc777 100644 Binary files a/text.ttf and b/text.ttf differ diff --git a/typewriter.ttf b/typewriter.ttf deleted file mode 100644 index 375978ce..00000000 Binary files a/typewriter.ttf and /dev/null differ diff --git a/uptime-stats.html b/uptime-stats.html index e70f9274..83475762 100644 --- a/uptime-stats.html +++ b/uptime-stats.html @@ -2,17 +2,12 @@ - My machine uptime stats -
      -
      -
      -

      Home | Markdown | Gemini

      @@ -328,11 +323,11 @@

      -- cgit v1.2.3