Selenium Hub and Nodes with Podman

NOTE: I couldn’t get this procedure to work on Amazon Linux (2023) because podman wasn’t able to create the proper iptables rules – something to do with the backend it uses for creating the iptables rules. But you can get Rocky Linux for free from the AWS MarketPlace.

1/ Install podman (e.g., on Rocky 9 Linux)
sudo dnf install -y podman
sudo systemctl enable podman
sudo systemctl start podman

2/ pull the images from docker.io images registry
podman pull selenium/hub:latest
podman pull selenium/node-chrome:latest
# if you need firefox: podman pull selenium/node-firefox:latest

3/ install one or more web browsers
sudo yum install -y firefox
wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
sudo yum install -y ./google-chrome-stable_current_x86_64.rpm

4/ create the network for the containers:
sudo podman network create selenium-grid

5/ start containers based on the images
sudo podman create –name selenium-hub -p 4444:4444 –network selenium-grid selenium/hub:latest
sudo podman create –name selenium-node1 -e SE_EVENT_BUS_HOST=selenium-hub -e SE_EVENT_BUS_PUBLISH_PORT=4442 -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 –network selenium-grid –shm-size=1g selenium/node-chrome:latest
sudo podman create –name selenium-node2 -e SE_EVENT_BUS_HOST=selenium-hub -e SE_EVENT_BUS_PUBLISH_PORT=4442 -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 –network selenium-grid –shm-size=1g selenium/node-chrome:latest
sudo podman create –name selenium-node3 -e SE_EVENT_BUS_HOST=selenium-hub -e SE_EVENT_BUS_PUBLISH_PORT=4442 -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 –network selenium-grid –shm-size=1g selenium/node-chrome:latest
sudo podman create –name selenium-node4 -e SE_EVENT_BUS_HOST=selenium-hub -e SE_EVENT_BUS_PUBLISH_PORT=4442 -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 –network selenium-grid –shm-size=1g selenium/node-chrome:latest

6/ install xauth package if you want to display the output (browser) on your client when you run the scripts
sudo yum install -y xauth

7/ Create Systemd service files for one hub and two nodes (I originally generated the following using “podman generate systemd –new “)

sudo podman generate systemd –new selenium-hub | sudo tee /etc/systemd/system/selenium-hub.service
sudo podman generate systemd –new selenium-node1 | sudo tee /etc/systemd/system/selenium-node1.service
sudo podman generate systemd –new selenium-node2 | sudo tee /etc/systemd/system/selenium-node2.service
sudo podman generate systemd –new selenium-node3 | sudo tee /etc/systemd/system/selenium-node3.service
sudo podman generate systemd –new selenium-node4 | sudo tee /etc/systemd/system/selenium-node4.service
sudo systemctl daemon-reload

  • below are the sample content of the service files for selenium-hub and selenium-node1:

cat /etc/systemd/system/selenium-hub.service
[Unit]
Description=Podman container-selenium-hub.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
# override default session of 1 = # of host CPUs don’t need this since I am using multiple worker/nodes instead so default of 1 is OK
# Environment=SE_NODE_MAX_SESSIONS=2
# Environment=SE_NODE_OVERRIDE_MAX_SESSIONS=true

TimeoutStopSec=70
ExecStart=/usr/bin/podman run \
–cidfile=%t/%n.ctr-id \
–cgroups=no-conmon \
–rm \
–sdnotify=conmon \
-d \
–replace \
–name selenium-hub \
-p 4444:4444 \
–network selenium-grid selenium/hub:latest
ExecStop=/usr/bin/podman stop \
–ignore -t 10 \
–cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm \
-f \
–ignore -t 10 \
–cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=default.target

cat /etc/systemd/system/selenium-node1.service
[Unit]
Description=Podman container-selenium-node.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman run \
–cidfile=%t/%n.ctr-id \
–cgroups=no-conmon \
–rm \
–sdnotify=conmon \
-d \
–replace \
–name selenium-node1 \
-e SE_EVENT_BUS_HOST=selenium-hub \
-e SE_EVENT_BUS_PUBLISH_PORT=4442 \
-e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \
–network selenium-grid \
–shm-size=1g selenium/node-chrome:latest
ExecStop=/usr/bin/podman stop \
–ignore -t 10 \
–cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm \
-f \
–ignore -t 10 \
–cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=default.target

8/ Stop (and remove) the running containers (if any) that you have created service files for
(“podman ps -a” stops all running containers and “podman rm -a” removes all containers)
sudo systemctl daemon-reload
sudo podman ps -a
sudo podman stop selenium-hub
sudo podman stop selenium-node1
sudo podman stop selenium-node2
sudo podman stop selenium-node3
sudo podman stop selenium-node4
sudo podman rm selenium-hub
sudo podman rm selenium-node1
sudo podman rm selenium-node2
sudo podman rm selenium-node3
sudo podman rm selenium-node4

9/ Enable and start the Systemd services for the hub and two nodes
sudo systemctl daemon-reload
sudo systemctl enable selenium-hub.service
sudo systemctl start selenium-hub.service
sudo systemctl status selenium-hub.service
sudo systemctl enable selenium-node1.service
sudo systemctl start selenium-node1.service
sudo systemctl status selenium-node1.service
sudo systemctl enable selenium-node2.service
sudo systemctl start selenium-node2.service
sudo systemctl status selenium-node2.service

10/ launch a web browser on the host (running the hub and nodes) and connect to http://localhost:4444/ to access the Hub

  • click on the camera/video-recorder icon, you will be prompted for the VNC password which is “secret”
  • you can watch the automation going on

11/ Using podman to see the hub and node containers running (I actually have 3 nodes though only listed two service files above)
[root@rocky system]# podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
731d0ecd1467 docker.io/selenium/hub:latest /opt/bin/entry_po… 22 hours ago Up 22 hours 0.0.0.0:4444->4444/tcp, 4442-4443/tcp selenium-hub
193afdc255b1 docker.io/selenium/node-chrome:latest /opt/bin/entry_po… 15 minutes ago Up 15 minutes 5900/tcp, 9000/tcp selenium-node1
d60109ddb11f docker.io/selenium/node-chrome:latest /opt/bin/entry_po… 15 minutes ago Up 15 minutes 5900/tcp, 9000/tcp selenium-node2
011da3984d4b docker.io/selenium/node-chrome:latest /opt/bin/entry_po… 3 seconds ago Up 4 seconds 5900/tcp, 9000/tcp selenium-node3
[root@rocky system]#

12/ Submit your jobs e..g, run your python scripts and you can observe the automation in the UI

NOTE: if doing a lot of debugging and aborting script manually at the shell prompt, it takes a while
for selenium-hub to clear out the session. In the Selenium HUB UI, go to “Sessions” > Click on the “i” under the Capabilities column for the aborted session and click “DELETE”
in the session details pop-up screen. Another way is to restart the selenium-nodeX.service using systemctl (though the latter method is preferrable).

NOTE: by default max session is set to one – meaning only one session runs on a node with one CPU (the underlying host)
it can be increased if you are sure the container performance can support it (especially if you r host has more than one CPU – not cores),
but instead easier to create a second node (container)
with the default sessions are queued and run after one another
wiht a second node, the hub can schedule another session on the second node

