The future of the Filipino

For those with little to no context about the Marcos family, or for those willing to swallow the red pill and face the atrocities of the Marcoses head on, please spare an hour and two-thirds to watch The Kingmaker (2019 film) on YouTube (for Philippine viewers) or Amazon Prime Video.

I remember a distinct hot afternoon in my Grade 6 classroom in elementary school. Araling Panlipunan (Social Studies; AP) was not my favorite subject; I was a notorious class sleeper. But a specific chapter of Philippine history never failed to captivate me: the years of Philippine history spanning 1965 to 1986. The presidency and dictatorship of Ferdinand E. Marcos.

My AP teacher was rather strict. He would give us difficult exams that focus on our memorization of the topics. At the time, I had a rather cold opinion of him. I’d later grow up and realize that he was perhaps the most important teacher I ever had. His manner of teaching was always to the point and demonstrative — he would readily provide examples of whatever he was discussing, or tell stories about it.

He and his family was a direct victim of the various human rights violations during the Marcos reign. When the time of the year comes when the Marcos martial law period were to be discussed, our classes would spend a lot of time on personal experience stories and testimonies. Here, I spent a lot of time listening to the atrocities of the Marcos dictatorship. Having access to a decent (albeit slow) internet connection during my younger years, his stories were corroborated by numerous documented incidences of human rights violations during the Marcos dictatorship. At home, I would watch documentaries of Benigno Aquino Jr.’s assassination and the People Power Revolution. Until now, I can still find these sources as easy as I can find my post office’s phone number online (that is to say, extremely easy).

Of all his stories, what stood out to me were the torture methods. He would graphically describe to us (mostly 10 and 11 year-olds) the San Juanico Bridge, aptly named after a Marcos project, which involved forcing a person to stay straight above two separate chairs, cots, or other surface, supported only by their feet and the tip of their heads. If the person’s body failed to stay straight, they would be beaten up. These aren’t isolated incidents that occurred only to people my teacher knew, but it was a method used nationwide against other Marcos critics.

I believe that without the guidance of my AP teacher, I would have never been highly against Marcos. For that, I am forever indebted to him. I’m lucky enough to know of the atrocities done by the Marcoses, and lucky enough to have the privilege to speak about them. If I were alive in 1985, I would have surely been killed, along with other political dissenters. Until now, the stories and experiences of thousands of Filipinos worldwide about Marcos continue to be shared, and of course, I continue to listen. I even listened some stories of my grandparents, who lived in fear during the Marcos regime. For a lot of the Filipino electorate however, this is not the case.

It is no theory that the Philippine government contains corrupt officials; it is fact. With the introduction of Rodrigo Duterte to the presidential office in 2016, this has never been more true, or at least since the presidency of Gloria Macapagal Arroyo, Joseph Estrada, or perhaps Ferdinand Marcos himself. Thousands of documented extrajudicial killings and human rights violations, abuses of our justice system leading to the unjust arrest of members of the opposition as political prisoners, and the continuous discovery of billions of pesos worth in illegal drugs as the president fails his promise to eradicate them. This is only a small portion of what makes the corruption evident. Duterte has funneled a copious amount of money into troll farms, feeding mass disinformation to the public. It is no mystery why the public do not know of the administration’s atrocities: they are blinded by the darkness.

Of course, Duterte is not the only one who took advantage of the Filipino susceptibility to disinformation. For the 2022 elections, the son of Ferdinand Marcos is gunning for a seat as the country’s president. And, indeed, the apple does not fall far from the tree.

A person claiming to be a Marcos Jr. troll called Magic 89.9 (DWTM-FM) and confessed to participating in spreading propaganda to better the Marcos name just yesterday; the recording of which has gone viral online. Rappler has done a wonderful Twitter thread telling the story of Marcos Jr. supporters. Their testimonies are, of course, disheartening. It seems that Marcos Jr.’s whitewashing campaign was a success, much to the dismay of much of the Filipino people who still believe in the empirical evidence.

I woke up late in the evening yesterday only to be horrified by the quick-count results. Our hopes for a brighter future vanished as Marcos held the top spot as president. The next candidate lagged by over 10 million votes behind. A major blow to the morale of the educated, and to mine as well. Considering now the cases of vote buying, electoral fraud, falsified ballots, and destroyed ballots, I know that the COMELEC will do absolutely nothing in their power to address the documented and recorded instances of clear election violations. Why should I, when its officials are no more than Duterte followers. But perhaps the worst of it all: we had elected the son of a former dictator, convicted tax evader, liar, and butt-hurt spoiled child in office. As Vicky Morales put it in the GMA election special coverage:

