Commit ce7749abd0

ce7749abd0114d836d116ecbeb001d0a3827be3b

parent: af67e996dc

Unsigned

cmc <hello@cleberg.net> · 2025-11-12 03:25 UTC

fix grammar in 2021 posts

Layout: unified · split

content/blog/2021-01-01-seum.org +6 −8
@@ -5,12 +5,9 @@
55
66* Game Description
77
8[[https://store.steampowered.com/app/457210/SEUM_Speedrunners_from_Hell/][SEUM:
9Speedrunners from Hell]] is an incredibly fast-paced mashup of a puzzle
10game and a free-running game. Created by [[https://pinestudio.co][Pine
11Studio]] in early 2016 and first released as a
12[[https://gamejolt.com/games/seum-speedrunners-from-hell-demo/154868][demo
13on GameJolt]], this game was successfully green-lit on Steam and has
8[[https://store.steampowered.com/app/457210/SEUM_Speedrunners_from_Hell/][SEUM: Speedrunners from Hell]] is an incredibly fast-paced mashup of a puzzle game
9and a free-running game. Created by [[https://pinestudio.co][Pine Studio]] in early 2016 and first released
10as a [[https://gamejolt.com/games/seum-speedrunners-from-hell-demo/154868][demo on GameJolt]], this game was successfully green-lit on Steam and has
1411amassed a cult following on multiple consoles.
1512
1613Here's the game description straight from the developers:
@@ -57,8 +54,9 @@ contains nine regular levels, one boss level, and one bonus level;
5754although you don't technically need to beat all levels to advance to the
5855next floor. # In-Game Screenshots
5956
60The main menu gives you plenty of options for tuning your system,
61playing main levels, playing the DLC, or exploring the other game modes.
57The main menu gives you plenty of options for tuning your system, playing main
58levels, playing the downloadable content (DLC), or exploring the other game
59modes.
6260
6361Once you enter a level, you can look around and strategize before
6462starting. Clicking any button will start the menu, and you'll have to
content/blog/2021-01-04-fediverse.org +41 −44
@@ -5,53 +5,51 @@
55
66* What is the Fediverse?
77
8The fediverse is a federated universe of servers commonly used for
9sharing content, like social media. So, instead of having to rely on a
10single organization to run the server (e.g. Facebook), the fediverse is
11a giant collection of servers across the world, owned by many people and
12organizations.
13
14Take a look at this depiction of a federated network. Each server in
15this photo is owned and run by different administrators/owners.
16Federated networks are best explained as email servers: you have an
17email account that exists on a server (e.g. Outlook), your friend has an
18account on a different server (e.g. GMail), and another friend has an
19account on a third server (e.g. ProtonMail). All three of you can talk
20and communicate back and forth without having to be on the same server.
21However, responsible email admins are there to set rules and control the
8The fediverse is a federated universe of servers commonly used for sharing
9content, like social media. So, instead of having to rely on a single
10organization to run the server (e.g. Facebook), the fediverse is a giant
11collection of servers across the world, owned by many people and organizations.
12
13Take a look at this depiction of a federated network. Each server in this photo
14is owned and run by different administrators/owners. Federated networks are best
15explained as email servers: you have an email account that exists on a server
16(e.g. Outlook), your friend has an account on a different server (e.g. GMail),
17and another friend has an account on a third server (e.g. ProtonMail). All three
18of you can talk and communicate back and forth without having to be on the same
19server. However, responsible email admins are there to set rules and control the
2220traffic going in/out of the server.
2321
24The main objective of this architecture is to decentralize the control
25within the internet connections. For example, if you run your own
26Mastodon instance, you and your users can't be censored or impacted in
27any way by authorities of another Mastodon instance. Some users have
28praised these features due to recent criticism of popular social media
29websites that may be over-censoring their users.
22The main objective of this architecture is to decentralize the control within
23the internet connections. For example, if you run your own Mastodon instance,
24you and your users can't be censored or impacted in any way by authorities of
25another Mastodon instance. Some users have praised these features due to recent
26criticism of popular social media websites that may be over-censoring their
27users.
3028
3129This strategy is great for making sure control of the social web isn't
32controlled by a single organization, but it also has some downsides. If
33I create a Mastodon instance and get a ton of users to sign up, I can
34shut the server down at any time. That means you're at risk of losing
35the content you've created unless you back it up, or the server backs it
36up for you. Also, depending on the software used (e.g. Mastodon,
37Pixelfed, etc.), censorship may still be an issue if the server admins
38decide they want to censor their users. Now, censorship isn't always a
39bad thing and can even benefit the community as a whole, but you'll want
40to determine which servers align with your idea of proper censorship.
41
42However, these are risks that we take when we sign up for any online
43platform. Whatever your reason is for trying out federated social
44networks, they are part of the future of the internet. However, the
45popularity of these services is still yet to be determined, especially
46with the increased difficulty understanding and signing up for these
47platforms. Perhaps increased regulation and litigation against current
48social media sites will push more users into the fediverse.
30controlled by a single organization, but it also has some downsides. If I create
31a Mastodon instance and get a ton of users to sign up, I can shut the server
32down at any time. That means you're at risk of losing the content you've created
33unless you back it up, or the server backs it up for you. Also, depending on the
34software used (e.g. Mastodon, Pixelfed, etc.), censorship may still be an issue
35if the server admins decide they want to censor their users. Now, censorship
36isn't always a bad thing and can even benefit the community as a whole, but
37you'll want to determine which servers align with your idea of proper
38censorship.
39
40However, these are risks that we take when we sign up for any online platform.
41Whatever your reason is for trying out federated social networks, they are part
42of the future of the internet. However, the popularity of these services is
43still yet to be determined, especially with the increased difficulty
44understanding and signing up for these platforms. Perhaps increased regulation
45and litigation against current social media sites will push more users into the
46fediverse.
4947
5048* Federated Alternatives to Popular Sites
5149
52The list below is a small guide that will show you federated
53alternatives to current popular websites. There are many more out there,
54so go and explore: you might just find the perfect home.
50The list below is a small guide that will show you federated alternatives to
51current popular websites. There are many more out there, so go and explore: you
52might just find the perfect home.
5553
5654** Reddit
5755
@@ -91,10 +89,9 @@ so go and explore: you might just find the perfect home.
9189
9290* Get Started
9391
94The best way to get started is to simply sign up and learn as you go. If
95you're comfortable signing up through a Mastodon, Pleroma, or Friendica
96server, here is [[https://fediverse.party/en/portal/servers][a list of
97themed servers]] to choose from. If you're looking for something else,
92The best way to get started is to simply sign up and learn as you go. If you're
93comfortable signing up through a Mastodon, Pleroma, or Friendica server, here is
94[[https://fediverse.party/en/portal/servers][a list of themed servers]] to choose from. If you're looking for something else,
9895try a web search for a federated alternative to your favorite sites.
9996
10097Find a server that focuses on your passions and start there!
content/blog/2021-01-07-ufw.org +49 −53
@@ -5,22 +5,21 @@
55
66* Uncomplicated Firewall
77
8Uncomplicated Firewall, also known as ufw, is a convenient and
9beginner-friendly way to enforce OS-level firewall rules. For those who
10are hosting servers or any device that is accessible to the world (i.e.,
11by public IP or domain name), it's critical that a firewall is properly
12implemented and active.
13
14Ufw is available by default in all Ubuntu installations after 8.04 LTS.
15For other distributions, you can look to install ufw or check if there
16are alternative firewalls installed already. There are usually
17alternatives available, such as Fedora's =firewall= and the package
18available on most distributions: =iptables=. Ufw is considered a
19beginner-friendly front-end to iptables.
20
21[[https://gufw.org][Gufw]] is available as a graphical user interface
22(GUI) application for users who are uncomfortable setting up a firewall
23through a terminal.
8Uncomplicated Firewall (UFW) is a convenient and beginner-friendly
9way to enforce operating system (OS)-level firewall rules. For those who are
10hosting servers or any device that is accessible to the world (i.e., by public
11IP or domain name), it's critical that a firewall is properly implemented and
12active.
13
14UFW is available by default in all Ubuntu installations after 8.04 LTS
15(long-term support). For other distributions, you can look to install UFW or
16check if there are alternative firewalls installed already. There are usually
17alternatives available, such as Fedora's =firewall= and the package available on
18most distributions: =iptables=. UFW is considered a beginner-friendly front-end
19to iptables.
20
21[[https://gufw.org][Gufw]] is available as a graphical user interface (GUI) application for users who
22are uncomfortable setting up a firewall through a terminal.
2423
2524* Getting Help
2625
@@ -33,20 +32,19 @@ sudo ufw --help
3332
3433* Set Default State
3534
36The proper way to run a firewall is to set a strict default state and
37slowly open up ports that you want to allow. This helps prevent anything
38malicious from slipping through the cracks. The following command
39prevents all incoming traffic (other than the rules we specify later),
40but you can also set this for outgoing connections, if necessary.
35The proper way to run a firewall is to set a strict default state and slowly
36open up ports that you want to allow. This helps prevent anything malicious from
37slipping through the cracks. The following command prevents all incoming traffic
38(other than the rules we specify later), but you can also set this for outgoing
39connections, if necessary.
4140
4241#+begin_src sh
4342sudo ufw default deny incoming
4443#+end_src
4544
46You should also allow outgoing traffic if you want to allow the device
47to communicate back to you or other parties. For example, media servers
48like Plex need to be able to send out data related to streaming the
49media.
45You should also allow outgoing traffic if you want to allow the device to
46communicate back to you or other parties. For example, media servers like Plex
47need to be able to send out data related to streaming the media.
5048
5149#+begin_src sh
5250sudo ufw default allow outgoing
@@ -54,23 +52,23 @@ sudo ufw default allow outgoing
5452
5553* Adding Port Rules
5654
57Now that we've disabled all incoming traffic by default, we need to open
58up some ports (or else no traffic would be able to come in). If you need
59to be able to =ssh= into the machine, you'll need to open up port 22.
55Now that we've disabled all incoming traffic by default, we need to open up some
56ports (or else no traffic would be able to come in). If you need to be able to
57=ssh= into the machine, you'll need to open up port 22.
6058
6159#+begin_src sh
6260sudo ufw allow 22
6361#+end_src
6462
65You can also issue more restrictive rules. The following rule will allow
66=ssh= connections only from machines on the local subnet.
63You can also issue more restrictive rules. The following rule will allow =ssh=
64(secure shell protocol) connections only from machines on the local subnet.
6765
6866#+begin_src sh
6967sudo ufw allow proto tcp from 192.168.0.0/24 to any port 22
7068#+end_src
7169
72If you need to set a rule that isn't tcp, just append your connection
73type to the end of the rule.
70If you need to set a rule that isn't TCP (Transmission Control Protocol) just
71append your connection type to the end of the rule.
7472
7573#+begin_src sh
7674sudo ufw allow 1900/udp
@@ -93,8 +91,7 @@ sudo reboot now
9391
9492* Checking Status
9593
96Now that the firewall is enabled, let's check and see what the rules
97look like.
94Now that the firewall is enabled, let's check and see what the rules look like.
9895
9996#+begin_src sh
10097sudo ufw status numbered
@@ -111,9 +108,9 @@ Status: active
111108
112109* Deleting Rules
113110
114If you need to delete a rule, you need to know the number associated
115with that rule. Let's delete the first rule in the table above. You'll
116be asked to confirm the deletion as part of this process.
111If you need to delete a rule, you need to know the number associated with that
112rule. Let's delete the first rule in the table above. You'll be asked to confirm
113the deletion as part of this process.
117114
118115#+begin_src sh
119116sudo ufw delete 1
@@ -121,11 +118,10 @@ sudo ufw delete 1
121118
122119* Managing App Rules
123120
124Luckily, there's a convenient way for installed applications to create
125files that ufw can easily implement so that you don't have to search and
126find which ports your application requires. To see if your device has
127any applications with pre-installed ufw rules, execute the following
128command:
121Luckily, there's a convenient way for installed applications to create files
122that ufw can easily implement so that you don't have to search and find which
123ports your application requires. To see if your device has any applications with
124pre-installed UFW rules, execute the following command:
129125
130126#+begin_src sh
131127sudo ufw app list
@@ -142,8 +138,8 @@ Available applications:
142138 plexmediaserver-dlna
143139#+end_src
144140
145If you want to get more information on a specific app rule, use the
146=info= command.
141If you want to get more information on a specific app rule, use the =info=
142command.
147143
148144#+begin_src sh
149145sudo ufw app info plexmediaserver-dlna
@@ -161,8 +157,8 @@ Ports:
161157 32469/tcp
162158#+end_src
163159
164You can add or delete app rules the same way that you'd add or delete
165specific port rules.
160You can add or delete app rules the same way that you'd add or delete specific
161port rules.
166162
167163#+begin_src sh
168164sudo ufw allow plexmediaserver-dlna
@@ -174,12 +170,12 @@ sudo ufw delete RULE|NUM
174170
175171* Creating App Rules
176172
177If you'd like to create you own app rule, you'll need to create a file
178in the =/etc/ufw/applications.d= directory. Within the file you create,
179you need to make sure the content is properly formatted.
173If you'd like to create you own app rule, you'll need to create a file in the
174=/etc/ufw/applications.d= directory. Within the file you create, you need to
175make sure the content is properly formatted.
180176
181For example, here are the contents my =plexmediaserver= file, which
182creates three distinct app rules for ufw:
177For example, here are the contents my =plexmediaserver= file, which creates
178three distinct app rules for ufw:
183179
184180#+begin_src config
185181[plexmediaserver]
@@ -198,8 +194,8 @@ description=The Plex Media Server (with additional DLNA capability)
198194ports=32400/tcp|3005/tcp|5353/udp|8324/tcp|32410:32414/udp|1900/udp|32469/tcp
199195#+end_src
200196
201So, if I wanted to create a custom app rule called "mycustomrule," I'd
202create a file and add my content like this:
197So, if I wanted to create a custom app rule called "mycustomrule," I'd create a
198file and add my content like this:
203199
204200#+begin_src sh
205201sudo nano /etc/ufw/applications.d/mycustomrule
content/blog/2021-02-19-macos.org +87 −96
@@ -5,74 +5,69 @@
55
66* Diving into macOS
77
8After spending nearly 15 years working with Windows and 8 years on
9Linux, I have experienced macOS for the first time. By chance, my spouse
10happened to buy a new MacBook and gifted me their 2013 model. Of course,
11I still consider my Linux desktop to be my daily driver and keep Windows
12around for gaming needs, but over the past week I've found myself using
13the MacBook more and more for things that don't require gaming specs or
14advanced dev tools.
8After spending nearly 15 years working with Windows and 8 years on Linux, I have
9experienced macOS for the first time. By chance, my spouse happened to buy a new
10MacBook and gifted me their 2013 model. Of course, I still consider my Linux
11desktop to be my daily driver and keep Windows around for gaming needs, but over
12the past week I've found myself using the MacBook more and more for things that
13don't require gaming specs or advanced developer tools.
1514
1615* Initial Thoughts
1716
18Before I move on to the technical aspects of my set-up, I want to take
19some time and express my thoughts on the overall OS.
20
21As expected, the initial computer setup is a breeze with Mac's guided
22GUI installer.
23
24The desktop itself reminds me of GNOME more than anything else I've
25seen: even Pantheon from [[https://elementary.io/][ElementaryOS]], which
26people commonly refer to as the closest Linux distro to macOS. The
27desktop toolbar is great and far surpasses the utility of the GNOME
28toolbar due to the fact that the extensions and icons /actually work/. I
29launch macOS and immediately see my shortcuts for Tresorit, Bitwarden,
30and Mullvad pop up as the computer loads.
31
32Even further, the app dock is very useful and will be yet another
33familiarity for GNOME users. I know many people like panels instead of
34docks, but I've always found docks to have a more pleasing UI. However,
35I had to disable the "Show recent applications in Dock" preference; I
36can't stand items taking up precious screen space if I'm not currently
37using them. On that same note, it's taking me some time to get use to
38the fact that I have to manually quit an app or else it will still stay
39open/active in the dock, even if I've closed out all windows for that
40app (e.g. Firefox).
41
42Overall, I'm having a lot of fun and for users who spend a large
43majority of their time performing basic tasks like web browsing,
44writing, watching media, etc., macOS is a fantastic option.
45
46The rest of this post explains the technicalities of how I set up my CLI
47environment to make me feel more at-home, similar to the environments I
48set up on Fedora, Ubuntu, etc.
17Before I move on to the technical aspects of my set-up, I want to take some time
18and express my thoughts on the overall operating system (OS).
19
20As expected, the initial computer setup is a breeze with Mac's guided graphical
21user interface (GUI) installer.
22
23The desktop itself reminds me of GNOME more than anything else I've seen: even
24Pantheon from [[https://elementary.io/][ElementaryOS]], which people commonly refer to as the closest Linux
25distro to macOS. The desktop toolbar is great and far surpasses the utility of
26the GNOME toolbar due to the fact that the extensions and icons /actually work/.
27I launch macOS and immediately see my shortcuts for Tresorit, Bitwarden, and
28Mullvad pop up as the computer loads.
29
30Even further, the app dock is very useful and will be yet another familiarity
31for GNOME users. I know many people like panels instead of docks, but I've
32always found docks to have a more pleasing UI. However, I had to disable the
33"Show recent applications in Dock" preference; I can't stand items taking up
34precious screen space if I'm not currently using them. On that same note, it's
35taking me some time to get use to the fact that I have to manually quit an app
36or else it will still stay open/active in the dock, even if I've closed out all
37windows for that app (e.g. Firefox).
38
39Overall, I'm having a lot of fun and for users who spend a large majority of
40their time performing basic tasks like web browsing, writing, watching media,
41etc., macOS is a fantastic option.
42
43The rest of this post explains the technicalities of how I set up my command
44line interface (CLI) environment to make me feel more at-home, similar to the
45environments I set up on Fedora, Ubuntu, etc.
4946
5047* Making it Feel Like Home
5148
52If you're someone who uses Linux primarily, no doubt your first thought
53when booting macOS will be the same as mine was: "Where is the terminal
54and how do I set up my favorite utilities?"
49If you're someone who uses Linux primarily, no doubt your first thought when
50booting macOS will be the same as mine was: "Where is the terminal and how do I
51set up my favorite utilities?"
5552
56Luckily, macOS hasn't completely hidden away the development tools from
57the average user. You can easily find the Terminal app in the Launchpad
58area, but it's probably not what you're used to. I was surprised (and
59happy) to see that the default shell is =zsh=, the shell I use on all of
60my Linux distros. However, the commands are not the same - even the ones
61you may think are native to the shell. Commands like =dir= do not exist,
62so other native commands like =ls -la= or =pwd= are more useful here.
53Luckily, macOS hasn't completely hidden away the development tools from the
54average user. You can easily find the Terminal app in the Launchpad area, but
55it's probably not what you're used to. I was surprised (and happy) to see that
56the default shell is =zsh=, the shell I use on all of my Linux distros. However,
57the commands are not the same - even the ones you may think are native to the
58shell. Commands like =dir= do not exist, so other native commands like =ls -la=
59or =pwd= are more useful here.
6360
64With only a few minutes of installing and tweaking a few packages, I was
65able to recreate a terminal environment that I feel very comfortable
66using. See the image below for a preview of the iTerm2 app with a split
67view between my macOS desktop shell and an SSH session into my server.
61With only a few minutes of installing and tweaking a few packages, I was able to
62recreate a terminal environment that I feel very comfortable using. See the
63image below for a preview of the iTerm2 app with a split view between my macOS
64desktop shell and an SSH (secure shell protocol) session into my server.
6865
6966* Xcode
7067
71My first step was to search the web for any hints on how to get =zsh=
72back up to the state I like, with extensions, themes, etc. My first step
73was to install the CLI tools for
74[[https://developer.apple.com/xcode/][Xcode]], Apple's suite of
75development tools.
68My first step was to search the web for any hints on how to get =zsh= back up to
69the state I like, with extensions, themes, etc. My first step was to install the
70CLI tools for [[https://developer.apple.com/xcode/][Xcode]], Apple's suite of development tools.
7671
7772#+begin_src sh
7873sudo xcode-select -r
@@ -84,25 +79,25 @@ sudo xcode-select --install
8479
8580* Homebrew
8681
87Next up is to install [[https://brew.sh][Homebrew]], a nifty package
88manager for macOS.
82Next up is to install [[https://brew.sh][Homebrew]], a nifty package manager for macOS.
8983
9084#+begin_src sh
91/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
85/bin/bash -c "$(curl -fsSL
86https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
9287#+end_src
9388
9489I ran into a permission error when installing Homebrew:
9590
9691#+begin_src sh
97Error: Failed to link all completions, docs and manpages:
98 Permission denied @ rb_file_s_symlink - (../../../Homebrew/completions/zsh/_brew, /usr/local/share/zsh/site-functions/_brew)
99Failed during: /usr/local/bin/brew update --force --quiet
92Error: Failed to link all completions, docs and manpages: Permission denied @
93 rb_file_s_symlink - (../../../Homebrew/completions/zsh/_brew,
94 /usr/local/share/zsh/site-functions/_brew) Failed during:
95 /usr/local/bin/brew update --force --quiet
10096#+end_src
10197
102I found that the following permission modification worked like a charm.
103However, I noted that some users online discussed the fact that this
104solution may not work if your system has multiple users who use
105Homebrew.
98I found that the following permission modification worked like a charm. However,
99I noted that some users online discussed the fact that this solution may not
100work if your system has multiple users who use Homebrew.
106101
107102#+begin_src sh
108103sudo chown -R $(whoami) $(brew --prefix)/*
@@ -120,16 +115,16 @@ brew cleanup
120115
121116* iTerm2
122117
123Now that I've installed the basic utilities for development, I moved
124onto installing iTerm2, a much better terminal than the default.
118Now that I've installed the basic utilities for development, I moved onto
119installing iTerm2, a much better terminal than the default.
125120
126121#+begin_src sh
127122brew install --cask iterm2
128123#+end_src
129124
130I also used the =Make iTerm2 Default Term= and
131=Install Shell Integration= options in the iTerm2 application menu to
132make sure I don't run into any issues later on with different terminals.
125I also used the =Make iTerm2 Default Term= and =Install Shell Integration=
126options in the iTerm2 application menu to make sure I don't run into any issues
127later on with different terminals.
133128
134129We will also install =zsh= so we can use it in iTerm2.
135130
@@ -139,54 +134,50 @@ brew install zsh
139134
140135* Oh-My-Zsh
141136
142I've shown the great aspects of [[https://ohmyz.sh][Oh My Zsh]] in other
143blog posts, so I'll skip over that speech for now. Simply install it and
144run an update.
137I've shown the great aspects of [[https://ohmyz.sh][Oh My Zsh]] in other blog posts, so I'll skip over
138that speech for now. Simply install it and run an update.
145139
146140#+begin_src sh
147sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
141sh -c "$(curl -fsSL
142https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
148143#+end_src
149144
150145#+begin_src sh
151146omz update
152147#+end_src
153148
154Finally, restart the iTerm2 application to ensure all changes go into
155effect.
149Finally, restart the iTerm2 application to ensure all changes go into effect.
156150
157151* Oh-My-Zsh Themes
158152
159Let's change the theme of the terminal to make it a little more
160friendly.
153Let's change the theme of the terminal to make it a little more friendly.
161154
162155#+begin_src sh
163156open ~/.zshrc
164157#+end_src
165158
166The third section of this file should contain a line like the code
167below. Change that theme to
168[[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][any theme you want]],
169save the file, and exit.
159The third section of this file should contain a line like the code below. Change
160that theme to [[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][any theme you want]], save the file, and exit.
170161
171162#+begin_src sh
172163ZSH_THEME="af-magic"
173164#+end_src
174165
175After changing the =.zshrc= file, you'll need to close your terminal and
176re-open it to see the changes. Optionally, just open a new tab if you're
177using iTerm2, and you'll see the new shell config.
166After changing the =.zshrc= file, you'll need to close your terminal and re-open
167it to see the changes. Optionally, just open a new tab if you're using iTerm2,
168and you'll see the new shell config.
178169
179170* Oh-My-Zsh Plugins
180171
181172Of course, my customization of =zsh= would not be complete without
182[[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]].
183This will bring up commands you've run in the past as you type them. For
184example, if you've run =ssh user@192.168.1.99= before, the terminal will
185show this command as soon as you start typing it (e.g. =zsh u=), and you
186can hit the right arrow to autocomplete the command.
173[[https://github.com/zsh-users/zsh-autosuggestions][zsh-autosuggestions]]. This will bring up commands you've run in the past as you
174type them. For example, if you've run =ssh user@192.168.1.99= before, the
175terminal will show this command as soon as you start typing it (e.g. =zsh u=),
176and you can hit the right arrow to autocomplete the command.
187177
188178#+begin_src sh
189git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
179git clone https://github.com/zsh-users/zsh-autosuggestions
180${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
190181#+end_src
191182
192183#+begin_src sh
@@ -198,6 +189,6 @@ open ~/.zshrc
198189plugins=(git zsh-autosuggestions)
199190#+end_src
200191
201Remember: After changing the =.zshrc= file, you'll need to close your
202terminal and re-open it to see the changes. Optionally, just open a new
203tab if you're using iTerm2, and you'll see the new shell config.
\ No newline at end of file
192Remember: After changing the =.zshrc= file, you'll need to close your terminal
193and re-open it to see the changes. Optionally, just open a new tab if you're
194using iTerm2, and you'll see the new shell config.
content/blog/2021-03-19-clone-github-repos.org +27 −27
@@ -5,14 +5,14 @@
55
66* Cloning from GitHub
77
8If you're like me and use a lot of different devices (and sometimes
9decide to just wipe your device and start with a new OS), you probably
10know the pain of cloning all your old code repositories down to your
11local file system.
8If you're like me and use a lot of different devices [and sometimes decide to
9just wipe your device and start with a new operating system (OS)], you probably know
10the pain of cloning all your old code repositories down to your local file
11system.
1212
13If you're using GitHub, you can easily clone all of your code back down
14in just seconds. First, create a bash script. I do so by opening a new
15file in =nano=, but you can use =gedit=, =vim=, or something else:
13If you're using GitHub, you can easily clone all of your code back down in just
14seconds. First, create a bash script. I do so by opening a new file in =nano=,
15but you can use =gedit=, =vim=, or something else:
1616
1717#+begin_src sh
1818nano clone_github_repos.sh
@@ -44,12 +44,12 @@ Now you can run the script and should see the cloning process begin.
4444
4545* Cloning from Sourcehut
4646
47I haven't fully figured out how to directly incorporate Sourcehut's
48GraphQL API into a bash script yet, so this one will take two steps.
47I haven't fully figured out how to directly incorporate Sourcehut's GraphQL
48application programming interface (API) into a bash script yet, so this one will
49take two steps.
4950
50First, log-in to Sourcehut and go to their
51[[https://git.sr.ht/graphql][GraphQL playground for Git]]. Next, paste
52the following query into the left box:
51First, log-in to Sourcehut and go to their [[https://git.sr.ht/graphql][GraphQL playground for Git]]. Next,
52paste the following query into the left box:
5353
5454#+begin_src sh
5555query {
@@ -65,10 +65,10 @@ query {
6565}
6666#+end_src
6767
68The output on the right side will give you an object of all your
69repositories. Just grab that text and remove all the characters such as
70quotation marks and curly brackets. You will need a single-line list of
71space-separated values for the next step.
68The output on the right side will give you an object of all your repositories.
69Just grab that text and remove all the characters such as quotation marks and
70curly brackets. You will need a single-line list of space-separated values for
71the next step.
7272
7373Now let's create the bash script:
7474
@@ -76,12 +76,12 @@ Now let's create the bash script:
7676nano clone_sourcehut_repos.sh
7777#+end_src
7878
79Next, paste the following bash script in with the list of repositories
80you obtained above and replace =your-username= with your username.
79Next, paste the following bash script in with the list of repositories you
80obtained above and replace =your-username= with your username.
8181
82Note that this uses the SSH-based Git cloning method
83(e.g. =git@git...=), so you'll need to ensure you have set up Sourcehut
84with your SSH key.
82Note that this uses the secure shell protocol (SSH)-based Git cloning method
83(e.g. =git@git...=), so you'll need to ensure you have set up Sourcehut with
84your SSH key.
8585
8686#+begin_src sh
8787repos=(repo1 repo2 repo3)
@@ -108,17 +108,17 @@ Now you can run the script and should see the cloning process begin.
108108
109109* Moving Repositories to a New Host
110110
111Now that you have all of your code repositories cloned to your local
112computer, you may want to change the remote host (e.g., moving from
113GitHub to GitLab). To do this, let's create another bash script:
111Now that you have all of your code repositories cloned to your local computer,
112you may want to change the remote host (e.g., moving from GitHub to GitLab). To
113do this, let's create another bash script:
114114
115115#+begin_src sh
116116nano change_remote_urls.sh
117117#+end_src
118118
119Past the following information and be sure to change the URL information
120to whichever host you are moving to. For this example, I am looping
121through all of my cloned GitHub directories and changing them to
119Past the following information and be sure to change the URL (uniform resource
120locator) information to whichever host you are moving to. For this example, I am
121looping through all of my cloned GitHub directories and changing them to
122122Sourcehut (e.g. =<YOUR_NEW_REMOTE_URL>= -> =git@git.sr.ht:~myusername=).
123123
124124#+begin_src sh
content/blog/2021-03-28-gemini-capsule.org +63 −73
@@ -5,21 +5,17 @@
55
66* What is Gemini?
77
8[[https://gemini.circumlunar.space/][Gemini]] is an internet protocol
9introduced in June 2019 as an alternative to HTTP(S) or Gopher. In
10layman's terms, it's an alternative way to browse sites (called
11capsules) that requires a special browser. Since Gemini is not
12standardized as an internet standard, normal web browsers won't be able
13to load a Gemini capsule. Instead, you'll need to use
14[[https://gemini.circumlunar.space/clients.html][a Gemini-specific
15browser]].
16
17The content found within a Gemini page is called
18[[https://gemini.circumlunar.space/docs/cheatsheet.gmi][Gemtext]] and is
19/extremely/ basic (on purpose). Gemini only processes the text, no media
20content like images. However, you're able to style 3 levels of headings,
21regular text, links (which will display on their own line), quotes, and
22an unordered list.
8[[https://gemini.circumlunar.space/][Gemini]] is an internet protocol introduced in June 2019 as an alternative to the
9Hypertext Transfer Protocol (HTTP) or Gopher. In layman's terms, it's an
10alternative way to browse sites (called capsules) that requires a special
11browser. Since Gemini is not standardized as an internet standard, normal web
12browsers won't be able to load a Gemini capsule. Instead, you'll need to use [[https://gemini.circumlunar.space/clients.html][a
13Gemini-specific browser]].
14
15The content found within a Gemini page is called [[https://gemini.circumlunar.space/docs/cheatsheet.gmi][Gemtext]] and is /extremely/
16basic (on purpose). Gemini only processes the text, no media content like
17images. However, you're able to style 3 levels of headings, regular text, links
18(which will display on their own line), quotes, and an unordered list.
2319
2420Here's a complete listing of valid Gemtext:
2521
@@ -44,78 +40,72 @@ My List:
4440
4541*** Free Option
4642
47There are probably numerous websites that allow you to create your
48personal Gemini capsule, but I'm going to focus on the two sites that I
49have personally tested. The first option below, Midnight Pub, allows you
50to create/edit any Gemini files you want in your account. This is
51essentially a GUI option with a built-in text box for editing. The
52second option below, Sourcehut, allows you to use a Git repository and
53automatic build process to deploy your personal Gemini capsule every
54time you push a commit.
43There are probably numerous websites that allow you to create your personal
44Gemini capsule, but I'm going to focus on the two sites that I have personally
45tested. The first option below, Midnight Pub, allows you to create/edit any
46Gemini files you want in your account. This is essentially a graphical option
47with a built-in text box for editing. The second option below, Sourcehut, allows
48you to use a Git repository and automatic build process to deploy your personal
49Gemini capsule every time you push a commit.
5550
5651** Midnight Pub - Beginner Friendly
5752
58[[https://midnight.pub/][Midnight Pub]] is a small, virtual community
59meant to reflect the atmosphere of wandering into a small alley pub. The
60site is built in Gemtext and has a server-side process to convert
61Gemtext to HTML if someone loads the site in an HTTP(S) browser.
53[[https://midnight.pub/][Midnight Pub]] is a small, virtual community meant to reflect the atmosphere of
54wandering into a small alley pub. The site is built in Gemtext and has a
55server-side process to convert Gemtext to HTML if someone loads the site in an
56HTTP(S) browser.
6257
63To create an account, you'll need to email the owner of the website to
64obtain a key. You can find their email on the Midnight Pub homepage.
65Once registered, head to [[https://midnight.pub/account][your account]]
66and select [[https://midnight.pub/site][manage site]]. This is the
67screen where you can upload or create any files to be displayed on the
68internet.
58To create an account, you'll need to email the owner of the website to obtain a
59key. You can find their email on the Midnight Pub homepage. Once registered,
60head to [[https://midnight.pub/account][your account]] and select [[https://midnight.pub/site][manage site]]. This is the screen where you can
61upload or create any files to be displayed on the internet.
6962
70For example, I've created both an HTML file and a Gemini file. Remember
71that Gemini is automatically converted to HTML on the Pub, so you don't
72need an HTML version. For example, I created an HTML version to add in
73some extra styling.
63For example, I've created both a Hypertext Markup Language (HTML) file and a
64Gemini file. Remember that Gemini is automatically converted to HTML on the Pub,
65so you don't need an HTML version. For example, I created an HTML version to add
66in some extra styling.
7467
75All you need to do is create a page like =index.gmi= and use your Gemini
76browser to head over to your-username.midnight.pub to see the result.
68All you need to do is create a page like =index.gmi= and use your Gemini browser
69to head over to your-username.midnight.pub to see the result.
7770
78That's all there is to it! Easy enough, right? Let's check out a more
79advanced version in the next section.
71That's all there is to it! Easy enough, right? Let's check out a more advanced
72version in the next section.
8073
8174* Paid Option
8275
83As of 2021, Sourcehut has decided to require users to have a paid
84account in order to utilize their automated build system. For now, paid
85accounts can be as low as $2/month.
76As of 2021, Sourcehut has decided to require users to have a paid account in
77order to utilize their automated build system. For now, paid accounts can be as
78low as $2/month.
8679
8780** Sourcehut
8881
89[[https://sourcehut.org/][Sourcehut]] is a collection of software
90development tools, but mostly surrounds their hosted Git repository
91service. Simply put, it's a minimal and more private alternative to
92services like GitHub.
82[[https://sourcehut.org/][Sourcehut]] is a collection of software development tools, but mostly surrounds
83their hosted Git repository service. Simply put, it's a minimal and more private
84alternative to services like GitHub.
9385
94This walkthrough is more advanced and involves things like Git, SSH, the
95command line. If you don't think you know enough to do this, check out
96my walkthrough on creating a Gemini capsule for the Midnight Pub
97instead.
86This walkthrough is more advanced and involves things like Git, SSH, the command
87line. If you don't think you know enough to do this, check out my walkthrough on
88creating a Gemini capsule for the Midnight Pub instead.
9889
99The first thing you'll need to do is create an SSH key pair, if you
100don't already have one on your system. Once created, grab the contents
101of =id_rsa.pub= and add it to your Sourcehut account settings - this
102will allow you to push and pull code changes without using a
103username/password.
90The first thing you'll need to do is create an SSH key pair, if you don't
91already have one on your system. Once created, grab the contents of =id_rsa.pub=
92and add it to your Sourcehut account settings - this will allow you to push and
93pull code changes without using a username/password.
10494
10595#+begin_src sh
10696ssh keygen
10797#+end_src
10898
109Next up, let's create a repository with the proper name so that the
110Sourcehut build system will know we want them to host a website for us.
111Use the following format exactly:
99Next up, let's create a repository with the proper name so that the Sourcehut
100build system will know we want them to host a website for us. Use the following
101format exactly:
112102
113103#+begin_src sh
114104mkdir your-username.srht.site && cd your-username.srht.site
115105#+end_src
116106
117Now that we've created the repo, let's initialize Git and add the proper
118remote URL.
107Now that we've created the repo, let's initialize Git and add the proper remote
108URL.
119109
120110#+begin_src sh
121111git init
@@ -125,14 +115,14 @@ git init
125115git remote add origin git@git.sr.ht:~your-username/your-username.srht.site
126116#+end_src
127117
128Now that our repository is set up and configured, we will need to create
129at least two files:
118Now that our repository is set up and configured, we will need to create at
119least two files:
130120
131121- =index.gmi=
132122- =.build.yml=
133123
134For your =.build.yml= file, use the following content and be sure to
135update the =site= line with your username!
124For your =.build.yml= file, use the following content and be sure to update the
125=site= line with your username!
136126
137127#+begin_src yaml
138128image: alpine/latest
@@ -147,11 +137,11 @@ tasks:
147137 acurl -f https://pages.sr.ht/publish/$site -Fcontent=@site.tar.gz -Fprotocol=GEMINI
148138#+end_src
149139
150For the =index.gmi= file, put whatever you want in there and save it.
151You could even just copy and paste the Gemtext cheatsheet.
140For the =index.gmi= file, put whatever you want in there and save it. You could
141even just copy and paste the Gemtext cheatsheet.
152142
153If you want to serve both HTML and Gemini files from this repository,
154just add a second command to the =upload= section:
143If you want to serve both HTML and Gemini files from this repository, just add a
144second command to the =upload= section:
155145
156146#+begin_src yaml
157147- upload: |
@@ -165,9 +155,9 @@ Lastly, commit your changes and push them to the remote repo.
165155git add .; git commit -m "initial commit"; git push --set-upstream origin HEAD
166156#+end_src
167157
168If you've successfully created the files with the proper format, you'll
169see the terminal print a message that lets you know where the automatic
170build is taking place. For example, here's what the terminal tells me:
158If you've successfully created the files with the proper format, you'll see the
159terminal print a message that lets you know where the automatic build is taking
160place. For example, here's what the terminal tells me:
171161
172162#+begin_src sh
173163remote: Build started:
content/blog/2021-03-28-vaporwave-vs-outrun.org +62 −74
@@ -5,15 +5,14 @@
55
66* Overview
77
8When it comes to an aesthetic that originated primarily online, there
9tends to be a lot of confusion around what falls into each specific
10genre. This post discusses Vaporwave and Outrun, which have become
11almost synonymous in the minds of those online who aren't hardcore into
12these genres of media. More specifically, Outrun is largely an unknown
13term while all aesthetics in these two genres are usually attributed to
14Vaporwave. For example, take a look at the image search results for
15Vaporwave: the results include *a lot* of Outrun-themed images. You'll
16find a similar trend almost everywhere.
8When it comes to an aesthetic that originated primarily online, there tends to
9be a lot of confusion around what falls into each specific genre. This post
10discusses Vaporwave and Outrun, which have become almost synonymous in the minds
11of those online who aren't hardcore into these genres of media. More
12specifically, Outrun is largely an unknown term while all aesthetics in these
13two genres are usually attributed to Vaporwave. For example, take a look at the
14image search results for Vaporwave: the results include *a lot* of Outrun-themed
15images. You'll find a similar trend almost everywhere.
1716
1817#+caption: Vaporwave Search Results
1918[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/vaporwave-search-results.webp]]
@@ -23,102 +22,91 @@ find a similar trend almost everywhere.
2322#+caption: Macintosh Plus Album Cover
2423[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/macintosh-plus.webp]]
2524
26Okay, so what is Vaporwave? I'm going to just copy-and-paste some
27general info from the Wikipedia article on
28[[https://en.wikipedia.org/wiki/Vaporwave][Vaporwave]], so that I'm not
29repeating everything you can already search for online:
25Okay, so what is Vaporwave? I'm going to just copy-and-paste some general info
26from the Wikipedia article on [[https://en.wikipedia.org/wiki/Vaporwave][Vaporwave]], so that I'm not repeating everything
27you can already search for online:
3028
3129#+begin_quote
32Vaporwave is a microgenre of electronic music, a visual art style, and
33an Internet meme that emerged in the early 2010s. It is defined partly
34by its slowed-down, chopped and screwed samples of smooth jazz,
35elevator, R&B, and lounge music from the 1980s and 1990s. The
36surrounding subculture is sometimes associated with an ambiguous or
37satirical take on consumer capitalism and pop culture, and tends to be
38characterized by a nostalgic or surrealist engagement with the popular
39entertainment, technology and advertising of previous decades. Visually,
40it incorporates early Internet imagery, late 1990s web design, glitch
41art, anime, 3D-rendered objects, and cyberpunk tropes in its cover
42artwork and music videos.
30Vaporwave is a microgenre of electronic music, a visual art style, and an
31Internet meme that emerged in the early 2010s. It is defined partly by its
32slowed-down, chopped and screwed samples of smooth jazz, elevator, R&B, and
33lounge music from the 1980s and 1990s. The surrounding subculture is sometimes
34associated with an ambiguous or satirical take on consumer capitalism and pop
35culture, and tends to be characterized by a nostalgic or surrealist engagement
36with the popular entertainment, technology and advertising of previous decades.
37Visually, it incorporates early Internet imagery, late 1990s web design, glitch
38art, anime, 3D-rendered objects, and cyberpunk tropes in its cover artwork and
39music videos.
4340#+end_quote
4441
45This is an excellent summary, and it helps address my point here: there
46are specific aspects that make Vaporwave unique:
42This is an excellent summary, and it helps address my point here: there are
43specific aspects that make Vaporwave unique:
4744
4845** Time Frame
4946
50The time frame for references, logos, etc. focuses mostly on the 1990s
51in Vaporwave. You'll see old school Pepsi logos, Microsoft 95 screens,
52tropical plants, classic marble sculptures, and many references from
53Japan's influence in the 90s.
47The time frame for references, logos, etc. focuses mostly on the 1990s in
48Vaporwave. You'll see old school Pepsi logos, Microsoft 95 screens, tropical
49plants, classic marble sculptures, and many references from Japan's influence in
50the 90s.
5451
5552** Art
5653
57The color scheme is generally a soft, light palette that uses pastel
58colors. The backdrop will often be in a light-pink or teal blue.
54The color scheme is generally a soft, light palette that uses pastel colors. The
55backdrop will often be in a light-pink or teal blue.
5956
6057** Music
6158
62The musical genre of Vaporwave incorporates soft tunes, lounge music,
63and sound effects that will make the listener reminisce of the 90s. The
64sounds of the genre are generally slower-paced and calming. The major
65breakthrough artist for Vaporwave was
66[[https://en.wikipedia.org/wiki/Vektroid][Macintosh Plus]], who released
67the album
68[[https://archive.org/details/MACINTOSHPLUS-FLORALSHOPPE_complete][Floral
69Shoppe]] in 2011. Another more recent example is the artist
70[[https://sadbert.bandcamp.com/][sadbert]], whose latest album
71incorporates the upbeat tones of the 1999 Dilbert TV series.
72
73Notice that Vaporwave doesn't include things like racing cars,
74futuristic technology, chrome, or the deep orange/purple color scheme.
75Vaporwave is a focus on the idyllic state of the world when technology
76was becoming common in households, a reality that we have already
77experienced. Focus on the most aesthetically-pleasing parts of that past
78is a large part of Vaporwave.
59The musical genre of Vaporwave incorporates soft tunes, lounge music, and sound
60effects that will make the listener reminisce of the 90s. The sounds of the
61genre are generally slower-paced and calming. The major breakthrough artist for
62Vaporwave was [[https://en.wikipedia.org/wiki/Vektroid][Macintosh Plus]], who released the album [[https://archive.org/details/MACINTOSHPLUS-FLORALSHOPPE_complete][Floral Shoppe]] in 2011.
63Another more recent example is the artist [[https://sadbert.bandcamp.com/][sadbert]], whose latest album
64incorporates the upbeat tones of the 1999 Dilbert television series.
65
66Notice that Vaporwave doesn't include things like racing cars, futuristic
67technology, chrome, or the deep orange/purple color scheme. Vaporwave is a focus
68on the idyllic state of the world when technology was becoming common in
69households, a reality that we have already experienced. Focus on the most
70aesthetically-pleasing parts of that past is a large part of Vaporwave.
7971
8072* Outrun
8173
8274#+caption: Outrun
8375[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/outrun.webp]]
8476
85Now, let's get to Outrun. This one is a little trickier since the genre
86has largely been lumped under the Vaporwave title for so long. However,
87it stems from the [[https://en.wikipedia.org/wiki/Synthwave][Synthwave]]
88music genre and is likely named after the 1986 racer game,
77Now, let's get to Outrun. This one is a little trickier since the genre has
78largely been lumped under the Vaporwave title for so long. However, it stems
79from the [[https://en.wikipedia.org/wiki/Synthwave][Synthwave]] music genre and is likely named after the 1986 racer game,
8980[[https://en.wikipedia.org/wiki/Out_Run][Out Run]].
9081
9182** Time Frame
9283
93Outrun can be thought of as a retro-futuristic aesthetic born from the
941980s.
84Outrun can be thought of as a retro-futuristic aesthetic born from the 1980s.
9585
9686** Art
9787
98The color scheme uses a very dark color palette with the main colors
99being deep oranges, blues, and purples. Red edges are common around
100objects in Outrun art. The background of the Outrun aesthetic is almost
101always a neon grid like you'd expect to see in Tron or a 1980s arcade
102machine.
88The color scheme uses a very dark color palette with the main colors being deep
89oranges, blues, and purples. Red edges are common around objects in Outrun art.
90The background of the Outrun aesthetic is almost always a neon grid like you'd
91expect to see in Tron or a 1980s arcade machine.
10392
104Classic sports cars, chrome robots, computer generated graphics and
105fonts, and the occasional use of rain or palm trees can be found in
106Outrun art.
93Classic sports cars, chrome robots, computer generated graphics and fonts, and
94the occasional use of rain or palm trees can be found in Outrun art.
10795
10896** Music
10997
110This aesthetic has a more aggressive and fast-paced style of music,
111which tends to match the subject of the art in this aesthetic.
98This aesthetic has a more aggressive and fast-paced style of music, which tends
99to match the subject of the art in this aesthetic.
112100
113Outrun enthusiasts love what people in the 1980s thought the future
114would look like. Take a look at a common video game discussed in Outrun
115circles, Far Cry 3: Blood Dragon:
101Outrun enthusiasts love what people in the 1980s thought the future would look
102like. Take a look at a common video game discussed in Outrun circles, Far Cry 3:
103Blood Dragon:
116104
117105#+caption: Fry Cry 3: Blood Dragon
118106[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/far-cry.webp]]
119107
120Another example that doesn't force the color scheme as hard as some
121online art does is Kung Fury:
108Another example that doesn't force the color scheme as hard as some online art
109does is Kung Fury:
122110
123111#+caption: Kung Fury
124112[[https://img.cleberg.net/blog/20210328-vaporwave-vs-outrun/kung-fury.webp]]
@@ -128,7 +116,7 @@ online art does is Kung Fury:
128116
129117* Conclusion
130118
131While Vaporwave and Outrun share similarities, they are two distinct
132aesthetics with many important distinctions. Someone who enjoys one may
133not necessarily enjoy the other, so it's important to make sure we
134properly describe the aesthetic we're looking for.
119While Vaporwave and Outrun share similarities, they are two distinct aesthetics
120with many important distinctions. Someone who enjoys one may not necessarily
121enjoy the other, so it's important to make sure we properly describe the
122aesthetic we're looking for.
content/blog/2021-03-30-vps-web-server.org +153 −178
@@ -3,108 +3,93 @@
33#+description: Instructional outline for setting up and maintaining a VPS environment configured for secure and scalable web hosting applications.
44#+slug: vps-web-server
55
6* Shared Hosting vs. VPS
7
8Choosing a place to host a website is one of the most confusing
9decisions for beginner web developers. Even for experienced web devs,
10choosing between different forms of web hosting can be a daunting
11choice.
12
13First, let's take a look at
14[[https://en.wikipedia.org/wiki/Shared_web_hosting_service][shared web
15hosting]]. Shared web hosting is a product where you are purchasing a
16small piece of a web server that is being shared between many websites.
17As a result, the cost of shared hosting is extremely low. You won't have
18access to the server itself, which means you can't install your own
19software on the server, such as Docker. Usually, you are simply allowed
20to connect your domains to the server, set up domain security, and other
21small utilities.
22
23In contrast, a
24[[https://en.wikipedia.org/wiki/Virtual_private_server][virtual private
25server]] (VPS) is a virtual machine that replicates the environment of
26having a dedicated server to yourself. You are able to control the
27virtual server's host names, base file system, package manager, etc.
28Another great upside of a VPS is that since it's virtual, the company
29providing the VPS can dynamically increase the disk size, RAM size, or
30number of CPUs at any time. However, the virtual server is still
31physically located on a server that is shared between multiple virtual
32servers.
33
34The choice between shared hosting and VPS mostly depends on your skill
35level with system administration. If you're comforting working on a
36server that is mostly left up to you (or you're willing to learn), then
37a VPS is usually a better option. However, shared hosting is a fantastic
38option for people who don't want to have to learn how to manage their
39server.
6* Shared Hosting vs. VPS (Virtual Private Server)
7
8Choosing a place to host a website is one of the most confusing decisions for
9beginner web developers. Even for experienced web devs, choosing between
10different forms of web hosting can be a daunting choice.
11
12First, let's take a look at [[https://en.wikipedia.org/wiki/Shared_web_hosting_service][shared web hosting]]. Shared web hosting is a product
13where you are purchasing a small piece of a web server that is being shared
14between many websites. As a result, the cost of shared hosting is extremely low.
15You won't have access to the server itself, which means you can't install your
16own software on the server, such as Docker. Usually, you are simply allowed to
17connect your domains to the server, set up domain security, and other small
18utilities.
19
20In contrast, a [[https://en.wikipedia.org/wiki/Virtual_private_server][virtual private server]] (VPS) is a virtual machine that replicates
21the environment of having a dedicated server to yourself. You are able to
22control the virtual server's host names, base file system, package manager, etc.
23Another great upside of a VPS is that since it's virtual, the company providing
24the VPS can dynamically increase the disk size, RAM size, or number of CPUs at
25any time. However, the virtual server is still physically located on a server
26that is shared between multiple virtual servers.
27
28The choice between shared hosting and VPS mostly depends on your skill level
29with system administration. If you're comforting working on a server that is
30mostly left up to you (or you're willing to learn), then a VPS is usually a
31better option. However, shared hosting is a fantastic option for people who
32don't want to have to learn how to manage their server.
4033
4134* My Situation
4235
43I had used shared hosting for approximately 5 years before trying my
44first VPS. I manage a homelab and have had success running a server and
45performing typical sysadmin duties, but I was still hesitant to get a
46VPS. One fear was that I always struggled to properly set up the
47networking part of a server - DNS and hostname configurations were not
48my friend.
49
50As a little bit of background, I originally used
51[[https://www.siteground.com][Siteground]] for my initially shared
52hosting and stayed on that platform for at least a year. However, the UI
53was clunky, and I didn't like how they handled certain technical
54aspects, so I switched to [[https://www.namecheap.com][Namecheap]].
55Namecheap was great because it is the service I primarily use for
56purchasing domain names, which made it incredibly easy to link them to
57my hosting service. However, it was still mediocre shared hosting, and
58Namecheap is notorious for not letting you use
59[[https://letsencrypt.org][Let's Encrypt]] to obtain free SSL/TLS
60certificates; Namecheap wants to make you purchase certificates through
61their store.
62
63Finally, I settled down with [[https://www.iwebfusion.net][iWebFusion]]
64for about the last year of my shared hosting. This service was pretty
65great, came with free SSL/TLS, and I never had any complaints.
66
67However, I finally grew tired of not being able to install software on
68my own web server. I wanted to be able to try out things like
69[[https://postmill.xyz][Postmill]] or [[https://matrix.org][Matrix]].
70This is possible with a VPS, so I decided to grab a new domain name to
71try it out.
36I had used shared hosting for approximately 5 years before trying my first VPS.
37I manage a homelab and have had success running a server and performing typical
38sysadmin duties, but I was still hesitant to get a VPS. One fear was that I
39always struggled to properly set up the networking part of a server - DNS and
40hostname configurations were not my friend.
41
42As a little bit of background, I originally used [[https://www.siteground.com][Siteground]] for my initially
43shared hosting and stayed on that platform for at least a year. However, the
44user interface (UI) was clunky, and I didn't like how they handled certain
45technical aspects, so I switched to [[https://www.namecheap.com][Namecheap]]. Namecheap was great because it is
46the service I primarily use for purchasing domain names, which made it
47incredibly easy to link them to my hosting service. However, it was still
48mediocre shared hosting, and Namecheap is notorious for not letting you use
49[[https://letsencrypt.org][Let's Encrypt]] to obtain free SSL (Secure Socket Layer)/TLS (Transport Layer
50Security) certificates; Namecheap wants to make you purchase certificates
51through their store.
52
53Finally, I settled down with [[https://www.iwebfusion.net][iWebFusion]] for about the last year of my shared
54hosting. This service was pretty great, came with free SSL/TLS, and I never had
55any complaints.
56
57However, I finally grew tired of not being able to install software on my own
58web server. I wanted to be able to try out things like [[https://postmill.xyz][Postmill]] or [[https://matrix.org][Matrix]]. This
59is possible with a VPS, so I decided to grab a new domain name to try it out.
7260
7361* Getting Started: Buying a VPS
7462
75The first step to moving over to a VPS is (you guessed it): finding a
76VPS provider. For my VPSs, I use [[https://1984hosting.com][1984]] and
77prefer their services much more than any alternative, due to their
78location (Iceland), their [[https://1984hosting.com/GDPR/][privacy
79policy]], their respect for GDPR, and the ability to remain anonymous if
80you pay in Bitcoin or Monero.
63The first step to moving over to a VPS is (you guessed it): finding a VPS
64provider. For my VPSs, I use [[https://1984hosting.com][1984]] and prefer their services much more than any
65alternative, due to their location (Iceland), their [[https://1984hosting.com/GDPR/][privacy policy]], their
66respect for GDPR, and the ability to remain anonymous if you pay in Bitcoin or
67Monero.
8168
82[[https://njal.la][Njalla]] is another good, privacy-oriented option for
83VPS services.
69[[https://njal.la][Njalla]] is another good, privacy-oriented option for VPS services.
8470
85You'll have to decide what specifications you want on your VPS. For me,
86I only build and deploy low-resource HTML, PHP, and Python websites.
87This means I can survive on the smallest VPS: 1 CPU, 1GB of RAM, and
8825GB SSD for $5.00 per month.
71You'll have to decide what specifications you want on your VPS. For me, I only
72build and deploy low-resource HTML, PHP, and Python websites. This means I can
73survive on the smallest VPS: 1 CPU, 1GB of RAM, and 25GB SSD for $5.00 per
74month.
8975
90As noted above, the great thing about a VPS is you can request your
91provider to increase the resources at any time.
76As noted above, the great thing about a VPS is you can request your provider to
77increase the resources at any time.
9278
9379* Configuring DNS Settings
9480
95Okay, so now let's get into some actual work that has to be done to get
96content moved from a shared host to a VPS. At this point, I'm assuming
97you have a shared host with website content that you can still access,
98and you've purchased a new VPS and can SSH into that server.
81Okay, so now let's get into some actual work that has to be done to get content
82moved from a shared host to a VPS. At this point, I'm assuming you have a shared
83host with website content that you can still access, and you've purchased a new
84VPS and can SSH into that server.
9985
100The first change is minor, but it should be done immediately in order to
101get things moving: DNS settings. Go to wherever your DNS settings are
102handled. If your shared host also managed your DNS settings, you'll need
103to first move that DNS over to your new VPS provider. For me, I route my
104DNS through [[https://www.gandi.net][Gandi]].
86The first change is minor, but it should be done immediately in order to get
87things moving: DNS settings. Go to wherever your DNS settings are handled. If
88your shared host also managed your DNS settings, you'll need to first move that
89DNS over to your new VPS provider. For me, I route my DNS through [[https://www.gandi.net][Gandi]].
10590
106Once you know where your DNS settings are, go ahead and update the =A=
107records to match the public IP address of your VPS. For example:
91Once you know where your DNS settings are, go ahead and update the =A= records
92to match the public IP address of your VPS. For example:
10893
10994#+begin_src txt
11095A example.com xxx.xxx.xxx.xxx
@@ -112,36 +97,35 @@ A subdomain xxx.xxx.xxx.xxx
11297CNAME www example.com.
11398#+end_src
11499
115If you have any other records that require updates, such as MX or TXT
116records for a mail server, be sure to update those accordingly.
117Personally, I don't host my own mail server. I route all mail on my
118custom domains to [[https://www.migadu.com][Migadu]]. Hosting your own
119email server can become complex quickly and is not for beginners.
100If you have any other records that require updates, such as MX or TXT records
101for a mail server, be sure to update those accordingly. Personally, I don't host
102my own mail server. I route all mail on my custom domains to [[https://www.migadu.com][Migadu]]. Hosting
103your own email server can become complex quickly and is not for beginners.
120104
121DNS changes can take up to 48 hours to propagate, so be sure to give it
122some time before assuming you've made an error.
105DNS changes can take up to 48 hours to propagate, so be sure to give it some
106time before assuming you've made an error.
123107
124108* Server Updates and Packages
125109
126Now that the DNS settings have been changed, let's set up our server
127while we wait for the DNS to propagate. First up is to ssh into your
128server. If you've signed up with a service like DigitalOcean, you can
129add your SSH key to your account and to your VPS droplet so that you
130don't need a password in order to SSH.
110Now that the DNS settings have been changed, let's set up our server while we
111wait for the DNS to propagate. First up is to ssh into your server. If you've
112signed up with a service like DigitalOcean, you can add your SSH key to your
113account and to your VPS droplet so that you don't need a password in order to
114SSH.
131115
132116#+begin_src sh
133117ssh root@xxx.xxx.xxx.xxx
134118#+end_src
135119
136The VPS that is used in this blog post runs Ubuntu 20.04 with an Apache
137web server. If you're working on a different operating system (OS) or
138want a different web server, such as Nginx, you'll have to use different
139commands to set it up.
120The VPS that is used in this blog post runs Ubuntu 20.04 with an Apache web
121server. If you're working on a different operating system (OS) or want a
122different web server, such as Nginx, you'll have to use different commands to
123set it up.
140124
141125First, let's update and upgrade our server.
142126
143*NOTE:* Since we have logged in to the server as =root= for now, we
144don't need to use the =sudo= modifier before our commands.
127*NOTE:* Since we have logged in to the server as =root= for now, we don't need
128to use the =sudo= modifier before our commands.
145129
146130#+begin_src sh
147131apt update && apt upgrade -y
@@ -149,11 +133,11 @@ apt update && apt upgrade -y
149133
150134* Create A User Account
151135
152While being able to use =root= can be beneficial at times, you shouldn't
153use =root= unless you have to.
136While being able to use =root= can be beneficial at times, you shouldn't use
137=root= unless you have to.
154138
155So let's set up a new user in our system. The =-m= option below tells
156the OS to create a home directory for the new user.
139So let's set up a new user in our system. The =-m= option below tells the OS to
140create a home directory for the new user.
157141
158142#+begin_src sh
159143adduser USERNAME
@@ -165,40 +149,40 @@ Now, create a password for that user.
165149passwd USERNAME
166150#+end_src
167151
168Finally, add the user to the sudoers file, so they can perform
169priveleged commands.
152Finally, add the user to the sudoers file, so they can perform privileged
153commands.
170154
171155#+begin_src sh
172156usermod -a -G sudo USERNAME
173157#+end_src
174158
175If you are using SSH keys and not passwords, you'll need to copy your
176SSH key from your local machine to the VPS. If you haven't disabled
177password-based SSH yet, the easiest way to do this is =ssh-copy-id= from
178your local computer (not from the VPS):
159If you are using SSH (secure shell protocol) keys and not passwords, you'll need
160to copy your SSH key from your local machine to the VPS. If you haven't disabled
161password-based SSH yet, the easiest way to do this is =ssh-copy-id= from your
162local computer (not from the VPS):
179163
180164#+begin_src sh
181165ssh-copy-id testuser@xxx.xxx.xxx.xxx
182166#+end_src
183167
184If you've disabled password-based SSH, you'll need to manually copy your
185SSH key into the =~/.ssh/authorized_keys= file.
168If you've disabled password-based SSH, you'll need to manually copy your SSH key
169into the =~/.ssh/authorized_keys= file.
186170
187171* Install Software
188172
189Our goal here is to host a web server, so the next step is to install
190the Apache web server and any other packages we need.
173Our goal here is to host a web server, so the next step is to install the Apache
174web server and any other packages we need.
191175
192From this point on, I will be logged in as a user (not =root=) and will
193need to use the =sudo= modifier for most commands.
176From this point on, I will be logged in as a user (not =root=) and will need to
177use the =sudo= modifier for most commands.
194178
195179#+begin_src sh
196180sudo apt update; sudo apt upgrade -y; sudo apt autoremove -y
197181sudo apt install apache2
198182#+end_src
199183
200If you need other language support, such as PHP, you'll need to install
201that too.
184If you need other language support, such as PHP, you'll need to install that
185too.
202186
203187#+begin_src sh
204188sudo apt install libapache2-mod-php php-dom
@@ -208,19 +192,18 @@ sudo systemctl restart apache2
208192
209193* Website Files & Folders
210194
211Next up is to create the directories for the domain(s) we want to be
212hosted on this web server.
195Next up is to create the directories for the domain(s) we want to be hosted on
196this web server.
213197
214198#+begin_src sh
215199cd /var/www
216200sudo mkdir example.com
217201#+end_src
218202
219We have a folder for =example.com= now, so let's add an =index.html=
220file and put it within a specific =public_html= folder. You don't need
221this =public_html= if you don't want it, but it helps with organizing
222items related to =example.com= that you don't want to publish to the
223internet.
203We have a folder for =example.com= now, so let's add an =index.html= file and
204put it within a specific =public_html= folder. You don't need this =public_html=
205if you don't want it, but it helps with organizing items related to
206=example.com= that you don't want to publish to the internet.
224207
225208#+begin_src sh
226209cd example.com
@@ -228,8 +211,8 @@ sudo mkdir public_html && cd public_html
228211sudo nano index.html
229212#+end_src
230213
231You can put anything you want in this =index.html= file. If you can't
232think of anything, paste this in there:
214You can put anything you want in this =index.html= file. If you can't think of
215anything, paste this in there:
233216
234217#+begin_src html
235218<!DOCTYPE html>
@@ -245,9 +228,8 @@ think of anything, paste this in there:
245228</html>
246229#+end_src
247230
248If you want something to be served at =example.com/page01/file.txt=,
249you'll have to create the =page01= directory under the =example.com=
250directory. For example:
231If you want something to be served at =example.com/page01/file.txt=, you'll have
232to create the =page01= directory under the =example.com= directory. For example:
251233
252234#+begin_src sh
253235cd /var/www/example.com/public_html
@@ -257,9 +239,9 @@ sudo nano file.txt
257239
258240* Apache Configuration
259241
260Now, let's set up the files that will tell the server where to find the
261files for =example.com=. We will copy the default configuration file and
262create our own.
242Now, let's set up the files that will tell the server where to find the files
243for =example.com=. We will copy the default configuration file and create our
244own.
263245
264246#+begin_src sh
265247cd /etc/apache2/sites-available
@@ -267,9 +249,8 @@ sudo cp 000-default.conf example.com.conf
267249sudo nano example.com.conf
268250#+end_src
269251
270This configuration file will have a few default lines, but you'll need
271to edit it to look similar to this (settings may change based on your
272personal needs):
252This configuration file will have a few default lines, but you'll need to edit
253it to look similar to this (settings may change based on your personal needs):
273254
274255#+begin_src config
275256<VirtualHost *:80>
@@ -291,31 +272,30 @@ sudo a2dissite 000-default.conf
291272sudo systemctl reload apache2
292273#+end_src
293274
294You can always run a test to make sure no errors or warnings are found
295in your configuration files.
275You can always run a test to make sure no errors or warnings are found in your
276configuration files.
296277
297278#+begin_src sh
298279sudo apache2ctl configtest
299280#+end_src
300281
301Now, restart the web server entirely. After this, you should be able to
302browse to =http://example.com= and see the HTML content you provided
303earlier. Note that SSL/TLS has not been enabled yet, so you won't be
282Now, restart the web server entirely. After this, you should be able to browse
283to =http://example.com= and see the HTML (hypertext markup language) content you
284provided earlier. Note that SSL/TLS has not been enabled yet, so you won't be
304285able to use the secure version yet (=https://example.com=).
305286
306287#+begin_src sh
307288sudo systemctl restart apache2
308289#+end_src
309290
310You can repeat this for as many websites as you need. Just create the
311domain folders in =/var/www/=, add the configuration file, enable the
312configuration, and restart =apache2=.
291You can repeat this for as many websites as you need. Just create the domain
292folders in =/var/www/=, add the configuration file, enable the configuration,
293and restart =apache2=.
313294
314295* SSL/TLS Certificates: Serve Websites Over HTTPS
315296
316In order to serve secure content, you'll need to obtain SSL/TLS
317certificates. Luckily, there's a free tool called
318[[https://certbot.eff.org][Certbot]] that helps us with the process.
297In order to serve secure content, you'll need to obtain SSL/TLS certificates.
298Luckily, there's a free tool called [[https://certbot.eff.org][Certbot]] that helps us with the process.
319299
320300The first step is to install =snapd= and =core= for Ubuntu.
321301
@@ -340,10 +320,10 @@ sudo ln -s /snap/bin/certbot /usr/bin/certbot
340320
341321Finally, you can run =certbot= one of two ways:
342322
3431. run it and let it alter your Apache configuration files automatically
344 to enable HTTPS redirects.
3452. run it and only allow it to create certificates. You'll need to
346 manually alter the config files to enable HTTPS redirects.
3231. run it and let it alter your Apache configuration files automatically to
324 enable HTTPS (Hypertext Transport Protocol) redirects.
3252. run it and only allow it to create certificates. You'll need to manually
326 alter the config files to enable HTTPS redirects.
347327
348328Run certbot and allow automatic config changes:
349329
@@ -358,11 +338,10 @@ files:
358338sudo certbot certonly --apache
359339#+end_src
360340
361The Certbot packages on your system come with a cron job or systemd
362timer that will renew your certificates automatically before they
363expire. You will not need to run Certbot again unless you change your
364configuration. You can test automatic renewal for your certificates by
365running this command:
341The Certbot packages on your system come with a =cron= job or =systemd= timer
342that will renew your certificates automatically before they expire. You will not
343need to run Certbot again unless you change your configuration. You can test
344automatic renewal for your certificates by running this command:
366345
367346#+begin_src sh
368347sudo certbot renew --dry-run
@@ -373,13 +352,11 @@ Now, test your domains by going to =https://example.com=.
373352* Firewall Security
374353
375354To enable better security on your server, you'll need to enable a basic
376firewall. For Ubuntu, we'll use
377[[../secure-your-network-with-the-uncomplicated-firewall/][the
378uncomplicated firewall]].
355firewall. For Ubuntu, we'll use [[https://cleberg.net/blog/ufw.html][the uncomplicated firewall]].
379356
380Now, add the following rules to the firewall allow SSH, Apache, and
381HTTP(S) connections. If you need to, you can enable different ports for
382specifics applications, SFTP, etc.
357Now, add the following rules to the firewall allow SSH, Apache, and HTTP(S)
358connections. If you need to, you can enable different ports for specific
359applications.
383360
384361#+begin_src sh
385362sudo ufw default deny incoming
@@ -397,15 +374,13 @@ sudo ufw enable
397374
398375* Troubleshooting
399376
400If you run into any issues during your VPS set-up, be sure to walk back
401through your actions and make sure you didn't miss any steps.
377If you run into any issues during your VPS set-up, be sure to walk back through
378your actions and make sure you didn't miss any steps.
402379
403Many websites have fantastic guides to setting up various web servers.
404This is one of the areas
405[[https://www.digitalocean.com/community/tutorials][where DigitalOcean
406shines]]. For simpler or more Linux-oriented questions, I suggest using
407[Linuxize] ([[https://linuxize.com]]).
380Many websites have fantastic guides to setting up various web servers. This is
381one of the areas [[https://www.digitalocean.com/community/tutorials][where DigitalOcean shines]]. For simpler or more Linux-oriented
382questions, I suggest using [Linuxize] ([[https://linuxize.com]]).
408383
409If you're getting certain errors (e.g. =500 Internal Server Error=) and
410need to debug locally, you can view the =access.log= and =error.log=
411files in the =/var/log/apache/= directory.
384If you're getting certain errors (e.g. =500 Internal Server Error=) and need to
385debug locally, you can view the =access.log= and =error.log= files in the
386=/var/log/apache/= directory.
content/blog/2021-04-17-gemini-server.org +33 −38
@@ -5,28 +5,26 @@
55
66* Similar Article Available
77
8To read more about Gemini and ways to test out this new protocol without
9your own server, see my previous post
10[[../launching-a-gemini-capsule/][Launching a Gemini Capsule]].
8To read more about Gemini and ways to test out this new protocol without your
9own server, see my previous post [[../launching-a-gemini-capsule/][Launching a Gemini Capsule]].
1110
1211* Preparation
1312
14This guide assumes you have access to a server accessible to the world
15through a public IP address and that you own a domain name used for this
16Gemini capsule.
13This guide assumes you have access to a server accessible to the world through a
14public IP (internet protocol) address and that you own a domain name used for
15this Gemini capsule.
1716
1817* Getting Started with Agate
1918
20We are going to use [[https://github.com/mbrubeck/agate][Agate]] for
21this tutorial. This is a basic Gemini server written in Rust. It takes
22very little time and maintenance to get it running.
19We are going to use [[https://github.com/mbrubeck/agate][Agate]] for this tutorial. This is a basic Gemini server
20written in Rust. It takes very little time and maintenance to get it running.
2321
2422* Install Dependencies
2523
26First, you will need to install the Rust package for your system. On
27Ubuntu, use the following commands (remember to use =sudo= if you are
28not the root user). The Rust installation will give you options to
29customize the installation; I used the default installation options.
24First, you will need to install the Rust package for your system. On Ubuntu, use
25the following commands (remember to use =sudo= if you are not the root user).
26The Rust installation will give you options to customize the installation; I
27used the default installation options.
3028
3129#+begin_src sh
3230sudo apt update && sudo apt upgrade -y
@@ -54,8 +52,8 @@ cargo install agate
5452
5553* Create Symlinks
5654
57Once Cargo has finished installing all the required packages, symlink
58the executable to your $PATH.
55Once Cargo has finished installing all the required packages, create a symbolic
56link to the executable to your $PATH.
5957
6058#+begin_src sh
6159sudo 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
6361
6462* Using Agate's Built-In Installation Tool
6563
66If you're running Ubuntu or Debian, use the Debian installation script
67found in Agate's GitHub repository, under the =tools/debian= folder.
64If you're running Ubuntu or Debian, use the Debian installation script found in
65Agate's GitHub repository, under the =tools/debian= folder.
6866
6967#+begin_src sh
7068git clone https://github.com/mbrubeck/agate
@@ -74,11 +72,11 @@ sudo ./install.sh
7472
7573* Configure the Gemini Service
7674
77We have a little more to do, but since this script tries to immediately
78run the service, it will likely fail with an exit code. Let's add our
79finishing touches. Edit the following file and replace the hostname with
80your desired URL. You can also change the directory where content will
81be served.
75We have a little more to do, but since this script tries to immediately run the
76service, it will likely fail with an exit code. Let's add our finishing touches.
77Edit the following file and replace the hostname with your desired URL (uniform
78resource locator). You can also change the directory where content will be
79served.
8280
8381#+begin_src sh
8482sudo nano /etc/systemd/system/gemini.service
@@ -97,8 +95,8 @@ WorkingDirectory=/var/gemini/
9795ExecStart=agate --hostname gemini.example.com --lang en
9896#+end_src
9997
100Since we've altered the systemd configuration files, we have to reload
101the daemon. Let's do that, restart our service, and check its status.
98Since we've altered the systemd configuration files, we have to reload the
99daemon. Let's do that, restart our service, and check its status.
102100
103101#+begin_src sh
104102sudo systemctl daemon-reload
@@ -108,8 +106,8 @@ sudo systemctl status gemini.service
108106
109107* Fixing Systemd Errors
110108
111If you're still getting errors, the installation process may not have
112properly enabled the gemini service. Fix it with the following commands.
109If you're still getting errors, the installation process may not have properly
110enabled the gemini service. Fix it with the following commands.
113111
114112#+begin_src sh
115113sudo systemctl enable gemini.service
@@ -119,9 +117,9 @@ sudo systemctl status gemini.service
119117
120118* Firewall Rules
121119
122Great! Our server is now functional and running. The first consideration
123now is that you need to be able to access port 1965 on the server. If
124you have a firewall enabled, you'll need to open that port up.
120Great! Our server is now functional and running. The first consideration now is
121that you need to be able to access port 1965 on the server. If you have a
122firewall enabled, you'll need to open that port up.
125123
126124#+begin_src sh
127125sudo ufw allow 1965
@@ -131,21 +129,18 @@ sudo ufw reload
131129* Creating Content
132130
133131Let's create the Gemini capsule. Note that wherever you set the
134WorkingDirectory variable to earlier, Agate will expect you to put your
135Gemini capsule contents in a sub-folder called "content." So, I place my
136files in "/var/gmi/content." I'm going to create that folder now and put
137a file in there.
132=WorkingDirectory= variable to earlier, Agate will expect you to put your Gemini
133capsule contents in a sub-folder called =content=. So, I place my files in
134=/var/gmi/content=. I'm going to create that folder now and put a file in there.
138135
139136#+begin_src sh
140137sudo mkdir /var/gemini/content
141138sudo nano /var/gemini/content/index.gmi
142139#+end_src
143140
144You can put whatever you want in the "index.gmi" file, just make sure
145it's valid Gemtext.
141You can put whatever you want in the =index.gmi= file, just make sure it's valid
142Gemtext.
146143
147144* The Results
148145
149To view the results, you can use a Gemini browser, such as
150[[https://gmi.skyjake.fi/lagrange/][Lagrange]] or
151[[https://github.com/makeworld-the-better-one/amfora][amfora]].
146To 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]].
content/blog/2021-04-23-php-comment-system.org +83 −94
@@ -5,84 +5,79 @@
55
66* The Terrible-ness of Commenting Systems
77
8The current state of affairs regarding interactive comment systems is,
9well, terrible. It is especially awful if you're a privacy conscious
10person who does not generally load third-party scripts or frames on the
11websites you visit.
8The current state of affairs regarding interactive comment systems is, well,
9terrible. It is especially awful if you're a privacy conscious person who does
10not generally load third-party scripts or frames on the websites you visit.
1211
13Even further, many comment systems are charging exorbitant fees for
14something that should be standard.
12Even further, many comment systems are charging exorbitant fees for something
13that should be standard.
1514
1615Of course, there are some really terrible options:
1716
1817- Facebook Comments
1918- Discourse
2019
21There are some options that are better but still use too many scripts,
22frames, or social integrations on your web page that could impact some
23users:
20There are some options that are better but still use too many scripts, frames,
21or social integrations on your web page that could impact some users:
2422
2523- Disqus
2624- Isso
2725- Remark42
2826
29Lastly, I looked into a few unique ways of generating blog comments,
30such as using Twitter threads or GitHub issues to automatically post
31issues. However, these both rely on external third-party sites that I
32don't currently use.
27Lastly, I looked into a few unique ways of generating blog comments, such as
28using Twitter threads or GitHub issues to automatically post issues. However,
29these both rely on external third-party sites that I don't currently use.
3330
3431* Stay Static with Server-Side Comments
3532
36The main issue for my personal use-case is that my blog is completely,
37100% static. I use PHP on the back-end but website visitors only see
38HTML and a single CSS file. No external javascript and no embedded
39frames.
33The main issue for my personal use-case is that my blog is completely, 100%
34static. I use PHP on the back-end but website visitors only see HTML (Hypertext
35Markup Language) and a single CSS (Cascading Style Sheets) file. No external
36JavaScript and no embedded frames.
4037
41So, how do we keep a site static and still allow users to interact with
42blog posts? The key actually pretty simple - I'm already using PHP, so
43why not rely on the classic HTML =<form>= and a PHP script to save the
44comments somewhere? As it turns out, this was a perfect solution for me.
38So, how do we keep a site static and still allow users to interact with blog
39posts? The key actually pretty simple - I'm already using PHP, so why not rely
40on the classic HTML =<form>= and a PHP script to save the comments somewhere? As
41it turns out, this was a perfect solution for me.
4542
46The second issue for my personal use-case is that I am trying to keep
47the contents of my website accessible over time, as described by
48[cite/t:@brandur], in his post entitled
49[[https://brandur.org/fragments/graceful-degradation-time][Blog with
50Markdown + Git, and degrade gracefully through time]] .
43The second issue for my personal use-case is that I am trying to keep the
44contents of my website accessible over time, as described by [cite/t:@brandur],
45in his post entitled [[https://brandur.org/fragments/graceful-degradation-time][Blog with Markdown + Git, and degrade gracefully through
46time]] .
5147
52This means I cannot rely on a database for comments, since I do not rely
53on a database for any other part of my websites.
48This means I cannot rely on a database for comments, since I do not rely on a
49database for any other part of my websites.
5450
55I blog in plain Markdown files, commit all articles to Git, and ensure
56that future readers will be able to see the source data long after I'm
57gone, or the website has gone offline. However, I still haven't
58committed any images served on my blog to Git, as I'm not entirely sold
59on Git LFS yet - for now, images can be found at
60[[https://img.cleberg.net][img.cleberg.net]].
51I blog in plain Markdown files, commit all articles to Git, and ensure that
52future readers will be able to see the source data long after I'm gone, or the
53website has gone offline. However, I still haven't committed any images served
54on my blog to Git, as I'm not entirely sold on Git LFS (large file storage)
55yet - for now, images can be found at [[https://img.cleberg.net][img.cleberg.net]].
6156
62Saving my comments back to the Git repository ensures that another
63aspect of my site will degrade gracefully.
57Saving my comments back to the Git repository ensures that another aspect of my
58site will degrade gracefully.
6459
6560* Create a Comment Form
6661
67Okay, let's get started. The first step is to create an HTML form that
68users can see and utilize to submit comments. This is fairly easy and
69can be changed depending on your personal preferences.
62Okay, let's get started. The first step is to create an HTML form that users can
63see and utilize to submit comments. This is fairly easy and can be changed
64depending on your personal preferences.
7065
71Take a look at the code block below for the form I currently use. Note
72that =<current-url>= is replaced automatically in PHP with the current
73post's URL, so that my PHP script used later will know which blog post
74the comment is related to.
66Take a look at the code block below for the form I currently use. Note that
67=<current-url>= is replaced automatically in PHP with the current post's URL
68(uniform resource locator), so that my PHP script used later will know which
69blog post the comment is related to.
7570
7671The form contains the following structure:
7772
781. =<form>= - This is the form and will determine which PHP script to
79 send the comment to.
802. =<section hidden>= - This section is hidden from the user and is used
81 to ensure that we know which blog post sent the comment.
823. =<section>= Display Name (Optional) - Used to accept a display name,
83 if entered.
844. =<section>= Comment (Required) - Used to accept the user's full
85 comment. Markdown is allowed.
731. =<form>= - This is the form and will determine which PHP script to send the
74 comment to.
752. =<section hidden>= - This section is hidden from the user and is used to
76 ensure that we know which blog post sent the comment.
773. =<section>= Display Name (Optional) - Used to accept a display name, if
78 entered.
794. =<section>= Comment (Required) - Used to accept the user's full comment.
80 Markdown is allowed.
86815. =<button>= - A button to submit the form.
8782
8883#+begin_src html
@@ -129,25 +124,25 @@ The form contains the following structure:
129124
130125* Handle Comments via POST
131126
132Now that we have a form and can submit comments, we need to create a PHP
133script so that the server can fetch the comment data and save it. Make
134sure your PHP script name matches the name you entered in the =action=
135field in your form.
127Now that we have a form and can submit comments, we need to create a PHP script
128so that the server can fetch the comment data and save it. Make sure your PHP
129script name matches the name you entered in the =action= field in your form.
136130
137See the code block below for the contents of my =comment.php= script. We
138perform the following tasks in this script:
131See the code block below for the contents of my =comment.php= script. We perform
132the following tasks in this script:
139133
1401341. Grab the POST data from the HTML form.
1411352. Sanitize the comment data with =htmlentities=.
1421363. Set the display name to =Anonymous= if it was left blank.
1431374. Create a PHP object that combines all of this data.
1441385. Check if our destination file =comments.json= exists.
1456. If so, convert the PHP object to JSON and save it to the file.
1467. If the =comments.json= file does not exist, the script will exit with
147 an error. You can alter this to ensure it creates the script, but my
148 source code includes the file by default, so it will always exist.
1498. Finally, send the user back to the =#comments= section of the blog
150 post they just read.
1396. If so, convert the PHP object to JSON (JavaScript Object Notation) and save
140 it to the file.
1417. If the =comments.json= file does not exist, the script will exit with an
142 error. You can alter this to ensure it creates the script, but my source code
143 includes the file by default, so it will always exist.
1448. Finally, send the user back to the =#comments= section of the blog post they
145 just read.
151146
152147#+begin_src php
153148// Get the content sent from the comment form
@@ -186,9 +181,9 @@ if (file_exists($file_name)) {
186181header('Location: ' . $post_url . '#comments');
187182#+end_src
188183
189If you're using Apache, make sure the =www-data= user on your server has
190the correct permissions to your website directory or else it will not be
191able to write to =comments.json=.
184If you're using Apache, make sure the =www-data= user on your server has the
185correct permissions to your website directory or else it will not be able to
186write to =comments.json=.
192187
193188#+begin_src sh
194189chgrp -R www-data /path/to/website/
@@ -197,18 +192,15 @@ chmod -R g+w comments.json
197192
198193* Display User Comments
199194
200Now that we can submit comments, and they are saved to the
201=comments.json= file, let's make sure we can show those comments in each
202blog post.
195Now that we can submit comments, and they are saved to the =comments.json= file,
196let's make sure we can show those comments in each blog post.
203197
204The code block below shows the function I use to decode my
205=comments.json= file, check if the comments apply to the current post,
206and then display them.
198The code block below shows the function I use to decode my =comments.json= file,
199check if the comments apply to the current post, and then display them.
207200
208This piece of code should *really* be inside a function (or at least in
209an organized PHP workflow). Don't just copy-and-paste and expect it to
210work. You need to at least supply a =$query= variable depending on the
211page visited.
201This piece of code should *really* be inside a function (or at least in an
202organized PHP workflow). Don't just copy-and-paste and expect it to work. You
203need to at least supply a =$query= variable depending on the page visited.
212204
213205#+begin_src php
214206$query = 'your-blog-post.html';
@@ -246,27 +238,24 @@ echo $comment_section;
246238
247239* Bonus: Create a 'Recent Comments' Page
248240
249Finally, the last part of my current system is to create a Recent
250Comments page so that I can easily check-in on my blog and moderate any
251spam. As an alternative, you could use PHP's =mail()= function to email
252you for each blog comment.
241Finally, the last part of my current system is to create a Recent Comments page
242so that I can easily check-in on my blog and moderate any spam. As an
243alternative, you could use PHP's =mail()= function to email you for each blog
244comment.
253245
254The code to do this is literally the same code as the previous section,
255I just make sure it is printed when someone visits
256=https://example.com/comments/=.
246The code to do this is literally the same code as the previous section, I just
247make sure it is printed when someone visits =https://example.com/comments/=.
257248
258249* Possible Enhancements
259250
260This comment system is by no means a fully-developed system. I have
261noted a few possible enhancements here that I may implement in the
262future:
251This comment system is by no means a fully-developed system. I have noted a few
252possible enhancements here that I may implement in the future:
263253
264254- Create a secure moderator page with user authentication at
265 =https://blog.example.com/mod/=. This page could have the option to
266 edit or delete any comment found in =comments.json=.
267- Create a temporary file, such as =pending_comments.json=, that will
268 store newly-submitted comments and won't display on blog posts until
269 approved by a moderator.
270- Create a =/modlog/= page with a chronological log, showing which
271 moderator approved which comments and why certain comments were
272 rejected.
255 =https://blog.example.com/mod/=. This page could have the option to edit or
256 delete any comment found in =comments.json=.
257- Create a temporary file, such as =pending_comments.json=, that will store
258 newly-submitted comments and won't display on blog posts until approved by a
259 moderator.
260- Create a =/modlog/= page with a chronological log, showing which moderator
261 approved which comments and why certain comments were rejected.
content/blog/2021-04-28-photography.org +39 −47
@@ -6,66 +6,58 @@
66* Why Photography?
77
88I've often wondered why photography is as enticing as it is. You can see
9billions of people around the world taking photographs every single
10moment of the day. New technology often boasts about their photographic
11capabilities, especially smartphones. I would even assume that we live
12in a world where there is never a moment in which a photograph is not
13being taken somewhere on Earth.
9billions of people around the world taking photographs every single moment of
10the day. New technology often boasts about their photographic capabilities,
11especially smartphones. I would even assume that we live in a world where there
12is never a moment in which a photograph is not being taken somewhere on Earth.
1413
15As for myself, I would simply say that I enjoy preserving a memory in
16physical (or digital) form. I've never had the best memory when it comes
17to recalling details of places and people gone by, so it helps to have a
18frame of reference lying around.
14As for myself, I would simply say that I enjoy preserving a memory in physical
15(or digital) form. I've never had the best memory when it comes to recalling
16details of places and people gone by, so it helps to have a frame of reference
17lying around.
1918
20Regardless of the reason, I think most people would agree that you
21simply cannot have too many hobbies.
19Regardless of the reason, I think most people would agree that you simply cannot
20have too many hobbies.
2221
2322* Older Cameras
2423
25I started playing around with the idea of photography when my family
26purchased a Fujifilm camera for family-specific events. I don't recall
27the specific model, but I do recall it was a point-and-shoot camera
28without an interchangeable lens. However, it was of great value to
29someone, like myself, who couldn't afford any other camera. I took about
3010,000 shots with that camera over a 3-5 year span. Most notably, all of
31my trips to California were documented through this camera.
24I started playing around with the idea of photography when my family purchased a
25Fujifilm camera for family-specific events. I don't recall the specific model,
26but I do recall it was a point-and-shoot camera without an interchangeable lens.
27However, it was of great value to someone, like myself, who couldn't afford any
28other camera. I took about 10,000 shots with that camera over a 3-5 year span.
29Most notably, all of my trips to California were documented through this camera.
3230
33When possible, I would borrow my sister's camera, which is a Sony
34SLT-A58. This camera was great and allowed for some of my best early
35shots, especially those taken in Utah's and Nevada's parks.
31When possible, I would borrow my sister's camera, which is a Sony SLT-A58. This
32camera was great and allowed for some of my best early shots, especially those
33taken in Utah's and Nevada's parks.
3634
3735* My Current Kit
3836
39I've finally come to a point in my life where I have the disposable
40income to invest in a solid photography kit. I played around with the
41idea of a lot of different cameras, different types, new vs used, etc.
42Finally, I settled on the
43[[https://en.wikipedia.org/wiki/Sony_%CE%B17_III][Sony α7 III]]. This
44camera is mirror-less and uses a full-frame image sensor at 24
45megapixels. I don't create large prints, and I am mostly focused on
46preserving memories in high quality for the next 5-10 years with this
47camera, so the specifications here are just perfect for me.
37I've finally come to a point in my life where I have the disposable income to
38invest in a solid photography kit. I played around with the idea of a lot of
39different cameras, different types, new vs used, etc. Finally, I settled on the
40[[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
41megapixels. I don't create large prints, and I am mostly focused on preserving
42memories in high quality for the next 5-10 years with this camera, so the
43specifications here are just perfect for me.
4844
4945For lenses, I decided to buy two lenses that could carry me through most
5046situations:
5147
52- [[https://electronics.sony.com/imaging/lenses/full-frame-e-mount/p/sel2470z][Vario-Tessar
53 T** FE 24-70 mm F4 ZA OSS]]
54- [[https://www.tamron-usa.com/product/lenses/a047.html][Tamron 70-300mm
55 f4.5-6.3 Di III RXD]]
48- [[https://electronics.sony.com/imaging/lenses/full-frame-e-mount/p/sel2470z][Vario-Tessar T** FE 24-70 mm F4 ZA OSS]]
49- [[https://www.tamron-usa.com/product/lenses/a047.html][Tamron 70-300mm f4.5-6.3 Di III RXD]]
5650
57In addition, I grabbed a couple
58[[https://www.promaster.com/Product/6725][HGX Prime 67mm]] protection
59filters for the lenses.
51In addition, I grabbed a couple [[https://www.promaster.com/Product/6725][HGX Prime 67mm]] protection filters for the
52lenses.
6053
61As I delve further into photography and pick up more skills, I will most
62likely go back and grab a lens with a higher f-stop value, such as
63f/1.8. I toyed with the idea of grabbing a 50 mm at =f/1.8=, but decided
64to keep things in a reasonable price range instead.
54As I delve further into photography and pick up more skills, I will most likely
55go back and grab a lens with a higher f-stop value, such as f/1.8. I toyed with
56the idea of grabbing a 50 mm at =f/1.8=, but decided to keep things in a
57reasonable price range instead.
6558
66Finally, I made sure to buy a photography-specific backpack with a rain
67guard, and the zipper on the back panel, to protect the equipment while
68wearing the bag. If you've ever had to haul around a DSLR (or camera of
69similar heft) in a bag that only has a shoulder strap, you'll know the
70pain it can cause. Putting all my equipment in a backpack was an easy
71decision.
59Finally, I made sure to buy a photography-specific backpack with a rain guard,
60and the zipper on the back panel, to protect the equipment while wearing the
61bag. If you've ever had to haul around a DSLR (digital single-lens reflex)
62camera in a bag that only has a shoulder strap, you'll know the pain it can
63cause. Putting all my equipment in a backpack was an easy decision.
content/blog/2021-05-30-changing-git-authors.org +8 −8
@@ -5,12 +5,12 @@
55
66* Changing Git Author/Email Based on Previously Committed Email
77
8Here's the dilemma: You've been committing changes to your git
9repository with an incorrect name or email (or multiple repositories),
10and now you want to fix it. Luckily, there's a semi-reliable way to fix
11that. While I have never experienced issues with this method, some
12people have warned that it can mess with historical hashes and integrity
13of commits, so use this method only if you're okay accepting that risk.
8Here's the dilemma: You've been committing changes to your Git repository with
9an incorrect name or email (or multiple repositories), and now you want to fix
10it. Luckily, there's a semi-reliable way to fix that. While I have never
11experienced issues with this method, some people have warned that it can mess
12with historical hashes and integrity of commits, so use this method only if
13you're okay accepting that risk.
1414
1515Okay, let's create the bash script:
1616
@@ -18,8 +18,8 @@ Okay, let's create the bash script:
1818nano change_git_authors.sh
1919#+end_src
2020
21The following information can be pasted directly into your bash script.
22The only changes you need to make are to the following variables:
21The following information can be pasted directly into your bash script. The only
22changes you need to make are to the following variables:
2323
2424- =OLD_EMAIL=
2525- =CORRECT_NAME=
content/blog/2021-07-15-delete-gitlab-repos.org +19 −22
@@ -5,31 +5,29 @@
55
66* Background
77
8Have you ever used GitLab to host your source code, moved to a different
9host, and wanted to delete everything from your GitLab account? Well,
10this post covers any scenario where you would want to delete all
11repositories from your GitLab account.
8Have you ever used GitLab to host your source code, moved to a different host,
9and wanted to delete everything from your GitLab account? Well, this post covers
10any scenario where you would want to delete all repositories from your GitLab
11account.
1212
13For me, I currently maintain around 30 repositories and don't like to
14manually delete them whenever I switch host. GitHub has a few different
15tools online to delete all repositories for you, but I have not found
16anything similar for GitLab, so I needed an alternative solution.
13For me, I currently maintain around 30 repositories and don't like to manually
14delete them whenever I switch host. GitHub has a few different tools online to
15delete all repositories for you, but I have not found anything similar for
16GitLab, so I needed an alternative solution.
1717
1818* Use a Python Script
1919
2020** Requirements
2121
22Before we look at the script, make sure you know your GitLab username.
23Next, [[https://gitlab.com/-/profile/personal_access_tokens][create an
24authorization token]] so that the Python script can delete your
25repositories. Don't lose this token or else you'll need to create a new
26one.
22Before we look at the script, make sure you know your GitLab username. Next,
23[[https://gitlab.com/-/profile/personal_access_tokens][create an authorization token]] so that the Python script can delete your
24repositories. Don't lose this token or else you'll need to create a new one.
2725
2826** Create the Script
2927
30To run a Python script, you must first create it. Open a terminal and
31enter the following commands in whichever directory you prefer to store
32the script. You can do the same things in a file manager if you prefer.
28To run a Python script, you must first create it. Open a terminal and enter the
29following commands in whichever directory you prefer to store the script. You
30can do the same things in a file manager if you prefer.
3331
3432#+begin_src sh
3533mkdir delete-gitlab
@@ -101,13 +99,12 @@ if __name__ == "__main__":
10199 main()
102100#+end_src
103101
104Now that you have the proper information, replace ={user-id}= with your
105GitLab username and ={auth-token}= with the authorization token you
106created earlier.
102Now that you have the proper information, replace ={user-id}= with your GitLab
103username and ={auth-token}= with the authorization token you created earlier.
107104
108Finally, simply run the script and watch the output. You can also use
109PyCharm Community Edition to edit and run the Python script if you don't
110want to work in a terminal.
105Finally, simply run the script and watch the output. You can also use PyCharm
106Community Edition to edit and run the Python script if you don't want to work in
107a terminal.
111108
112109#+begin_src sh
113110python3 main.py
content/blog/2021-08-25-audit-sampling.org +98 −101
@@ -6,44 +6,40 @@
66* Introduction
77
88For anyone who is familiar with internal auditing, external auditing, or
9consulting, you will understand how tedious audit testing can become
10when you are required to test large swaths of data. When we cannot
11establish an automated means of testing an entire population, we
12generate samples to represent the population of data. This helps ensure
13we can have a small enough data pool to test and that our results still
14represent the population.
15
16However, sampling data within the world of audit still seems to confuse
17quite a lot of people. While some audit-focused tools have introduced
18sampling functionality (e.g. Wdesk), many audit departments and firms
19cannot use software like this due to certain constraints, such as the
20team's budget or knowledge. Here is where this article comes in: we're
21going to use [[https://www.python.org][Python]], a free and open-source
22programming language, to generate random samples from a dataset in order
23to suffice numerous audit situations.
9consulting, you will understand how tedious audit testing can become when you
10are required to test large swaths of data. When we cannot establish an automated
11means of testing an entire population, we generate samples to represent the
12population of data. This helps ensure we can have a small enough data pool to
13test and that our results still represent the population.
14
15However, sampling data within the world of audit still seems to confuse quite a
16lot of people. While some audit-focused tools have introduced sampling
17functionality (e.g. Wdesk), many audit departments and firms cannot use software
18like this due to certain constraints, such as the team's budget or knowledge.
19Here is where this article comes in: we're going to use [[https://www.python.org][Python]], a free and
20open-source programming language, to generate random samples from a dataset in
21order to suffice numerous audit situations.
2422
2523* Audit Requirements for Sampling
2624
27Before we get into the details of how to sample with Python, I want to
28make sure I discuss the different requirements that auditors may have of
29samples used within their projects.
25Before we get into the details of how to sample with Python, I want to make sure
26I discuss the different requirements that auditors may have of samples used
27within their projects.
3028
3129** Randomness
3230
33First, let's discuss randomness. When testing out new technology to help
34assist with audit sampling, you need to understand exactly how your
35samples are being generated. For example, if the underlying function is
36just picking every 57th element from a list, that's not truly random;
37it's a systematic form of sampling. Luckily, since Python is
38open-source, we have access to its codebase. Through this blog post, I
39will be using the [[https://pandas.pydata.org][pandas]] module in order
40to generate the random samples. More specifically, I will be using the
41[[https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sample.html][pandas.DataFrame.sample]]
42function provided by Pandas.
43
44Now that you know what you're using, you can always check out the code
45behind =pandas.DataFrame.sample=. This function does a lot of work, but
46we really only care about the following snippets of code:
31First, let's discuss randomness. When testing out new technology to help assist
32with audit sampling, you need to understand exactly how your samples are being
33generated. For example, if the underlying function is just picking every 57th
34element from a list, that's not truly random; it's a systematic form of
35sampling. Luckily, since Python is open-source, we have access to its codebase.
36Through this blog post, I will be using the [[https://pandas.pydata.org][pandas]] module in order to generate
37the random samples. More specifically, I will be using the
38[[https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.sample.html][pandas.DataFrame.sample]] function provided by Pandas.
39
40Now that you know what you're using, you can always check out the code behind
41=pandas.DataFrame.sample=. This function does a lot of work, but we really only
42care about the following snippets of code:
4743
4844#+begin_src python
4945# Process random_state argument
@@ -59,61 +55,62 @@ result.index = ibase.default_index(len(result))
5955return result
6056#+end_src
6157
62The block of code above shows you that if you assign a =random_state=
63argument when you run the function, that will be used as a seed number
64in the random generation and will allow you to reproduce a sample, given
65that nothing else changes. This is critical to the posterity of audit
66work. After all, how can you say your audit process is adequately
67documented if the next person can't run the code and get the same
68sample? The final piece here on randomness is to look at the
69[[https://docs.python.org/3/library/random.html#random.choice][choice]]
70function used above. This is the crux of the generation and can also be
71examined for more detailed analysis on its reliability. As far as
72auditing goes, we will trust that these functions are mathematically
73random.
58The block of code above shows you that if you assign a =random_state= argument
59when you run the function, that will be used as a seed number in the random
60generation and will allow you to reproduce a sample, given that nothing else
61changes. This is critical to the posterity of audit work. After all, how can you
62say your audit process is adequately documented if the next person can't run the
63code and get the same sample? The final piece here on randomness is to look at
64the [[https://docs.python.org/3/library/random.html#random.choice][choice]] function used above. This is the crux of the generation and can also
65be examined for more detailed analysis on its reliability. As far as auditing
66goes, we will trust that these functions are mathematically random.
7467
7568** Sample Sizes
7669
77As mentioned in the intro, sampling is only an effective method of
78auditing when it truly represents the entire population. While some
79audit departments or firms may consider certain judgmental sample sizes
80to be adequate, you may need to rely on statistically-significant
81confidence levels of sample testing at certain points. I will
82demonstrate both here. For statistically-significant confidence levels,
83most people will assume a 90% - 99% confidence level. In order to
84actually calculate the correct sample size, it is best to use
85statistical tools due to the tedious math work required. For example,
86for a population of 1000, and a 90% confidence level that no more than
875% of the items are nonconforming, you would sample 45 items.
88
89However, in my personal experience, many audit departments and firms do
90not use statistical sampling. Most people use a predetermined, often
91proprietary, table that will instruct auditors which sample sizes to
92choose. This allows for uniform testing and reduces overall workload.
93See the table below for a common implementation of sample sizes:
94
95Control Frequency Sample Size - High Risk Sample Size - Low Risk
96
97--------------
98
99More Than Daily 40 25 Daily 40 25 Weekly 12 5 Monthly 5 3 Quarterly 2 2
100Semi-Annually 1 1 Annually 1 1 Ad-hoc 1 1
70As mentioned in the intro, sampling is only an effective method of auditing when
71it truly represents the entire population. While some audit departments or firms
72pmay consider certain judgmental sample sizes to be adequate, you may need to
73rely on statistically-significant confidence levels of sample testing at certain
74
75points. I will demonstrate both here. For statistically-significant confidence
76levels, most people will assume a 90% - 99% confidence level. In order to
77actually calculate the correct sample size, it is best to use statistical tools
78due to the tedious math work required. For example, for a population of 1000,
79and a 90% confidence level that no more than 5% of the items are nonconforming,
80you would sample 45 items.
81
82However, in my personal experience, many audit departments and firms do not use
83statistical sampling. Most people use a predetermined, often proprietary, table
84that will instruct auditors which sample sizes to choose. This allows for
85uniform testing and reduces overall workload. See the table below for a common
86implementation of sample sizes:
87
88| Control Frequency | Sample Size (High Risk) | Sample Size (Low Risk) |
89|-------------------+-------------------------+------------------------|
90| More Than Daily | 40 | 25 |
91| Daily | 40 | 25 |
92| Weekly | 12 | 5 |
93| Monthly | 5 | 3 |
94| Quarterly | 2 | 2 |
95| Semi-Annually | 1 | 1 |
96| Annually | 1 | 1 |
97| Ad-hoc | 1 | 1 |
10198
10299*** Sampling with Python & Pandas
103100
104In this section, I am going to cover a few basic audit situations that
105require sampling. While some situations may require more effort, the
106syntax, organization, and intellect used remain largely the same. If
107you've never used Python before, note that lines starting with a '=#='
108symbol are called comments, and they will be skipped by Python. I highly
109recommend taking a quick tutorial online to understand the basics of
110Python if any of the code below is confusing to you.
101In this section, I am going to cover a few basic audit situations that require
102sampling. While some situations may require more effort, the syntax,
103organization, and intellect used remain largely the same. If you've never used
104Python before, note that lines starting with a '=#=' symbol are called comments,
105and they will be skipped by Python. I highly recommend taking a quick tutorial
106online to understand the basics of Python if any of the code below is confusing
107to you.
111108
112109** Simple Random Sample
113110
114First, let's look at a simple, random sample. The code block below will
115import the =pandas= module, load a data file, sample the data, and
116export the sample to a file.
111First, let's look at a simple, random sample. The code block below will import
112the =pandas= module, load a data file, sample the data, and export the sample to
113a file.
117114
118115#+begin_src python
119116# Import the Pandas module
@@ -137,8 +134,8 @@ sample.to_excel(file_output)
137134
138135** Simple Random Sample: Using Multiple Input Files
139136
140Now that we've created a simple sample, let's create a sample from
141multiple files.
137Now that we've created a simple sample, let's create a sample from multiple
138files.
142139
143140#+begin_src python
144141# Import the Pandas module
@@ -171,9 +168,9 @@ sample.to_excel(file_output)
171168
172169** Stratified Random Sample
173170
174Well, what if you need to sample distinct parts of a single file? For
175example, let's write some code to separate our data by "Region" and
176sample those regions independently.
171Well, what if you need to sample distinct parts of a single file? For example,
172let's write some code to separate our data by "Region" and sample those regions
173independently.
177174
178175#+begin_src python
179176# Import the Pandas module
@@ -205,10 +202,10 @@ sample.to_excel(file_output)
205202
206203** Stratified Systematic Sample
207204
208This next example is quite useful if you need audit coverage over a
209certain time period. This code will generate samples for each month in
210the data and combine them all together at the end. Obviously, this code
211can be modified to stratify by something other than months, if needed.
205This next example is quite useful if you need audit coverage over a certain time
206period. This code will generate samples for each month in the data and combine
207them all together at the end. Obviously, this code can be modified to stratify
208by something other than months, if needed.
212209
213210#+begin_src python
214211# Import the Pandas module
@@ -254,17 +251,17 @@ sample.to_excel(file_output)
254251
255252*** Documenting the Results
256253
257Once you've generated a proper sample, there are a few things left to do
258in order to properly ensure your process is reproducible.
259
2601. Document the sample. Make sure the resulting file is readable and
261 includes the documentation listed in the next bullet.
2622. Include documentation around the data source, extraction techniques,
263 any modifications made to the data, and be sure to include a copy of
264 the script itself.
2653. Whenever possible, perform a completeness and accuracy test to ensure
266 your sample is coming from a complete and accurate population. To
267 ensure completeness, compare the record count from the data source to
268 the record count loaded into Python. To ensure accuracy, test a small
269 sample against the source data (e.g., test 5 sales against the
270 database to see if the details are accurate).
254Once you've generated a proper sample, there are a few things left to do in
255order to properly ensure your process is reproducible.
256
2571. Document the sample. Make sure the resulting file is readable and includes
258 the documentation listed in the next bullet.
2592. Include documentation around the data source, extraction techniques, any
260 modifications made to the data, and be sure to include a copy of the script
261 itself.
2623. Whenever possible, perform a completeness and accuracy test to ensure your
263 sample is coming from a complete and accurate population. To ensure
264 completeness, compare the record count from the data source to the record
265 count loaded into Python. To ensure accuracy, test a small sample against the
266 source data (e.g., test 5 sales against the database to see if the details
267 are accurate).
content/blog/2021-10-09-apache-redirect.org +14 −17
@@ -5,15 +5,13 @@
55
66* The Problem
77
8After recently switching static site generators (SSG), my blog URLs
9changed with no option to preserve the classic =.html= extension at the
10end of my blog post URLs.
8After recently switching static site generators (SSG), my blog URLs (uniform
9resource locators) changed with no option to preserve the classic =.html=
10extension at the end of my blog post URLs.
1111
12I really disliked using my old SSG ([[https://jekyllrb.com][Jekyll]])
13and prefer my new tool ([[https://www.getzola.org][Zola]]) much more, so
14I was determined to figure out a way to get the proper redirect set up
15so that people who find my posts online aren't constantly met by 404
16errors.
12I really disliked using my old SSG ([[https://jekyllrb.com][Jekyll]]) and prefer my new tool ([[https://www.getzola.org][Zola]]) much
13more, so I was determined to figure out a way to get the proper redirect set up
14so that people who find my posts online aren't constantly met by 404 errors.
1715
1816* The Solution
1917
@@ -21,11 +19,10 @@ To solve this problem, I really needed to solve two pieces:
2119
22201. Redirect all blog post URL requests from =/blog/some-post.html= to
2321 =/blog/some-post/=.
242. Ensure that no other =.html= files are redirected, such as
25 =index.html=.
222. Ensure that no other =.html= files are redirected, such as =index.html=.
2623
27After /a lot/ of tweaking and testing, I believe I have finally found
28the solution. The solution is shown below.
24After /a lot/ of tweaking and testing, I believe I have finally found the
25solution. The solution is shown below.
2926
3027#+begin_src conf
3128RewriteEngine On
@@ -38,8 +35,8 @@ following:
3835
39361. Turn on the RewriteEngine so that we can modify URLs.
40372. Ignore any =index.html= files from the rule we are about to specify.
413. Find any =.html= files within the website directory and redirect it
42 to exclude the file extension.
434. The final piece is adding the trailing slash (=/=) at the end of the
44 URL - you'll notice that I don't have an Apache rule for that since
45 Apache handles that automatically.
383. Find any =.html= files within the website directory and redirect it to
39 exclude the file extension.
404. The final piece is adding the trailing slash (=/=) at the end of the URL -
41 you'll notice that I don't have an Apache rule for that since Apache handles
42 that automatically.
content/blog/2021-12-04-cisa.org +142 −156
@@ -6,200 +6,186 @@
66* What is the CISA?
77
88For those of you lucky enough not to be knee-deep in the world of IT/IS
9Auditing, [[https://www.isaca.org/credentialing/cisa][CISA]] stands for
10Certified Information Systems Auditor. This certification and exam are
11part of ISACA's suite of certifications. As I often explain it to people
12like my family, it basically means you're employed to use your knowledge
13of information systems, regulations, common threats, risks, etc. in
14order to assess an organization's current control of their risk. If a
15risk isn't controlled (and the company doesn't want to accept the risk),
16an IS auditor will suggest implementing a control to address that risk.
17
18Now, the CISA certification itself is, in my opinion, the main
19certification for this career. While certifications such as the CPA or
20CISSP are beneficial, nothing matches the power of the CISA for an IS
21auditor when it comes to getting hired, getting a raise/bonus, or
22earning respect in the field.
23
24However, to be honest, I am a skeptic of most certifications. I
25understand the value they hold in terms of how much you need to commit
26to studying or learning on the job, as well as the market value for
27certifications such as the CISA. But I also have known some very
28+incompetent+ /less than stellar/ auditors who have CPAs, CISAs, CIAs,
29etc.
30
31The same goes for most industries: if a person is good at studying, they
32can earn the certification. However, that knowledge means nothing unless
33you're actually able to use it in real life and perform as expected of a
34certification holder. The challenge comes when people are hired or
35connected strictly because of their certifications or resume; you need
36to see a person work before you can assume them having a CISA means
37they're better than someone without the CISA.
38
39Okay, rant over. Certifications are generally accepted as a measuring
40stick of commitment and quality of an employee, so I am accepting it
41too.
9Auditing, the [[https://www.isaca.org/credentialing/cisa][CISA]] (Certified Information Systems Auditor) certification and
10exam are part of ISACA's suite of certifications. As I often explain it to
11people like my family, it basically means you're employed to use your knowledge
12of information systems, regulations, common threats, risks, etc. in order to
13assess an organization's current control of their risk. If a risk isn't
14controlled (and the company doesn't want to accept the risk), an IS auditor will
15suggest implementing a control to address that risk.
16
17Now, the CISA certification itself is, in my opinion, the main certification for
18this career. While certifications such as the CPA (Certified Public Accountant)
19or CISSP (Certified Information Systems Security Professional) are beneficial,
20nothing matches the power of the CISA for an information systems (IS) auditor
21when it comes to getting hired, getting a raise/bonus, or earning respect in the
22field.
23
24However, to be honest, I am a skeptic of most certifications. I understand the
25value they hold in terms of how much you need to commit to studying or learning
26on the job, as well as the market value for certifications such as the CISA. But
27I also have known some very +incompetent+ /less than stellar/ auditors who have
28relevant certifications.
29
30The same goes for most industries: if a person is good at studying, they can
31earn the certification. However, that knowledge means nothing unless you're
32actually able to use it in real life and perform as expected of a certification
33holder. The challenge comes when people are hired or connected strictly because
34of their certifications or resume; you need to see a person work before you can
35assume them having a CISA means they're better than someone without the CISA.
36
37Okay, rant over. Certifications are generally accepted as a measuring stick of
38commitment and quality of an employee, so I am accepting it too.
4239
4340* Exam Content
4441
45The CISA is broken down into five sections, each weighted with a
46percentage of test questions that may appear.
42The CISA is broken down into five sections, each weighted with a percentage of
43test questions that may appear.
4744
48Since the exam contains 150 questions, here's how those sections break
49down:
45Since the exam contains 150 questions, here's how those sections break down:
5046
51| Exam Section | Percentage of Exam | Questions |
47| Exam Section | Percentage of Exam | Questions |
5248|---------------+--------------------+-----------|
53| 1 | 21% | 32 |
54| 2 | 17% | 26 |
55| 3 | 12% | 18 |
56| 4 | 23% | 34 |
57| 5 | 27% | 40 |
58| *Grand Total* | *100%* | *150* |
49| 1 | 21% | 32 |
50| 2 | 17% | 26 |
51| 3 | 12% | 18 |
52| 4 | 23% | 34 |
53| 5 | 27% | 40 |
54| *Grand Total* | *100%* | *150* |
5955
6056* My Studying Habits
6157
62This part is a little hard for me to break down into specific detail due
63to the craziness of the last year. While I officially purchased my
64studying materials in December 2020 and opened them to "start studying"
65in January 2021, I really wasn't able to study much due to the demands
66of my job and personal life.
58This part is a little hard for me to break down into specific detail due to the
59craziness of the last year. While I officially purchased my studying materials
60in December 2020 and opened them to "start studying" in January 2021, I really
61wasn't able to study much due to the demands of my job and personal life.
6762
6863Let me approach this from a few different viewpoints.
6964
7065** Study Materials
7166
72Let's start by discussing the study materials I purchased. I'll be
73referring to #1 as the CRM and #2 as the QAE.
74
751. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCbEAK][CISA
76 Review Manual, 27th Edition | Print]]
772. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCcEAK][CISA
78 Review Questions, Answers & Explanations Manual, 12th Edition |
79 Print]]
80
81The CRM is an excellent source of information and could honestly be used
82as a reference for most IS auditors as a learning reference during their
83daily audit responsibilities. However, it is *full* of information and
84can be overloading if you're not good at filtering out useless
85information while studying.
86
87The QAE is the real star of the show here. This book contains 1000
88questions, separated by exam section, and a practice exam. My only
89complaint about the QAE is that each question is immediately followed
90with the correct answer and explanations below it, which means I had to
91use something to constantly cover the answers while I was studying.
92
93I didn't use the online database version of the QAE, but I've heard that
94it's easier to use than the printed book. However, it is more expensive
95($299 database vs $129 book) which might be important if you're paying
96for materials yourself.
97
98In terms of question difficulty, I felt that the QAE was a good
99representation of the actual exam. I've seen a lot of people online say
100it wasn't accurate to the exam or that it was much easier/harder, but I
101disagree with all of those. The exam was fairly similar to the QAE, just
102focusing on whichever topics they chose for my version of the exam.
103
104If you understand the concepts, skim the CRM (and read in-depth on
105topics you struggle with), and use the QAE to continue practicing
106exam-like questions, you should be fine. I didn't use any online
107courses, videos, etc. - the ISACA materials are more than enough.
67Let's start by discussing the study materials I purchased. I'll be referring to
68#1 as the CRM (CISA Review Manual) and #2 as the QAE (Questions, Answers &
69Explanations).
70
711. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCbEAK][CISA Review Manual, 27th Edition | Print]]
722. [[https://store.isaca.org/s/store#/store/browse/detail/a2S4w000004KoCcEAK][CISA Review Questions, Answers & Explanations Manual, 12th Edition | Print]]
73
74The CRM is an excellent source of information and could honestly be used as a
75reference for most IS auditors as a learning reference during their daily audit
76responsibilities. However, it is *full* of information and can be overloading if
77you're not good at filtering out useless information while studying.
78
79The QAE is the real star of the show here. This book contains 1000 questions,
80separated by exam section, and a practice exam. My only complaint about the QAE
81is that each question is immediately followed with the correct answer and
82explanations below it, which means I had to use something to constantly cover
83the answers while I was studying.
84
85I didn't use the online database version of the QAE, but I've heard that it's
86easier to use than the printed book. However, it is more expensive ($299
87database vs $129 book) which might be important if you're paying for materials
88yourself.
89
90In terms of question difficulty, I felt that the QAE was a good representation
91of the actual exam. I've seen a lot of people online say it wasn't accurate to
92the exam or that it was much easier/harder, but I disagree with all of those.
93The exam was fairly similar to the QAE, just focusing on whichever topics they
94chose for my version of the exam.
95
96If you understand the concepts, skim the CRM (and read in-depth on topics you
97struggle with), and use the QAE to continue practicing exam-like questions, you
98should be fine. I didn't use any online courses, videos, etc. - the ISACA
99materials are more than enough.
108100
109101** Studying Process
110102
111While I was able to briefly read through sections 1 and 2 in early 2021,
112I had to stop and take a break from February/March to September. I
113switched jobs in September, which allowed me a lot more free time to
114study.
103While I was able to briefly read through sections 1 and 2 in early 2021, I had
104to stop and take a break from February/March to September. I switched jobs in
105September, which allowed me a lot more free time to study.
115106
116In September, I studied sections 3-5, took notes, and did a quick review
117of the section topics. Once I felt comfortable with my notes, I took a
118practice exam from the QAE manual and scored 70% (105/150).
107In September, I studied sections 3-5, took notes, and did a quick review of the
108section topics. Once I felt comfortable with my notes, I took a practice exam
109from the QAE manual and scored 70% (105/150).
119110
120111Here's a breakdown of my initial practice exam:
121112
122| Exam Section | Incorrect | Correct | Grand Total | Percent |
113| Exam Section | Incorrect | Correct | Grand Total | Percent |
123114|---------------+-----------+---------+-------------+---------|
124| 1 | 8 | 25 | 33 | 76% |
125| 2 | 5 | 20 | 25 | 80% |
126| 3 | 6 | 12 | 18 | 67% |
127| 4 | 10 | 23 | 33 | 70% |
128| 5 | 16 | 25 | 41 | 61% |
129| *Grand Total* | *45* | *105* | *150* | *70%* |
115| 1 | 8 | 25 | 33 | 76% |
116| 2 | 5 | 20 | 25 | 80% |
117| 3 | 6 | 12 | 18 | 67% |
118| 4 | 10 | 23 | 33 | 70% |
119| 5 | 16 | 25 | 41 | 61% |
120| *Grand Total* | *45* | *105* | *150* | *70%* |
130121
131122As I expected, my toughest sections were related to project management,
132123development, implementation, and security.
133124
134This just leaves October and November. For these months, I tried to
135practice every few days, doing 10 questions for each section, until the
136exam. This came out to 13 practice sessions, ~140 questions per section,
137and ~700 questions total.
125This just leaves October and November. For these months, I tried to practice
126every few days, doing 10 questions for each section, until the exam. This came
127out to 13 practice sessions, ~140 questions per section, and ~700 questions
128total.
138129
139While some practice sessions were worse and some were better, the final
140results were similar to my practice exam results. As you can see below,
141my averages were slightly worse than my practice exam. However, I got in
142over 700 questions of practice and, most importantly, /I read through
143the explanations every time I answered incorrectly and learned from my
144mistakes/.
130While some practice sessions were worse and some were better, the final results
131were similar to my practice exam results. As you can see below, my averages were
132slightly worse than my practice exam. However, I got in over 700 questions of
133practice and, most importantly, /I read through the explanations every time I
134answered incorrectly and learned from my mistakes/.
145135
146| Exam Section | Incorrect | Correct | Grand Total | Percent |
136| Exam Section | Incorrect | Correct | Grand Total | Percent |
147137|---------------+-----------+---------+-------------+---------|
148| 1 | 33 | 108 | 141 | 77% |
149| 2 | 33 | 109 | 142 | 77% |
150| 3 | 55 | 89 | 144 | 62% |
151| 4 | 52 | 88 | 140 | 63% |
152| 5 | 55 | 85 | 140 | 61% |
153| *Grand Total* | *228* | *479* | *707* | *68%* |
138| 1 | 33 | 108 | 141 | 77% |
139| 2 | 33 | 109 | 142 | 77% |
140| 3 | 55 | 89 | 144 | 62% |
141| 4 | 52 | 88 | 140 | 63% |
142| 5 | 55 | 85 | 140 | 61% |
143| *Grand Total* | *228* | *479* | *707* | *68%* |
154144
155145* Results
156146
157Now, how do the practice scores reflect my actual results? After all,
158it's hard to tell how good a practice regimen is unless you see how it
159turns out.
147Now, how do the practice scores reflect my actual results? After all, it's hard
148to tell how good a practice regimen is unless you see how it turns out.
160149
161150| Exam Section | Section Name | Score |
162151|--------------+------------------------------------------------------------------+-------|
163| 1 | Information Systems Auditing Process | 678 |
164| 2 | Governance and Management of IT | 590 |
165| 3 | Information Systems Acquisition, Development, and Implementation | 721 |
166| 4 | Information Systems Operations and Business Resilience | 643 |
167| 5 | Protection of Information Assets | 511 |
168
169Now, in order to pass the CISA, you need at least 450 on a sliding scale
170of 200-800. Personally, I really have no clue what an average CISA score
171is. After a /very/ brief look online, I can see that the high end is
172usually in the low 700s. In addition, only about 50-60% of people pass
173the exam.
174
175Given this information, I feel great about my scores. 616 may not be
176phenomenal, and I wish I had done better on sections 2 & 5, but my
177practicing seems to have worked very well overall.
178
179However, the practice results do not conform to the actual results.
180Section 2 was one of my highest practice sections and was my
181second-lowest score in the exam. Conversely, section 3 was my
182second-lowest practice section and turned out to be my highest actual
183score!
184
185After reflecting, it is obvious that if you have any background on the
186CISA topics at all, the most important part of studying is doing
187practice questions. You really need to understand how to read the
188questions critically and pick the best answer.
152| 1 | Information Systems Auditing Process | 678 |
153| 2 | Governance and Management of IT | 590 |
154| 3 | Information Systems Acquisition, Development, and Implementation | 721 |
155| 4 | Information Systems Operations and Business Resilience | 643 |
156| 5 | Protection of Information Assets | 511 |
157
158Now, in order to pass the CISA, you need at least 450 on a sliding scale of
159200-800. Personally, I really have no clue what an average CISA score is. After
160a /very/ brief look online, I can see that the high end is usually in the low
161700s. In addition, only about 50-60% of people pass the exam.
162
163Given this information, I feel great about my scores. 616 may not be phenomenal,
164and I wish I had done better on sections 2 & 5, but my practicing seems to have
165worked very well overall.
166
167However, the practice results do not conform to the actual results. Section 2
168was one of my highest practice sections and was my second-lowest score in the
169exam. Conversely, section 3 was my second-lowest practice section and turned out
170to be my highest actual score!
171
172After reflecting, it is obvious that if you have any background on the CISA
173topics at all, the most important part of studying is doing practice questions.
174You really need to understand how to read the questions critically and pick the
175best answer.
189176
190177* Looking Forward
191178
192I am extremely happy that I was finally able to pass the CISA. Looking
193to the future, I'm not sure what's next in terms of professional
194learning. My current company offers internal learning courses, so I will
195most likely focus on that if I need to gain more knowledge in certain
196areas.
197
198To be fair, even if you pass the CISA, it's hard to become an expert on
199any specific topic found within. My career may take me in a different
200direction, and I might need to focus more on security or networking
201certifications (or possibly building a better analysis/visualization
202portfolio if I want to go into data analysis/science).
179I am extremely happy that I was finally able to pass the CISA. Looking to the
180future, I'm not sure what's next in terms of professional learning. My current
181company offers internal learning courses, so I will most likely focus on that if
182I need to gain more knowledge in certain areas.
183
184To be fair, even if you pass the CISA, it's hard to become an expert on any
185specific topic found within. My career may take me in a different direction, and
186I might need to focus more on security or networking certifications (or possibly
187building a better analysis/visualization portfolio if I want to go into data
188analysis/science).
203189
204190All I know is that I am content at the moment and extremely proud of my
205191accomplishment.