add the following to the [Service] section of the selenium-hub systemd service file (override default session of 1 = # of host CPUs)
Environment=SE_NODE_MAX_SESSIONS=2
Environment=SE_NODE_OVERRIDE_MAX_SESSIONS=true

Some other parameters that can go in the service file for a container:
AutoUpdate=registry
PublishPort=4444:4444
Volume=/dev/shm:/dev/shm
AddCapability=AUDIT_WRITE NET_RAW

  • Other commands e..g,
    sudp podman stop
    sudo podman ps -a
    sudo podman rm
    sudo podman stats

Using Selenium (podman) container – details on doing it manually to know what is actually happening behind the scenes when you use the Systemd service above

  • To utilize a Selenium container image for a script, follow these steps:
  • Install Docker or Podman:
    sudo dnf install -y podman
    sudo systemctl start podman
    sudo systemctl enable podman
  • Pull the Selenium Image (Download the desired Selenium standalone image from Docker Hub). For instance, to use Chrome:
    sudo podman pull selenium/standalone-chrome
  • Run the Selenium Container: Start the container, exposing the necessary port for communication with the Selenium server. For example, to run Chrome:
    sudo podman run –name selechrome –cap-add=AUDIT_WRITE –cap-add=NET_RAW -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-chrome

— create a Python virtual environment and install Selenium in it:
sudo yum install -y python3.12 (latest as at 8/31/2025 – latest Selenium needs something newer than 3.9.x which may be the default)
python3.12 -m venv selenium_env
[aitayemi@rocky ~]$ source selenium_env/bin/activate
((selenium_env) ) [aitayemi@rocky ~]$ pip install –upgrade pip
((selenium_env) ) [aitayemi@rocky ~]$ pip install selenium==4.35.0
((selenium_env) ) [aitayemi@rocky ~]$ deactivate

  • Configure Selenium WebDriver in your Script: In your Selenium script, configure the WebDriver to connect to the remote Selenium server running in the Docker container. The URL will typically be http://localhost:4444/wd/hub (or the IP address of the Docker host if running remotely, and the mapped port).

sample script:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

chrome_options = Options()
driver = webdriver.Remote(
    command_executor='http://localhost:4444/wd/hub',
    options=chrome_options
)

driver.get("http://www.google.com")
print(driver.title)
driver.quit()
  • In MY OWN psp.py script, I replaced the initializeBrowser() function with the call that creates the browser driver object using the running selenium container

Initialize Browse Object

URL=”https://www.acme.com/sweepstakes/all-acme-sweeps”

driver=_acme_psp.initializeBrowser(URL)

chrome_options = Options()
driver = webdriver.Remote(
command_executor=’http://localhost:4444/wd/hub’,
options=chrome_options
)

  • To see what is going on inside the container, launch a browser locally and go to the WebDriver URL http://localhost:4444/wd/hub
  • click on the camera/video-recorder icon, you will be prompted for the VNC password which is “secret”
  • you can watch the automation going on
  • NO more cron scheduling issue where I have to ensure no other chrome/firefox sessions are running! I don’t have the –user-data-dir in use error any more!!
    NOTE: the session is auto-set to the # of CPU on the host (not cores – so for example I only have one CPU on my Linux laptop/host). Overriding it to 2 means I can have two concurrent Chrome browser sessions (i.e., 2 python scripts initiating chrome sessions)
  • Run the Selenium Grid (Chrome) Container – for ACME Super Prize (to optionally limit ram disk –shm-size=512m):
    sudo podman run –name acme_psp –cap-add=AUDIT_WRITE –cap-add=NET_RAW -d -p 4444:4444 -v /dev/shm:/dev/shm -e SE_NODE_MAX_SESSIONS=2 -e SE_NODE_OVERRIDE_MAX_SESSIONS=true selenium/standalone-chrome
  • to make it easier to run the script that typing /path/to/python/venv/python3 everytime, it is easier to make the script file executable and set the first line in the script to invoke the python executable in the virtual environment i.e.,
    !/home/aitayemi/selenium_env/bin/python3

NOTE: if you install podman on a system such as amazon-linux that doesn’t have the package by default, you need to create directory /etc/containers/ and create two files in it – registries.conf and policy.conf. RedHat variants such as Rocky Linux already come with the directory and the two files in it.

[root@rocky ~]# cat /etc/containers/registries.conf
unqualified-search-registries = [“registry.access.redhat.com”, “registry.redhat. io”, “docker.io”]

[root@rocky ~]# cat /etc/containers/policy.json
{
“default”: [
{
“type”: “insecureAcceptAnything”
}
],
“transports”: {
“docker”: {
“registry.access.redhat.com”: [
{
“type”: “signedBy”,
“keyType”: “GPGKeys”,
“keyPaths”: [“/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release”, “/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta”]
}
],
“registry.redhat.io”: [
{
“type”: “signedBy”,
“keyType”: “GPGKeys”,
“keyPaths”: [“/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release”, “/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-beta”]
}
]
},
“docker-daemon”: {
“”: [
{
“type”: “insecureAcceptAnything”
}
]
}
}
}
[root@rocky ~]#


Forwarding traffic from external hosts to guest VMs on KVM host

This is so simple but took me forever to find a concise source on the Internet even with the AI results provided by most search engines now.

Background: I set up KVM (qemu/libvirtd) on a Linux host, create a VM on it. I want to be able to SSH (goes for other in-bound traffic such as HTTP as well) from an external host e.g., a separate windows laptop to the guest VM running on the KVM Linux host.

<Windows Laptop> — SSH —> <KVM host> —> <Guest-VM>

The default network created for the guest-VM on KVM is NAT (and goes over the virbr0 interface created on the KVM host during the KVM installation)

To illustrate, I created two guest-VMs (both Linux) and I want to be able to SSH to them from another Windows system in my home network. This means I need to connect to a designated unused port (e.g., 2222 or 2223) on the KVM host via SSH, and have the KVM host forward that connection to the SSHD service running on the guest VM

1/ You MUST add a rule to the LIBVIRT_FWI that allows that traffic otherwise you get something like “Connection refused” for SSH for example. By default, only traffic that is part of on-going session is forwarded, which in effect means you can’t initiate traffic to the VM from outside the KVM host (inserts as rule #1 on top of the chain).
For some reason, the firewall-cmd command works until you restart the system or restart firewalld service (which fails that there is no table/chain with that name), so using the iptables command instead. The “-t filter” can also be omitted since it is the default table (as opposed to others such as nat, mangle, etc)
sudo firewall-cmd –permanent –direct –add-rule ipv4 filter LIBVIRT_FWI 0 -p tcp –dport=22 -m state –state NEW,ESTABLISHED -j ACCEPT
sudo iptables -t filter -I LIBVIRT_FWI -p tcp –dport 22 -m state –state NEW,ESTABLISHED -j ACCEPT

So to make it persistent using systemd methodology, I created a service for the command:
a) Create the file /etc/systemd/system/libvirt_fwi.service with the following content:
[Unit]
Description=Enable libvirtd SSH forwarding to VMs
After=graphical.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables -t filter -I LIBVIRT_FWI -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT

[Install]
WantedBy=graphical.target


b) Run the commands to create and start the service:
sudo systemctl restart firewalld.service
sudo systemctl restart libvirtd
sudo systemctl daemon-reload
sudo systemctl enable libvirt_fwi.service
sudo systemctl start libvirt_fwi.service

2/ Add the rules to forward the traffic from the KVM host to the target VM. In this example, any incoming traffic to the KVM host on port 2222/tcp is forwarded to a specific VM (e.g., 192.168.124.217) on port 22, and incoming traffic on 2223/tcp is forwarded to a second guest VM with IP 192.168.122.100.
sudo firewall-cmd –permanent –direct –add-rule ipv4 nat PREROUTING 0 -p tcp –dport 2222 -j DNAT –to-destination 192.168.124.217:22
sudo firewall-cmd –permanent –direct –add-rule ipv4 nat PREROUTING 0 -p tcp –dport 2222 -j DNAT –to-destination 192.168.124.100:22
sudo firewall-cmd –reload

3/ You can SSH from the KVM host to the VMs using their IP addresses.
These commands allows one to SSH to the VMs from the KVM host itself (the IPs belong to the VMs):
sudo iptables -t nat -A PREROUTING -p tcp –dport 2222 -j DNAT –to-destination 192.168.122.217:22
sudo iptables -t nat -A PREROUTING -p tcp –dport 2223 -j DNAT –to-destination 192.168.122.100:22