Basically this election has rewritten the way elections are run in this country. Hindi na kailangan maglatag ng plataproma sa mga debate, hindi na kailangan mag-grant ng mga interview. Kailangan mo lang ng, siguro, tanyag na apelyido, solid yung machinery mo, yung social media, and maybe a long history in politics. (You no longer need to show your platform in debates, you no longer need to grant interviews. You only need, perhaps, a recognizable last name, solid machinery, social media, and maybe a long history in politics.)

Vicky Morales, GMA Election 2022 Special Coverage, 10 May 2022, 10:58 PM

I had thought about this outcome for a while now, and I’ve aired my concerns once on my Twitter account. Being extremely anti-Marcos and participating in preventing the ongoing historical revisionism done by Marcos supporters on Wikipedia has made me fear for my future. Perhaps in this new presidency, I may be charged for my Wikipedia edits. Like the people in my AP teacher’s stories, I could be tortured and killed by police or Marcos–Duterte bodyguards. Or perhaps, much like Primitivo Mijares who I had wrote about on Wikipedia, forcefully made to disappear. But perhaps my biggest fear of all is what’s next for the Filipino people.

With these shocking results, we have failed our ancestors. We have failed the millions of Filipinos who were victims of Ferdinand Marcos’ dictatorship. We have failed those tortured and killed. We have failed our own country. We have failed our own history. The supporters of Marcos Jr. claim that we choose to believe in “lies”, a claim that itself was fueled by the lies peddled by Marcos Jr. and his cronies. An empty can with a pebble makes a lot of noise; a comparable analogy to the skulls of these supporters.

Six years is a long time. Long enough to push us into deeper debt from the economic crises sustained during the Duterte presidency. Long enough to accumulate countless innocent children killed under the pretense of drug control. Long enough to whitewash the history of the Marcoses across the nation. But for some reason, not long enough to release Leila de Lima, a political prisoner. No one knows what is in store for us; Marcos Jr. never even mentioned a platform to begin with other than hollow promises. We could be brought further into debt. Scores of Filipino families can be brought further into poverty. The Philippines could become a completely authoritarian state. At this point, we can only speculate, but I worry for the fate of the Filipino people.

But the fight does not end here. We’ll take this injustice to the Filipino people to the streets. We’ll fight to preserve the proven and factual truth — not the fodder that the Marcoses feed the unsuspecting masses. We’ve done it once, and we’ll do it again. Never again should a Marcos be in charge of the country. Never again should we allow thieves to set foot in the Malacañang Palace. This is our country. We won’t let them steal it from us too.

Hindi ako natatakot. Hindi ako kinakabahan.

Vice President Leni Robredo, Feburary 9, 2022

I am ready to fight for our people, our culture, and our history. If not on the streets, on the digital battleground — the heart of the disinformation campaigns that attempt to cleanse the Marcos name of their crimes. The battle has only just begun. Papunta palang tayo sa exciting part.


Saving important files with an encrypted virtual drive and BitLocker

Since Windows 8.1, Windows provides the option to create a virtual hard drives (VHDs) from the Disk Manager. This basically means you’re able to create a file that can be mounted on your computer just like any old drive. Its intended purpose is to be used in virtual applications (such as Hyper-V), but since you can mount those drives on your Windows PC like normal, you can easily use them as a “flash drive” of-sorts that is always on your computer.

I leveraged that ability to store many of my files in VHDs. Right now, most of my development work is in a VHD, since I can easily back up by just copying a single file across multiple hard drives (which means I can simply do away with using Macrium Reflect or dd or some other copying tool). It’s been a lifesaver for many things: namely, I was able to immediately start working after my laptop broke down because I had a VHD copy, and I no longer had to extract those files from a ZIP file, download them from online, nor did I need to use an entire drive for saving things.

Around 2018, I remembered BitLocker exists for Windows 10 Pro. To summarize BitLocker, it uses the computer’s Trusted Platform Module (TPM) or a startup key saved on a flash drive (if your computer doesn’t have a TPM) to encrypt entire drives. To open a BitLocker-encrypted drive, you’ll need to enter a password.

