aboutsummaryrefslogtreecommitdiff
path: root/content
diff options
context:
space:
mode:
Diffstat (limited to 'content')
-rw-r--r--content/blog/2021-01-01-seum.org14
-rw-r--r--content/blog/2021-01-04-fediverse.org85
-rw-r--r--content/blog/2021-01-07-ufw.org102
-rw-r--r--content/blog/2021-02-19-macos.org183
-rw-r--r--content/blog/2021-03-19-clone-github-repos.org54
-rw-r--r--content/blog/2021-03-28-gemini-capsule.org136
-rw-r--r--content/blog/2021-03-28-vaporwave-vs-outrun.org136
-rw-r--r--content/blog/2021-03-30-vps-web-server.org331
-rw-r--r--content/blog/2021-04-17-gemini-server.org71
-rw-r--r--content/blog/2021-04-23-php-comment-system.org177
-rw-r--r--content/blog/2021-04-28-photography.org86
-rw-r--r--content/blog/2021-05-30-changing-git-authors.org16
-rw-r--r--content/blog/2021-07-15-delete-gitlab-repos.org41
-rw-r--r--content/blog/2021-08-25-audit-sampling.org199
-rw-r--r--content/blog/2021-10-09-apache-redirect.org31
-rw-r--r--content/blog/2021-12-04-cisa.org298
16 files changed, 924 insertions, 1036 deletions
diff --git a/content/blog/2021-01-01-seum.org b/content/blog/2021-01-01-seum.org
index 7c4eabb..83f871f 100644
--- a/content/blog/2021-01-01-seum.org
+++ b/content/blog/2021-01-01-seum.org
@@ -5,12 +5,9 @@
* Game Description
-[[https://store.steampowered.com/app/457210/SEUM_Speedrunners_from_Hell/][SEUM:
-Speedrunners from Hell]] is an incredibly fast-paced mashup of a puzzle
-game and a free-running game. Created by [[https://pinestudio.co][Pine
-Studio]] in early 2016 and first released as a
-[[https://gamejolt.com/games/seum-speedrunners-from-hell-demo/154868][demo
-on GameJolt]], this game was successfully green-lit on Steam and has
+[[https://store.steampowered.com/app/457210/SEUM_Speedrunners_from_Hell/][SEUM: Speedrunners from Hell]] is an incredibly fast-paced mashup of a puzzle game
+and a free-running game. Created by [[https://pinestudio.co][Pine Studio]] in early 2016 and first released
+as a [[https://gamejolt.com/games/seum-speedrunners-from-hell-demo/154868][demo on GameJolt]], this game was successfully green-lit on Steam and has
amassed a cult following on multiple consoles.
Here's the game description straight from the developers:
@@ -57,8 +54,9 @@ contains nine regular levels, one boss level, and one bonus level;
although you don't technically need to beat all levels to advance to the
next floor. # In-Game Screenshots
-The main menu gives you plenty of options for tuning your system,
-playing main levels, playing the DLC, or exploring the other game modes.
+The main menu gives you plenty of options for tuning your system, playing main
+levels, playing the downloadable content (DLC), or exploring the other game
+modes.
Once you enter a level, you can look around and strategize before
starting. Clicking any button will start the menu, and you'll have to
diff --git a/content/blog/2021-01-04-fediverse.org b/content/blog/2021-01-04-fediverse.org
index 27cae98..f32ed66 100644
--- a/content/blog/2021-01-04-fediverse.org
+++ b/content/blog/2021-01-04-fediverse.org
@@ -5,53 +5,51 @@
* What is the Fediverse?
-The fediverse is a federated universe of servers commonly used for
-sharing content, like social media. So, instead of having to rely on a
-single organization to run the server (e.g. Facebook), the fediverse is
-a giant collection of servers across the world, owned by many people and
-organizations.
-
-Take a look at this depiction of a federated network. Each server in
-this photo is owned and run by different administrators/owners.
-Federated networks are best explained as email servers: you have an
-email account that exists on a server (e.g. Outlook), your friend has an
-account on a different server (e.g. GMail), and another friend has an
-account on a third server (e.g. ProtonMail). All three of you can talk
-and communicate back and forth without having to be on the same server.
-However, responsible email admins are there to set rules and control the
+The fediverse is a federated universe of servers commonly used for sharing
+content, like social media. So, instead of having to rely on a single
+organization to run the server (e.g. Facebook), the fediverse is a giant
+collection of servers across the world, owned by many people and organizations.
+
+Take a look at this depiction of a federated network. Each server in this photo
+is owned and run by different administrators/owners. Federated networks are best
+explained as email servers: you have an email account that exists on a server
+(e.g. Outlook), your friend has an account on a different server (e.g. GMail),
+and another friend has an account on a third server (e.g. ProtonMail). All three
+of you can talk and communicate back and forth without having to be on the same
+server. However, responsible email admins are there to set rules and control the
traffic going in/out of the server.
-The main objective of this architecture is to decentralize the control
-within the internet connections. For example, if you run your own
-Mastodon instance, you and your users can't be censored or impacted in
-any way by authorities of another Mastodon instance. Some users have
-praised these features due to recent criticism of popular social media
-websites that may be over-censoring their users.
+The main objective of this architecture is to decentralize the control within
+the internet connections. For example, if you run your own Mastodon instance,
+you and your users can't be censored or impacted in any way by authorities of
+another Mastodon instance. Some users have praised these features due to recent
+criticism of popular social media websites that may be over-censoring their
+users.
This strategy is great for making sure control of the social web isn't
-controlled by a single organization, but it also has some downsides. If
-I create a Mastodon instance and get a ton of users to sign up, I can
-shut the server down at any time. That means you're at risk of losing
-the content you've created unless you back it up, or the server backs it
-up for you. Also, depending on the software used (e.g. Mastodon,
-Pixelfed, etc.), censorship may still be an issue if the server admins
-decide they want to censor their users. Now, censorship isn't always a
-bad thing and can even benefit the community as a whole, but you'll want
-to determine which servers align with your idea of proper censorship.
-
-However, these are risks that we take when we sign up for any online
-platform. Whatever your reason is for trying out federated social
-networks, they are part of the future of the internet. However, the
-popularity of these services is still yet to be determined, especially
-with the increased difficulty understanding and signing up for these
-platforms. Perhaps increased regulation and litigation against current
-social media sites will push more users into the fediverse.
+controlled by a single organization, but it also has some downsides. If I create
+a Mastodon instance and get a ton of users to sign up, I can shut the server
+down at any time. That means you're at risk of losing the content you've created
+unless you back it up, or the server backs it up for you. Also, depending on the
+software used (e.g. Mastodon, Pixelfed, etc.), censorship may still be an issue
+if the server admins decide they want to censor their users. Now, censorship
+isn't always a bad thing and can even benefit the community as a whole, but
+you'll want to determine which servers align with your idea of proper
+censorship.
+
+However, these are risks that we take when we sign up for any online platform.
+Whatever your reason is for trying out federated social networks, they are part
+of the future of the internet. However, the popularity of these services is
+still yet to be determined, especially with the increased difficulty
+understanding and signing up for these platforms. Perhaps increased regulation
+and litigation against current social media sites will push more users into the
+fediverse.
* Federated Alternatives to Popular Sites
-The list below is a small guide that will show you federated
-alternatives to current popular websites. There are many more out there,
-so go and explore: you might just find the perfect home.
+The list below is a small guide that will show you federated alternatives to
+current popular websites. There are many more out there, so go and explore: you
+might just find the perfect home.
** Reddit
@@ -91,10 +89,9 @@ so go and explore: you might just find the perfect home.
* Get Started
-The best way to get started is to simply sign up and learn as you go. If
-you're comfortable signing up through a Mastodon, Pleroma, or Friendica
-server, here is [[https://fediverse.party/en/portal/servers][a list of
-themed servers]] to choose from. If you're looking for something else,
+The best way to get started is to simply sign up and learn as you go. If you're
+comfortable signing up through a Mastodon, Pleroma, or Friendica server, here is
+[[https://fediverse.party/en/portal/servers][a list of themed servers]] to choose from. If you're looking for something else,
try a web search for a federated alternative to your favorite sites.
Find a server that focuses on your passions and start there!
diff --git a/content/blog/2021-01-07-ufw.org b/content/blog/2021-01-07-ufw.org
index 78a85a0..9c853a6 100644
--- a/content/blog/2021-01-07-ufw.org
+++ b/content/blog/2021-01-07-ufw.org
@@ -5,22 +5,21 @@
* Uncomplicated Firewall
-Uncomplicated Firewall, also known as ufw, is a convenient and
-beginner-friendly way to enforce OS-level firewall rules. For those who
-are hosting servers or any device that is accessible to the world (i.e.,
-by public IP or domain name), it's critical that a firewall is properly
-implemented and active.
-
-Ufw is available by default in all Ubuntu installations after 8.04 LTS.
-For other distributions, you can look to install ufw or check if there
-are alternative firewalls installed already. There are usually
-alternatives available, such as Fedora's =firewall= and the package
-available on most distributions: =iptables=. Ufw is considered a
-beginner-friendly front-end to iptables.
-
-[[https://gufw.org][Gufw]] is available as a graphical user interface
-(GUI) application for users who are uncomfortable setting up a firewall
-through a terminal.
+Uncomplicated Firewall (UFW) is a convenient and beginner-friendly
+way to enforce operating system (OS)-level firewall rules. For those who are
+hosting servers or any device that is accessible to the world (i.e., by public
+IP or domain name), it's critical that a firewall is properly implemented and
+active.
+
+UFW is available by default in all Ubuntu installations after 8.04 LTS
+(long-term support). For other distributions, you can look to install UFW or
+check if there are alternative firewalls installed already. There are usually
+alternatives available, such as Fedora's =firewall= and the package available on
+most distributions: =iptables=. UFW is considered a beginner-friendly front-end
+to iptables.
+
+[[https://gufw.org][Gufw]] is available as a graphical user interface (GUI) application for users who
+are uncomfortable setting up a firewall through a terminal.
* Getting Help
@@ -33,20 +32,19 @@ sudo ufw --help
* Set Default State
-The proper way to run a firewall is to set a strict default state and
-slowly open up ports that you want to allow. This helps prevent anything
-malicious from slipping through the cracks. The following command
-prevents all incoming traffic (other than the rules we specify later),
-but you can also set this for outgoing connections, if necessary.
+The proper way to run a firewall is to set a strict default state and slowly
+open up ports that you want to allow. This helps prevent anything malicious from
+slipping through the cracks. The following command prevents all incoming traffic
+(other than the rules we specify later), but you can also set this for outgoing
+connections, if necessary.
#+begin_src sh
sudo ufw default deny incoming
#+end_src
-You should also allow outgoing traffic if you want to allow the device
-to communicate back to you or other parties. For example, media servers
-like Plex need to be able to send out data related to streaming the
-media.
+You should also allow outgoing traffic if you want to allow the device to
+communicate back to you or other parties. For example, media servers like Plex
+need to be able to send out data related to streaming the media.
#+begin_src sh
sudo ufw default allow outgoing
@@ -54,23 +52,23 @@ sudo ufw default allow outgoing
* Adding Port Rules
-Now that we've disabled all incoming traffic by default, we need to open
-up some ports (or else no traffic would be able to come in). If you need
-to be able to =ssh= into the machine, you'll need to open up port 22.
+Now that we've disabled all incoming traffic by default, we need to open up some
+ports (or else no traffic would be able to come in). If you need to be able to
+=ssh= into the machine, you'll need to open up port 22.
#+begin_src sh
sudo ufw allow 22
#+end_src
-You can also issue more restrictive rules. The following rule will allow
-=ssh= connections only from machines on the local subnet.
+You can also issue more restrictive rules. The following rule will allow =ssh=
+(secure shell protocol) connections only from machines on the local subnet.
#+begin_src sh
sudo ufw allow proto tcp from 192.168.0.0/24 to any port 22
#+end_src
-If you need to set a rule that isn't tcp, just append your connection
-type to the end of the rule.
+If you need to set a rule that isn't TCP (Transmission Control Protocol) just
+append your connection type to the end of the rule.
#+begin_src sh
sudo ufw allow 1900/udp
@@ -93,8 +91,7 @@ sudo reboot now
* Checking Status
-Now that the firewall is enabled, let's check and see what the rules
-look like.
+Now that the firewall is enabled, let's check and see what the rules look like.
#+begin_src sh
sudo ufw status numbered
@@ -111,9 +108,9 @@ Status: active
* Deleting Rules
-If you need to delete a rule, you need to know the number associated
-with that rule. Let's delete the first rule in the table above. You'll
-be asked to confirm the deletion as part of this process.
+If you need to delete a rule, you need to know the number associated with that
+rule. Let's delete the first rule in the table above. You'll be asked to confirm
+the deletion as part of this process.
#+begin_src sh
sudo ufw delete 1
@@ -121,11 +118,10 @@ sudo ufw delete 1
* Managing App Rules
-Luckily, there's a convenient way for installed applications to create
-files that ufw can easily implement so that you don't have to search and
-find which ports your application requires. To see if your device has
-any applications with pre-installed ufw rules, execute the following
-command:
+Luckily, there's a convenient way for installed applications to create files
+that ufw can easily implement so that you don't have to search and find which
+ports your application requires. To see if your device has any applications with
+pre-installed UFW rules, execute the following command:
#+begin_src sh
sudo ufw app list
@@ -142,8 +138,8 @@ Available applications:
plexmediaserver-dlna
#+end_src
-If you want to get more information on a specific app rule, use the
-=info= command.
+If you want to get more information on a specific app rule, use the =info=
+command.
#+begin_src sh
sudo ufw app info plexmediaserver-dlna
@@ -161,8 +157,8 @@ Ports:
32469/tcp
#+end_src
-You can add or delete app rules the same way that you'd add or delete
-specific port rules.
+You can add or delete app rules the same way that you'd add or delete specific
+port rules.
#+begin_src sh
sudo ufw allow plexmediaserver-dlna
@@ -174,12 +170,12 @@ sudo ufw delete RULE|NUM
* Creating App Rules
-If you'd like to create you own app rule, you'll need to create a file
-in the =/etc/ufw/applications.d= directory. Within the file you create,
-you need to make sure the content is properly formatted.
+If you'd like to create you own app rule, you'll need to create a file in the
+=/etc/ufw/applications.d= directory. Within the file you create, you need to
+make sure the content is properly formatted.
-For example, here are the contents my =plexmediaserver= file, which
-creates three distinct app rules for ufw:
+For example, here are the contents my =plexmediaserver= file, which creates
+three distinct app rules for ufw:
#+begin_src config
[plexmediaserver]
@@ -198,8 +194,8 @@ description=The Plex Media Server (with additional DLNA capability)
ports=32400/tcp|3005/tcp|5353/udp|8324/tcp|32410:32414/udp|1900/udp|32469/tcp
#+end_src
-So, if I wanted to create a custom app rule called "mycustomrule," I'd
-create a file and add my content like this:
+So, if I wanted to create a custom app rule called "mycustomrule," I'd create a
+file and add my content like this:
#+begin_src sh
sudo nano /etc/ufw/applications.d/mycustomrule
diff --git a/content/blog/2021-02-19-macos.org b/content/blog/2021-02-19-macos.org
index a6e5cbd..46ac966 100644
--- a/content/blog/2021-02-19-macos.org
+++ b/content/blog/2021-02-19-macos.org
@@ -5,74 +5,69 @@
* Diving into macOS
-After spending nearly 15 years working with Windows and 8 years on
-Linux, I have experienced macOS for the first time. By chance, my spouse
-happened to buy a new MacBook and gifted me their 2013 model. Of course,
-I still consider my Linux desktop to be my daily driver and keep Windows
-around for gaming needs, but over the past week I've found myself using
-the MacBook more and more for things that don't require gaming specs or
-advanced dev tools.
+After spending nearly 15 years working with Windows and 8 years on Linux, I have
+experienced macOS for the first time. By chance, my spouse happened to buy a new
+MacBook and gifted me their 2013 model. Of course, I still consider my Linux
+desktop to be my daily driver and keep Windows around for gaming needs, but over
+the past week I've found myself using the MacBook more and more for things that
+don't require gaming specs or advanced developer tools.
* Initial Thoughts
-Before I move on to the technical aspects of my set-up, I want to take
-some time and express my thoughts on the overall OS.
-
-As expected, the initial computer setup is a breeze with Mac's guided
-GUI installer.
-
-The desktop itself reminds me of GNOME more than anything else I've
-seen: even Pantheon from [[https://elementary.io/][ElementaryOS]], which
-people commonly refer to as the closest Linux distro to macOS. The
-desktop toolbar is great and far surpasses the utility of the GNOME
-toolbar due to the fact that the extensions and icons /actually work/. I
-launch macOS and immediately see my shortcuts for Tresorit, Bitwarden,
-and Mullvad pop up as the computer loads.
-
-Even further, the app dock is very useful and will be yet another
-familiarity for GNOME users. I know many people like panels instead of
-docks, but I've always found docks to have a more pleasing UI. However,
-I had to disable the "Show recent applications in Dock" preference; I
-can't stand items taking up precious screen space if I'm not currently
-using them. On that same note, it's taking me some time to get use to
-the fact that I have to manually quit an app or else it will still stay
-open/active in the dock, even if I've closed out all windows for that
-app (e.g. Firefox).
-
-Overall, I'm having a lot of fun and for users who spend a large
-majority of their time performing basic tasks like web browsing,
-writing, watching media, etc., macOS is a fantastic option.
-
-The rest of this post explains the technicalities of how I set up my CLI
-environment to make me feel more at-home, similar to the environments I
-set up on Fedora, Ubuntu, etc.
+Before I move on to the technical aspects of my set-up, I want to take some time
+and express my thoughts on the overall operating system (OS).
+
+As expected, the initial computer setup is a breeze with Mac's guided graphical
+user interface (GUI) installer.
+
+The desktop itself reminds me of GNOME more than anything else I've seen: even
+Pantheon from [[https://elementary.io/][ElementaryOS]], which people commonly refer to as the closest Linux
+distro to macOS. The desktop toolbar is great and far surpasses the utility of
+the GNOME toolbar due to the fact that the extensions and icons /actually work/.
+I launch macOS and immediately see my shortcuts for Tresorit, Bitwarden, and
+Mullvad pop up as the computer loads.
+
+Even further, the app dock is very useful and will be yet another familiarity
+for GNOME users. I know many people like panels instead of docks, but I've
+always found docks to have a more pleasing UI. However, I had to disable the
+"Show recent applications in Dock" preference; I can't stand items taking up
+precious screen space if I'm not currently using them. On that same note, it's
+taking me some time to get use to the fact that I have to manually quit an app
+or else it will still stay open/active in the dock, even if I've closed out all
+windows for that app (e.g. Firefox).
+
+Overall, I'm having a lot of fun and for users who spend a large majority of
+their time performing basic tasks like web browsing, writing, watching media,
+etc., macOS is a fantastic option.
+
+The rest of this post explains the technicalities of how I set up my command
+line interface (CLI) environment to make me feel more at-home, similar to the
+environments I set up on Fedora, Ubuntu, etc.
* Making it Feel Like Home
-If you're someone who uses Linux primarily, no doubt your first thought
-when booting macOS will be the same as mine was: "Where is the terminal
-and how do I set up my favorite utilities?"
+If you're someone who uses Linux primarily, no doubt your first thought when
+booting macOS will be the same as mine was: "Where is the terminal and how do I
+set up my favorite utilities?"
-Luckily, macOS hasn't completely hidden away the development tools from
-the average user. You can easily find the Terminal app in the Launchpad
-area, but it's probably not what you're used to. I was surprised (and
-happy) to see that the default shell is =zsh=, the shell I use on all of
-my Linux distros. However, the commands are not the same - even the ones
-you may think are native to the shell. Commands like =dir= do not exist,
-so other native commands like =ls -la= or =pwd= are more useful here.
+Luckily, macOS hasn't completely hidden away the development tools from the
+average user. You can easily find the Terminal app in the Launchpad area, but
+it's probably not what you're used to. I was surprised (and happy) to see that
+the default shell is =zsh=, the shell I use on all of my Linux distros. However,
+the commands are not the same - even the ones you may think are native to the
+shell. Commands like =dir= do not exist, so other native commands like =ls -la=
+or =pwd= are more useful here.
-With only a few minutes of installing and tweaking a few packages, I was
-able to recreate a terminal environment that I feel very comfortable
-using. See the image below for a preview of the iTerm2 app with a split
-view between my macOS desktop shell and an SSH session into my server.
+With only a few minutes of installing and tweaking a few packages, I was able to
+recreate a terminal environment that I feel very comfortable using. See the
+image below for a preview of the iTerm2 app with a split view between my macOS
+desktop shell and an SSH (secure shell protocol) session into my server.
* Xcode
-My first step was to search the web for any hints on how to get =zsh=
-back up to the state I like, with extensions, themes, etc. My first step
-was to install the CLI tools for
-[[https://developer.apple.com/xcode/][Xcode]], Apple's suite of
-development tools.
+My first step was to search the web for any hints on how to get =zsh= back up to
+the state I like, with extensions, themes, etc. My first step was to install the
+CLI tools for [[https://developer.apple.com/xcode/][Xcode]], Apple's suite of development tools.
#+begin_src sh
sudo xcode-select -r
@@ -84,25 +79,25 @@ sudo xcode-select --install
* Homebrew
-Next up is to install [[https://brew.sh][Homebrew]], a nifty package
-manager for macOS.
+Next up is to install [[https://brew.sh][Homebrew]], a nifty package manager for macOS.
#+begin_src sh
-/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+/bin/bash -c "$(curl -fsSL
+https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
#+end_src
I ran into a permission error when installing Homebrew:
#+begin_src sh
-Error: Failed to link all completions, docs and manpages:
- Permission denied @ rb_file_s_symlink - (../../../Homebrew/completions/zsh/_brew, /usr/local/share/zsh/site-functions/_brew)
-Failed during: /usr/local/bin/brew update --force --quiet
+Error: Failed to link all completions, docs and manpages: Permission denied @
+ rb_file_s_symlink - (../../../Homebrew/completions/zsh/_brew,
+ /usr/local/share/zsh/site-functions/_brew) Failed during:
+ /usr/local/bin/brew update --force --quiet
#+end_src
-I found that the following permission modification worked like a charm.
-However, I noted that some users online discussed the fact that this
-solution may not work if your system has multiple users who use
-Homebrew.
+I found that the following permission modification worked like a charm. However,
+I noted that some users online discussed the fact that this solution may not
+work if your system has multiple users who use Homebrew.
#+begin_src sh
sudo chown -R $(whoami) $(brew --prefix)/*
@@ -120,16 +115,16 @@ brew cleanup
* iTerm2
-Now that I've installed the basic utilities for development, I moved
-onto installing iTerm2, a much better terminal than the default.
+Now that I've installed the basic utilities for development, I moved onto
+installing iTerm2, a much better terminal than the default.
#+begin_src sh
brew install --cask iterm2
#+end_src
-I also used the =Make iTerm2 Default Term= and
-=Install Shell Integration= options in the iTerm2 application menu to
-make sure I don't run into any issues later on with different terminals.
+I also used the =Make iTerm2 Default Term= and =Install Shell Integration=
+options in the iTerm2 application menu to make sure I don't run into any issues
+later on with different terminals.
We will also install =zsh= so we can use it in iTerm2.
@@ -139,54 +134,50 @@ brew install zsh
* Oh-My-Zsh
-I've shown the great aspects of [[https://ohmyz.sh][Oh My Zsh]] in other
-blog posts, so I'll skip over that speech for now. Simply install it and
-run an update.
+I've shown the great aspects of [[https://ohmyz.sh][Oh My Zsh]] in other blog posts, so I'll skip over
+that speech for now. Simply install it and run an update.
#+begin_src sh
-sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
+sh -c "$(curl -fsSL
+https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
#+end_src
#+begin_src sh
omz update
#+end_src
-Finally, restart the iTerm2 application to ensure all changes go into
-effect.
+Finally, restart the iTerm2 application to ensure all changes go into effect.
* Oh-My-Zsh Themes
-Let's change the theme of the terminal to make it a little more
-friendly.
+Let's change the theme of the terminal to make it a little more friendly.
#+begin_src sh
open ~/.zshrc
#+end_src
-The third section of this file should contain a line like the code
-below. Change that theme to
-[[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][any theme you want]],
-save the file, and exit.
+The third section of this file should contain a line like the code below. Change
+that theme to [[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][any theme you want]], save the file, and exit.
#+begin_src sh
ZSH_THEME="af-magic"
#+end_src
-After changing the =.zshrc= file, you'll need to close your terminal and
-re-open it to see the changes. Optionally, just open a new tab if you're
-using iTerm2, and you'll see the new shell config.
+After changing the =.zshrc= file, you'll need to close your terminal and re-open
+it to see the changes. Optionally, just open a new tab if you're using iTerm2,
+and you'll see the new shell config.
* Oh-My-Zsh Plugins
Of course, my customization of =zsh= would not be complete without
-[[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]].
-This will bring up commands you've run in the past as you type them. For
-example, if you've run =ssh [email protected]= before, the terminal will
-show this command as soon as you start typing it (e.g. =zsh u=), and you
-can hit the right arrow to autocomplete the command.
+[[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]]. This will bring up commands you've run in the past as you
+type them. For example, if you've run =ssh [email protected]= before, the
+terminal will show this command as soon as you start typing it (e.g. =zsh u=),
+and you can hit the right arrow to autocomplete the command.
#+begin_src sh
-git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
+git clone https://github.com/zsh-users/zsh-autosuggestions
+${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
#+end_src
#+begin_src sh
@@ -198,6 +189,6 @@ open ~/.zshrc
plugins=(git zsh-autosuggestions)
#+end_src
-Remember: After changing the =.zshrc= file, you'll need to close your
-terminal and re-open it to see the changes. Optionally, just open a new
-tab if you're using iTerm2, and you'll see the new shell config. \ No newline at end of file
+Remember: After changing the =.zshrc= file, you'll need to close your terminal
+and re-open it to see the changes. Optionally, just open a new tab if you're
+using iTerm2, and you'll see the new shell config.
diff --git a/content/blog/2021-03-19-clone-github-repos.org b/content/blog/2021-03-19-clone-github-repos.org
index 02b290f..399e4bc 100644
--- a/content/blog/2021-03-19-clone-github-repos.org
+++ b/content/blog/2021-03-19-clone-github-repos.org
@@ -5,14 +5,14 @@
* Cloning from GitHub
-If you're like me and use a lot of different devices (and sometimes
-decide to just wipe your device and start with a new OS), you probably
-know the pain of cloning all your old code repositories down to your
-local file system.
+If you're like me and use a lot of different devices [and sometimes decide to
+just wipe your device and start with a new operating system (OS)], you probably know
+the pain of cloning all your old code repositories down to your local file
+system.
-If you're using GitHub, you can easily clone all of your code back down
-in just seconds. First, create a bash script. I do so by opening a new
-file in =nano=, but you can use =gedit=, =vim=, or something else:
+If you're using GitHub, you can easily clone all of your code back down in just
+seconds. First, create a bash script. I do so by opening a new file in =nano=,
+but you can use =gedit=, =vim=, or something else:
#+begin_src sh
nano clone_github_repos.sh
@@ -44,12 +44,12 @@ Now you can run the script and should see the cloning process begin.
* Cloning from Sourcehut
-I haven't fully figured out how to directly incorporate Sourcehut's
-GraphQL API into a bash script yet, so this one will take two steps.
+I haven't fully figured out how to directly incorporate Sourcehut's GraphQL
+application programming interface (API) into a bash script yet, so this one will
+take two steps.
-First, log-in to Sourcehut and go to their
-[[https://git.sr.ht/graphql][GraphQL playground for Git]]. Next, paste
-the following query into the left box:
+First, log-in to Sourcehut and go to their [[https://git.sr.ht/graphql][GraphQL playground for Git]]. Next,
+paste the following query into the left box:
#+begin_src sh
query {
@@ -65,10 +65,10 @@ query {
}
#+end_src
-The output on the right side will give you an object of all your
-repositories. Just grab that text and remove all the characters such as
-quotation marks and curly brackets. You will need a single-line list of
-space-separated values for the next step.
+The output on the right side will give you an object of all your repositories.
+Just grab that text and remove all the characters such as quotation marks and
+curly brackets. You will need a single-line list of space-separated values for
+the next step.
Now let's create the bash script:
@@ -76,12 +76,12 @@ Now let's create the bash script:
nano clone_sourcehut_repos.sh
#+end_src
-Next, paste the following bash script in with the list of repositories
-you obtained above and replace =your-username= with your username.
+Next, paste the following bash script in with the list of repositories you
+obtained above and replace =your-username= with your username.
-Note that this uses the SSH-based Git cloning method
-(e.g. =git@git...=), so you'll need to ensure you have set up Sourcehut
-with your SSH key.
+Note that this uses the secure shell protocol (SSH)-based Git cloning method
+(e.g. =git@git...=), so you'll need to ensure you have set up Sourcehut with
+your SSH key.
#+begin_src sh
repos=(repo1 repo2 repo3)
@@ -108,17 +108,17 @@ Now you can run the script and should see the cloning process begin.
* Moving Repositories to a New Host
-Now that you have all of your code repositories cloned to your local
-computer, you may want to change the remote host (e.g., moving from
-GitHub to GitLab). To do this, let's create another bash script:
+Now that you have all of your code repositories cloned to your local computer,
+you may want to change the remote host (e.g., moving from GitHub to GitLab). To
+do this, let's create another bash script:
#+begin_src sh
nano change_remote_urls.sh
#+end_src
-Past the following information and be sure to change the URL information
-to whichever host you are moving to. For this example, I am looping
-through all of my cloned GitHub directories and changing them to
+Past the following information and be sure to change the URL (uniform resource
+locator) information to whichever host you are moving to. For this example, I am
+looping through all of my cloned GitHub directories and changing them to
Sourcehut (e.g. =<YOUR_NEW_REMOTE_URL>= -> [email protected]:~myusername=).
#+begin_src sh
diff --git a/content/blog/2021-03-28-gemini-capsule.org b/content/blog/2021-03-28-gemini-capsule.org
index 954e2bc..2bd98d1 100644
--- a/content/blog/2021-03-28-gemini-capsule.org
+++ b/content/blog/2021-03-28-gemini-capsule.org
@@ -5,21 +5,17 @@
* What is Gemini?
-[[https://gemini.circumlunar.space/][Gemini]] is an internet protocol
-introduced in June 2019 as an alternative to HTTP(S) or Gopher. In
-layman's terms, it's an alternative way to browse sites (called
-capsules) that requires a special browser. Since Gemini is not
-standardized as an internet standard, normal web browsers won't be able
-to load a Gemini capsule. Instead, you'll need to use
-[[https://gemini.circumlunar.space/clients.html][a Gemini-specific
-browser]].
-
-The content found within a Gemini page is called
-[[https://gemini.circumlunar.space/docs/cheatsheet.gmi][Gemtext]] and is
-/extremely/ basic (on purpose). Gemini only processes the text, no media
-content like images. However, you're able to style 3 levels of headings,
-regular text, links (which will display on their own line), quotes, and
-an unordered list.
+[[https://gemini.circumlunar.space/][Gemini]] is an internet protocol introduced in June 2019 as an alternative to the
+Hypertext Transfer Protocol (HTTP) or Gopher. In layman's terms, it's an
+alternative way to browse sites (called capsules) that requires a special
+browser. Since Gemini is not standardized as an internet standard, normal web
+browsers won't be able to load a Gemini capsule. Instead, you'll need to use [[https://gemini.circumlunar.space/clients.html][a
+Gemini-specific browser]].
+
+The content found within a Gemini page is called [[https://gemini.circumlunar.space/docs/cheatsheet.gmi][Gemtext]] and is /extremely/
+basic (on purpose). Gemini only processes the text, no media content like
+images. However, you're able to style 3 levels of headings, regular text, links
+(which will display on their own line), quotes, and an unordered list.
Here's a complete listing of valid Gemtext:
@@ -44,78 +40,72 @@ My List:
*** Free Option
-There are probably numerous websites that allow you to create your
-personal Gemini capsule, but I'm going to focus on the two sites that I
-have personally tested. The first option below, Midnight Pub, allows you
-to create/edit any Gemini files you want in your account. This is
-essentially a GUI option with a built-in text box for editing. The
-second option below, Sourcehut, allows you to use a Git repository and
-automatic build process to deploy your personal Gemini capsule every
-time you push a commit.
+There are probably numerous websites that allow you to create your personal
+Gemini capsule, but I'm going to focus on the two sites that I have personally
+tested. The first option below, Midnight Pub, allows you to create/edit any
+Gemini files you want in your account. This is essentially a graphical option
+with a built-in text box for editing. The second option below, Sourcehut, allows
+you to use a Git repository and automatic build process to deploy your personal
+Gemini capsule every time you push a commit.
** Midnight Pub - Beginner Friendly
-[[https://midnight.pub/][Midnight Pub]] is a small, virtual community
-meant to reflect the atmosphere of wandering into a small alley pub. The
-site is built in Gemtext and has a server-side process to convert
-Gemtext to HTML if someone loads the site in an HTTP(S) browser.
+[[https://midnight.pub/][Midnight Pub]] is a small, virtual community meant to reflect the atmosphere of
+wandering into a small alley pub. The site is built in Gemtext and has a
+server-side process to convert Gemtext to HTML if someone loads the site in an
+HTTP(S) browser.
-To create an account, you'll need to email the owner of the website to
-obtain a key. You can find their email on the Midnight Pub homepage.
-Once registered, head to [[https://midnight.pub/account][your account]]
-and select [[https://midnight.pub/site][manage site]]. This is the
-screen where you can upload or create any files to be displayed on the
-internet.
+To create an account, you'll need to email the owner of the website to obtain a
+key. You can find their email on the Midnight Pub homepage. Once registered,
+head to [[https://midnight.pub/account][your account]] and select [[https://midnight.pub/site][manage site]]. This is the screen where you can
+upload or create any files to be displayed on the internet.
-For example, I've created both an HTML file and a Gemini file. Remember
-that Gemini is automatically converted to HTML on the Pub, so you don't
-need an HTML version. For example, I created an HTML version to add in
-some extra styling.
+For example, I've created both a Hypertext Markup Language (HTML) file and a
+Gemini file. Remember that Gemini is automatically converted to HTML on the Pub,
+so you don't need an HTML version. For example, I created an HTML version to add
+in some extra styling.
-All you need to do is create a page like =index.gmi= and use your Gemini
-browser to head over to your-username.midnight.pub to see the result.
+All you need to do is create a page like =index.gmi= and use your Gemini browser
+to head over to your-username.midnight.pub to see the result.
-That's all there is to it! Easy enough, right? Let's check out a more
-advanced version in the next section.
+That's all there is to it! Easy enough, right? Let's check out a more advanced
+version in the next section.
* Paid Option
-As of 2021, Sourcehut has decided to require users to have a paid
-account in order to utilize their automated build system. For now, paid
-accounts can be as low as $2/month.
+As of 2021, Sourcehut has decided to require users to have a paid account in
+order to utilize their automated build system. For now, paid accounts can be as
+low as $2/month.
** Sourcehut
-[[https://sourcehut.org/][Sourcehut]] is a collection of software
-development tools, but mostly surrounds their hosted Git repository
-service. Simply put, it's a minimal and more private alternative to
-services like GitHub.
+[[https://sourcehut.org/][Sourcehut]] is a collection of software development tools, but mostly surrounds
+their hosted Git repository service. Simply put, it's a minimal and more private
+alternative to services like GitHub.
-This walkthrough is more advanced and involves things like Git, SSH, the
-command line. If you don't think you know enough to do this, check out
-my walkthrough on creating a Gemini capsule for the Midnight Pub
-instead.
+This walkthrough is more advanced and involves things like Git, SSH, the command
+line. If you don't think you know enough to do this, check out my walkthrough on
+creating a Gemini capsule for the Midnight Pub instead.
-The first thing you'll need to do is create an SSH key pair, if you
-don't already have one on your system. Once created, grab the contents
-of =id_rsa.pub= and add it to your Sourcehut account settings - this
-will allow you to push and pull code changes without using a
-username/password.
+The first thing you'll need to do is create an SSH key pair, if you don't
+already have one on your system. Once created, grab the contents of =id_rsa.pub=
+and add it to your Sourcehut account settings - this will allow you to push and
+pull code changes without using a username/password.
#+begin_src sh
ssh keygen
#+end_src
-Next up, let's create a repository with the proper name so that the
-Sourcehut build system will know we want them to host a website for us.
-Use the following format exactly:
+Next up, let's create a repository with the proper name so that the Sourcehut
+build system will know we want them to host a website for us. Use the following
+format exactly:
#+begin_src sh
mkdir your-username.srht.site && cd your-username.srht.site
#+end_src
-Now that we've created the repo, let's initialize Git and add the proper
-remote URL.
+Now that we've created the repo, let's initialize Git and add the proper remote
+URL.
#+begin_src sh
git init
@@ -125,14 +115,14 @@ git init
git remote add origin [email protected]:~your-username/your-username.srht.site
#+end_src
-Now that our repository is set up and configured, we will need to create
-at least two files:
+Now that our repository is set up and configured, we will need to create at
+least two files:
- =index.gmi=
- =.build.yml=
-For your =.build.yml= file, use the following content and be sure to
-update the =site= line with your username!
+For your =.build.yml= file, use the following content and be sure to update the
+=site= line with your username!
#+begin_src yaml
image: alpine/latest
@@ -147,11 +137,11 @@ tasks:
acurl -f https://pages.sr.ht/publish/$site [email protected] -Fprotocol=GEMINI
#+end_src
-For the =index.gmi= file, put whatever you want in there and save it.
-You could even just copy and paste the Gemtext cheatsheet.
+For the =index.gmi= file, put whatever you want in there and save it. You could
+even just copy and paste the Gemtext cheatsheet.
-If you want to serve both HTML and Gemini files from this repository,
-just add a second command to the =upload= section:
+If you want to serve both HTML and Gemini files from this repository, just add a
+second command to the =upload= section:
#+begin_src yaml
- upload: |
@@ -165,9 +155,9 @@ Lastly, commit your changes and push them to the remote repo.
git add .; git commit -m "initial commit"; git push --set-upstream origin HEAD
#+end_src
-If you've successfully created the files with the proper format, you'll
-see the terminal print a message that lets you know where the automatic
-build is taking place. For example, here's what the terminal tells me:
+If you've successfully created the files with the proper format, you'll see the
+terminal print a message that lets you know where the automatic build is taking
+place. For example, here's what the terminal tells me:
#+begin_src sh
remote: Build started:
diff --git a/content/blog/2021-03-28-vaporwave-vs-outrun.org b/content/blog/2021-03-28-vaporwave-vs-outrun.org
index ef7f892..61074a7 100644
--- a/content/blog/2021-03-28-vaporwave-vs-outrun.org
+++ b/content/blog/2021-03-28-vaporwave-vs-outrun.org
@@ -5,15 +5,14 @@
* Overview
-When it comes to an aesthetic that originated primarily online, there
-tends to be a lot of confusion around what falls into each specific
-genre. This post discusses Vaporwave and Outrun, which have become
-almost synonymous in the minds of those online who aren't hardcore into
-these genres of media. More specifically, Outrun is largely an unknown
-term while all aesthetics in these two genres are usually attributed to
-Vaporwave. For example, take a look at the image search results for
-Vaporwave: the results include *a lot* of Outrun-themed images. You'll
-find a similar trend almost everywhere.
+When it comes to an aesthetic that originated primarily online, there tends to
+be a lot of confusion around what falls into each specific genre. This post
+discusses Vaporwave and Outrun, which have become almost synonymous in the minds
+of those online who aren't hardcore into these genres of media. More
+specifically, Outrun is largely an unknown term while all aesthetics in these
+two genres are usually attributed to Vaporwave. For example, take a look at the
+image search results for Vaporwave: the results include *a lot* of Outrun-themed
+images. You'll find a similar trend almost everywhere.
#+caption: Vaporwave Search Results
[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/vaporwave-search-results.webp]]
@@ -23,102 +22,91 @@ find a similar trend almost everywhere.
#+caption: Macintosh Plus Album Cover
[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/macintosh-plus.webp]]
-Okay, so what is Vaporwave? I'm going to just copy-and-paste some
-general info from the Wikipedia article on
-[[https://en.wikipedia.org/wiki/Vaporwave][Vaporwave]], so that I'm not
-repeating everything you can already search for online:
+Okay, so what is Vaporwave? I'm going to just copy-and-paste some general info
+from the Wikipedia article on [[https://en.wikipedia.org/wiki/Vaporwave][Vaporwave]], so that I'm not repeating everything
+you can already search for online:
#+begin_quote
-Vaporwave is a microgenre of electronic music, a visual art style, and
-an Internet meme that emerged in the early 2010s. It is defined partly
-by its slowed-down, chopped and screwed samples of smooth jazz,
-elevator, R&B, and lounge music from the 1980s and 1990s. The
-surrounding subculture is sometimes associated with an ambiguous or
-satirical take on consumer capitalism and pop culture, and tends to be
-characterized by a nostalgic or surrealist engagement with the popular
-entertainment, technology and advertising of previous decades. Visually,
-it incorporates early Internet imagery, late 1990s web design, glitch
-art, anime, 3D-rendered objects, and cyberpunk tropes in its cover
-artwork and music videos.
+Vaporwave is a microgenre of electronic music, a visual art style, and an
+Internet meme that emerged in the early 2010s. It is defined partly by its
+slowed-down, chopped and screwed samples of smooth jazz, elevator, R&B, and
+lounge music from the 1980s and 1990s. The surrounding subculture is sometimes
+associated with an ambiguous or satirical take on consumer capitalism and pop
+culture, and tends to be characterized by a nostalgic or surrealist engagement
+with the popular entertainment, technology and advertising of previous decades.
+Visually, it incorporates early Internet imagery, late 1990s web design, glitch
+art, anime, 3D-rendered objects, and cyberpunk tropes in its cover artwork and
+music videos.
#+end_quote
-This is an excellent summary, and it helps address my point here: there
-are specific aspects that make Vaporwave unique:
+This is an excellent summary, and it helps address my point here: there are
+specific aspects that make Vaporwave unique:
** Time Frame
-The time frame for references, logos, etc. focuses mostly on the 1990s
-in Vaporwave. You'll see old school Pepsi logos, Microsoft 95 screens,
-tropical plants, classic marble sculptures, and many references from
-Japan's influence in the 90s.
+The time frame for references, logos, etc. focuses mostly on the 1990s in
+Vaporwave. You'll see old school Pepsi logos, Microsoft 95 screens, tropical
+plants, classic marble sculptures, and many references from Japan's influence in
+the 90s.
** Art
-The color scheme is generally a soft, light palette that uses pastel
-colors. The backdrop will often be in a light-pink or teal blue.
+The color scheme is generally a soft, light palette that uses pastel colors. The
+backdrop will often be in a light-pink or teal blue.
** Music
-The musical genre of Vaporwave incorporates soft tunes, lounge music,
-and sound effects that will make the listener reminisce of the 90s. The
-sounds of the genre are generally slower-paced and calming. The major
-breakthrough artist for Vaporwave was
-[[https://en.wikipedia.org/wiki/Vektroid][Macintosh Plus]], who released
-the album
-[[https://archive.org/details/MACINTOSHPLUS-FLORALSHOPPE_complete][Floral
-Shoppe]] in 2011. Another more recent example is the artist
-[[https://sadbert.bandcamp.com/][sadbert]], whose latest album
-incorporates the upbeat tones of the 1999 Dilbert TV series.
-
-Notice that Vaporwave doesn't include things like racing cars,
-futuristic technology, chrome, or the deep orange/purple color scheme.
-Vaporwave is a focus on the idyllic state of the world when technology
-was becoming common in households, a reality that we have already
-experienced. Focus on the most aesthetically-pleasing parts of that past
-is a large part of Vaporwave.
+The musical genre of Vaporwave incorporates soft tunes, lounge music, and sound
+effects that will make the listener reminisce of the 90s. The sounds of the
+genre are generally slower-paced and calming. The major breakthrough artist for
+Vaporwave was [[https://en.wikipedia.org/wiki/Vektroid][Macintosh Plus]], who released the album [[https://archive.org/details/MACINTOSHPLUS-FLORALSHOPPE_complete][Floral Shoppe]] in 2011.
+Another more recent example is the artist [[https://sadbert.bandcamp.com/][sadbert]], whose latest album
+incorporates the upbeat tones of the 1999 Dilbert television series.
+
+Notice that Vaporwave doesn't include things like racing cars, futuristic
+technology, chrome, or the deep orange/purple color scheme. Vaporwave is a focus
+on the idyllic state of the world when technology was becoming common in
+households, a reality that we have already experienced. Focus on the most
+aesthetically-pleasing parts of that past is a large part of Vaporwave.
* Outrun
#+caption: Outrun
[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/outrun.webp]]
-Now, let's get to Outrun. This one is a little trickier since the genre
-has largely been lumped under the Vaporwave title for so long. However,
-it stems from the [[https://en.wikipedia.org/wiki/Synthwave][Synthwave]]
-music genre and is likely named after the 1986 racer game,
+Now, let's get to Outrun. This one is a little trickier since the genre has
+largely been lumped under the Vaporwave title for so long. However, it stems
+from the [[https://en.wikipedia.org/wiki/Synthwave][Synthwave]] music genre and is likely named after the 1986 racer game,
[[https://en.wikipedia.org/wiki/Out_Run][Out Run]].
** Time Frame
-Outrun can be thought of as a retro-futuristic aesthetic born from the
-1980s.
+Outrun can be thought of as a retro-futuristic aesthetic born from the 1980s.
** Art
-The color scheme uses a very dark color palette with the main colors
-being deep oranges, blues, and purples. Red edges are common around
-objects in Outrun art. The background of the Outrun aesthetic is almost
-always a neon grid like you'd expect to see in Tron or a 1980s arcade
-machine.
+The color scheme uses a very dark color palette with the main colors being deep
+oranges, blues, and purples. Red edges are common around objects in Outrun art.
+The background of the Outrun aesthetic is almost always a neon grid like you'd
+expect to see in Tron or a 1980s arcade machine.
-Classic sports cars, chrome robots, computer generated graphics and
-fonts, and the occasional use of rain or palm trees can be found in
-Outrun art.
+Classic sports cars, chrome robots, computer generated graphics and fonts, and
+the occasional use of rain or palm trees can be found in Outrun art.
** Music
-This aesthetic has a more aggressive and fast-paced style of music,
-which tends to match the subject of the art in this aesthetic.
+This aesthetic has a more aggressive and fast-paced style of music, which tends
+to match the subject of the art in this aesthetic.
-Outrun enthusiasts love what people in the 1980s thought the future
-would look like. Take a look at a common video game discussed in Outrun
-circles, Far Cry 3: Blood Dragon:
+Outrun enthusiasts love what people in the 1980s thought the future would look
+like. Take a look at a common video game discussed in Outrun circles, Far Cry 3:
+Blood Dragon:
#+caption: Fry Cry 3: Blood Dragon
[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/far-cry.webp]]
-Another example that doesn't force the color scheme as hard as some
-online art does is Kung Fury:
+Another example that doesn't force the color scheme as hard as some online art
+does is Kung Fury:
#+caption: Kung Fury
[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/kung-fury.webp]]
@@ -128,7 +116,7 @@ online art does is Kung Fury:
* Conclusion
-While Vaporwave and Outrun share similarities, they are two distinct
-aesthetics with many important distinctions. Someone who enjoys one may
-not necessarily enjoy the other, so it's important to make sure we
-properly describe the aesthetic we're looking for.
+While Vaporwave and Outrun share similarities, they are two distinct aesthetics
+with many important distinctions. Someone who enjoys one may not necessarily
+enjoy the other, so it's important to make sure we properly describe the
+aesthetic we're looking for.
diff --git a/content/blog/2021-03-30-vps-web-server.org b/content/blog/2021-03-30-vps-web-server.org
index b27572e..0b38eaa 100644
--- a/content/blog/2021-03-30-vps-web-server.org
+++ b/content/blog/2021-03-30-vps-web-server.org
@@ -3,108 +3,93 @@
#+description: Instructional outline for setting up and maintaining a VPS environment configured for secure and scalable web hosting applications.
#+slug: vps-web-server
-* Shared Hosting vs. VPS
-
-Choosing a place to host a website is one of the most confusing
-decisions for beginner web developers. Even for experienced web devs,
-choosing between different forms of web hosting can be a daunting
-choice.
-
-First, let's take a look at
-[[https://en.wikipedia.org/wiki/Shared_web_hosting_service][shared web
-hosting]]. Shared web hosting is a product where you are purchasing a
-small piece of a web server that is being shared between many websites.
-As a result, the cost of shared hosting is extremely low. You won't have
-access to the server itself, which means you can't install your own
-software on the server, such as Docker. Usually, you are simply allowed
-to connect your domains to the server, set up domain security, and other
-small utilities.
-
-In contrast, a
-[[https://en.wikipedia.org/wiki/Virtual_private_server][virtual private
-server]] (VPS) is a virtual machine that replicates the environment of
-having a dedicated server to yourself. You are able to control the
-virtual server's host names, base file system, package manager, etc.
-Another great upside of a VPS is that since it's virtual, the company
-providing the VPS can dynamically increase the disk size, RAM size, or
-number of CPUs at any time. However, the virtual server is still
-physically located on a server that is shared between multiple virtual
-servers.
-
-The choice between shared hosting and VPS mostly depends on your skill
-level with system administration. If you're comforting working on a
-server that is mostly left up to you (or you're willing to learn), then
-a VPS is usually a better option. However, shared hosting is a fantastic
-option for people who don't want to have to learn how to manage their
-server.
+* Shared Hosting vs. VPS (Virtual Private Server)
+
+Choosing a place to host a website is one of the most confusing decisions for
+beginner web developers. Even for experienced web devs, choosing between
+different forms of web hosting can be a daunting choice.
+
+First, let's take a look at [[https://en.wikipedia.org/wiki/Shared_web_hosting_service][shared web hosting]]. Shared web hosting is a product
+where you are purchasing a small piece of a web server that is being shared
+between many websites. As a result, the cost of shared hosting is extremely low.
+You won't have access to the server itself, which means you can't install your
+own software on the server, such as Docker. Usually, you are simply allowed to
+connect your domains to the server, set up domain security, and other small
+utilities.
+
+In contrast, a [[https://en.wikipedia.org/wiki/Virtual_private_server][virtual private server]] (VPS) is a virtual machine that replicates
+the environment of having a dedicated server to yourself. You are able to
+control the virtual server's host names, base file system, package manager, etc.
+Another great upside of a VPS is that since it's virtual, the company providing
+the VPS can dynamically increase the disk size, RAM size, or number of CPUs at
+any time. However, the virtual server is still physically located on a server
+that is shared between multiple virtual servers.
+
+The choice between shared hosting and VPS mostly depends on your skill level
+with system administration. If you're comforting working on a server that is
+mostly left up to you (or you're willing to learn), then a VPS is usually a
+better option. However, shared hosting is a fantastic option for people who
+don't want to have to learn how to manage their server.
* My Situation
-I had used shared hosting for approximately 5 years before trying my
-first VPS. I manage a homelab and have had success running a server and
-performing typical sysadmin duties, but I was still hesitant to get a
-VPS. One fear was that I always struggled to properly set up the
-networking part of a server - DNS and hostname configurations were not
-my friend.
-
-As a little bit of background, I originally used
-[[https://www.siteground.com][Siteground]] for my initially shared
-hosting and stayed on that platform for at least a year. However, the UI
-was clunky, and I didn't like how they handled certain technical
-aspects, so I switched to [[https://www.namecheap.com][Namecheap]].
-Namecheap was great because it is the service I primarily use for
-purchasing domain names, which made it incredibly easy to link them to
-my hosting service. However, it was still mediocre shared hosting, and
-Namecheap is notorious for not letting you use
-[[https://letsencrypt.org][Let's Encrypt]] to obtain free SSL/TLS
-certificates; Namecheap wants to make you purchase certificates through
-their store.
-
-Finally, I settled down with [[https://www.iwebfusion.net][iWebFusion]]
-for about the last year of my shared hosting. This service was pretty
-great, came with free SSL/TLS, and I never had any complaints.
-
-However, I finally grew tired of not being able to install software on
-my own web server. I wanted to be able to try out things like
-[[https://postmill.xyz][Postmill]] or [[https://matrix.org][Matrix]].
-This is possible with a VPS, so I decided to grab a new domain name to
-try it out.
+I had used shared hosting for approximately 5 years before trying my first VPS.
+I manage a homelab and have had success running a server and performing typical
+sysadmin duties, but I was still hesitant to get a VPS. One fear was that I
+always struggled to properly set up the networking part of a server - DNS and
+hostname configurations were not my friend.
+
+As a little bit of background, I originally used [[https://www.siteground.com][Siteground]] for my initially
+shared hosting and stayed on that platform for at least a year. However, the
+user interface (UI) was clunky, and I didn't like how they handled certain
+technical aspects, so I switched to [[https://www.namecheap.com][Namecheap]]. Namecheap was great because it is
+the service I primarily use for purchasing domain names, which made it
+incredibly easy to link them to my hosting service. However, it was still
+mediocre shared hosting, and Namecheap is notorious for not letting you use
+[[https://letsencrypt.org][Let's Encrypt]] to obtain free SSL (Secure Socket Layer)/TLS (Transport Layer
+Security) certificates; Namecheap wants to make you purchase certificates
+through their store.
+
+Finally, I settled down with [[https://www.iwebfusion.net][iWebFusion]] for about the last year of my shared
+hosting. This service was pretty great, came with free SSL/TLS, and I never had
+any complaints.
+
+However, I finally grew tired of not being able to install software on my own
+web server. I wanted to be able to try out things like [[https://postmill.xyz][Postmill]] or [[https://matrix.org][Matrix]]. This
+is possible with a VPS, so I decided to grab a new domain name to try it out.
* Getting Started: Buying a VPS
-The first step to moving over to a VPS is (you guessed it): finding a
-VPS provider. For my VPSs, I use [[https://1984hosting.com][1984]] and
-prefer their services much more than any alternative, due to their
-location (Iceland), their [[https://1984hosting.com/GDPR/][privacy
-policy]], their respect for GDPR, and the ability to remain anonymous if
-you pay in Bitcoin or Monero.
+The first step to moving over to a VPS is (you guessed it): finding a VPS
+provider. For my VPSs, I use [[https://1984hosting.com][1984]] and prefer their services much more than any
+alternative, due to their location (Iceland), their [[https://1984hosting.com/GDPR/][privacy policy]], their
+respect for GDPR, and the ability to remain anonymous if you pay in Bitcoin or
+Monero.
-[[https://njal.la][Njalla]] is another good, privacy-oriented option for
-VPS services.
+[[https://njal.la][Njalla]] is another good, privacy-oriented option for VPS services.
-You'll have to decide what specifications you want on your VPS. For me,
-I only build and deploy low-resource HTML, PHP, and Python websites.
-This means I can survive on the smallest VPS: 1 CPU, 1GB of RAM, and
-25GB SSD for $5.00 per month.
+You'll have to decide what specifications you want on your VPS. For me, I only
+build and deploy low-resource HTML, PHP, and Python websites. This means I can
+survive on the smallest VPS: 1 CPU, 1GB of RAM, and 25GB SSD for $5.00 per
+month.
-As noted above, the great thing about a VPS is you can request your
-provider to increase the resources at any time.
+As noted above, the great thing about a VPS is you can request your provider to
+increase the resources at any time.
* Configuring DNS Settings
-Okay, so now let's get into some actual work that has to be done to get
-content moved from a shared host to a VPS. At this point, I'm assuming
-you have a shared host with website content that you can still access,
-and you've purchased a new VPS and can SSH into that server.
+Okay, so now let's get into some actual work that has to be done to get content
+moved from a shared host to a VPS. At this point, I'm assuming you have a shared
+host with website content that you can still access, and you've purchased a new
+VPS and can SSH into that server.
-The first change is minor, but it should be done immediately in order to
-get things moving: DNS settings. Go to wherever your DNS settings are
-handled. If your shared host also managed your DNS settings, you'll need
-to first move that DNS over to your new VPS provider. For me, I route my
-DNS through [[https://www.gandi.net][Gandi]].
+The first change is minor, but it should be done immediately in order to get
+things moving: DNS settings. Go to wherever your DNS settings are handled. If
+your shared host also managed your DNS settings, you'll need to first move that
+DNS over to your new VPS provider. For me, I route my DNS through [[https://www.gandi.net][Gandi]].
-Once you know where your DNS settings are, go ahead and update the =A=
-records to match the public IP address of your VPS. For example:
+Once you know where your DNS settings are, go ahead and update the =A= records
+to match the public IP address of your VPS. For example:
#+begin_src txt
A example.com xxx.xxx.xxx.xxx
@@ -112,36 +97,35 @@ A subdomain xxx.xxx.xxx.xxx
CNAME www example.com.
#+end_src
-If you have any other records that require updates, such as MX or TXT
-records for a mail server, be sure to update those accordingly.
-Personally, I don't host my own mail server. I route all mail on my
-custom domains to [[https://www.migadu.com][Migadu]]. Hosting your own
-email server can become complex quickly and is not for beginners.
+If you have any other records that require updates, such as MX or TXT records
+for a mail server, be sure to update those accordingly. Personally, I don't host
+my own mail server. I route all mail on my custom domains to [[https://www.migadu.com][Migadu]]. Hosting
+your own email server can become complex quickly and is not for beginners.
-DNS changes can take up to 48 hours to propagate, so be sure to give it
-some time before assuming you've made an error.
+DNS changes can take up to 48 hours to propagate, so be sure to give it some
+time before assuming you've made an error.
* Server Updates and Packages
-Now that the DNS settings have been changed, let's set up our server
-while we wait for the DNS to propagate. First up is to ssh into your
-server. If you've signed up with a service like DigitalOcean, you can
-add your SSH key to your account and to your VPS droplet so that you
-don't need a password in order to SSH.
+Now that the DNS settings have been changed, let's set up our server while we
+wait for the DNS to propagate. First up is to ssh into your server. If you've
+signed up with a service like DigitalOcean, you can add your SSH key to your
+account and to your VPS droplet so that you don't need a password in order to
+SSH.
#+begin_src sh
#+end_src
-The VPS that is used in this blog post runs Ubuntu 20.04 with an Apache
-web server. If you're working on a different operating system (OS) or
-want a different web server, such as Nginx, you'll have to use different
-commands to set it up.
+The VPS that is used in this blog post runs Ubuntu 20.04 with an Apache web
+server. If you're working on a different operating system (OS) or want a
+different web server, such as Nginx, you'll have to use different commands to
+set it up.
First, let's update and upgrade our server.
-*NOTE:* Since we have logged in to the server as =root= for now, we
-don't need to use the =sudo= modifier before our commands.
+*NOTE:* Since we have logged in to the server as =root= for now, we don't need
+to use the =sudo= modifier before our commands.
#+begin_src sh
apt update && apt upgrade -y
@@ -149,11 +133,11 @@ apt update && apt upgrade -y
* Create A User Account
-While being able to use =root= can be beneficial at times, you shouldn't
-use =root= unless you have to.
+While being able to use =root= can be beneficial at times, you shouldn't use
+=root= unless you have to.
-So let's set up a new user in our system. The =-m= option below tells
-the OS to create a home directory for the new user.
+So let's set up a new user in our system. The =-m= option below tells the OS to
+create a home directory for the new user.
#+begin_src sh
adduser USERNAME
@@ -165,40 +149,40 @@ Now, create a password for that user.
passwd USERNAME
#+end_src
-Finally, add the user to the sudoers file, so they can perform
-priveleged commands.
+Finally, add the user to the sudoers file, so they can perform privileged
+commands.
#+begin_src sh
usermod -a -G sudo USERNAME
#+end_src
-If you are using SSH keys and not passwords, you'll need to copy your
-SSH key from your local machine to the VPS. If you haven't disabled
-password-based SSH yet, the easiest way to do this is =ssh-copy-id= from
-your local computer (not from the VPS):
+If you are using SSH (secure shell protocol) keys and not passwords, you'll need
+to copy your SSH key from your local machine to the VPS. If you haven't disabled
+password-based SSH yet, the easiest way to do this is =ssh-copy-id= from your
+local computer (not from the VPS):
#+begin_src sh
ssh-copy-id [email protected]
#+end_src
-If you've disabled password-based SSH, you'll need to manually copy your
-SSH key into the =~/.ssh/authorized_keys= file.
+If you've disabled password-based SSH, you'll need to manually copy your SSH key
+into the =~/.ssh/authorized_keys= file.
* Install Software
-Our goal here is to host a web server, so the next step is to install
-the Apache web server and any other packages we need.
+Our goal here is to host a web server, so the next step is to install the Apache
+web server and any other packages we need.
-From this point on, I will be logged in as a user (not =root=) and will
-need to use the =sudo= modifier for most commands.
+From this point on, I will be logged in as a user (not =root=) and will need to
+use the =sudo= modifier for most commands.
#+begin_src sh
sudo apt update; sudo apt upgrade -y; sudo apt autoremove -y
sudo apt install apache2
#+end_src
-If you need other language support, such as PHP, you'll need to install
-that too.
+If you need other language support, such as PHP, you'll need to install that
+too.
#+begin_src sh
sudo apt install libapache2-mod-php php-dom
@@ -208,19 +192,18 @@ sudo systemctl restart apache2
* Website Files & Folders
-Next up is to create the directories for the domain(s) we want to be
-hosted on this web server.
+Next up is to create the directories for the domain(s) we want to be hosted on
+this web server.
#+begin_src sh
cd /var/www
sudo mkdir example.com
#+end_src
-We have a folder for =example.com= now, so let's add an =index.html=
-file and put it within a specific =public_html= folder. You don't need
-this =public_html= if you don't want it, but it helps with organizing
-items related to =example.com= that you don't want to publish to the
-internet.
+We have a folder for =example.com= now, so let's add an =index.html= file and
+put it within a specific =public_html= folder. You don't need this =public_html=
+if you don't want it, but it helps with organizing items related to
+=example.com= that you don't want to publish to the internet.
#+begin_src sh
cd example.com
@@ -228,8 +211,8 @@ sudo mkdir public_html && cd public_html
sudo nano index.html
#+end_src
-You can put anything you want in this =index.html= file. If you can't
-think of anything, paste this in there:
+You can put anything you want in this =index.html= file. If you can't think of
+anything, paste this in there:
#+begin_src html
<!DOCTYPE html>
@@ -245,9 +228,8 @@ think of anything, paste this in there:
</html>
#+end_src
-If you want something to be served at =example.com/page01/file.txt=,
-you'll have to create the =page01= directory under the =example.com=
-directory. For example:
+If you want something to be served at =example.com/page01/file.txt=, you'll have
+to create the =page01= directory under the =example.com= directory. For example:
#+begin_src sh
cd /var/www/example.com/public_html
@@ -257,9 +239,9 @@ sudo nano file.txt
* Apache Configuration
-Now, let's set up the files that will tell the server where to find the
-files for =example.com=. We will copy the default configuration file and
-create our own.
+Now, let's set up the files that will tell the server where to find the files
+for =example.com=. We will copy the default configuration file and create our
+own.
#+begin_src sh
cd /etc/apache2/sites-available
@@ -267,9 +249,8 @@ sudo cp 000-default.conf example.com.conf
sudo nano example.com.conf
#+end_src
-This configuration file will have a few default lines, but you'll need
-to edit it to look similar to this (settings may change based on your
-personal needs):
+This configuration file will have a few default lines, but you'll need to edit
+it to look similar to this (settings may change based on your personal needs):
#+begin_src config
<VirtualHost *:80>
@@ -291,31 +272,30 @@ sudo a2dissite 000-default.conf
sudo systemctl reload apache2
#+end_src
-You can always run a test to make sure no errors or warnings are found
-in your configuration files.
+You can always run a test to make sure no errors or warnings are found in your
+configuration files.
#+begin_src sh
sudo apache2ctl configtest
#+end_src
-Now, restart the web server entirely. After this, you should be able to
-browse to =http://example.com= and see the HTML content you provided
-earlier. Note that SSL/TLS has not been enabled yet, so you won't be
+Now, restart the web server entirely. After this, you should be able to browse
+to =http://example.com= and see the HTML (hypertext markup language) content you
+provided earlier. Note that SSL/TLS has not been enabled yet, so you won't be
able to use the secure version yet (=https://example.com=).
#+begin_src sh
sudo systemctl restart apache2
#+end_src
-You can repeat this for as many websites as you need. Just create the
-domain folders in =/var/www/=, add the configuration file, enable the
-configuration, and restart =apache2=.
+You can repeat this for as many websites as you need. Just create the domain
+folders in =/var/www/=, add the configuration file, enable the configuration,
+and restart =apache2=.
* SSL/TLS Certificates: Serve Websites Over HTTPS
-In order to serve secure content, you'll need to obtain SSL/TLS
-certificates. Luckily, there's a free tool called
-[[https://certbot.eff.org][Certbot]] that helps us with the process.
+In order to serve secure content, you'll need to obtain SSL/TLS certificates.
+Luckily, there's a free tool called [[https://certbot.eff.org][Certbot]] that helps us with the process.
The first step is to install =snapd= and =core= for Ubuntu.
@@ -340,10 +320,10 @@ sudo ln -s /snap/bin/certbot /usr/bin/certbot
Finally, you can run =certbot= one of two ways:
-1. run it and let it alter your Apache configuration files automatically
- to enable HTTPS redirects.
-2. run it and only allow it to create certificates. You'll need to
- manually alter the config files to enable HTTPS redirects.
+1. run it and let it alter your Apache configuration files automatically to
+ enable HTTPS (Hypertext Transport Protocol) redirects.
+2. run it and only allow it to create certificates. You'll need to manually
+ alter the config files to enable HTTPS redirects.
Run certbot and allow automatic config changes:
@@ -358,11 +338,10 @@ files:
sudo certbot certonly --apache
#+end_src
-The Certbot packages on your system come with a cron job or systemd
-timer that will renew your certificates automatically before they
-expire. You will not need to run Certbot again unless you change your
-configuration. You can test automatic renewal for your certificates by
-running this command:
+The Certbot packages on your system come with a =cron= job or =systemd= timer
+that will renew your certificates automatically before they expire. You will not
+need to run Certbot again unless you change your configuration. You can test
+automatic renewal for your certificates by running this command:
#+begin_src sh
sudo certbot renew --dry-run
@@ -373,13 +352,11 @@ Now, test your domains by going to =https://example.com=.
* Firewall Security
To enable better security on your server, you'll need to enable a basic
-firewall. For Ubuntu, we'll use
-[[../secure-your-network-with-the-uncomplicated-firewall/][the
-uncomplicated firewall]].
+firewall. For Ubuntu, we'll use [[https://cleberg.net/blog/ufw.html][the uncomplicated firewall]].
-Now, add the following rules to the firewall allow SSH, Apache, and
-HTTP(S) connections. If you need to, you can enable different ports for
-specifics applications, SFTP, etc.
+Now, add the following rules to the firewall allow SSH, Apache, and HTTP(S)
+connections. If you need to, you can enable different ports for specific
+applications.
#+begin_src sh
sudo ufw default deny incoming
@@ -397,15 +374,13 @@ sudo ufw enable
* Troubleshooting
-If you run into any issues during your VPS set-up, be sure to walk back
-through your actions and make sure you didn't miss any steps.
+If you run into any issues during your VPS set-up, be sure to walk back through
+your actions and make sure you didn't miss any steps.
-Many websites have fantastic guides to setting up various web servers.
-This is one of the areas
-[[https://www.digitalocean.com/community/tutorials][where DigitalOcean
-shines]]. For simpler or more Linux-oriented questions, I suggest using
-[Linuxize] ([[https://linuxize.com]]).
+Many websites have fantastic guides to setting up various web servers. This is
+one of the areas [[https://www.digitalocean.com/community/tutorials][where DigitalOcean shines]]. For simpler or more Linux-oriented
+questions, I suggest using [Linuxize] ([[https://linuxize.com]]).
-If you're getting certain errors (e.g. =500 Internal Server Error=) and
-need to debug locally, you can view the =access.log= and =error.log=
-files in the =/var/log/apache/= directory.
+If you're getting certain errors (e.g. =500 Internal Server Error=) and need to
+debug locally, you can view the =access.log= and =error.log= files in the
+=/var/log/apache/= directory.
diff --git a/content/blog/2021-04-17-gemini-server.org b/content/blog/2021-04-17-gemini-server.org
index b915fb6..f223822 100644
--- a/content/blog/2021-04-17-gemini-server.org
+++ b/content/blog/2021-04-17-gemini-server.org
@@ -5,28 +5,26 @@
* Similar Article Available
-To read more about Gemini and ways to test out this new protocol without
-your own server, see my previous post
-[[../launching-a-gemini-capsule/][Launching a Gemini Capsule]].
+To read more about Gemini and ways to test out this new protocol without your
+own server, see my previous post [[../launching-a-gemini-capsule/][Launching a Gemini Capsule]].
* Preparation
-This guide assumes you have access to a server accessible to the world
-through a public IP address and that you own a domain name used for this
-Gemini capsule.
+This guide assumes you have access to a server accessible to the world through a
+public IP (internet protocol) address and that you own a domain name used for
+this Gemini capsule.
* Getting Started with Agate
-We are going to use [[https://github.com/mbrubeck/agate][Agate]] for
-this tutorial. This is a basic Gemini server written in Rust. It takes
-very little time and maintenance to get it running.
+We are going to use [[https://github.com/mbrubeck/agate][Agate]] for this tutorial. This is a basic Gemini server
+written in Rust. It takes very little time and maintenance to get it running.
* Install Dependencies
-First, you will need to install the Rust package for your system. On
-Ubuntu, use the following commands (remember to use =sudo= if you are
-not the root user). The Rust installation will give you options to
-customize the installation; I used the default installation options.
+First, you will need to install the Rust package for your system. On Ubuntu, use
+the following commands (remember to use =sudo= if you are not the root user).
+The Rust installation will give you options to customize the installation; I
+used the default installation options.
#+begin_src sh
sudo apt update && sudo apt upgrade -y
@@ -54,8 +52,8 @@ cargo install agate
* Create Symlinks
-Once Cargo has finished installing all the required packages, symlink
-the executable to your $PATH.
+Once Cargo has finished installing all the required packages, create a symbolic
+link to the executable to your $PATH.
#+begin_src sh
sudo ln -s $HOME/.cargo/bin/agate /usr/local/bin/agate
@@ -63,8 +61,8 @@ sudo ln -s $HOME/.cargo/bin/agate /usr/local/bin/agate
* Using Agate's Built-In Installation Tool
-If you're running Ubuntu or Debian, use the Debian installation script
-found in Agate's GitHub repository, under the =tools/debian= folder.
+If you're running Ubuntu or Debian, use the Debian installation script found in
+Agate's GitHub repository, under the =tools/debian= folder.
#+begin_src sh
git clone https://github.com/mbrubeck/agate
@@ -74,11 +72,11 @@ sudo ./install.sh
* Configure the Gemini Service
-We have a little more to do, but since this script tries to immediately
-run the service, it will likely fail with an exit code. Let's add our
-finishing touches. Edit the following file and replace the hostname with
-your desired URL. You can also change the directory where content will
-be served.
+We have a little more to do, but since this script tries to immediately run the
+service, it will likely fail with an exit code. Let's add our finishing touches.
+Edit the following file and replace the hostname with your desired URL (uniform
+resource locator). You can also change the directory where content will be
+served.
#+begin_src sh
sudo nano /etc/systemd/system/gemini.service
@@ -97,8 +95,8 @@ WorkingDirectory=/var/gemini/
ExecStart=agate --hostname gemini.example.com --lang en
#+end_src
-Since we've altered the systemd configuration files, we have to reload
-the daemon. Let's do that, restart our service, and check its status.
+Since we've altered the systemd configuration files, we have to reload the
+daemon. Let's do that, restart our service, and check its status.
#+begin_src sh
sudo systemctl daemon-reload
@@ -108,8 +106,8 @@ sudo systemctl status gemini.service
* Fixing Systemd Errors
-If you're still getting errors, the installation process may not have
-properly enabled the gemini service. Fix it with the following commands.
+If you're still getting errors, the installation process may not have properly
+enabled the gemini service. Fix it with the following commands.
#+begin_src sh
sudo systemctl enable gemini.service
@@ -119,9 +117,9 @@ sudo systemctl status gemini.service
* Firewall Rules
-Great! Our server is now functional and running. The first consideration
-now is that you need to be able to access port 1965 on the server. If
-you have a firewall enabled, you'll need to open that port up.
+Great! Our server is now functional and running. The first consideration now is
+that you need to be able to access port 1965 on the server. If you have a
+firewall enabled, you'll need to open that port up.
#+begin_src sh
sudo ufw allow 1965
@@ -131,21 +129,18 @@ sudo ufw reload
* Creating Content
Let's create the Gemini capsule. Note that wherever you set the
-WorkingDirectory variable to earlier, Agate will expect you to put your
-Gemini capsule contents in a sub-folder called "content." So, I place my
-files in "/var/gmi/content." I'm going to create that folder now and put
-a file in there.
+=WorkingDirectory= variable to earlier, Agate will expect you to put your Gemini
+capsule contents in a sub-folder called =content=. So, I place my files in
+=/var/gmi/content=. I'm going to create that folder now and put a file in there.
#+begin_src sh
sudo mkdir /var/gemini/content
sudo nano /var/gemini/content/index.gmi
#+end_src
-You can put whatever you want in the "index.gmi" file, just make sure
-it's valid Gemtext.
+You can put whatever you want in the =index.gmi= file, just make sure it's valid
+Gemtext.
* The Results
-To view the results, you can use a Gemini browser, such as
-[[https://gmi.skyjake.fi/lagrange/][Lagrange]] or
-[[https://github.com/makeworld-the-better-one/amfora][amfora]].
+To view the results, you can use a Gemini browser, such as [[https://gmi.skyjake.fi/lagrange/][Lagrange]] or [[https://github.com/makeworld-the-better-one/amfora][amfora]].
diff --git a/content/blog/2021-04-23-php-comment-system.org b/content/blog/2021-04-23-php-comment-system.org
index c00cb75..33ba763 100644
--- a/content/blog/2021-04-23-php-comment-system.org
+++ b/content/blog/2021-04-23-php-comment-system.org
@@ -5,84 +5,79 @@
* The Terrible-ness of Commenting Systems
-The current state of affairs regarding interactive comment systems is,
-well, terrible. It is especially awful if you're a privacy conscious
-person who does not generally load third-party scripts or frames on the
-websites you visit.
+The current state of affairs regarding interactive comment systems is, well,
+terrible. It is especially awful if you're a privacy conscious person who does
+not generally load third-party scripts or frames on the websites you visit.
-Even further, many comment systems are charging exorbitant fees for
-something that should be standard.
+Even further, many comment systems are charging exorbitant fees for something
+that should be standard.
Of course, there are some really terrible options:
- Facebook Comments
- Discourse
-There are some options that are better but still use too many scripts,
-frames, or social integrations on your web page that could impact some
-users:
+There are some options that are better but still use too many scripts, frames,
+or social integrations on your web page that could impact some users:
- Disqus
- Isso
- Remark42
-Lastly, I looked into a few unique ways of generating blog comments,
-such as using Twitter threads or GitHub issues to automatically post
-issues. However, these both rely on external third-party sites that I
-don't currently use.
+Lastly, I looked into a few unique ways of generating blog comments, such as
+using Twitter threads or GitHub issues to automatically post issues. However,
+these both rely on external third-party sites that I don't currently use.
* Stay Static with Server-Side Comments
-The main issue for my personal use-case is that my blog is completely,
-100% static. I use PHP on the back-end but website visitors only see
-HTML and a single CSS file. No external javascript and no embedded
-frames.
+The main issue for my personal use-case is that my blog is completely, 100%
+static. I use PHP on the back-end but website visitors only see HTML (Hypertext
+Markup Language) and a single CSS (Cascading Style Sheets) file. No external
+JavaScript and no embedded frames.
-So, how do we keep a site static and still allow users to interact with
-blog posts? The key actually pretty simple - I'm already using PHP, so
-why not rely on the classic HTML =<form>= and a PHP script to save the
-comments somewhere? As it turns out, this was a perfect solution for me.
+So, how do we keep a site static and still allow users to interact with blog
+posts? The key actually pretty simple - I'm already using PHP, so why not rely
+on the classic HTML =<form>= and a PHP script to save the comments somewhere? As
+it turns out, this was a perfect solution for me.
-The second issue for my personal use-case is that I am trying to keep
-the contents of my website accessible over time, as described by
-[cite/t:@brandur], in his post entitled
-[[https://brandur.org/fragments/graceful-degradation-time][Blog with
-Markdown + Git, and degrade gracefully through time]] .
+The second issue for my personal use-case is that I am trying to keep the
+contents of my website accessible over time, as described by [cite/t:@brandur],
+in his post entitled [[https://brandur.org/fragments/graceful-degradation-time][Blog with Markdown + Git, and degrade gracefully through
+time]] .
-This means I cannot rely on a database for comments, since I do not rely
-on a database for any other part of my websites.
+This means I cannot rely on a database for comments, since I do not rely on a
+database for any other part of my websites.
-I blog in plain Markdown files, commit all articles to Git, and ensure
-that future readers will be able to see the source data long after I'm
-gone, or the website has gone offline. However, I still haven't
-committed any images served on my blog to Git, as I'm not entirely sold
-on Git LFS yet - for now, images can be found at
-[[https://img.cleberg.net][img.cleberg.net]].
+I blog in plain Markdown files, commit all articles to Git, and ensure that
+future readers will be able to see the source data long after I'm gone, or the
+website has gone offline. However, I still haven't committed any images served
+on my blog to Git, as I'm not entirely sold on Git LFS (large file storage)
+yet - for now, images can be found at [[https://img.cleberg.net][img.cleberg.net]].
-Saving my comments back to the Git repository ensures that another
-aspect of my site will degrade gracefully.
+Saving my comments back to the Git repository ensures that another aspect of my
+site will degrade gracefully.
* Create a Comment Form
-Okay, let's get started. The first step is to create an HTML form that
-users can see and utilize to submit comments. This is fairly easy and
-can be changed depending on your personal preferences.
+Okay, let's get started. The first step is to create an HTML form that users can
+see and utilize to submit comments. This is fairly easy and can be changed
+depending on your personal preferences.
-Take a look at the code block below for the form I currently use. Note
-that =<current-url>= is replaced automatically in PHP with the current
-post's URL, so that my PHP script used later will know which blog post
-the comment is related to.
+Take a look at the code block below for the form I currently use. Note that
+=<current-url>= is replaced automatically in PHP with the current post's URL
+(uniform resource locator), so that my PHP script used later will know which
+blog post the comment is related to.
The form contains the following structure:
-1. =<form>= - This is the form and will determine which PHP script to
- send the comment to.
-2. =<section hidden>= - This section is hidden from the user and is used
- to ensure that we know which blog post sent the comment.
-3. =<section>= Display Name (Optional) - Used to accept a display name,
- if entered.
-4. =<section>= Comment (Required) - Used to accept the user's full
- comment. Markdown is allowed.
+1. =<form>= - This is the form and will determine which PHP script to send the
+ comment to.
+2. =<section hidden>= - This section is hidden from the user and is used to
+ ensure that we know which blog post sent the comment.
+3. =<section>= Display Name (Optional) - Used to accept a display name, if
+ entered.
+4. =<section>= Comment (Required) - Used to accept the user's full comment.
+ Markdown is allowed.
5. =<button>= - A button to submit the form.
#+begin_src html
@@ -129,25 +124,25 @@ The form contains the following structure:
* Handle Comments via POST
-Now that we have a form and can submit comments, we need to create a PHP
-script so that the server can fetch the comment data and save it. Make
-sure your PHP script name matches the name you entered in the =action=
-field in your form.
+Now that we have a form and can submit comments, we need to create a PHP script
+so that the server can fetch the comment data and save it. Make sure your PHP
+script name matches the name you entered in the =action= field in your form.
-See the code block below for the contents of my =comment.php= script. We
-perform the following tasks in this script:
+See the code block below for the contents of my =comment.php= script. We perform
+the following tasks in this script:
1. Grab the POST data from the HTML form.
2. Sanitize the comment data with =htmlentities=.
3. Set the display name to =Anonymous= if it was left blank.
4. Create a PHP object that combines all of this data.
5. Check if our destination file =comments.json= exists.
-6. If so, convert the PHP object to JSON and save it to the file.
-7. If the =comments.json= file does not exist, the script will exit with
- an error. You can alter this to ensure it creates the script, but my
- source code includes the file by default, so it will always exist.
-8. Finally, send the user back to the =#comments= section of the blog
- post they just read.
+6. If so, convert the PHP object to JSON (JavaScript Object Notation) and save
+ it to the file.
+7. If the =comments.json= file does not exist, the script will exit with an
+ error. You can alter this to ensure it creates the script, but my source code
+ includes the file by default, so it will always exist.
+8. Finally, send the user back to the =#comments= section of the blog post they
+ just read.
#+begin_src php
// Get the content sent from the comment form
@@ -186,9 +181,9 @@ if (file_exists($file_name)) {
header('Location: ' . $post_url . '#comments');
#+end_src
-If you're using Apache, make sure the =www-data= user on your server has
-the correct permissions to your website directory or else it will not be
-able to write to =comments.json=.
+If you're using Apache, make sure the =www-data= user on your server has the
+correct permissions to your website directory or else it will not be able to
+write to =comments.json=.
#+begin_src sh
chgrp -R www-data /path/to/website/
@@ -197,18 +192,15 @@ chmod -R g+w comments.json
* Display User Comments
-Now that we can submit comments, and they are saved to the
-=comments.json= file, let's make sure we can show those comments in each
-blog post.
+Now that we can submit comments, and they are saved to the =comments.json= file,
+let's make sure we can show those comments in each blog post.
-The code block below shows the function I use to decode my
-=comments.json= file, check if the comments apply to the current post,
-and then display them.
+The code block below shows the function I use to decode my =comments.json= file,
+check if the comments apply to the current post, and then display them.
-This piece of code should *really* be inside a function (or at least in
-an organized PHP workflow). Don't just copy-and-paste and expect it to
-work. You need to at least supply a =$query= variable depending on the
-page visited.
+This piece of code should *really* be inside a function (or at least in an
+organized PHP workflow). Don't just copy-and-paste and expect it to work. You
+need to at least supply a =$query= variable depending on the page visited.
#+begin_src php
$query = 'your-blog-post.html';
@@ -246,27 +238,24 @@ echo $comment_section;
* Bonus: Create a 'Recent Comments' Page
-Finally, the last part of my current system is to create a Recent
-Comments page so that I can easily check-in on my blog and moderate any
-spam. As an alternative, you could use PHP's =mail()= function to email
-you for each blog comment.
+Finally, the last part of my current system is to create a Recent Comments page
+so that I can easily check-in on my blog and moderate any spam. As an
+alternative, you could use PHP's =mail()= function to email you for each blog
+comment.
-The code to do this is literally the same code as the previous section,
-I just make sure it is printed when someone visits
-=https://example.com/comments/=.
+The code to do this is literally the same code as the previous section, I just
+make sure it is printed when someone visits =https://example.com/comments/=.
* Possible Enhancements
-This comment system is by no means a fully-developed system. I have
-noted a few possible enhancements here that I may implement in the
-future:
+This comment system is by no means a fully-developed system. I have noted a few
+possible enhancements here that I may implement in the future:
- Create a secure moderator page with user authentication at
- =https://blog.example.com/mod/=. This page could have the option to
- edit or delete any comment found in =comments.json=.
-- Create a temporary file, such as =pending_comments.json=, that will
- store newly-submitted comments and won't display on blog posts until
- approved by a moderator.
-- Create a =/modlog/= page with a chronological log, showing which
- moderator approved which comments and why certain comments were
- rejected.
+ =https://blog.example.com/mod/=. This page could have the option to edit or
+ delete any comment found in =comments.json=.
+- Create a temporary file, such as =pending_comments.json=, that will store
+ newly-submitted comments and won't display on blog posts until approved by a
+ moderator.
+- Create a =/modlog/= page with a chronological log, showing which moderator
+ approved which comments and why certain comments were rejected.
diff --git a/content/blog/2021-04-28-photography.org b/content/blog/2021-04-28-photography.org
index 9e63743..a044a7d 100644
--- a/content/blog/2021-04-28-photography.org
+++ b/content/blog/2021-04-28-photography.org
@@ -6,66 +6,58 @@
* Why Photography?
I've often wondered why photography is as enticing as it is. You can see
-billions of people around the world taking photographs every single
-moment of the day. New technology often boasts about their photographic
-capabilities, especially smartphones. I would even assume that we live
-in a world where there is never a moment in which a photograph is not
-being taken somewhere on Earth.
+billions of people around the world taking photographs every single moment of
+the day. New technology often boasts about their photographic capabilities,
+especially smartphones. I would even assume that we live in a world where there
+is never a moment in which a photograph is not being taken somewhere on Earth.
-As for myself, I would simply say that I enjoy preserving a memory in
-physical (or digital) form. I've never had the best memory when it comes
-to recalling details of places and people gone by, so it helps to have a
-frame of reference lying around.
+As for myself, I would simply say that I enjoy preserving a memory in physical
+(or digital) form. I've never had the best memory when it comes to recalling
+details of places and people gone by, so it helps to have a frame of reference
+lying around.
-Regardless of the reason, I think most people would agree that you
-simply cannot have too many hobbies.
+Regardless of the reason, I think most people would agree that you simply cannot
+have too many hobbies.
* Older Cameras
-I started playing around with the idea of photography when my family
-purchased a Fujifilm camera for family-specific events. I don't recall
-the specific model, but I do recall it was a point-and-shoot camera
-without an interchangeable lens. However, it was of great value to
-someone, like myself, who couldn't afford any other camera. I took about
-10,000 shots with that camera over a 3-5 year span. Most notably, all of
-my trips to California were documented through this camera.
+I started playing around with the idea of photography when my family purchased a
+Fujifilm camera for family-specific events. I don't recall the specific model,
+but I do recall it was a point-and-shoot camera without an interchangeable lens.
+However, it was of great value to someone, like myself, who couldn't afford any
+other camera. I took about 10,000 shots with that camera over a 3-5 year span.
+Most notably, all of my trips to California were documented through this camera.
-When possible, I would borrow my sister's camera, which is a Sony
-SLT-A58. This camera was great and allowed for some of my best early
-shots, especially those taken in Utah's and Nevada's parks.
+When possible, I would borrow my sister's camera, which is a Sony SLT-A58. This
+camera was great and allowed for some of my best early shots, especially those
+taken in Utah's and Nevada's parks.
* My Current Kit
-I've finally come to a point in my life where I have the disposable
-income to invest in a solid photography kit. I played around with the
-idea of a lot of different cameras, different types, new vs used, etc.
-Finally, I settled on the
-[[https://en.wikipedia.org/wiki/Sony_%CE%B17_III][Sony α7 III]]. This
-camera is mirror-less and uses a full-frame image sensor at 24
-megapixels. I don't create large prints, and I am mostly focused on
-preserving memories in high quality for the next 5-10 years with this
-camera, so the specifications here are just perfect for me.
+I've finally come to a point in my life where I have the disposable income to
+invest in a solid photography kit. I played around with the idea of a lot of
+different cameras, different types, new vs used, etc. Finally, I settled on the
+[[https://en.wikipedia.org/wiki/Sony_%CE%B17_III][Sony α7 III]]. This camera is mirror-less and uses a full-frame image sensor at 24
+megapixels. I don't create large prints, and I am mostly focused on preserving
+memories in high quality for the next 5-10 years with this camera, so the
+specifications here are just perfect for me.
For lenses, I decided to buy two lenses that could carry me through most
situations:
-- [[https://electronics.sony.com/imaging/lenses/full-frame-e-mount/p/sel2470z][Vario-Tessar
- T** FE 24-70 mm F4 ZA OSS]]
-- [[https://www.tamron-usa.com/product/lenses/a047.html][Tamron 70-300mm
- f4.5-6.3 Di III RXD]]
+- [[https://electronics.sony.com/imaging/lenses/full-frame-e-mount/p/sel2470z][Vario-Tessar T** FE 24-70 mm F4 ZA OSS]]
+- [[https://www.tamron-usa.com/product/lenses/a047.html][Tamron 70-300mm f4.5-6.3 Di III RXD]]
-In addition, I grabbed a couple
-[[https://www.promaster.com/Product/6725][HGX Prime 67mm]] protection
-filters for the lenses.
+In addition, I grabbed a couple [[https://www.promaster.com/Product/6725][HGX Prime 67mm]] protection filters for the
+lenses.
-As I delve further into photography and pick up more skills, I will most
-likely go back and grab a lens with a higher f-stop value, such as
-f/1.8. I toyed with the idea of grabbing a 50 mm at =f/1.8=, but decided
-to keep things in a reasonable price range instead.
+As I delve further into photography and pick up more skills, I will most likely
+go back and grab a lens with a higher f-stop value, such as f/1.8. I toyed with
+the idea of grabbing a 50 mm at =f/1.8=, but decided to keep things in a
+reasonable price range instead.
-Finally, I made sure to buy a photography-specific backpack with a rain
-guard, and the zipper on the back panel, to protect the equipment while
-wearing the bag. If you've ever had to haul around a DSLR (or camera of
-similar heft) in a bag that only has a shoulder strap, you'll know the
-pain it can cause. Putting all my equipment in a backpack was an easy
-decision.
+Finally, I made sure to buy a photography-specific backpack with a rain guard,
+and the zipper on the back panel, to protect the equipment while wearing the
+bag. If you've ever had to haul around a DSLR (digital single-lens reflex)
+camera in a bag that only has a shoulder strap, you'll know the pain it can
+cause. Putting all my equipment in a backpack was an easy decision.
diff --git a/content/blog/2021-05-30-changing-git-authors.org b/content/blog/2021-05-30-changing-git-authors.org
index c5d7441..86dd84c 100644
--- a/content/blog/2021-05-30-changing-git-authors.org
+++ b/content/blog/2021-05-30-changing-git-authors.org
@@ -5,12 +5,12 @@
* Changing Git Author/Email Based on Previously Committed Email
-Here's the dilemma: You've been committing changes to your git
-repository with an incorrect name or email (or multiple repositories),
-and now you want to fix it. Luckily, there's a semi-reliable way to fix
-that. While I have never experienced issues with this method, some
-people have warned that it can mess with historical hashes and integrity
-of commits, so use this method only if you're okay accepting that risk.
+Here's the dilemma: You've been committing changes to your Git repository with
+an incorrect name or email (or multiple repositories), and now you want to fix
+it. Luckily, there's a semi-reliable way to fix that. While I have never
+experienced issues with this method, some people have warned that it can mess
+with historical hashes and integrity of commits, so use this method only if
+you're okay accepting that risk.
Okay, let's create the bash script:
@@ -18,8 +18,8 @@ Okay, let's create the bash script:
nano change_git_authors.sh
#+end_src
-The following information can be pasted directly into your bash script.
-The only changes you need to make are to the following variables:
+The following information can be pasted directly into your bash script. The only
+changes you need to make are to the following variables:
- =OLD_EMAIL=
- =CORRECT_NAME=
diff --git a/content/blog/2021-07-15-delete-gitlab-repos.org b/content/blog/2021-07-15-delete-gitlab-repos.org
index 389ef46..84b08ad 100644
--- a/content/blog/2021-07-15-delete-gitlab-repos.org
+++ b/content/blog/2021-07-15-delete-gitlab-repos.org
@@ -5,31 +5,29 @@
* Background
-Have you ever used GitLab to host your source code, moved to a different
-host, and wanted to delete everything from your GitLab account? Well,
-this post covers any scenario where you would want to delete all
-repositories from your GitLab account.
+Have you ever used GitLab to host your source code, moved to a different host,
+and wanted to delete everything from your GitLab account? Well, this post covers
+any scenario where you would want to delete all repositories from your GitLab
+account.
-For me, I currently maintain around 30 repositories and don't like to
-manually delete them whenever I switch host. GitHub has a few different
-tools online to delete all repositories for you, but I have not found
-anything similar for GitLab, so I needed an alternative solution.
+For me, I currently maintain around 30 repositories and don't like to manually
+delete them whenever I switch host. GitHub has a few different tools online to
+delete all repositories for you, but I have not found anything similar for
+GitLab, so I needed an alternative solution.
* Use a Python Script
** Requirements
-Before we look at the script, make sure you know your GitLab username.
-Next, [[https://gitlab.com/-/profile/personal_access_tokens][create an
-authorization token]] so that the Python script can delete your
-repositories. Don't lose this token or else you'll need to create a new
-one.
+Before we look at the script, make sure you know your GitLab username. Next,
+[[https://gitlab.com/-/profile/personal_access_tokens][create an authorization token]] so that the Python script can delete your
+repositories. Don't lose this token or else you'll need to create a new one.
** Create the Script
-To run a Python script, you must first create it. Open a terminal and
-enter the following commands in whichever directory you prefer to store
-the script. You can do the same things in a file manager if you prefer.
+To run a Python script, you must first create it. Open a terminal and enter the
+following commands in whichever directory you prefer to store the script. You
+can do the same things in a file manager if you prefer.
#+begin_src sh
mkdir delete-gitlab
@@ -101,13 +99,12 @@ if __name__ == "__main__":
main()
#+end_src
-Now that you have the proper information, replace ={user-id}= with your
-GitLab username and ={auth-token}= with the authorization token you
-created earlier.
+Now that you have the proper information, replace ={user-id}= with your GitLab
+username and ={auth-token}= with the authorization token you created earlier.
-Finally, simply run the script and watch the output. You can also use
-PyCharm Community Edition to edit and run the Python script if you don't
-want to work in a terminal.
+Finally, simply run the script and watch the output. You can also use PyCharm
+Community Edition to edit and run the Python script if you don't want to work in
+a terminal.
#+begin_src sh
python3 main.py
diff --git a/content/blog/2021-08-25-audit-sampling.org b/content/blog/2021-08-25-audit-sampling.org
index 1f6c244..a2cc1d4 100644
--- a/content/blog/2021-08-25-audit-sampling.org
+++ b/content/blog/2021-08-25-audit-sampling.org
@@ -6,44 +6,40 @@
* Introduction
For anyone who is familiar with internal auditing, external auditing, or
-consulting, you will understand how tedious audit testing can become
-when you are required to test large swaths of data. When we cannot
-establish an automated means of testing an entire population, we
-generate samples to represent the population of data. This helps ensure
-we can have a small enough data pool to test and that our results still
-represent the population.
-
-However, sampling data within the world of audit still seems to confuse
-quite a lot of people. While some audit-focused tools have introduced
-sampling functionality (e.g. Wdesk), many audit departments and firms
-cannot use software like this due to certain constraints, such as the
-team's budget or knowledge. Here is where this article comes in: we're
-going to use [[https://www.python.org][Python]], a free and open-source
-programming language, to generate random samples from a dataset in order
-to suffice numerous audit situations.
+consulting, you will understand how tedious audit testing can become when you
+are required to test large swaths of data. When we cannot establish an automated
+means of testing an entire population, we generate samples to represent the
+population of data. This helps ensure we can have a small enough data pool to
+test and that our results still represent the population.
+
+However, sampling data within the world of audit still seems to confuse quite a
+lot of people. While some audit-focused tools have introduced sampling
+functionality (e.g. Wdesk), many audit departments and firms cannot use software
+like this due to certain constraints, such as the team's budget or knowledge.
+Here is where this article comes in: we're going to use [[https://www.python.org][Python]], a free and
+open-source programming language, to generate random samples from a dataset in
+order to suffice numerous audit situations.
* Audit Requirements for Sampling
-Before we get into the details of how to sample with Python, I want to
-make sure I discuss the different requirements that auditors may have of
-samples used within their projects.
+Before we get into the details of how to sample with Python, I want to make sure
+I discuss the different requirements that auditors may have of samples used
+within their projects.
** Randomness
-First, let's discuss randomness. When testing out new technology to help
-assist with audit sampling, you need to understand exactly how your
-samples are being generated. For example, if the underlying function is
-just picking every 57th element from a list, that's not truly random;
-it's a systematic form of sampling. Luckily, since Python is
-open-source, we have access to its codebase. Through this blog post, I
-will be using the [[https://pandas.pydata.org][pandas]] module in order
-to generate the random samples. More specifically, I will be using the
-[[https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sample.html][pandas.DataFrame.sample]]
-function provided by Pandas.
-
-Now that you know what you're using, you can always check out the code
-behind =pandas.DataFrame.sample=. This function does a lot of work, but
-we really only care about the following snippets of code:
+First, let's discuss randomness. When testing out new technology to help assist
+with audit sampling, you need to understand exactly how your samples are being
+generated. For example, if the underlying function is just picking every 57th
+element from a list, that's not truly random; it's a systematic form of
+sampling. Luckily, since Python is open-source, we have access to its codebase.
+Through this blog post, I will be using the [[https://pandas.pydata.org][pandas]] module in order to generate
+the random samples. More specifically, I will be using the
+[[https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sample.html][pandas.DataFrame.sample]] function provided by Pandas.
+
+Now that you know what you're using, you can always check out the code behind
+=pandas.DataFrame.sample=. This function does a lot of work, but we really only
+care about the following snippets of code:
#+begin_src python
# Process random_state argument
@@ -59,61 +55,62 @@ result.index = ibase.default_index(len(result))
return result
#+end_src
-The block of code above shows you that if you assign a =random_state=
-argument when you run the function, that will be used as a seed number
-in the random generation and will allow you to reproduce a sample, given
-that nothing else changes. This is critical to the posterity of audit
-work. After all, how can you say your audit process is adequately
-documented if the next person can't run the code and get the same
-sample? The final piece here on randomness is to look at the
-[[https://docs.python.org/3/library/random.html#random.choice][choice]]
-function used above. This is the crux of the generation and can also be
-examined for more detailed analysis on its reliability. As far as
-auditing goes, we will trust that these functions are mathematically
-random.
+The block of code above shows you that if you assign a =random_state= argument
+when you run the function, that will be used as a seed number in the random
+generation and will allow you to reproduce a sample, given that nothing else
+changes. This is critical to the posterity of audit work. After all, how can you
+say your audit process is adequately documented if the next person can't run the
+code and get the same sample? The final piece here on randomness is to look at
+the [[https://docs.python.org/3/library/random.html#random.choice][choice]] function used above. This is the crux of the generation and can also
+be examined for more detailed analysis on its reliability. As far as auditing
+goes, we will trust that these functions are mathematically random.
** Sample Sizes
-As mentioned in the intro, sampling is only an effective method of
-auditing when it truly represents the entire population. While some
-audit departments or firms may consider certain judgmental sample sizes
-to be adequate, you may need to rely on statistically-significant
-confidence levels of sample testing at certain points. I will
-demonstrate both here. For statistically-significant confidence levels,
-most people will assume a 90% - 99% confidence level. In order to
-actually calculate the correct sample size, it is best to use
-statistical tools due to the tedious math work required. For example,
-for a population of 1000, and a 90% confidence level that no more than
-5% of the items are nonconforming, you would sample 45 items.
-
-However, in my personal experience, many audit departments and firms do
-not use statistical sampling. Most people use a predetermined, often
-proprietary, table that will instruct auditors which sample sizes to
-choose. This allows for uniform testing and reduces overall workload.
-See the table below for a common implementation of sample sizes:
-
-Control Frequency Sample Size - High Risk Sample Size - Low Risk
-
---------------
-
-More Than Daily 40 25 Daily 40 25 Weekly 12 5 Monthly 5 3 Quarterly 2 2
-Semi-Annually 1 1 Annually 1 1 Ad-hoc 1 1
+As mentioned in the intro, sampling is only an effective method of auditing when
+it truly represents the entire population. While some audit departments or firms
+pmay consider certain judgmental sample sizes to be adequate, you may need to
+rely on statistically-significant confidence levels of sample testing at certain
+
+points. I will demonstrate both here. For statistically-significant confidence
+levels, most people will assume a 90% - 99% confidence level. In order to
+actually calculate the correct sample size, it is best to use statistical tools
+due to the tedious math work required. For example, for a population of 1000,
+and a 90% confidence level that no more than 5% of the items are nonconforming,
+you would sample 45 items.
+
+However, in my personal experience, many audit departments and firms do not use
+statistical sampling. Most people use a predetermined, often proprietary, table
+that will instruct auditors which sample sizes to choose. This allows for
+uniform testing and reduces overall workload. See the table below for a common
+implementation of sample sizes:
+
+| Control Frequency | Sample Size (High Risk) | Sample Size (Low Risk) |
+|-------------------+-------------------------+------------------------|
+| More Than Daily | 40 | 25 |
+| Daily | 40 | 25 |
+| Weekly | 12 | 5 |
+| Monthly | 5 | 3 |
+| Quarterly | 2 | 2 |
+| Semi-Annually | 1 | 1 |
+| Annually | 1 | 1 |
+| Ad-hoc | 1 | 1 |
*** Sampling with Python & Pandas
-In this section, I am going to cover a few basic audit situations that
-require sampling. While some situations may require more effort, the
-syntax, organization, and intellect used remain largely the same. If
-you've never used Python before, note that lines starting with a '=#='
-symbol are called comments, and they will be skipped by Python. I highly
-recommend taking a quick tutorial online to understand the basics of
-Python if any of the code below is confusing to you.
+In this section, I am going to cover a few basic audit situations that require
+sampling. While some situations may require more effort, the syntax,
+organization, and intellect used remain largely the same. If you've never used
+Python before, note that lines starting with a '=#=' symbol are called comments,
+and they will be skipped by Python. I highly recommend taking a quick tutorial
+online to understand the basics of Python if any of the code below is confusing
+to you.
** Simple Random Sample
-First, let's look at a simple, random sample. The code block below will
-import the =pandas= module, load a data file, sample the data, and
-export the sample to a file.
+First, let's look at a simple, random sample. The code block below will import
+the =pandas= module, load a data file, sample the data, and export the sample to
+a file.
#+begin_src python
# Import the Pandas module
@@ -137,8 +134,8 @@ sample.to_excel(file_output)
** Simple Random Sample: Using Multiple Input Files
-Now that we've created a simple sample, let's create a sample from
-multiple files.
+Now that we've created a simple sample, let's create a sample from multiple
+files.
#+begin_src python
# Import the Pandas module
@@ -171,9 +168,9 @@ sample.to_excel(file_output)
** Stratified Random Sample
-Well, what if you need to sample distinct parts of a single file? For
-example, let's write some code to separate our data by "Region" and
-sample those regions independently.
+Well, what if you need to sample distinct parts of a single file? For example,
+let's write some code to separate our data by "Region" and sample those regions
+independently.
#+begin_src python
# Import the Pandas module
@@ -205,10 +202,10 @@ sample.to_excel(file_output)
** Stratified Systematic Sample
-This next example is quite useful if you need audit coverage over a
-certain time period. This code will generate samples for each month in
-the data and combine them all together at the end. Obviously, this code
-can be modified to stratify by something other than months, if needed.
+This next example is quite useful if you need audit coverage over a certain time
+period. This code will generate samples for each month in the data and combine
+them all together at the end. Obviously, this code can be modified to stratify
+by something other than months, if needed.
#+begin_src python
# Import the Pandas module
@@ -254,17 +251,17 @@ sample.to_excel(file_output)
*** Documenting the Results
-Once you've generated a proper sample, there are a few things left to do
-in order to properly ensure your process is reproducible.
-
-1. Document the sample. Make sure the resulting file is readable and
- includes the documentation listed in the next bullet.
-2. Include documentation around the data source, extraction techniques,
- any modifications made to the data, and be sure to include a copy of
- the script itself.
-3. Whenever possible, perform a completeness and accuracy test to ensure
- your sample is coming from a complete and accurate population. To
- ensure completeness, compare the record count from the data source to
- the record count loaded into Python. To ensure accuracy, test a small
- sample against the source data (e.g., test 5 sales against the
- database to see if the details are accurate).
+Once you've generated a proper sample, there are a few things left to do in
+order to properly ensure your process is reproducible.
+
+1. Document the sample. Make sure the resulting file is readable and includes
+ the documentation listed in the next bullet.
+2. Include documentation around the data source, extraction techniques, any
+ modifications made to the data, and be sure to include a copy of the script
+ itself.
+3. Whenever possible, perform a completeness and accuracy test to ensure your
+ sample is coming from a complete and accurate population. To ensure
+ completeness, compare the record count from the data source to the record
+ count loaded into Python. To ensure accuracy, test a small sample against the
+ source data (e.g., test 5 sales against the database to see if the details
+ are accurate).
diff --git a/content/blog/2021-10-09-apache-redirect.org b/content/blog/2021-10-09-apache-redirect.org
index c1de6be..6160882 100644
--- a/content/blog/2021-10-09-apache-redirect.org
+++ b/content/blog/2021-10-09-apache-redirect.org
@@ -5,15 +5,13 @@
* The Problem
-After recently switching static site generators (SSG), my blog URLs
-changed with no option to preserve the classic =.html= extension at the
-end of my blog post URLs.
+After recently switching static site generators (SSG), my blog URLs (uniform
+resource locators) changed with no option to preserve the classic =.html=
+extension at the end of my blog post URLs.
-I really disliked using my old SSG ([[https://jekyllrb.com][Jekyll]])
-and prefer my new tool ([[https://www.getzola.org][Zola]]) much more, so
-I was determined to figure out a way to get the proper redirect set up
-so that people who find my posts online aren't constantly met by 404
-errors.
+I really disliked using my old SSG ([[https://jekyllrb.com][Jekyll]]) and prefer my new tool ([[https://www.getzola.org][Zola]]) much
+more, so I was determined to figure out a way to get the proper redirect set up
+so that people who find my posts online aren't constantly met by 404 errors.
* The Solution
@@ -21,11 +19,10 @@ To solve this problem, I really needed to solve two pieces:
1. Redirect all blog post URL requests from =/blog/some-post.html= to
=/blog/some-post/=.
-2. Ensure that no other =.html= files are redirected, such as
- =index.html=.
+2. Ensure that no other =.html= files are redirected, such as =index.html=.
-After /a lot/ of tweaking and testing, I believe I have finally found
-the solution. The solution is shown below.
+After /a lot/ of tweaking and testing, I believe I have finally found the
+solution. The solution is shown below.
#+begin_src conf
RewriteEngine On
@@ -38,8 +35,8 @@ following:
1. Turn on the RewriteEngine so that we can modify URLs.
2. Ignore any =index.html= files from the rule we are about to specify.
-3. Find any =.html= files within the website directory and redirect it
- to exclude the file extension.
-4. The final piece is adding the trailing slash (=/=) at the end of the
- URL - you'll notice that I don't have an Apache rule for that since
- Apache handles that automatically.
+3. Find any =.html= files within the website directory and redirect it to
+ exclude the file extension.
+4. The final piece is adding the trailing slash (=/=) at the end of the URL -
+ you'll notice that I don't have an Apache rule for that since Apache handles
+ that automatically.
diff --git a/content/blog/2021-12-04-cisa.org b/content/blog/2021-12-04-cisa.org
index 07b264c..7ff508d 100644
--- a/content/blog/2021-12-04-cisa.org
+++ b/content/blog/2021-12-04-cisa.org
@@ -6,200 +6,186 @@
* What is the CISA?
For those of you lucky enough not to be knee-deep in the world of IT/IS
-Auditing, [[https://www.isaca.org/credentialing/cisa][CISA]] stands for
-Certified Information Systems Auditor. This certification and exam are
-part of ISACA's suite of certifications. As I often explain it to people
-like my family, it basically means you're employed to use your knowledge
-of information systems, regulations, common threats, risks, etc. in
-order to assess an organization's current control of their risk. If a
-risk isn't controlled (and the company doesn't want to accept the risk),
-an IS auditor will suggest implementing a control to address that risk.
-
-Now, the CISA certification itself is, in my opinion, the main
-certification for this career. While certifications such as the CPA or
-CISSP are beneficial, nothing matches the power of the CISA for an IS
-auditor when it comes to getting hired, getting a raise/bonus, or
-earning respect in the field.
-
-However, to be honest, I am a skeptic of most certifications. I
-understand the value they hold in terms of how much you need to commit
-to studying or learning on the job, as well as the market value for
-certifications such as the CISA. But I also have known some very
-+incompetent+ /less than stellar/ auditors who have CPAs, CISAs, CIAs,
-etc.
-
-The same goes for most industries: if a person is good at studying, they
-can earn the certification. However, that knowledge means nothing unless
-you're actually able to use it in real life and perform as expected of a
-certification holder. The challenge comes when people are hired or
-connected strictly because of their certifications or resume; you need
-to see a person work before you can assume them having a CISA means
-they're better than someone without the CISA.
-
-Okay, rant over. Certifications are generally accepted as a measuring
-stick of commitment and quality of an employee, so I am accepting it
-too.
+Auditing, the [[https://www.isaca.org/credentialing/cisa][CISA]] (Certified Information Systems Auditor) certification and
+exam are part of ISACA's suite of certifications. As I often explain it to
+people like my family, it basically means you're employed to use your knowledge
+of information systems, regulations, common threats, risks, etc. in order to
+assess an organization's current control of their risk. If a risk isn't
+controlled (and the company doesn't want to accept the risk), an IS auditor will
+suggest implementing a control to address that risk.
+
+Now, the CISA certification itself is, in my opinion, the main certification for
+this career. While certifications such as the CPA (Certified Public Accountant)
+or CISSP (Certified Information Systems Security Professional) are beneficial,
+nothing matches the power of the CISA for an information systems (IS) auditor
+when it comes to getting hired, getting a raise/bonus, or earning respect in the
+field.
+
+However, to be honest, I am a skeptic of most certifications. I understand the
+value they hold in terms of how much you need to commit to studying or learning
+on the job, as well as the market value for certifications such as the CISA. But
+I also have known some very +incompetent+ /less than stellar/ auditors who have
+relevant certifications.
+
+The same goes for most industries: if a person is good at studying, they can
+earn the certification. However, that knowledge means nothing unless you're
+actually able to use it in real life and perform as expected of a certification
+holder. The challenge comes when people are hired or connected strictly because
+of their certifications or resume; you need to see a person work before you can
+assume them having a CISA means they're better than someone without the CISA.
+
+Okay, rant over. Certifications are generally accepted as a measuring stick of
+commitment and quality of an employee, so I am accepting it too.
* Exam Content
-The CISA is broken down into five sections, each weighted with a
-percentage of test questions that may appear.
+The CISA is broken down into five sections, each weighted with a percentage of
+test questions that may appear.
-Since the exam contains 150 questions, here's how those sections break
-down:
+Since the exam contains 150 questions, here's how those sections break down:
-| Exam Section | Percentage of Exam | Questions |
+| Exam Section | Percentage of Exam | Questions |
|---------------+--------------------+-----------|
-| 1 | 21% | 32 |
-| 2 | 17% | 26 |
-| 3 | 12% | 18 |
-| 4 | 23% | 34 |
-| 5 | 27% | 40 |
-| *Grand Total* | *100%* | *150* |
+| 1 | 21% | 32 |
+| 2 | 17% | 26 |
+| 3 | 12% | 18 |
+| 4 | 23% | 34 |
+| 5 | 27% | 40 |
+| *Grand Total* | *100%* | *150* |
* My Studying Habits
-This part is a little hard for me to break down into specific detail due
-to the craziness of the last year. While I officially purchased my
-studying materials in December 2020 and opened them to "start studying"
-in January 2021, I really wasn't able to study much due to the demands
-of my job and personal life.
+This part is a little hard for me to break down into specific detail due to the
+craziness of the last year. While I officially purchased my studying materials
+in December 2020 and opened them to "start studying" in January 2021, I really
+wasn't able to study much due to the demands of my job and personal life.
Let me approach this from a few different viewpoints.
** Study Materials
-Let's start by discussing the study materials I purchased. I'll be
-referring to #1 as the CRM and #2 as the QAE.
-
-1. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCbEAK][CISA
- Review Manual, 27th Edition | Print]]
-2. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCcEAK][CISA
- Review Questions, Answers & Explanations Manual, 12th Edition |
- Print]]
-
-The CRM is an excellent source of information and could honestly be used
-as a reference for most IS auditors as a learning reference during their
-daily audit responsibilities. However, it is *full* of information and
-can be overloading if you're not good at filtering out useless
-information while studying.
-
-The QAE is the real star of the show here. This book contains 1000
-questions, separated by exam section, and a practice exam. My only
-complaint about the QAE is that each question is immediately followed
-with the correct answer and explanations below it, which means I had to
-use something to constantly cover the answers while I was studying.
-
-I didn't use the online database version of the QAE, but I've heard that
-it's easier to use than the printed book. However, it is more expensive
-($299 database vs $129 book) which might be important if you're paying
-for materials yourself.
-
-In terms of question difficulty, I felt that the QAE was a good
-representation of the actual exam. I've seen a lot of people online say
-it wasn't accurate to the exam or that it was much easier/harder, but I
-disagree with all of those. The exam was fairly similar to the QAE, just
-focusing on whichever topics they chose for my version of the exam.
-
-If you understand the concepts, skim the CRM (and read in-depth on
-topics you struggle with), and use the QAE to continue practicing
-exam-like questions, you should be fine. I didn't use any online
-courses, videos, etc. - the ISACA materials are more than enough.
+Let's start by discussing the study materials I purchased. I'll be referring to
+#1 as the CRM (CISA Review Manual) and #2 as the QAE (Questions, Answers &
+Explanations).
+
+1. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCbEAK][CISA Review Manual, 27th Edition | Print]]
+2. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCcEAK][CISA Review Questions, Answers & Explanations Manual, 12th Edition | Print]]
+
+The CRM is an excellent source of information and could honestly be used as a
+reference for most IS auditors as a learning reference during their daily audit
+responsibilities. However, it is *full* of information and can be overloading if
+you're not good at filtering out useless information while studying.
+
+The QAE is the real star of the show here. This book contains 1000 questions,
+separated by exam section, and a practice exam. My only complaint about the QAE
+is that each question is immediately followed with the correct answer and
+explanations below it, which means I had to use something to constantly cover
+the answers while I was studying.
+
+I didn't use the online database version of the QAE, but I've heard that it's
+easier to use than the printed book. However, it is more expensive ($299
+database vs $129 book) which might be important if you're paying for materials
+yourself.
+
+In terms of question difficulty, I felt that the QAE was a good representation
+of the actual exam. I've seen a lot of people online say it wasn't accurate to
+the exam or that it was much easier/harder, but I disagree with all of those.
+The exam was fairly similar to the QAE, just focusing on whichever topics they
+chose for my version of the exam.
+
+If you understand the concepts, skim the CRM (and read in-depth on topics you
+struggle with), and use the QAE to continue practicing exam-like questions, you
+should be fine. I didn't use any online courses, videos, etc. - the ISACA
+materials are more than enough.
** Studying Process
-While I was able to briefly read through sections 1 and 2 in early 2021,
-I had to stop and take a break from February/March to September. I
-switched jobs in September, which allowed me a lot more free time to
-study.
+While I was able to briefly read through sections 1 and 2 in early 2021, I had
+to stop and take a break from February/March to September. I switched jobs in
+September, which allowed me a lot more free time to study.
-In September, I studied sections 3-5, took notes, and did a quick review
-of the section topics. Once I felt comfortable with my notes, I took a
-practice exam from the QAE manual and scored 70% (105/150).
+In September, I studied sections 3-5, took notes, and did a quick review of the
+section topics. Once I felt comfortable with my notes, I took a practice exam
+from the QAE manual and scored 70% (105/150).
Here's a breakdown of my initial practice exam:
-| Exam Section | Incorrect | Correct | Grand Total | Percent |
+| Exam Section | Incorrect | Correct | Grand Total | Percent |
|---------------+-----------+---------+-------------+---------|
-| 1 | 8 | 25 | 33 | 76% |
-| 2 | 5 | 20 | 25 | 80% |
-| 3 | 6 | 12 | 18 | 67% |
-| 4 | 10 | 23 | 33 | 70% |
-| 5 | 16 | 25 | 41 | 61% |
-| *Grand Total* | *45* | *105* | *150* | *70%* |
+| 1 | 8 | 25 | 33 | 76% |
+| 2 | 5 | 20 | 25 | 80% |
+| 3 | 6 | 12 | 18 | 67% |
+| 4 | 10 | 23 | 33 | 70% |
+| 5 | 16 | 25 | 41 | 61% |
+| *Grand Total* | *45* | *105* | *150* | *70%* |
As I expected, my toughest sections were related to project management,
development, implementation, and security.
-This just leaves October and November. For these months, I tried to
-practice every few days, doing 10 questions for each section, until the
-exam. This came out to 13 practice sessions, ~140 questions per section,
-and ~700 questions total.
+This just leaves October and November. For these months, I tried to practice
+every few days, doing 10 questions for each section, until the exam. This came
+out to 13 practice sessions, ~140 questions per section, and ~700 questions
+total.
-While some practice sessions were worse and some were better, the final
-results were similar to my practice exam results. As you can see below,
-my averages were slightly worse than my practice exam. However, I got in
-over 700 questions of practice and, most importantly, /I read through
-the explanations every time I answered incorrectly and learned from my
-mistakes/.
+While some practice sessions were worse and some were better, the final results
+were similar to my practice exam results. As you can see below, my averages were
+slightly worse than my practice exam. However, I got in over 700 questions of
+practice and, most importantly, /I read through the explanations every time I
+answered incorrectly and learned from my mistakes/.
-| Exam Section | Incorrect | Correct | Grand Total | Percent |
+| Exam Section | Incorrect | Correct | Grand Total | Percent |
|---------------+-----------+---------+-------------+---------|
-| 1 | 33 | 108 | 141 | 77% |
-| 2 | 33 | 109 | 142 | 77% |
-| 3 | 55 | 89 | 144 | 62% |
-| 4 | 52 | 88 | 140 | 63% |
-| 5 | 55 | 85 | 140 | 61% |
-| *Grand Total* | *228* | *479* | *707* | *68%* |
+| 1 | 33 | 108 | 141 | 77% |
+| 2 | 33 | 109 | 142 | 77% |
+| 3 | 55 | 89 | 144 | 62% |
+| 4 | 52 | 88 | 140 | 63% |
+| 5 | 55 | 85 | 140 | 61% |
+| *Grand Total* | *228* | *479* | *707* | *68%* |
* Results
-Now, how do the practice scores reflect my actual results? After all,
-it's hard to tell how good a practice regimen is unless you see how it
-turns out.
+Now, how do the practice scores reflect my actual results? After all, it's hard
+to tell how good a practice regimen is unless you see how it turns out.
| Exam Section | Section Name | Score |
|--------------+------------------------------------------------------------------+-------|
-| 1 | Information Systems Auditing Process | 678 |
-| 2 | Governance and Management of IT | 590 |
-| 3 | Information Systems Acquisition, Development, and Implementation | 721 |
-| 4 | Information Systems Operations and Business Resilience | 643 |
-| 5 | Protection of Information Assets | 511 |
-
-Now, in order to pass the CISA, you need at least 450 on a sliding scale
-of 200-800. Personally, I really have no clue what an average CISA score
-is. After a /very/ brief look online, I can see that the high end is
-usually in the low 700s. In addition, only about 50-60% of people pass
-the exam.
-
-Given this information, I feel great about my scores. 616 may not be
-phenomenal, and I wish I had done better on sections 2 & 5, but my
-practicing seems to have worked very well overall.
-
-However, the practice results do not conform to the actual results.
-Section 2 was one of my highest practice sections and was my
-second-lowest score in the exam. Conversely, section 3 was my
-second-lowest practice section and turned out to be my highest actual
-score!
-
-After reflecting, it is obvious that if you have any background on the
-CISA topics at all, the most important part of studying is doing
-practice questions. You really need to understand how to read the
-questions critically and pick the best answer.
+| 1 | Information Systems Auditing Process | 678 |
+| 2 | Governance and Management of IT | 590 |
+| 3 | Information Systems Acquisition, Development, and Implementation | 721 |
+| 4 | Information Systems Operations and Business Resilience | 643 |
+| 5 | Protection of Information Assets | 511 |
+
+Now, in order to pass the CISA, you need at least 450 on a sliding scale of
+200-800. Personally, I really have no clue what an average CISA score is. After
+a /very/ brief look online, I can see that the high end is usually in the low
+700s. In addition, only about 50-60% of people pass the exam.
+
+Given this information, I feel great about my scores. 616 may not be phenomenal,
+and I wish I had done better on sections 2 & 5, but my practicing seems to have
+worked very well overall.
+
+However, the practice results do not conform to the actual results. Section 2
+was one of my highest practice sections and was my second-lowest score in the
+exam. Conversely, section 3 was my second-lowest practice section and turned out
+to be my highest actual score!
+
+After reflecting, it is obvious that if you have any background on the CISA
+topics at all, the most important part of studying is doing practice questions.
+You really need to understand how to read the questions critically and pick the
+best answer.
* Looking Forward
-I am extremely happy that I was finally able to pass the CISA. Looking
-to the future, I'm not sure what's next in terms of professional
-learning. My current company offers internal learning courses, so I will
-most likely focus on that if I need to gain more knowledge in certain
-areas.
-
-To be fair, even if you pass the CISA, it's hard to become an expert on
-any specific topic found within. My career may take me in a different
-direction, and I might need to focus more on security or networking
-certifications (or possibly building a better analysis/visualization
-portfolio if I want to go into data analysis/science).
+I am extremely happy that I was finally able to pass the CISA. Looking to the
+future, I'm not sure what's next in terms of professional learning. My current
+company offers internal learning courses, so I will most likely focus on that if
+I need to gain more knowledge in certain areas.
+
+To be fair, even if you pass the CISA, it's hard to become an expert on any
+specific topic found within. My career may take me in a different direction, and
+I might need to focus more on security or networking certifications (or possibly
+building a better analysis/visualization portfolio if I want to go into data
+analysis/science).
All I know is that I am content at the moment and extremely proud of my
accomplishment.