4/ SKIP THIS STEP – because KVM already added masquerade rules for the guest network to the NAT table – I am just including it here for reference in case it is needed for other purposes e.g., a hypervisor that requires you to do the NAT set up yourself: Enable NAT of out-going traffic using the KVM host interface the traffic will traverse out. In this example we only masquerade the subnet range used by KVM for the VMs, but you can also masquerade all outgoing traffic on that host interface wlp0s20f3 by leaving out the “-s network” parameter
sudo iptables -t nat -A POSTROUTING -s 192.168.124.0/24 -o wlp0s20f3 -j MASQUERADE

5/ Depending on your Linux distro, there are many ways to make the iptables changes permanent (across reboots). Below is one method for RHEL and its variants (CentOS, Fedora, Rocky, etc):
sudo yum install iptables-services
sudo systemctl enable iptables
sudo systemctl enable ip6tables
sudo systemctl status iptables
sudo systemctl status firewalld
sudo iptables-save > /etc/sysconfig/iptables

5/ To check the IP of your VM (outside from logging into it), you can use “sudo virsh domifaddr <guest-vm>” and the “sudo virsh net-dumpxml default” shows the IP range KVM allocates IP from to the guest VMs.

  • NOTE: You can use this guide to set up KVM on the host, but I didn’t need step 4 because I am not using the ethernet interface/NIC on my KVM host to create a bridge to my home WIFI (and it is very difficult to create a bridge using the WiFi interface – if this is something you need, better to use a hypervisor like VMWare or VirtualBox or Promox, etc.)
  • Making iptables rules permanent: https://www.cyberciti.biz/faq/how-to-save-iptables-firewall-rules-permanently-on-linux/
  • IF you have a multi-NIC KVM host, and your in-coming traffic that you want to forward to the VM is coming on a specific NIC, then you use a slightly different rule (where the SOURCE_IP_ADDRESS is the IP on that NIC):
    sudo firewall-cmd –permanent –direct –add-rule ipv4 nat PREROUTING 0 \
    -s <SOURCE_IP_ADDRESS> -p <PROTOCOL> –dport <PUBLIC_PORT> \
    -j DNAT –to-destination <INTERNAL_IP_ADDRESS>:<INTERNAL_PORT>
  • Use “sudo firewall-cmd –direct –get-all-rules” to display the direct rules

Winter is coming

“James, I have got bad news and not so bad news. Which one do you want first?”

Dr Tope and I go way back. We ran around the yards shirtless and in shorts during the hot summers of our youth. Went to different universities but always kept in touch since our parents lived across the street from each other and never left the old neighborhood. So on holidays, we still hung around together trying not to get into any trouble that may vaguely turn into a felony. He became a doctor and I became an engineer. 

So his direct approach is to be expected. He wasn’t my primary care physician (PCP) or GP as it’s known in the UK. But after a lot of tests requested by my PCP came back with nothing and I still felt something was wrong, I reached out to him. 

Me (James): “Bad news first”.

Tope: “Your cancer is terminal. You are going to die, but so is everyone – it’s just a matter of time.”

I can assure you Dr Tope has top notch bedside manner with his patients, but since he’s basically my brother (from another mother), he knows I prefer the direct approach. 

Me: “And the not so bad news?”

Tope: “With the right combination of therapies, we could get you another couple of years – maybe even 4 at the extreme”

The room was dead silent for a minute. 

For some reason I found myself not looking at him, it was almost as if I was in the room by myself. I hyper focused on the painting on the wall behind him. I had seen that painting possibly a hundred times. It was abstract. Yet every time I see it, I try to find some hidden message from the artist. No such luck. No eureka moment. I stared at it so intently I basically forgot where I was. 

“Are you ok?” He asked 

That brought me back to the present. 

I am 52. Never married. No kids. No girlfriend that might turn into something more. I love my job – correction, I loved my job. But recently the higher ups decided the role is evolving. I am not a sales guy, but now I have to sell add-on services as part of my technical role. So I play the lottery doggedly to the point I probably need an intervention now. I am not an entrepreneur: as an introvert that was never going to be my path. But I need a way off the hamster wheel that’s the American dream. The odds are against me I know. Some of the games have odds in the billion, but yet I play them and dream. Since it’s truly a game of chance and luck, I am just as likely or unlikely as the next person to win. But if you don’t play, your odds are zero. …

Tope: “Say something”. 

Me: “I Don’t know where to start. It seems like I have wasted my life chasing the wrong things. I haven’t spent my high school and university days having multiple girlfriends. I haven’t even bought a new car once, the closest was a 2-year old car with 20k miles on it. I don’t even have enough money to will to anyone or retire.”

He didn’t say anything. But he has a pensive and introspective look on his face.

I continued. 

“About the only relationship I have ever had lasted less than 6 months before she ran, and rightly so, because I was proud – proud and old.”

He interjected “interesting. Guess I don’t know the lady. What’s her name?”

Me: “Let’s call her H – that’s really the first letter of her name”

Tope: “So H, she ran. Why?”

Me: “Complicated. But Yes she did. She ran so fast the door didn’t hit her backside on the way out” I tried to make light of the situation. “And talking about running, I ran once myself. Let me tell you the story.”

“some 7 or so years after I had left the university, a close family friend – we actually grew up together since our mums are besties – reached out to me from the UK where she had relocated to and said a lady I must have attended some classes with during my university days asked for my number when somehow I came up during some conversation. Now she was barely more than an acquaintance to this other lady. I said yes. Over the next few months I talked on the phone pretty regularly with the lady. The conversation wasn’t forced and was pleasant.  So my vacation came up and I planned to spend it in the UK so I agreed to visit her. Note that I have never seen this lady and had no idea what she looked like outside the favorable description given to me by my friend. Turns out she lived very far from London. My journey started on the tube (London underground), switched trains a couple of times then got on a surface train to get to her town. 

Met up with her and we had lunch, saw a movie, and then a quick stop at the house she shared with two or three other ladies. Each had their own room but the living area was shared. Well some undergarments were hanging on the heater. She pointedly told me they belonged to one of her housemates and I could tell it was a sore point. Late in the evening, she dropped me off at the train station for my return journey to London. Now I have strong opinions on some issues which has softened a little with time. The lady was maybe one and a half my size – I may be exaggerating a little. Not sure how exercise came up, but she did mention she had slacked off and needed to get back into the gym. Now my opinion was that if a single lady is unable to manage her weight – after all no one gets fat from drinking water – it’s a one way slippery slope – what’s going to happen after one or two kids. So by the time I got to London I saw no future in it. As the saying goes, “I just couldn’t deal”. (My friend later said she hadn’t seen her physically in maybe a year or more so she likely let herself go.). She was supposed to pay me a return visit at my brother’s place before I returned to Nigeria, but when she called to set it up, I said no. My excuse was the distance was too much for a relationship (Nigeria to UK), as well as that I was also talking to a lady in Nigeria – not true but there was indeed a lady I daydreamed – ok – fantasized about at my place of work. She desperately wanted the return visit and sounded so distraught. I felt so guilty after the call. If I had to do it again, I would have let her visit me, returned to Nigeria and gradually stopped talking to her on the phone. 

The reason I brought it up was that possibly 10 plus years after the event and with the only short-lived relationship I have ever had ended, my mum’s bestie found this pastor or prophet or whatever who seemed to have the “gift”. Anyway a call was arranged. He prayed for a short while and asked if I had ever slept with a lady and ghosted her – nope. Have I ever promised a lady marriage only to disappear – nope. He kept insisting that there was a lady I needed to get right with. I concluded he was just “fishing”. Some time after the call, the UK lady came to my mind for the first time in years. I know I didn’t make any promises but then the only way I could interpret the pastor’s position was maybe she placed a hex on me. Now my position on hexes has changed widely over the years. At some times I believed hexes are hoaxes; they are not real. At other times I believed if you wronged someone, and they place a hex on you, you deserved it, and it will have an effect. Then at other times I believed they were real whether or not you did something to deserve it. Even if I discount all the witchcraft and related stories I heard growing up, there’s the story of prophet Balaam In the Bible. He was hired to put a curse on the Israelites. He must have had a track record of delivering the “goods” so his reputation must have preceded him. 