Given that you can create virtual drives, and that you can encrypt drives, I’ve been storing some rather important secrets in a BitLocker-encrypted VHD for a while now. Such a system doesn’t need anything more than just Windows, and you have the option of creating such a setup as soon as Windows is installed.

Quick disclaimer: This only works for Windows 10 Pro and above. Windows 10/11 Home does not provide BitLocker. Also, I made this tutorial for a friend, and is primarily intended for those who are not tech-savvy but still want to keep their files secure with whatever they have now.

So, how do we get started?

1. Create a virtual hard disk

First, open Disk Management. There’s many ways to do this, and it’s up to you on what you want to do.

  • Press your Windows Key + R to open the Run dialog, enter “diskmgmt.msc” and press “OK”.
  • Search for “Disk Management” on Windows search.
  • Open your start menu, look for “Disk Management” (commonly in the “Windows Administration Tools” folder), and open it.
  • Open your Control Panel, find “Windows Tools” when viewing by large icons, open “Computer Management”, and select the “Disk Management” option on the left side (under “Storage”).

Whichever method you follow, you should now see the Disk Management panel.

The Disk Management panel on Windows 11.

On the toolbar, click on “Action”, and “Create VHD”. What should appear is a dialog that looks like this.

The VHD creation menu.

Specify a location by clicking the “Browse” button, or type the location in yourself. For the size: this is up to you. For my encrypted drive, I went went a decent 16 GB, which is enough to fit a large amount of documents, images, and videos. You can even go as low as 512 MB if you’re not storing much anyway.

For the format, select VHDX unless you’re not insane. This should automatically select the virtual hard disk type: dynamically expanding. If you want the drive to take up all the space it will ever need, set it to fixed size (note that you will have a file almost as big as the size you entered earlier, as expected).

Once you’ve pressed “OK”, your disk should appear at the bottom of the lower panel. It will have a red arrow pointing down, and text saying “Not Initialized”. To be able to use this drive, you’ll need to initialize it first, by right-clicking the drive and clicking on “Initialize Disk”.

That Initialize Disk button is what you’re looking for.

You’ll be asked which partition style you want to use, either Master Boot Record (MBR) or GUID Partition Table (GPT). Since BitLocker only works for Windows 10 and above anyway, you can get away with just using GPT here.

The Initialize Disk dialog.

After this, you can now create the partition responsible for holding all your files. This is the last step in creating the VHD. Right click on the “Unallocated” block, and select “New Simple Volume…”

This time, that New Simple Volume… button is what you’re looking for.

You’ll see a wizard pop up, which will guide you through creating a partition.

You can just rapidly click on your “Next” button here, since none of the options are that important. However, you can chose to configure the drive letter (or path) that the partition will mount to, and the name of the partition to create. Stick to NTFS, here, since only Windows 10 and above supports BitLocker anyway. No need to experience the pain of using a FAT32 partition for no reason. Once you’re done with that, click on the “Finish” button and you should find your virtual drive on your list of drives in Windows Explorer.


Congratulations! You now have a virtual drive. Now let’s encrypt it with BitLocker.

2. Encrypt the drive with BitLocker

Head into your Control Panel, set the view to “Large Icons”, and look for “BitLocker Drive Encryption”.

It’s at the top. Tiny button, I know.

From there, you should be able to see the drive you just made, with a “Turn on BitLocker” link when opening the dropdown.

There’s the link you have to click on. It should be obvious.

Select on “use a password to unlock the drive” and type in the password you want to use. You can also use a smart card (if you have one).

I went with Hank Anderson’s password for this one.

You’ll be asked to save your recovery key somewhere. This is used in case you ever forget your password or if you lose your smart card.

Back it up with all four options if you’re fancy.

You can also choose to save it to your Microsoft account, in case you’ve logged into Windows with one. Note that this step is required and will prevent you from proceeding if you didn’t save it anywhere.

You then have the option of selecting how much of the drive you want encrypted. Since we just made the drive a few minutes ago, you can just encrypt the entire drive without fuss.

Select the second option (entire drive), in case that wasn’t obvious yet.

You then have another question on encryption mode if you’re on Windows 10 version 1511 and above. Since this drive is staying on your PC, just stick with the new encryption mode.

Do I really have to write a caption for all of these? Bruh.

Once you press next, you’ll be asked if you’re ready to start encrypting. Click on “Start encrypting” whenever you’re ready. This usually won’t even take a few seconds since the drive is practically empty. After that, congrats! Your drive is now encrypted with BitLocker. You can prove this by opening the “BitLocker Drive Encryption” page on the Control Panel earlier, and see settings related to the drive’s BitLocker (including an option to turn it off).

