It's been so long since I wrote a new piece. That's due to the hectic lifestyle I lived since I got married and my wonderful daughter is born. Juggling between fulfilling my work duties, being a loving husband and a doting father, it's been a struggle. Even my personal downtime is also compromised.
But recently, with the help of AI, I'm starting to reclaim back my time. It started with Google's Gemini Chat. To be accurate, Gemini wasn't the first LLM I used, but it's the first LLM that I use extensively for productivity. ChatGPT was the first, but it was more like exploratory usage.
Initially, Gemini was merely a tool to learn something really fast. Instead of scouring the internet with search engines to look for answers, Gemini could give me the answer within 1 minute. It used to be that I had to piece the information puzzle together one by one, page by page, site by site. What took hours was done in minutes.
Eventually, I started feeding it some PHP codes. It started to give me good solutions in tiny code snippets. It was readable and understandable. After all, when the code is 10 lines, it's easy to vet through line by line within 5 minutes. But then, that's the part I have a realization. While it took me 5 minutes to fully digest the code, Gemini took less than 1 minute to generate not just the code, but also the conversation explaining the code. This was the start of me handing over my over-20 years of engineering to AI.
Next came larger codebase. I started feeding entire files to Gemini to analyse and generate code. Since Gemini limited uploading 10 files at a time, I couldn't simply drag-and-drop. Some files were way too large. I could extract code snippets manually, but it turns out that the time I take extracting code snippets takes far longer than uploading 20 files, 10 files each time.
I also happen to have a deprecated Tabnine subscription, which was simply a VSCode extension and it was just plain old chat wrapper with some Claude models. Tabnine had restrictions, so I often hit rate-limits. But still, I made use of it as much as possible by combining Gemini and Tabnine's models intelligence by facilitating both of them to argue the best way to approach problems. Then came the day I anticipated. Tabnine cancelled my deprecated subscription and refunding any remaining unused amount.
At this point, I also started a new project and, this time, Gemini built the entire app without me writing a line of code. I was still using Gemini Chat up to this point. So it was a lot of drag-and-drop and copy-pasting between Visual Studio Code and Gemini Chat. Other administrative task include creating new files and folders, and performing git actions. Even git messages were written by Gemini at this point. But not a single line of code was written by me. It worked!
It is also around at this time that OpenClaw started to gain in popularity. After reading the horrors of security vulnerabilities, I decided to approach OpenClaw with extreme caution. Till today, I never expose OpenClaw to any chat app. I've also heard about the token burn using OpenClaw, so I used my company's Gemini subscription to experiment with OpenClaw. At that time, Gemini CLI had a generous limit and it could be hooked up with OpenClaw. This is the moment I realise the capabilities of Agentic AI. This is the moment everything changed.
Agentic AI is the future!
At least for the foreseeable future. So I poured by heart and soul towards this new shiny thing: Agentic AI. I was deeply amazed by Gemini's agentic capabilities riding on OpenClaw. This time round, instead of my usual drag-and-drop to Gemini, I simply pointed OpenClaw to my codebase and described my problem. Almost instantly, my problem was solved. It even ran tests to confirm that it works.
But of course, such generosity from Google couldn't last. Google tighten the rate limits to the extent that OpenClaw was unusable. I was almost always hitting rate limits. But I wasn't about to give up so soon. I knew I must eventually pay for my usage, but I wasn't sure how expensive it's going to be. Since I'm already an AWS customer and AWS bedrock host many models, I decided to try out Nova and Claude.
Both Nova and Claude is good. It solved every problem I threw at it, though it needed some guidance with context from time to time. I guess the problems I needed solving isn't that difficult, so both models worked fine. But strangely, when the first bill came, Nova cost more than Claude despite that Nova's token costs was lower than Claude. It turns out that OpenClaw didn't use cached prompts, so every prompt was charged at the input non-cached rates.
So I switched to using Claude exclusively. I used mostly Haiku 3.5, since most of my prompts ain't some complex math. Then the 2nd bill came. I was burning ~USD100 a day. Ouch!
That's it. I had to stop using hosted models. They are fast and good, but it burned a whole in my wallet. This time round, I focused on running local models. Since I already have a RTX 4060, why not make good use of it. Turns out, easier said than done.
Models that can fit inside the limited 8GB vRAM simply ain't smart enough. It can hold a basic conversation, but that's it. Getting it to power OpenClaw is nearly impossible. Offloading some of the weights to RAM just slows the model and makes the whole system unstable. Like the whole computer gets unstable. The best I could use my GPU for is to run Ornith1.5 9B to generate git messages through Zoo Code.
So my mission is the find a way to run larger models on dedicated hardware. The largest vRAM non-Apple, consumer-grade device I could get my hands on at that time was Ryzen AI Max+ 395, costing me more than SGD4000. But hey, if I was burning USD100 a day running Claude Haiku, I would easily break even within 2 months, running to smartest open-weight model I could fit.
And there I got it. The day it arrived, I was so eager to try it. I allocated the maximum amount of RAM I could to GPU and get my hands dirty with the first LLM. Since it was my first attempt, I just wanted it working as soon as possible. Ollama was my immediate choice. It was simple and effective. Most importantly, it simply worked.
But as smooth as it was with deployment, the price of this simplicity was the prefill and inference speed. Ollama comes with a tax on every prompt. After spending this money, I wasn't about to leave any performance on the table. So I figured out how to run Llama.cpp raw. No more middleman.
Turns out to be harder, but once I get the execution commands right, I just needed a script to execute the same way every time.
These days, I'm running Qwen3.8-flash-next. It's a bit on the slow side, but it works as good as Claude Sonnet 5. I'm contemplating whether to switch to Ornith1.5 35B A3B. It is suppose to be slightly worse than Qwen3.8-flash-next, but runs at far better speeds. But for now, as long as it works and gets things right, maybe I'll stick to Qwen3.8-flash-next for the time being.
I'm also using Cursor for my development. I've got to say, the auto+composer token limit sure is generous. Of my months of subscription since May, I only ever consumed 100% of my monthly allocation once. I could use my Qwen3.8-flash-next with Cursor, but I don't see the need to. Why use my local model when I still have plenty of tokens left with Cursor?
My electricity bill has certainly gone up. I'm using at least an extra 100kWh every month, with amounts to more than SGD34 every month. But this amount is far easier to swallow than the USD100/day I was spending before. I would happily pay my utilities over AWS bedrock. The other effects of running my local model is that the GPU is heating up my room, so I am using a bit more air-conditioning than I usually would. Still, happy to pay this bill over AWS bedrock anytime.
I don't know when I can write my next post, but I guess this post is a sign that I have managed to reclaim back a little bit of my time so far. I hope I can continue to reclaim further more. I won't let AI write my posts. After all, you're here because you want to hear my voice, not some version of AI I control. But the images will be AI generated.
The 4 steps of innovation with Digital Mindset
We all have been told that we are now living in the digital era. That we should have a digital mindset. Your boss comes to you and tells you that the company needs to digitise their product. Digital, digital, digital. What does that even mean?
Have you ever asked yourself this question? How do you even define it? Is it the smartphone you carry in your pocket? Is it that self-driving car that just sped past you? Is it that vending machine that refuses to dispense your favourite soda? Is it that facebook like button that you are monitoring?
We sort of know the difference between what's digital and what's not:
But that still don't quite get the definition of Digital.
The Wiktionary explains that digital comes from the word digit, refering to our fingers and toes. We know in history that we used our fingers as a basis of counting up to ten. That's why globally, the number system we use is base10, from 0 to 9. In electronics, it uses base2 numbers, which is just 0 and 1. For computer scientists, they use the base16 numbers, from 0 to 9 and A to F. But whether we use base2, base10, or base16, doesn't matter. These number systems are representations of quantity.
What does it mean when we talk about changing something from analog to digital? One famous example is the conversion of analog sound to digital sound.
We know that sound waves are represented in a continuous wave form. This wave form can be stored in physical mediums like a phonograph record. So what did we do in order to digitise it? We measured the wave position at a specific moment of time. Let's say you measured the wave position once per second, you have a sampling rate of 1Hz. That isn't going to built a meaningful soundwave. But if you measured the wave position 44 100 times in a second, you will have a sampling rate of 44.1 kHz, which is the sampling rate of CD quality music. It is from this measurements you have taken that you build a meaningful soundwave. That's how you and I enjoy music today.
When we say that something is digital, it means that something is quantifiable. Nature is analog. We live in a analog world. In order to make sense of our world, we need it to be quantifiable, so that we can make measurements. That's why we have units like metres, inches, grams, pounds, dollars, seconds, minutes, hours, and so on. From this quantifiable units, we make our measurements. From these measurements, we make meaning out of it.
We, as humans, have come a long way to quantify the world. We quantify soundwaves, leading us to innovations like MP3. We quantify colour, leading to colourful screens like the one you are looking at now. We quantify electromagnetic waves, leading to phones, WiFi, and X-Ray machines.
If we are able to associate numbers to whatever is analog, we can make measurements. We measure in various ways. We have percentages, natural numbers, whole numbers, floating point numbers, logarithmic scales, matrices, and several others. We can also use a combination of them.
Percentages are suitable when there is known boundaries and expectations.
Natural numbers are suitable when we know that there are literal quantities of objects.
Floating point numbers, or commonly known as decimal numbers, are suitable when precision is required.
Now that you know how to measure, you can find meaning in the measurements.
When you set percentages to the object, you already define the boundaries, or limitations, of the object in question. When the values of the object measurements are outside of 0 to 100, it is outside of intended range. Thus we associate percentage with what's normal, and what's not. We can also attach meaning to the values of the percentage, whether it is within normal range, or out of it.
Sometimes, we use percentage as a frame of reference. We use a more familiar object as the frame of reference to something less familiar. Like you weigh 16.5% on the moon. Despite not mentioning Earth, you know I'm referring to your weight on Earth. You need that reference because I'm betting that you haven't stepped on lunar soil.
Natural numbers are good to measure existence of objects at a moment of time. When you don't need to deal with the past or the future, you don't need to credits and debts. Specifically, you don't need negative numbers.
That's where whole numbers come in. Negative numbers means that you can have debt. You can subtract beyond the existing quantity.
But when precision is needed, floating numbers is your best friend. You can be as divisive as you want, only limited by what's humanly possible.
Knowing what type of quantity representation to use will give you the meaning you look for.
Now that you have the meaning, apply it.
Let's try quantifying something that doesn't seem logical, and so analogous. Let's try to quantify emotion. It doesn't quite make any sense to say I'm feeling 50% emotion or 98% emotion. So let's take Robert Plutchik's 8 basic emotions. Now it would make more sense to say I'm feeling 10% fear, 50% trust, and 80% joy. We have made emotions quantifiable and measured it. At this percentages, you could probably guess I'm having an adventure with a trainer, thus the presence of fear, trust, and joy. If you're the trainer, I'm sure you have done a good job with making sure the adventure is joyous without too much fear. You probably want to work on gaining trust.
Today in the tech world, we hear so many tech terms used. Where does each of these terms fall under in digital mindset?
In today's cyberscape, we have a lot of data. A lot of data comes from how we use online services. Big companies, like Google, Facebook, Apple, Amazon, Microsoft, have been collecting a lot of data on our way of life. So much so that our privacy is invaded, but that's a topic for another blog post.
There's a lot of data contributed by the internet of things. With the increasing computing power and lowering of prices, more and more of our everyday objects are equipped with sensors. These sensors then collect the data and sends it to the cloud for further processing. Your smartphone is one such object, with a Samsung Galaxy S7 edge having 8 sensors itself. We call these objects The Internet of Things.
All these data is possible simply because we have quantified many things in our world. With the costs of storage dropping, we can afford to store massive amounts of data.
Machine learning has come a long way. Along with dropping storage costs, computational power has increased, and better and specialised machine learning algorithms have been created.
All of these have resulted in meaningful insights that would have taken humans an incredible long time to figure out. At the same time, it has allowed machines to do the things what was once possible only by a human.
With machines being able to do what we humans used to have an advantage over, and in some cases, better than what we can do, we are increasingly allowing this artificial intelligence to govern our lives. From all the data it has collected and the meaningful insights it can compute in an instant, we allowed artificial intelligence to drive our cars, tell us how sick we are, beat us in our own game, gamble till we're broke, be our oracle, compose melody, paint art, and be racists.
The digital mindset is a very useful innovation framework for start-ups to begin with and mature companies to transform.
If you are a start-up, having trouble coming up with a way to disrupt, try the digital mindset approach. There is already a lot of data generated and publicly available, like Google's public data. If there isn't the data you are looking for, try to collect that data. Then find meaning out of that data by running the data through machine learning. Then from the meaning, apply it in the society with a sustainable business model.
If you're in a mature company with the task to innovate, you probably have lots of data in the company to begin with. You can approach this by finding new meaning from the data, or automating the process that your fellow colleagues are already doing. From there, there are several ways to apply it, perhaps a new business model, a faster operational procedure, or an entirely new operation.
Whatever the case is, I look forward to something innovative from what you read here.
TCP BBR is designed to counter traffic congestion. If your application frequently suffer from performance due to congestion, try TCP BBR.
LVM is an excellent abstraction tool for storage. Highly recommended if you want to expand your storage needs in the future. Check out a related article on setting up infinite storage for web server but ignore the part on web server. Zimbra is commonly install on /opt/ so set the LVM to this folder.
Zimbra recommends a minimum of 8GB RAM. However, if you are faced with a situation where you do not have enough RAM and you can't just add more ram easily, you could use swap space partition or file.
Edit /etc/hosts and add the following line:
| 10.0.0.168 mail.shurn.me mail |
Change 10.0.0.168 to your ip address and the hostname to your hostname.
Edit /etc/hostname to this:
Reboot your server and test that you see your hostname:
| root@mail:~# hostname -f |
| mail.shurn.me |
| root@mail:~# hostname |
Install bind9
| root@mail:~# apt install bind9 bind9utils |
Edit /etc/bind/named.conf.options, uncomment the forwarders, and add DNS server IP:
| forwarders { |
| 8.8.8.8; 8.8.4.4; |
| }; |
The DNS server is hosted by Google. Choose your own DNS server if you have a preference.
Add the following to /etc/bind/named.conf.local:
| zone "shurn.me" { |
| type master; |
| file "/etc/bind/db.shurn.me"; |
| }; |
| zone "0.0.10.in-addr.arpa" { |
| type master; |
| file "/etc/bind/db.0.0.10"; |
| }; |
| zone "0.0.127.in-addr.arpa" { |
| type master; |
| file "/etc/bind/db.0.0.127"; |
| }; |
Change 0.0.10 to the reverse of your first 3 octet of your IP address, and shurn.me to your top level domain name.
Create /etc/bind/db.shurn.me and add the following:
| $TTL 604800 |
| @ IN SOA mail.shurn.me. admin.shurn.me. ( |
| 20180217 ; Serial |
| 604800 ; Refresh |
| 86400 ; Retry |
| 2419200 ; Expire |
| 604800);Negative Cache TTL |
| ; |
| @ IN NS mail |
| IN MX 10 mail |
| IN A 10.0.0.168 |
| mail IN A 10.0.0.168 |
Change the domain and IP address accordingly.
Create /etc/bind/db.0.0.127 and add the following:
| $TTL 3D |
| @ IN SOA mail.shurn.me. admin.shurn.me. ( |
| 2 ; Serial |
| 8H ; Refresh |
| 2H ; Retry |
| 4W ; Expire |
| 1D);Minimum TTL |
| NS mail.shurn.me |
| 1 PTR localhost. |
Change the domain accordingly
Create /etc/bind/db.0.0.10 and add the following:
| $TTL 3D |
| @ IN SOA mail.shurn.me. admin.shurn.me. ( |
| 1 ; Serial |
| 8H ; Refresh |
| 2H ; Retry |
| 4W ; Expire |
| 1D);Minimum TTL |
| NS shurn.me. |
| 10 PTR shurn.me. |
Change the domain and IP address accordingly.
Restart bind service
| root@mail:~# /etc/init.d/bind9 restart |
| [ ok ] Restarting bind9 (via systemctl): bind9.service. |
Configure static IP address in /etc/network/interfaces:
| source /etc/network/interfaces.d/*.cfg |
| auto eth0 |
| iface eth0 inet static |
| address 10.0.0.168 |
| netmask 255.255.255.0 |
| network 10.0.0.0 |
| broadcast 10.0.0.255 |
| gateway 10.0.0.1 |
| dns-search shurn.me |
| dns-nameservers 10.0.0.168 |
Change the domain and IP address accordingly.
Edit /etc/resolvconf/resolve.conf.d/base to the following:
| nameserver 10.0.0.168 |
| search shurn.me |
CHange the domain and IP address accordingly. Then regenerate resolve.conf with:
| root@mail:~# resolvconf -u |
Disable firewall first:
| root@mail:~# service ufw stop |
Download and install the latest stable version of Zimbra. At the time of writing, the latest version is 8.8.8.
| root@mail:~# wget https://files.zimbra.com/downloads/8.8.8_GA/zcs-8.8.8_GA_2009.UBUNTU16_64.20180322150747.tgz |
| root@mail:~# tar xf zcs-8.8.8_GA_2009.UBUNTU16_64.20180322150747.tgz |
| root@mail:~# cd zcs-8.8.8_GA_2009.UBUNTU16_64.20180322150747 |
| root@mail:~# ./install.sh |
Read the instructions and answer accordingly to your requirements:
| Do you agree with the terms of the software license agreement? [N] y |
| Use Zimbra's package repository [Y] y |
| Install zimbra-ldap [Y] |
| Install zimbra-logger [Y] y |
| Install zimbra-mta [Y] y |
| Install zimbra-dnscache [Y] n |
| Install zimbra-snmp [Y] y |
| Install zimbra-store [Y] y |
| Install zimbra-apache [Y] y |
| Install zimbra-spell [Y] y |
| Install zimbra-memcached [Y] y |
| Install zimbra-proxy [Y] y |
| Install zimbra-chat [Y] y |
| Install zimbra-drive [Y] y |
| Install zimbra-imapd (BETA - for evaluation only) [N] n |
| The system will be modified. Continue? [N] y |
| Change domain name? [Yes] Yes |
| Create domain: [mail.shurn.me] shurn.me |
| Select from menu, or press 'a' to apply config (? - help) a |
| Save configuration data to a file? [Yes] y |
| Save config in file: [/opt/zimbra/config.12345] |
| The system will be modified - continue? [No] y |
Note: This is an abstract of the full installation details. You may experience different installation path.
Highly recommended to set the admin password. Notifying zimbra of your installation is entirely optional.
Go to your favourite browser and browse to your domain like ://mail.shurn.me:7071. Take note of the port 7071, which is the administive login. Without it, you will be presented with the mailbox login.
Send and receive a mail and see that it works.
Follow the instructions here to install certbot for your system.
Follow the instructions here for the script to automate letsencrypt installation, except the following:
On point 4, the configs/sudoers.conf is located at /opt/letsencrypt-zimbra/configs/sudoers.conf. So you should run the command like this:
| root@mail:~# cp /opt/letsencrypt-zimbra/configs/sudoers.conf /etc/sudoers.d/zimbra_certbot |
The maintainer has taken note that there is a restart issue at the time of this writing. As such, you need to manually restart zimbra with:
| root@mail:~# sudo -Hu zimbra /opt/zimbra/bin/zmcontrol restart |
If zmlogswatch does not start, check /opt/zimbra/log/zmlogswatch.out and see if it reports any error related to Date::Parse and Swatchdog. If it does, you should proceed with the installation. Then attempt to restart zimbra again. Obtain the URL from Swatchdog here.
| root@mail:~# apt install build-essential |
| root@mail:~# cpan |
| cpan[1]> install Date::Parse |
| cpan[2]> install Time::HiRes |
| cpan[3]> install Date::Calc |
| cpan[4]> install Date::Format |
| cpan[5]> install Date::Manip |
| cpan[6]> install File::Tail |
| cpan[7]> install Term::ANSIColor |
| cpan[8]> exit |
| root@mail:~# wget https://excellmedia.dl.sourceforge.net/project/swatch/swatchdog/swatchdog-3.2.4.tar.gz |
| root@mail:~# tar -zxvf swatchdog-3.2.4.tar.gz |
| root@mail:~# mv swatchdog-3.2.4 swatchdog |
| root@mail:~# cd swatchdog |
| root@mail:~# perl Makefile.PL |
| root@mail:~# make |
| root@mail:~# make test |
| root@mail:~# make install |
| root@mail:~# make realclean |
| root@mail:~# sudo -Hu zimbra /opt/zimbra/bin/zmcontrol restart |
You may set up cron job to renew the certificate. Edit your cron job file with:
| root@mail:~# crontab -e |
Add the following:
| 12 12 * * * sudo -Hu zimbra /opt/letsencrypt-zimbra/obtain-and-deploy-letsencrypt-cert.sh | sudo -Hu zimbra /opt/zimbra/bin/zmcontrol restart |
Installing Memcache on Ubuntu 16.04 is a breeze. All you need are just the right commands and everything is installed.
apt install memcached php-memcached
After that, you just need to restart apache.
systemctl apache2 restart
That's it. You can verify that memcached is installed and working with phpinfo().
I have been using Sonarwhal to check my website for errors for quite a while. This is quite a useful tool to get your website checked for best practises and errors.
So far, I have managed to clear out many issues raised by the online scanner provided free-of-charge. The downside of the scanner right now, is that I cannot set rules. Like I can't ignore certain domains that are not under my control, thus it reports their errors. The other downside I face is also a timeout between scans. I like to scan each time I make a site modification, which can be seconds apart. You can install this tool on your local premise to overcome these limitations.
Some of the errors and best practises I have done are like HSTS, PWA, and other security headers. It's a good tool to use. Give it a try for free.
Greetings Earthlings , Shurn the Awesomer is here to give you an awesome time.
This little site is a record of my life, opinions, and views. I'm mainly writing about Technology & Gadgets, Busting Creationist Myths, and other philosophical stuff.
This site is done using CakePHP.
With this uptime, how much more can I be proud of to showcase to the world? This uptime monitoring is brought to you by StatusCake since 13th May 2017.
I will always check for copyright usage before using any materials on my site. Whenever due, credit shall be given.
However, if you notice that I may have infringed on any copyright material. Please do not hesitate to contact me. All works of every artist deserves to be honoured and respected.