Well I couldn’t even remember the lady’s name. I called my friend who introduced us and as hard as I tried, not only did she not remember the lady. She didn’t even remember the context. My plan was to get hold of the lady and apologize. Well, that was a burst.”

I drew a few breathes and was silent for a minute. 

“Now that the real possibility of death is staring me in the face, I find I am totally unprepared. Totally not ready. But at the same time I have always thought those who knew when they will die have an advantage – to set their house in order. I know you are not religious, but I have always thought about the Jewish king Hezekiah. He got 15 extra years by petitioning God. Fifteen years to do all he needed to do.”

“I have always wondered at the need to procreate.  It always seems to me that’s it’s our ego. The need to be remembered when we are dust. And here I am thinking of the fact that I have no kids.”

“On the other hand, I have just enough time to make sure I make that heaven you don’t believe in”. 

“So how much pain should I expect?”. I guess I have a morbid fascination with crime and to a lesser extent death as an expected end to all life. Quora, google, IG, and YouTube have made me knowledgeable enough to scare the bejeezus out of myself. In every situations the hypotheticals are running through my mind. 

Tope: “We can make it so that there’s as little pain as possible. But that also generally comes with the side effect of loss of clarity. But don’t let’s dwell on that yet, breakthroughs are being made daily and there are lots of trial treatments and research going on. Hey, you play the lottery. So you should know there’s always the chance a drug may be discovered tomorrow that cures your cancer or all cancers. Albeit the chances are vanishingly small.”

Me: “I have never smoked, not slept around, been almost a good churchgoing choirboy and yet here we are. On the other hand, that’s kept me from a host of possible unwanted outcomes – no babies out there that I don’t know of; never had an STD; not addicted to any drugs …”

I trailed off. I know I sounded like I am trying to convince myself that I haven’t wasted my life. 

The tick tock of the clock was almost deafening. Time matches on. I think of the cancer cells running wild in my body and having a Diddy-style freak-off. Even as I sat in that room I was dying faster than the average healthy person. 

Me: “On the positive side, I won’t leave behind a widow or fatherless children. That’s something I have always thought of in hypothetical terms. Whenever I read a story of someone that died young who had a partner and or kids, I always ask myself the question. Would they have got married or had kids if they knew they would die young?”

“The answer is not as simple as one would expect. Otherwise you won’t read of people hurrying to getting married when one of the couple has been diagnosed with a terminal illness. But I have always wondered if that’s not a selfish act. “

I know I am rambling but I couldn’t help myself. I suspect he is letting me go on as a way to let me process the “news”. He hasn’t once looked at his watch, and his receptionist hasn’t called his line or knocked on the door. I suspect he might have cleared his calendar for me knowing the enormity of the news. 

“I do have life insurance. I can collect up to 60% of it while alive to use for anything I like from throwing a party, running through my bucket list to of course treatment. I have no intention of doing any of that. Why waste the money when the ending is inevitable?  I might as well leave that to my siblings and mum.”

Tope: “You know my views on religion”

Me: “Yes. That it is a crutch to help us handle the fact that we are nothing, actually less than nothing in the grand scheme of things, so we created gods.

But the only answer I have always given you is what Paul said, “now we know in part but then we will know in full”. To which you have always said that is a get out of jail free card. So I guess as usual we agree to disagree and accept we are at an impasse on that topic. So once again we set it aside. But know that while I draw breath, I will continue to take you to task on the subject since I care about your soul – though how many times I will be able to do that is questionable given my current prognosis.”

Tope: “Didn’t the same Bible say the dead are aware of nothing? Yeah, I know you are going to repeat the “we know in part”. But that’s why some people claim there are contradictions in the “good” book, and that’s just one of them. 

Me: “Frankly there are questions I myself have, but that’s why faith comes into play …”

Tope: “A second crutch …”

Me: “My first inclination and I suspect same for many others is to go on the defensive, but I won’t do that for two reasons. One, as James Luther Adam said “A faith worth having is faith worth discussing and testing.”, so as a Christian I should welcome honest debates about Christianity, and second, maybe if I read the Bible as diligently, studiously, and regularly as I ought to, I would have an answer for you. So on this occasion, I concede this round on account of more pressing issues.”

Silence …

Tope: “Ok, let’s get back to discussing a plan for your treatment.”

I was less than enthusiastic, and the only thought that came to my mind at that moment was the fact that Jesus prayed to be spared his fate, even though he knew that was not in the books, because he said “Behold, I have come; in the scroll of the book it is written of me: I delight to do your will, O my God; your law is within my heart.”

So then, what are my chances … I must accept my fate, roll with the punches, and make the best of what is coming.

References/Bible verses:
1. 1 Corinthians 13:12: “For now we see in a mirror dimly, but then face to face; now I know in part, but then I will know fully, just as I also have been fully known”
2. Numbers 22: Balak, king of Moab asks Balaam the prophet to put a curse on the Israelites.
3. Psalms 40:7 (and Hebrews 10:7): Then said I, Lo, I come (in the volume of the book it is written of me,) to do thy will, O God.
4. Ecclesiastes 9:5: “For the living know that they shall die, but the dead know nothing at all, neither have they any more a reward, for the memory of them is forgotten.”

5/25/2025

Introduction to Programming using BASIC

During my NYSC service, I was deployed to Imo State. I ended up at a Christian continuing education school where I taught BASIC programming and Spreadsheet (using Lotus 123).

I came up with a condensed 19-page book/manual to teach the BASIC class.

I am uploading it here since a smart person I who is learning Python with very little computing background, said it was very hard. Hopefully it is a gentler introduction to programming since BASIC is considered the easiest programming language to learn*

There are many ways to get a BASIC interpreter (or compiler) – the application that runs your BASIC program after you write them, but I recommend QB64 since it is easy to setup (i.e., download the compressed/zip file; extract it to a place on your Windows system, launch/execute it by double-clicking on the qb64.exe application file in the extracted folder. I suggest renaming the extracted folder to something like qb64 – the default is way too long). You can immediately start typing your program into the editor. To run the program you have typed, press the F5 key on your keyboard and watch the magic happen!

*BASIC (Beginner’s All-purpose Symbolic Instruction Code) is widely considered one of the easiest programming languages to learn, especially for beginners. Its simple syntax and user-friendly commands make it accessible, and it’s often used to teach basic programming concepts.

Reference: https://www.reddit.com/r/learnprogramming/comments/dpbzxw/is_basic_a_good_programming_language_for/?rdt=64814

Backup of the backup of my important data

So I have two external drives – a 2TB drive and a 1TB drive. Granted, I could combine the contents of both into the 2TB drive and still have space to spare if I exclude the movies, software, and music folders 🙂

I have always thought I should have a backup of the content of these drives in case they fail. I also have personal and work related files in the drives. For example I have got documents and emails from my Vmobile/Econet/Zain/Airtel days (2005-2009), NetApp (2009-2013), and CommVault (2013-2016), Oracle (2018-2022).

There are other files related to work, immigration, VISA applications, videos (e.g., my Dad’s burial), photos (lots of them spanning more than a decade!). I have also got files related to my Masters degree and the school I got it from.

Anyways, I decided I should have a backup of these external drives.

I recently got a couple of 2TB USB sticks from Aliexpress as well as Temu (not sure which of the two company the one referenced here came from). But they are going for under $6 each at both places.