Additional options presented when BitLocker is enabled.

This drive will automatically lock itself whenever it is unmounted. Unmounting the drive is as easy as right clicking it in the Windows Explorer and clicking “Eject”.

Final notes

If you want to remount the drive, just double click on the VHDX file you created earlier.

Whenever you’re remounting the drive after ejecting it, you might get an “Access is denied” message. This is expected, since you’ll need to enter your password first before accessing the drive. To unlock it, click on the notification from BitLocker Drive Encryption that appears when the drive is remounted.

This thing. Click on this.
Then type your password into this.

After that, your encrypted drive should just open normally. Don’t forget to eject it when you’re done using it! Or else you would defeat the purpose of having an encrypted drive.

If you want to back up the drive, just copy the VHDX file somewhere else. It will retain its encryption no matter how many times you move/copy the file, as long as you don’t damage it.

How secure is BitLocker and should you trust it for storing important data? Look no further than the programmer’s best friend, Stack Exchange, for an answer.

In general, Bitlocker is secure and is used by companies all over the world. You can’t just extract keys out of the TPM hardware. Evil maid attacks are mitigated also since TPM will validate the pre-boot components to make sure that nothing has been tampered with. Booting into another OS like Linux to extract passwords or the data will not be possible also, since the TPM will not release its keys if it sees you’re booting into another OS (even if it is another Windows OS).

from “Is Windows BitLocker secure?“, CC BY-SA 3.0

Then again, I could be entirely missing the mark and the use of BitLocker. But who cares? At least I don’t have to pay for some freemium folder locking program.


Easy npm version synchronization for monorepos

TL;DR: Head to the Bespoke…er? section.

I sometimes have to unexpectedly operate a monorepo because of how some of my projects work (i.e., has a backend and a frontend). This has happened to my PAGASA Parser Web project and much recently, my Zoomiebot Wikipedia bot. Everything works fine and dandy at first, until eventually I get hit by a truck: how am I supposed to synchronize version numbers between both frontend and backend?

Let’s look at the structure first: the repository has a backend and frontend folder, each with their own package.json files and their own set of dependencies.


The “broke” way of doing this would be to manually run npm version on every workspace. This, unfortunately, does not scale up. You shouldn’t spend more than a minute updating version numbers, and definitely not for a monorepo with +20 packages. Not to mention the amount of Git commits you’d be making without setting --no-git-tag-version on.


So what’s a better method? You can, of course, rely on a script to do this for you. This works decently as well — you can use a JavaScript file (for cross-platform compatibility) to just rewrite all the package.json files, add them into the Git commit with an npm hook, and then call it a day. Sure, this works. I even did it once. Is it the best solution? Hell no. You need a few extra scripts and some hacky workarounds with child_process. Works? Yes. Good? No.

So let’s try to find a better method. Introducing: npm workspaces. You’ve probably heard of these before, and you might even be using it for your own monorepo-related business. In short, you can set up a workspace with a package.json at root of the repository, and then declare those smaller packages as workspaces of the root package.

// /package.json
    // ...
    "workspaces": [
        // Each of the folders in this array have their own
        // package.json files.
        "package-a", // /package-a/package.json
        "package-b"  // /package-b/package.json
    // ...

So now, we can just easily run npm version on the root repository and it’ll update everything right? WRONG! It’ll only update the root repository’s version. Frustrating, I know. But it’s just because you’re running it wrong right? Right?

The npm version documentation indicates that you need to use the --workspaces flag to update the version on all workspaces. Easy enough, right? WRONG AGAIN! This will update the version on all workspaces, but never actually update the version for the root package. Frustrating again, I know. But we’re almost there!


If you wanted to run some task after the npm version command on the root repository, you’d usually use a hook. In this case, we want a hook to update the versions of all workspaces. Conveniently, npm provides a hook right before making the Git commit but after the root repository’s version has been update: version. Also conveniently, npm exposes the current package.json being run as environment variables! In this case, we’re specifically looking for $npm_package_version (all lowercase, yeah, I know, right?) which also conveniently updates even in the middle of the version command! How… convenient!

npm workspaces also consolidates your node_modules folder so that you don’t end up reinstalling dependencies across packages. There’s many other advantages workspaces have, but I digress for now.

So, with that in mind, our version script will now look like this in the root repository’s package.json, right?

// /package.json
    // ...
    "workspaces": [
    "scripts": {
        "version": "npm version $npm_package_version --no-git-tag-version --workspaces && git add ."
    // ...

Yes. Not kidding this time; this will actually work. Will it work for everyone though? Still no. There’s one glaring problem here that you might have already noticed if you develop on Windows: the shell $ symbol is used to reference the version environment variable. That’s a big no-no for fancy ol’ Windows which needs %s. So how do we address this? Unfortunately, you will need a dev dependency for this, but one that you probably already had if you had any sort of cross-platform npm script: cross-env.

As it turns out, cross-env also provides a very useful script aside from cross-env, and that’s cross-env-shell. With cross-env-shell, you can write environment variables in their shell format and it’ll just work on Windows. You can install cross-env as a development dependency with the following command:

npm install --save-dev cross-env


So what’s the real solution? If you came here without reading the other sections, install cross-env first. With that out of the way, here it is:

// /package.json
    // ...
    "workspaces": [
    "scripts": {
        "version": "cross-env-shell \"npm version $npm_package_version --no-git-tag-version --workspaces\" && git add **/package*.json"
    // ...

An explanation to what the script does is below:

  • Latching on the version hook has the script run after the version is updated on the package.json file but before the Git commit and tag is made.
  • cross-env-shell runs the following command, replacing any $ environment variables if needed (because Windows support is a thing).
  • npm version $npm_package_version will set the npm versions with the following conditions/arguments:
    • --no-git-tag-version ensures that an extra Git commit won’t be made for every version update.
    • --workspaces tells npm to run this command on all workspaces.
  • git add **/package*.json will add in the modified package.json files to the staged Git changes. You can get away with using . if you feel fancy, since npm checks if the Git repo is clean before making any version changes, but this just seems safer for me.

After the hook runs, all versions across all packages and the root package will be synchronized. How exciting! Running npm version patch on the root repository will now properly bump the version number and update the version for all dependencies!

C:\Users\chlod\Workspace\haha_you_looked>npm version major

> version
> cross-env-shell "npm version $npm_package_version --no-git-tag-version --workspaces" && git add **/package*.json


C:\Users\chlod\Workspace\haha_you_looked>git status
On branch master
nothing to commit, working tree clean

This doesn’t prevent anyone from running npm version on a subpackage and updating the version of that specific package. If you want to ban that entirely, just attach a command that always exits with a non-zero code to the preversion hook. You should already know how to do this, so I’ll leave that up to you!

That’s all for now, folks! Enjoy your version-synchronized monorepo packages.


Next steps and a new project

I’ve been contemplating a lot about the cost of operating all these web tools and I have to say: damn, a lot of these are expensive. Especially when you’re starting a new project.

Right now, my server costs stand at around US$81 a year. That’s a $15.16/year domain name and a €4.90 VPS. With currency conversions and other fun stuff, that’s around ₱300/month with a big fat ₱700 payment on January for the domain name. Now that might not seem to be a lot of expenses, but you need to remember that I’m still in high school. My school gives me a monthly stipend of around $10 and my parents give me an additional allowance which is highly variable.

Of course, server expenses aren’t the only thing I spend my allowance on. I still buy physical things such as, these being the most recent, an Arduino Mega 2560, some 22AWG wire, an Arduino prototype hat, and of course, the occasional McDonald’s. So that leaves me with a small amount of money by the end of the month while I wait for my next set of allowances, probably around $2 to $5.

Now, if you’re like me who plans to spend $1,000 on a plane flight to the United States to meet your internet girlfriend, then it’d take me (and you, I guess) 16 years to get $1,000 from just $5 every month.

What is within reason that I want to do is to get a job. Perhaps a remote one where I make software. But as of now, I cannot work. Not because I don’t want to or because there’s a legal roadblock for it, but because I am too swamped in academics in order to dedicate time to a job. I was even bargaining to myself about getting a part-time IT job at a local hospital while I’m still in high school, but with COVID still in the air (literally), it doesn’t seem like a safe option for me.

I’m 100% committed to making software that’s available for free to everyone and I definitely don’t want to put any of my tools and bots, such as the PAGASA Parser, JTWC Archiver, and Ralsei Bot, under a paywall or ad campaign. And with the amount of ideas I have in my head and the accompanying lack of resources that can help me execute them for free, I’ve gone ahead and set up a Patreon.

This is the shittiest I could get it to be.

Let me make it clear: the last thing I want to do is beg for money by forcefully filling my websites with ads. That just sucks. But I still want to be able to do some of my ideas without the repercussions of being broke as hell. So that’s hopefully where the Patreon comes in. It’ll help shoulder some of the costs that come along with hosting a server, and will allow me to spend even more money on things that I want to start.

Speaking of things that I want to start, this is where the new project comes in. Feel free to skip if you’re not that into tropical cyclones or storms of the sort. My storm archiving efforts have been completely independent so far, with the PAGASA Parser running on my VPS under, the PAGASA Archiver running on Toolforge, and the JTWC Archiver also running my VPS but under I’ve really been aiming to also include JMA archives into the list, along with other PD or freely-licensed bulletins and advisories. So I’ll be merging the archivers into one big website and then sunsetting the subpath within the next year or so. Hopefully that’d centralize everything better and also prevent my name from popping up on English Wikipedia mainspace (since it’s part of the archiver URL).

As part of my commitment to open-source, the new website will also be fully open-source and freely-accessible to all. As of now, I’m also paying for the new domain out of my own pocket, but I’m okay with sacrificing a few dollars for another domain name.

So that’s basically it for this very short update post. I have a Patreon now, and I’m working on a new website. I’ll be documenting most of these as time goes on for fun (probably). Sam also told me to start streaming, which I might do. Who knows? 🤷

Addendum: Why did I register for Patreon instead of Ko-fi? Ko-fi unfortunately doesn’t allow minors.


RSS/Atom feed

I just noticed that WordPress doesn’t give a fancy button for subscribing to RSS. That’s annoying.

For this blog, the link is in case you need it.


An extremely comprehensive guide to running your own school elections with old software

Nothing drives me more crazy than abandonware.

A modified version of Halalan tweaked for the 2021 PSHS-CVC SSG Elections. It includes a few graphical and functional changes, the most prominent in this screenshot being the notice right before the form box.

Facilitating school elections is no easy feat. Not because it’s hard for students to understand how to vote, but because the software is hilariously painful to deal with. The software currently suggested by PSHS-CVC teachers, and also used by the University of the Philippines, Halalan, is 9 years old as of writing. It relies on older software, and makes life extremely difficult. But no worries, since this “guide” of sorts will help you through everything you need to know.

Do note, however, that you will be severely violating some common security practices for the sake of compatibility. It is suggested that you move on to a different platform for elections, or else you might compromise the elections’ security.

A screenshot of the Halalan GitHub repository, dating its last commit back to September 19, 2012.

Halalan, literally “election” in Filipino, is an open-source voting platform developed by University of the Philippines students in early 2006. It supports both English and Filipino (albeit the language configured on the server side) and the first-past-the-post voting.

Getting started

Prior to literally anything at all, you should probably scout for where you’re going to host the election server. As a small benchmark, we ended up using a single-vCore, 2 GB RAM VPS for the elections. OVH gave this exact configuration and was also available on their Singapore datacenter, which made latencies to the Philippines extremely low. Luckily, this dedicated server allowed us to perform the elections smoothly with peak RAM usage hitting only up to 40%. Granted, we did spread out the election period quite a bit in order to decrease simultaneous traffic, but the server would have been perfectly capable of handling a hundred voters at the same time.

That said, if you’re planning to run this on an existing server that may have its own web server already installed, you might want to use Docker to completely isolate the software versions that you need. Spoiler alert: unless you’re using something as ancient as Ubuntu 12.04, it likely won’t run. Halalan requires PHP 5, which was declared EOL at the start of 2019. Luckily there’s not much restrictions on what MySQL server to use, but we’ll get to that later.

For now, you can get away with installing Apache HTTP Server (httpd) 2.4 together with PHP 5.6. This highly depends on what flavor of Linux you have installed, so I’ll leave this up to you. If you happen to be using Docker, your life will be made easy if you just use the provided PHP 5.6 images. But then there’s a catch! You need gd (with at least PNG and JPEG support so that you won’t lose your mind) and the rewrite module for httpd. So you’ll need to get those installed and enabled as well.

After installing httpd and PHP 5.6, you’ll need to get MySQL up and running. I’d actually advise against using MySQL, and instead use a fork of it, MariaDB, as it contains significant performance improvements which can help make the elections much more smooth. The latest version of MariaDB (currently 10.6.4) works perfect for the job, as it is backwards compatible with the required MySQL version for Halalan.

Now, however, is where I introduce the first dealbreaker for Halalan: you must disable strict mode in your SQL server’s configuration. Merely attempting to open the website without disabling strict mode will show an error immediately on load. That’s definitely something you should not be looking for.

After disabling strict mode, you can now begin installation of Halalan. You can download the latest version as a zip file from GitHub, or just clone the repository on your server. You’ll need to extract the repository onto your document root (probably /var/www/html) to get the files in and then modify the system/application/config/database.php file to set up the database. After entering your configuration, the installation menu should now be accessible. This is located in the /install subdirectory of your web server. Halalan will present you with a few options for setup.

The configuration screen for Halalan.

The configuration here is mostly up to you. For the SSG elections, we went with PINs disabled, candidate details showing, and password lengths at 10 characters. This would make the election process simple and straightforward for students, without compromising security.

Here comes the second dealbreaker for Halalan: passwords are stored as SHA-1 hashes. I don’t have to go in-depth on how insecure SHA-1 is since there’s hundreds of articles about that already. When you’re dealing with elections, you really want to aim for the best security possible. SHA-1 just doesn’t make that any helpful. You can spend some time in the depths of the code trying to replace all the instances of SHA-1 hashing with SHA-256, or better yet Blowfish with password_hash, but this takes time, effort, and technical knowledge of what you’re doing and what you might get wrong. Granted, all passwords are automatically generated by Halalan, so this might not be an issue for you as long as you tightly secure your server.

Once you’ve configured your server and moved the configuration file to its final destination, you will now be able to configure your elections. Congratulations! You can already stop here if you really just wanted to play around. If you want to use this in production though, you need to secure your server quite a bit before you get the elections going.

Locking down

You might be tempted to just start the elections with this setup. If you do that, you’ll be leaving some vital components exposed to the wild by default (at least on Debian or Ubuntu). Your next step would be to open up a firewall to allow access only to ports 80 and 443 (if you’re using SSL, which you should). You might wonder just how the hell are you supposed to control the server if you block off all ports. I present to you the wonders of ZeroTier: a service that’s kind of like Hamachi, but completely free and open source.

ZeroTier opens up a virtual private network between you and the server, and it counts as a completely separate interface, which means your firewall can easily whitelist that interface. You can do things like access the SSH and SQL ports over the private connection without risking exposure to the rest of the internet. There’s separate tutorials for this, so I’ll spare you the details on how exactly this is done.

After getting ZeroTier set up, whitelisting ZeroTier connections in nftables is as easy as two lines:

udp dport 9993 accept  # ZT communication port
iifname zt* accept     # or jump to a different chain

When you’re done with that, you’ve mostly secured yourself against intrusion. It is still best to follow other security practices though, such as only allowing public key authentication with SSH and disallowing root logins for both SSH and SQL.

Next step is to get a domain. A cheap one if possible. The reason why you’d likely want to register a domain is because Let’s Encrypt provides SSL certificates completely for free, and because requesting an SSL certificate IP address is extremely expensive. If you had the money to buy an SSL certificate for your election server’s IP address, you might as well have bought a domain name, heck, even invested in a custom-made election server.

Your focus now is to improve on client-side security, which means adding in an SSL certificate so that the elections can be conducted through HTTPS. Of course, you can skip this option entirely, but you might lose voter trust if they find out that their votes are being transmitted unencrypted through the internet. You can also opt for a self-signed certificate which, although is better than HTTP, will still throw warnings for a browser.

That’s it!

A diagram with the complete infrastructure used by the PSHS-CVC SSG Election server.

If you were able to follow all those steps, then you’re 100% ready for an actual election using Halalan. The only thing left now is to add in the data through the administration panel, and to begin the elections yourself. For the SSG elections, I opted not to use the in-built results panel (since the candidates preferred to keep the actual count of votes hidden) and instead created my own live results dashboard using Grafana. Such a thing is out of the scope of this guide, however it’s not that hard to set up if you know how to deal with SQL and Grafana itself.

If you did happen to stumble upon a few issues, remember that Halalan is nearly 9-year-old software. It is expected to break at this point given changes to the other software and services that it depends on now. In case you do end up giving up and moving on, you can always turn to GitHub for some alternatives that might fit your use case.

The total cost of everything ended up at around US$ 3 and my sanity. Not bad, if I do say so myself.

That’s all for now. Good luck with getting your elections running!


Hello World

Just made this blog. Yeet.