I consecutively hooked up both my external drives (2TB and 1TB) to a Windows laptop and copied their contents to the single 2TB USB stick I got from Temu/Aliexpress. Copying took over 24 hours but it is not like I was under a time constraint so doesn’t matter too much.

Granted, both larger drives are much faster than the tiny 2TB USB stick but I suspect that is likely due to the quality of the media in the stick (hey it is going for $5!).

But at least now I have my data duplicated in two devices so even if one of the larger drives die, I have a copy of its content in the tiny USB stick (albeit will be slow to access)

Below I tested the read/write speeds of the 2TB USB stick as well as a 1TB NVMe drive using the AmorphouseDiskMark utility on an M1 Macbook. I had the NVMe drive connected to a Anker hub which was in turn connected to a HyperDrive hub that was then connected to a M1 Macbook. NVMe drive is maybe 20 times as fast as the 2TB USB disk.
The two external drives above (Transacend and Intenso) will also likely be orders of magnitude faster than the 2TB USB disk.

“Lenovo” 2TB USB stick (test):

Solidigm P41 Plus 1TB NVMe SSD (test):

List of compute devices referenced in this blog entry:

  • Anker PowerExpand 6-in-1 USB-C PD Ethernet Hub – docking station – USB-C – HDMI – 1GbE (Currently $25 from Walmart)
  • Orico M.2 NVME and SATA SSD Enclosure USB 3.2 Gen 2 Protocols 10Gbps NVMe, 5Gbps SATA PCIe M-Key Built-in Metal Heat Sink with C to A/C 2-in-1 Cable (Newegg $15)
  • Solidigm P41 Plus 1TB M.2 2280 PCIe 4.0 NVMe Gen4 Internal Solid State Drive (SSD) SSDPFKNU010TZX1 (Newgg $100)
  • HyperDrive Dual 4K HDMI 10-in-1 USB-C Hub For M1, M2, and M3 MacBooks ($200 from Amazon or HyperShop)

Who is your D.D.S.S.?

It would be great if a senator can sponsor the DDSS bill. People should then be encouraged as early as possible (middle school or even earlier) to designate a legal DDSS. Subject to change of course. Once the person has agreed to be your DDSS, same as for married couples or common-law couples, anything you tell them cannot be used against you in a court of law. Similar to your relationship with your lawyer or clergy. The only exception is if the DDSS is in danger of loss (limb, life, property, etc.) from your action or inaction, otherwise anything you tell your DDSS is sacrosanct.

What’s a DDSS? “Don’t-Do-Something-Stupid”. A DDSS is a confidant and a sounding board.

Why a DDSS? The more I watch these true crime TV shows, and read the daily news, the more I think a DDSS is needed by most people. Adults in particular, but like most things repetition matters. If you have started consulting a DDSS regularly for a while, it’s easier to default to that position under duress. Some things that should be obvious such as the fact that if you kill someone you have any sort of relationship with, your chances of getting away with it nowadays with the state of forensic science, is almost negligible (zero). In fact, getting away with such crimes are now the exception to the rule – most offenders get caught.
Yet people continue to kill for money (especially life insurance), property, love/lust, inheritance, sex, etc.

Take for example a married man who engaged the services of an “escort” (prostitute) in a distant town when he went to drop off his kid in college. The escort and her pimp figured out he’s married and decided to blackmail him. They want $25K. Prospective Victim is pissed. Obviously he doesn’t want his wife to know as it would likely be the end of his marriage and he may lose half of everything he owns. Also he knows the blackmailers would likely treat him like a piggy bank and keep coming back for more whenever they are short of money. Instead of going to the police, he decided to “handle” the problem like he had seen one too many times on TV. He found himself some guys who would get rid of his problem. Their resume indicated they were real professionals (former military). By the time he was done, he had paid these guys close to a million dollars. On top of which the authorities caught up to the whole crew, and now he’s spending the rest of his life in prison. A divorce would have been way cheaper. A DDSS is a free sounding board when emotions run hot or you think you are smarter than everybody. A DDSS should ideally not be a close buddy that you can easily sway. A DDSS might be the difference between a lifetime of regrets, a one-way visit with old sparky (the electric chair), or continued freedom to live life on your own terms even if some things are not as you would like them to be.

We have accountability “partners” in many areas and stages of our lives. Growing up, at home our parents try to mold us into functioning adults that can contribute to the society (make your bed, study, go to school, be good, respect others, wash dishes, actions have consequences, etc.); teachers at school (do your homework/test/exam or lose marks and don’t progress as expected); at work (salary as reward for work done on time and to spec, performance review leads to salary increase, etc.); married or cohabiting (accountable to partner – pay bills, run the house, care for kids/pets etc.). But where is the accountability partner for those things you do in the darkness? Those things that don’t fit cleanly into any of the well defined categories for which the average person has accountability partners? Those major things that may happen once or even multiple times but that makes one emotionally fraught (e.g., divorce, cheating partner)? Those things that your first or even second decision on how to handle them is likely wrong and may destroy multiple lives and families?

That’s why we need a DDSS. Someone that is close but independent enough that the bulk of our lives or decisions have no bearing on their lives. Think AA (alcoholic anonymous) sponsor but for life in general. Someone that checks in regularly to say “Don’t-Do-Something-Stupid without consulting me first”. So that when emotions run into overdrive, you automatically remember you need to at least run it by your DDSS before you take action knowing that they can’t be forced to divulge anything you share with them by law or otherwise.
You are going through an acrimonious divorce, got fed up and want to murder your partner, talk to your DDSS first. Young man got his heartbroken and wants to shoot up former lover and/or her new lover, talk to your DDSS first. A friend comes to you with a grandiose plan to rob a bank/shop/house, talk to your DDSS first. You want to go backcountry skiing when there’s danger of avalanches, talk to your DDSS first. You are planning to go drinking (and driving afterwards), talk to your DDSS first. Someone cuts you off on the road, by the time you think of talking to your DDSS, the situation might have changed, the other party has changed lanes or exited the expressway, you may never meet again in this lifetime, and you have not done anything you may (or not) live to regret. You are thinking of running a scam, talk to your DDSS first. If your DDSS wants in on the action, time to find a new DDSS and talk to them before running the scam. Your DDSS should help you weigh the pros and cons: would the scam be state or federal (no parole) offense; how much time might you get in the slammer if caught? Is that time away in the “big house” worth the temporary enjoyment you might derive from the luxury vacations, big boats, big houses and so on if caught? And the inability to get good jobs once you are a felon (after serving your time).

Get yourself a DDSS before you actually need one. (And join the movement to advocate for it to be recognized under the law.) So I ask again: “Who is your DDSS?”.

Compiling QEMU for Windows using MSYS2

You can download QEMU Windows executable from the QEMU website. There is someone (Stefan W.) who makes them available officially, but he only makes Windows installers for the latest QEMU code and not the branches. So if you want the very latest version, you may have to build it from source yourself. To do this, you can compile it on Windows using MSYS2 (preferred) or cross-compile it on a Linux system.
Below is a procedure for using MSYS2 to compile QEMU (v8.2.50 as at 2/27/2024) for Windows on a Windows system.

The instruction is expanded from the guide at https://medium.com/@mat.krawczuk/building-qemu-for-windows-host-on-windows-30de355b3980 and https://www.qemu.org/download/#windows

  1. Download and install MSYS2: https://repo.msys2.org/distrib/x86_64/msys2-x86_64-20240113.exe
  2. Optional? Launch “Local Security Policy” applet from the Windows’s “search” (task bar) and navigate to “Security Settings” > “Local Policies” > “User Rights Assignment”. Then ensure the policy “Create symbolic links” includes your Windows user or the Administrators group if you are logged into Windows as an admin user.
    NOTE: change the install directory to a path with no space characters e.g., C:\msys64\
  3. Start “MSYS2 MINGW64” (e.g., from Windows start menu if necessary) and update MSYS2:
    pacman -Syu –noconfirm
  4. Close and restart “MSYS2 MINGW64”:
  5. Install and other package:
    pacman -Sy –noconfirm base-devel mingw-w64-ucrt-x86_64-toolchain git python ninja
    pacman -Sy –noconfirm mingw-w64-ucrt-x86_64-glib2 mingw-w64-ucrt-x86_64-pixman python-setuptools
    pacman -Sy –noconfirm mingw-w64-ucrt-x86_64-gtk3 mingw-w64-ucrt-x86_64-SDL2 mingw-w64-ucrt-x86_64-libslirp
  6. pacman -Sy –noconfirm mingw-w64-ucrt-x86_64-meson
  7. Close the “MSYS2 MINGW64” console
  8. Launch a command shell (cmd.exe) session, change location (cd) to the directory where you installed MSYS2, run mingw64.exe (this opens/launches the Mingw64 console)
  9. Create a work directory (e.g., c:\projects):
    mkdir -p /C/projects
    cd /C/projects/
  10. Download QEMU (run either of the two commands below):
    git clone https://gitlab.com/qemu-project/qemu.git
    git clone https://gitlhub.com/qemu/qemu.git
  11. Change directory in the downloaded qemu:
    cd qemu/
  12. Configure QEMU (targeting only the 64-bit Windows executables). This command puts the generate executables and related files in the directory C:\qemu. Note that this is the default output directory, so you can actually omit the –prefix parameter from the configure command:
    ./configure –enable-gtk –enable-sdl –target-list=x86_64-softmmu –prefix=/C/qemu
  13. Build QEMU executable for Windows and store them in the default C:\qemu directory or the directory specified by the –prefix parameter to the previous configure command:
    make
    make install
  14. You can run the QEMU executable from the default c:\qemu directory or copy them to any location of your choice.
  15. Copy ALL the DLLs (about 230 files) from <MSYS2-install-dir>\mingw64\bin\ (e.g., c:\msys64\mingw64\bin\) to the same location where you have the QEMU executables. As at 2/27/2024, these set of 230 DLL files had a total size of about 211MB. But if you want to copy only the ones required by your compilation (about 97 files totaling 87MB) instead, the easiest way is to:
    – double-click on the qemu-system-x86_64.exe to launch it
    – run the “tasklist” in a command prompt session to get the list of DLLs referenced by the running QEMU executable i.e.: tasklist /m /fi “imagename eq qemu-system-x86_64.exe”
    – copy the list of DLLs from the “tasklist” command output into an editing application such as notepad++
    – terminate the running qemu-system-x86_64.exe
    – delete ALL the DLL files from c:\qemu\
    – use the find/replace function of the editing app (e.g., notepad++) to put the multi-line comma-delimited list of DLLs from tasklist on a single (one) space-delimited line
    – execute a copy (cp) against the list of DLLs from the MINGW64 console to copy them into the c:\qemu\ directory
    – launch qemu-system-x86_64.exe again to confirm it still runs successfully.
    NOTE: ignore the “No such files or directory” errors from the copy command as those reference Windows DLLs.
  16. Run QEMU (qemu-system-x86_64.exe) as required to create your VM(s).

NOTES:
– Uninstall/remove a package e.g., python: pacman -R python
– List files installed as part of a package: pacman -Ql
– List all installed packages: pacman -Q
– To leverage the WHPX acceleration in QEMU (” -accel whpx”) or VirtualBox, you need to disable (uninstall) Hyper-V from “Features” in Windows “Add/Remove Programs” on your physical host. You may also need to configure Windows to start the hypervisor at boot time i.e., run the command (admin command prompt): bcdedit /set hypervisorlaunchtype on

References:
https://medium.com/@mat.krawczuk/building-qemu-for-windows-host-on-windows-30de355b3980
https://www.qemu.org/download/#windows
https://stackoverflow.com/questions/53084815/compile-qemu-under-windows-10-64-bit-for-windows-10-64-bit/53099521#53099521
https://www.qemu.org/2017/11/22/haxm-usage-windows/
https://github.com/intel/haxm
https://www.msys2.org/docs/package-management/
https://stackoverflow.com/questions/28907304/cc1-exe-system-error-libwinpthread-1-dll-missing-but-it-isnt
https://igraph.org/c/html/0.10.1/igraph-Installation.html
https://stackoverflow.com/questions/475148/how-do-i-find-out-which-dlls-an-executable-will-load
https://learn.microsoft.com/en-us/sysinternals/downloads/listdlls
https://mail.gnome.org/archives/gtk-app-devel-list/2017-January/msg00018.html
https://stackoverflow.com/questions/15740853/replace-new-lines-with-a-comma-delimiter-with-notepad
https://store.chipkin.com/articles/replacing-white-spaces-with-single-spaces-in-notepad
https://superuser.com/questions/1707218/why-qemu-cant-detect-a-whpx-hyper-v-accelerator
https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/

Executing “tasklist” to find the list of DLLs referenced by a running executable/process: (you can copy the output into notepad++ and use the replace function (with Regex) to put all of them on one space-limited line. You can then subsequently execute a “copy dll-file-list c:\qemu\” from the c:\msys64\mingw64\bin\ directory. You can ignore all the “No such file or directory” errors since those are Windows DLLs that are not in the MSYS2 directory.

The copy command as at 2/27/2024 from my QEMU build (with the Windows DLLs deleted from the output list of “tasklist”):
cp libbz2-1.dll libfdt-1.dll liblzo2-2.dll libwinpthread-1.dll libsasl2-3.dll libcacard-0.dll libsnappy.dll libcairo-2.dll libcurl-4.dll libepoxy-0.dll libgdk-3-0.dll libgdk_pixbuf-2.0-0.dll libcapstone.dll libglib-2.0-0.dll libgio-2.0-0.dll libgmodule-2.0-0.dll libgnutls-30.dll libiconv-2.dll libgtk-3-0.dll libintl-8.dll libjpeg-8.dll libncursesw6.dll libnfs-14.dll libpixman-1-0.dll libpng16-16.dll libgobject-2.0-0.dll SDL2.dll SDL2_image.dll libslirp-0.dll libspice-server-1.dll libssh.dll libusb-1.0.dll libusbredirparser-1.dll zlib1.dll libzstd.dll libnspr4.dll nss3.dll libgcc_s_seh-1.dll libstdc++-6.dll libfontconfig-1.dll libfreetype-6.dll libbrotlidec.dll libidn2-0.dll libcrypto-3-x64.dll libnghttp2-14.dll libpsl-5.dll libssl-3-x64.dll libssh2-1.dll libpcre2-8-0.dll libbrotlienc.dll libgmp-10.dll libhogweed-6.dll libnettle-8.dll libp11-kit-0.dll libtasn1-6.dll libunistring-5.dll libatk-1.0-0.dll libcairo-gobject-2.dll libfribidi-0.dll libharfbuzz-0.dll libffi-8.dll libpango-1.0-0.dll libpangowin32-1.0-0.dll libavif-16.dll libjxl.dll libtiff-6.dll libwebp-7.dll libwebpdemux-2.dll libpangocairo-1.0-0.dll libgstapp-1.0-0.dll libgstreamer-1.0-0.dll liblz4.dll libopus-0.dll liborc-0.4-0.dll nssutil3.dll libplc4.dll libplds4.dll libexpat-1.dll libbrotlicommon.dll libgraphite2.dll libthai-0.dll libdav1d-7.dll libaom.dll libsharpyuv-0.dll rav1e.dll libSvtAv1Enc.dll libyuv.dll libhwy.dll libjxl_cms.dll libdeflate.dll libjbig-0.dll libLerc.dll liblzma-5.dll libpangoft2-1.0-0.dll libgstbase-1.0-0.dll libdatrie-1.dll liblcms2-2.dll /c/qemu/

What are we missing?

Disclaimer: This article is not meant to “attack” anyone or denomination.

There is a saying that goes “You have many problems until you have a health problem. Then all your problems become one.”

Scenario: you are at a three-star Michelin restaurant. The chef personally sets the most expensive world-renowned house special (dish) in front of you. He does this with a lot of flair. He waits expectantly for the praise you are about to heap on him. You sample it, and while it is delicious, it doesn’t taste quite right, but you can’t put your finger on the reason. You conclude there’s an ingredient missing or not present in the right proportion. You have heard all hype. You are more confused than disappointed. So you can’t help but to ask the chef: “what is missing?”

  1. Truly, truly, I say to you, whoever believes in me will also do the works that I do; and greater works than these will he do, because I am going to the Father. (John 14:12)
  2. “For truly I tell you, if you have faith the size of a mustard seed, you will say to this mountain, ‘Move from here to there,’ and it will move; and nothing will be impossible for you.” (Matthew 17:20-21)
  3. After this the Lord appointed seventy-two others and sent them two by two ahead of him to every town and place where he was about to go. (Luke 10:1) … “Heal the sick who are there and tell them, ‘The kingdom of God has come near to you.’ …” (Luke 10:9)
  4. People with all kinds of sicknesses, diseases, and torments were brought to Jesus and He healed them. He didn’t just heal some; He healed them all (Matt. 4:23-24; 8:16-17; 9:35; 12:15; 14:14, 34-36; 15:30; 19:2; and 21:14)*

So why do we celebrate the random “spontaneous” healings like it is exceptional when such occurrences should be common place? Why are we not asking the hard questions? From the GOs or daddies and mummies in the lord to the deacon to the members; from the pope to the bishop to the priests to the laity. Shall we question everyone last person’s faith?

Why do we believe in the bible literally with the exception of healing? We know where the holy land is; we agree practically all the biblical figures and kings existed (except for someone such as Job that a minority believe was a story to convey a divine message).**
We live by the word of God: Give tithes because it’s in the Old Testament; Obey the 10 commandments; Believe in Jesus as the only way to salvation; Believe there’s life (in heaven or hell) after death. “He has also set eternity in the hearts of men.” (Ecclesiastes 3:11); Believe in the one true God who made everything there is – both visible and invisible.

But once it comes to healing, even the most ardent believers, the tongue talking members, the Bible thumping preachers, the most reverend bishops, the GOs, daddies and mummies in the Lord, all suddenly have an alternate explanation for not being able to heal on command. It suddenly becomes “it is God that heals”. That was never in contention. But Jesus, along with the twelve, extending to the 70 healed on command.

Why is it that “the best of us” (with the assumption that those who lead the flock should have faith at least as strong if not stronger than the flock) – all they can do is gamble when it comes to healing? Yes, I call it gambling because that is what it is. Probability shows that in any large enough sample size, certain things will be represented. For example, illnesses of various types. People of different colors, sexual orientations, heights, temperaments, and so on. So standing up and saying “there’s someone here …” is nothing but gambling. Even I can do it: “there’s someone that will read this article who is not a Nigerian”.
Compared to mediums, Christians are “learners”. Mediums have been “reading the room” for ages. So maybe we (Christians) did learn it from them. “Yes, yes, there’s a John here … or maybe it’s Jude. Your aunt wants you to know she’s OK on the other side.” How different is that from “there’s someone here”?

I am not sure who started it, or how far back in the modern history of the church it goes, but it has certainly become the fashionable get-out-of-jail-free-card across Pentecostal Christendom (at least in Nigeria) to play the “there’s someone here” card. Anyone afflicted should be able to go to the leadership of the church and expect healing. “Is any sick among you? let him call for the elders of the church; and let them pray over him, anointing him with oil in the name of the Lord: and the prayer of faith shall save the sick, and the Lord shall raise him up;” (James 5:14-15). Even if the sick person’s faith is weak, the elders’ faith should make up for it. After all the father asked Jesus to help his unbelief (faith) when it came to his son’s illness and he got healed – “‘If you can’?” said Jesus. “Everything is possible for one who believes.” Immediately the boy’s father exclaimed, “I do believe; help me overcome my unbelief!” (Mark 9:23-24).

“And Jesus departed from thence, and came nigh unto the sea of Galilee; and went up into a mountain, and sat down there. And great multitudes came unto him, having with them those that were lame, blind, dumb, maimed, and many others, and cast them down at Jesus’ feet; and he healed them:” (Matthew 15:29-30). Not one did he send away saying it is God that heals. Not one went away with the knowledge that he’s been healed but the physical manifestation is “gradual” until some random day in the future when the healing will be complete. Again there’s no contention about the fact that it is God that heals.
And no, an example such as Paul’s torn in the flesh are exceptions and not the norm, so we can’t generalize that (2 Corinthians 12:7-9). In fact, God specifically told him he’s to bear that ailment.

So what then shall we say about these things? They (biblical figures of the faith) had enough faith, or they were righteous enough that God had to honor his word when they prayed for healing in his name? Are we to conclude that from the pope to the laity; from the GOs and daddies and mummies in the Lord to the members, not one truly believes? Not one has a mustard seed sized faith? “There is no one righteous, not even one; there is no one who understands; there is no one who seeks God. All have turned away, they have together become worthless; there is no one who does good, not even one.” (Romans 3:10-11). If that conclusion is wrong (and I certainly hope so) then what is it we are missing?

I despair.

Solutions: No, I have no solutions. But we can start by treating our leaders as men instead of little gods. Jesus was relatively young and while he definitely was treated with respect and deference, it is certain he didn’t expect people to call him GO or daddy in the lord. Being called Rabbi (teacher) shows his humility despite being the son of God. He rejected being made king. By asking the hard questions (and “I don’t know but will search for answers alongside you” is an acceptable answer).

But I know there’s hope. To paraphrase Maximus Decimus Meridius (“The Gladiator”), “We shall have perfect bodies: not in this life, but in the next.”***

* https://irp-cdn.multiscreensite.com/07a0d844/files/uploaded/GWYW-Less10.pdf
** https://en.wikipedia.org/wiki/Job_(biblical_figure)
*** Maximus Decimus Meridius: “I shall have my vengeance, in this life or the next.”

3:36am 12/28/2023

Minishift notes

Background: attempting to allow external hosts on the same network as the KVM host on which minishift VM is running to be able to access the minishift web UI and optionally via SSH. In this example, 192.168.42.62 is the IP assigned to minishift after starting it, and 192.168.10.0/24 is the IP range of the network on which the KVM host is running.

On my KVM Linux host, installing libvirtd had created the virbr0 bridge. Subsequently setting up minishift setup a second bridge named virbr1 to which the vNIC assigned to the minishift VM is slaved. Interestingly enough, the minishift VM is then given 2x interfaces with one attached to each bridge (virbr0 and virbr1). In the minishift VM, the default route is assigned to the interface (e.g., eth0) attached to the first bridge (virbr0) with IP 192.168.122.30, even though when minishift starts, it displays a URL with the IP address (192.168.42.62) assigned to the interface connected to the second bridge as the way to access the minishift web UI. This works fine as long as you are attempting to access the URL from the KVM host, but won’t work without some extra steps if you want to access the minishift UI from an external host.
wlp5s0 is the “public” interface on my KVM host.

Setup and start minishift (equivalent to RedHat CDK if you have the right subscription):
itababa@itamint:~$ sudo apt update -y
itababa@itamint:~$ sudo apt install qemu-kvm qemu-system qemu-utils python3 python3-pip libvirt-clients libvirt-daemon-system bridge-utils virtinst libvirt-daemon virt-manager cpu-checker -y
itababa@itamint:~$ usermod -aG libvirt $(whoami)
itababa@itamint:~$ newgrp libvirt
itababa@itamint:~$ sudo systemctl enable libvirtd
itababa@itamint:~$ sudo systemctl start libvirtd
itababa@itamint:~$ sudo virsh net-start default
itababa@itamint:~$ sudo virsh net-autostart default
itababa@itamint:~$ sudo curl -L https://github.com/dhiltgen/docker-machine-kvm/releases/download/v0.10.0/docker-machine-driver-kvm-ubuntu16.04 -o /usr/local/bin/docker-machine-driver-kvm
itababa@itamint:~$ sudo chmod +x /usr/local/bin/docker-machine-driver-kvm
itababa@itamint:~$ curl -L https://github.com/minishift/minishift/releases/download/v1.34.3/minishift-1.34.3-linux-amd64.tgz -o minishift-1.34.3-linux-amd64.tgz
itababa@itamint:~$ tar xf minishift-1.34.3-linux-amd64.tgz
itababa@itamint:~$ sudo cp minishift-1.34.3-linux-amd64/minishift /usr/local/bin/
itababa@itamint:~$ minishift start

NOTE: the “minishift start” command will display the minishift web UI at the end of its run.
Reference: https://docs.okd.io/3.11/minishift/getting-started/setting-up-virtualization-environment.html

Settings needed on the KVM host:
- configure the KVM host to redirect connections to it on 8443/tcp to the minishift host (192.168.42.62). Optionally redirect 2222/tcp to the minishift host as well:
iptables -F
iptables -F -t nat
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/conf/virbr0/proxy_arp
echo 1 > /proc/sys/net/ipv4/conf/virbr1/proxy_arp    (required so that the host can answer when the minishift VM tries to find the ARP of external hosts, otherwise the minshift will not respond to any connection attempts)
iptables -t nat -A POSTROUTING -o wlp5s0 -j MASQUERADE
iptables -t nat -A PREROUTING -i wlp5s0 -p tcp --dport 8443 -j DNAT --to-destination 192.168.42.62:8443
iptables -t nat -A PREROUTING -i wlp5s0 -p tcp --dport 2222 -j DNAT --to-destination 192.168.42.62:22
Settings needed on the minishift VM after it is running (execute "minishift ssh" to SSH into the minishift VM):
- NOTE: this whole section can be skipped if you choose to "DNAT" port 8443/tcp to the IP address on the virbr0 vNIC on the KVM host:
itababa@itamint:~$ minishift ssh
[docker@minishift ~]$
 sudo su -
[root@minishift ~]# ip a     (find the interface with the 192.168.42.x IP e.g., eth1)
[root@minishift ~]# ip route add 192.168.10.0/0 via dev eth1 (this is because the default route uses the NIC with 192.168.122.x IP and used by minishift to access the Internet)
[root@minishift ~]# ip route show
default via 192.168.122.1 dev eth0 proto dhcp metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.10.0/24 dev eth1 scope link
192.168.42.0/24 dev eth1 proto kernel scope link src 192.168.42.62 metric 101
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.30 metric 100
[root@minishift ~]#
NOTE: if you restart the minishift VM, you have to SSH into it again and re-add the route to the 192.168.10.0/24 network.

Settings needed on the external host:
In this example, I want to access the minishift web UI from a Windows system on my home network. You need to add a route to the minishift IP address using the KVM host IP as the gateway (using the command line):
C:\Windows\system32>route add 192.168.42.206 MASK 255.255.255.255 192.168.10.4

Launch a browser on the external host and go directly to https://192.168.42.206:8443/console/catalog

NOTE: if you attempt to login via https://192.168.42.206:8443/console , you will encounter this error/bug after entering your credentials (e.g., admin/admin)
Error: “Error. Invalid request. Client state could not be verified. Return to the console.”
Bug: “https://bugzilla.redhat.com/show_bug.cgi?id=1589072”
Found the solution (use the /console/catalog URL): https://github.com/openshift/openshift-azure/issues/236

after login with admin/admin credential

Other commands:
minishift delete –clear-cache (solution for error similar to: “Cannot start – error starting the VM: error getting the state for host: machine dies not exist-docker” when attempting to start minishift)
sudo virsh stop minishift; sudo undefine minishift; (might be needed if the “delete” command does not fix the issue)
sudo arp -a (on the KVM host to see the IPs of the minishift VM or SSH into the minishift VM)

– To restart the minishift VM (from the KVM host):
itababa@itamint:~$ minishift stop
itababa@itamint:~$ minishift start


Making Waara or Wagashi

Nigerians call it Waara or Wara, and Ghanaians call it Wagashi. Waara/Wagashi originated from the Fulani Northerners in both countries, and possibly other West African countries as well. Note that there is a Japanese sweet that is named Wagashi as well.

I have always thought it should be possible to make it here (in the USA), but always wondered what would replace the plant leaves used to initiate the curdling of the milk.

Recently it came to my mind again, and after a bit of Googling, I came across several sites and a few Youtube videos. As usual, some of the instructions are based on “feel free to go on until the spirit of your ancestors tap you on the shoulder and say stop.”

I ended up basing my attempt on two YouTube videos (links below).

Some notes:

I set the milk to boil on the low end of medium. After more than an hour, it didn’t quite start to boil, but when I could see a bubble rise here and there, I gave up and added the white vinegar, and stirred the pot briefly. The milk started to curdle almost immediately, and I think that separation lowered the boiling point of the separated liquid because it started to boil vigorously. In one of the videos, even though the lady used the word “boiling” she actually indicated a temperature of about 88F (32C) which is lower than the boiling point of water. As she said, getting a food-grade thermometer is likely a good investment.
Don’t forget (as I did) to add a little salt. Without salt, the Waara is almost tasteless.
One thing I noticed is that the Waara turned out quite hard. Though not sure why, but I suspect it may be due to one or more reasons – I boiled the milk too much or too long; or used too much vinegar; or squeezed too much of the fluid out of it.

Ingredients and equipment:
– 4 liters of whole milk from Walmart ($4). Each whole milk container is about 3.78 liter so you need a little more than one container.
– one cup of white vinegar (Walmart’s Great Value brand is $3). You can also use lemon juice as an alternative.
– a little salt to taste.
– a sieve ($7).
– Cheese cloth or something similar. Got one from Walmart for $3. After cutting off about 2 feet of it to use to squeeze the liquid from the Waara curds, I had to shake it vigorously first as some of the threads came off. Though it is labeled food-grade, I prefer not to eat too many of the threads :-).

Process:
– Put the milk in a pot and bring to almost boiling point.
– Add the white vinegar and stir (it will start to curdle immediately).
– leave it on the fire for another 15 to 20 mins.
– scoop out the curds into a sieve to get rid of the liquid.
– transfer the drained curds into a cheese cloth and squeeze out as much of the liquid as possible.

Pictures:

Boling the milk (add a little salt to taste).
White vinegar (I added 1.5 cups instead of one, maybe reason the curds turned out hard?)

after adding the white vinegar and stirring briefly.

About 15 minutes after adding the vinegar and turning off the stove.

Drained some of the liquid from the pot.

Waara curds in a sieve.

A little burnt milk at the bottom of the pot.

Waara curds in a sieve. The side plate is 8.25 inches in diameter for reference purposes.

Waara curds in a sieve. The side plate is 8.25 inches in diameter for reference purposes.

Food-grade Cheese cloth.

Wrapped the curds in a piece of cheese cloth.

Wrapped the curds in a piece of cheese cloth.

Using the milk container as leverage to squeeze out the fluid from the Waara curds in the cheese cloth. Feel free to wash your hands or put on gloves, and squeeze it through the cheese cloth 🙂
After squeezing out as much of the liquid as I could.

Waara (final product).

Texture is like hard cheese after a few hours.

Reference videos:
https://youtu.be/BrmtP91ufME
https://youtu.be/2gzsc0lN7HQ